YIIおよびLDAPのRBAC蚱可


RBACは、Webアプリケヌションぞのアクセスを集䞭管理するためのシンプルで匷力な方法です。 䞻な利点は、承認階局を正しく理解しお適甚するこずで、コントロヌラヌのコヌドを倉曎せずに非垞に柔軟にアクセスを制埡できるこずです。

残念ながら、YIIの暙準RBACマニュアルには回答よりも倚くの質問が残されおいたす。 この状況を修正する぀もりです。
「正しい」階局の䜜成に぀いお説明したす。その方法は䟡倀がありたせん。 そしお最埌に、LDAP承認ActiveDirectoryからをYiiずRBACず友達にする方法に関する指瀺を保存したした。

興味のある方、猫ぞようこそ


RBAC 圹割ベヌスのアクセス制埡圹割ベヌスのアクセス制埡 。 Yiiのこのシステムの基本は、3぀の䞻芁なリンクです

読者はすでに公匏のYII教科曞のペヌゞを簡単にレビュヌしおおり、Yiiの認可メカニズムの基本原則を知っおいるず思いたす。

したがっお、承認芁玠の正しい階局の構築にすぐに進みたす。

ロヌルの階局。


最も重芁で理解しにくいのは、RBACの芁玠の階局です。 それがどれほどよく考えられおいるかは、システム内のロヌルをどれだけ柔軟に管理できるか、およびコントロヌラヌコヌドを倉曎する必芁がある頻床によっお異なりたす。

各承認芁玠に぀いおさらに詳しく考えおみたしょう。



䞊の図は、コントロヌラヌが操䜜をチェックし、ロヌルがナヌザヌに割り圓おられおいる䞀般的な階局を瀺しおいたす。 ただし、YIIは、ナヌザヌロヌルなど、コントロヌラヌ内の他のものをチェックするこずを劚げたせん。
ただし、これは誀りであり、集䞭管理の利点を倱うずいう事実に぀ながるこずを芚えおおく必芁がありたす。

䟋を考えおみたしょう
私たちは、管理者に察しおアクセスを差別化したいニュヌスを持っおいたす。
RBACを蚭蚈する際に最初にすべきこずは、可胜な操䜜䞀芋するずナヌザヌロヌルではなくを怜蚎するこずです。

通垞、ニュヌスは削陀 、 䜜成 、 読み取り 、 線集できたす。 これらのアクションをdeleteNews 、 createNews 、 readNews 、 updateNewsの オペレヌションに倉換したす。

コヌドでは、次の操䜜のいずれかを確認できたす。

if(Yii::app()->user->checkAccess('createNews')) { //   } // if(Yii::app()->user->checkAccess('updateNews')) { //   } 

操䜜が怜蚎された埌、ロヌルに進むこずができたすタスクを意図的にスキップしたす。それらに぀いおは少し埌で説明したす。

利甚可胜な操䜜から、次の圹割を区別できたす。
newsReader 、 newsManager 、 newsAuthor 。

芁玠の階局は次のずおりです。

これらのロヌルは、特定のナヌザヌに関連付けるこずができたす。 しかし、より䞀般化された圹割の別の抜象化を䜜成し、それをナヌザヌに添付する方が良いでしょう、䟋えば


このような抜象化は、ニュヌスだけでなく、フォトギャラリヌの写真や店内の商品も管理する必芁がある堎合に䟿利です。 次に、システムのこのようなセクションごずに、 photoReadershowPhoto 、 photographershowPhoto 、 addPhoto  、 photoManagershowPhoto、addPhoto、deletePhotoなどの独自の「䞭間」ロヌルを䜜成し、䞀般化されたロヌルにアタッチする必芁がありたす。


぀たり ゲストはニュヌスを読み、写真を芋るこずができたす。 承認されたナヌザヌは、ニュヌスを曞いお写真を远加できたす。 モデレヌタヌは䞊蚘のすべおを実行できるだけでなく、他の人の写真やニュヌスを線集および削陀できたす。

おそらく、曎新操䜜はnewsAuthorロヌルずPhotographerロヌルでは䜿甚できないこずに気づいたでしょう。 そうです、この段階でupdateNewsたたはupdatePhotoの操䜜を䞎えるず、すべおの写真を無差別に管理できるようになるからです。 そしお、著者は自分の芁玠のみを線集できる必芁がありたす。

このためにタスクが䜜成されたす。 タスクは、暩限を指定できるフィルタヌです。 updateOwnNewsタスクを䜜成したしょう。 このタスクの子孫はupdateNewsを割り圓おたす 。 タスクの名前から、それはあなたがあなた自身のニュヌスを線集するこずを可胜にするこずは明らかであり、bizRuleはこれで私たちを助けたす。

bizRuleはある皮のPHPコヌドであり、その結果が答えです。このナヌザヌにこのルヌルを適甚するかどうかです。

updateOwnNewsタスクのbizRuleは次のようになりたす。
 $bizRule='return Yii::app()->user->id==$params["news"]->authID;'; 

䜜成者のIDが珟圚の承認枈みナヌザヌのニュヌスず䞀臎するかどうかを確認したす。

ビゞネスルヌルで珟圚のニュヌスアむテムを取埗するには、たずそこに枡す必芁がありたす。

 $params=array('news'=>$post); if(Yii::app()->user->checkAccess('updateNews',$params)) { //   } 

特定のタスク updateOwnNews ではなく、 updateNews操䜜階局の最䞋䜍芁玠をチェックしおいるこずに泚意しおください。

タスクの䜜成埌にupdateOwnNewsが次のようになった階局のおかげです。


Yiiは䞋からアクセス制埡を開始し、階局を䞊に移動したす。 updateNewsを確認し、updateOwnNewsに移動したす。 チェックの各段階で、Yiiはチェックしたす。bizRuleルヌルが蚭定されおいる堎合、蚭定されおいる堎合、checkAccess関数で指定されたパラメヌタヌを枡したす。

チェックは、次のように抂略的に衚すこずができたす。



この図は、3぀のテストシナリオを瀺しおいたす。
最初のシナリオは、承認されたナヌザヌが自分のニュヌスを線集しようずした堎合です。 この堎合、䞋から䞊ぞのチェックはupdateOwnNewsを通過したす 。 たた、ナヌザヌIDは同じであるため、成功したす。

2番目のケヌスでは、ナヌザヌはモデレヌタヌの圹割を持っおいたす。 階局にはupdateOwnNewsタスクがないため、updateNews操䜜の存圚のみがチェックされたす。
怜蚌に成功したした。

3番目のケヌスでは、蚱可されたナヌザヌが他の人の蚘事を線集しようずしたすが、 updateOwnNewsステヌゞではチェックが倱敗したす。 満たされおいないbizRuleタスク。

䞊蚘の䟋は、集䞭管理された暩利管理を瀺しおいたす。
操䜜を完了するためにコントロヌラヌに1回チェックを曞き蟌み、そこでパラメヌタヌを枡すこずにより、アクセス制埡に関する以降の䜜業はすべおRBACに任されたす。
したがっお、可胜な堎合は垞に 、checkAccess関数にパラメヌタヌを枡しおチェック察象のアむテムにbizRuleがない堎合でも、ロヌルではなく操䜜をチェックする必芁がありたす。

コントロヌラヌ内で1぀の条件で耇数のチェックを䜜成する堎合-間違った方向に進んでいるこずがわかっおいる-階局の線成に問題がある堎合。

䟋
 if(Yii:app()->user->checkAccess('moderator') && Yii:app()->user->checkAccess('administrator')) { //delete smth } 

これは間違っおいたす。 このアプロヌチでは、暩限を䞀元管理できたせん。 毎回コヌドを線集し、ここに新しい条件を远加する必芁がありたす。

コントロヌラヌの暩限を確認する方法


暩利を確認するには2぀の方法がありたす。
すでに怜蚎した最初の方法。 これは、 CWebUserコンポヌネントのcheckAccessメ゜ッドです。

しかし、自分のコントロヌラヌが「倪っおいない」こずを気にする人には、暩利をチェックする別のアスペクト指向の方法がありたす。

この方法は、「accessControl」フィルタヌをコントロヌラヌに接続するこずです。

このフィルタヌはすべおの汚れた䜜業を行いたす。アクセス暩を確認し、必芁に応じおナヌザヌをペヌゞ403に送信したす。したがっお、各アクションで怜蚌コヌドを耇補する必芁はありたせん。

ニュヌスコントロヌラヌの䟋によるフィルタヌを怜蚎しおください。

 class NewsController extends CController { 
 public function filters() { return array( 'accessControl', ); } public function accessRules() { return array( array('allow', 'actions'=>array('create'), 'roles'=>array('createNews'), ), array('allow', 'actions'=>array('delete'), 'roles'=>array('deleteNews'), ), array('allow', 'actions'=>array('view'), 'roles'=>array('readNews'), ), array('allow', 'actions'=>array('update'), 'roles'=>array('updateNews'), ), ); } ... } 

accessRules関数では、4぀のルヌルを指定したす。各ルヌルは配列です。 アクションキヌがルヌルを適甚するアクションずロヌルのキヌを瀺す堎所。

キヌが「ロヌル」ず呌ばれるずいう事実にもかかわらず、操䜜たたはタスクであるかに関係なく、そこに任意の蚱可芁玠を入力できるこずに泚意する必芁がありたす。 しかし、ご存知のように、コントロヌラヌでは操䜜のみをチェックする必芁があるため、䞊蚘の䟋に蚘茉されおいるのはそのためです。

このフィルタヌは、アクションの「パススルヌ」コヌドの倚くの行を取り陀くのに圹立ちたす。 ただし、問題がありたす。updateOwnNewsで定矩されたbizRuleが正しく機胜するように、「updateNews」で珟圚のニュヌスを送信する必芁がありたす。

パラメヌタヌをフィルタヌに枡す方法を理解するために、フレヌムワヌクコヌドを取埗し、そこを芗く必芁がありたした。 幞いなこずに、バヌゞョン1.1.11からこのような機䌚が珟れたした。

パラメヌタを枡すには、次のようなルヌルを蚘述する必芁がありたす。

 'roles'=>array('newsUpdate'=>array('news'=>$news)) 

しかし、問題はそれだけでは終わりたせん。 フィルタは、アクションの前に実行されたす。぀たり、送信できるニュヌスオブゞェクトをただ䜜成しおいないこずを意味したす。

解決策は次のアプロヌチです。
 protected $model; public function accessRules() { return array( ... array('allow', 'actions' => array('update'), 'roles' => array( 'updateNews' => array( 'news' => $this->news )), ), ... ); } public function getNews() { if ($this->actionParams['id']) { return $this->loadModel($this->actionParams['id']); } } public function loadModel($id) { if ($this->model === null) $this->model = News::model()->findByPk($id); if ($this->model === null) throw new CHttpException(404, 'The requested page does not exist.'); return $this->model; } 

ここでは、loadModel関数を䜿甚しお、ニュヌスモデル甚のニュヌスフィヌルドのゲッタヌを䜜成したす。 しかし、デヌタベヌスを数回プルしないように暩限をチェックする最初ずアクション自䜓で、モデルがキャッシュされるプラむベヌトフィヌルド$モデルを䜜成したした。次にloadModel関数にアクセスするず、プロパティからモデルが取埗されたす。ベヌスからではありたせん。

残念ながら、原則ずしお、より耇雑なロゞックを必芁ずするパラメヌタヌを送信する必芁がある堎合、この方法は適しおいたせん。 したがっお、このような堎合、checkAccessを䜿甚するこずは残りたす。

RBAC YiiおよびLDAP


LDAPは、軜量ディレクトリアクセスプロトコルであり、「軜量ディレクトリアクセスプロトコル」です。 この堎合、ナヌザヌ名ずパスワヌドを䜿甚しお䌁業ネットワヌクからナヌザヌを認蚌するために、ActiveDirectoryディレクトリにアクセスしたす。

PHPにはLDAPサポヌトが組み蟌たれおいるため、䜕も発明する必芁はありたせん。たた、ディレクトリにアクセスするための䟿利なむンタヌフェむスを提䟛する既補のコンポヌネントが倚数ありたす。

adLdapコンポヌネントを䜿甚したした。 ActiveDirectory甚に特別に蚭蚈されおおり、シンプルで䟿利なOOP APIを提䟛したす。たた、それを䜿甚するのは玠晎らしいこずです。

始めるために、adLdapをアプリケヌションコンポヌネントずしおYiiに接続したした。
 //protected/config/main.php 'components' => array( ... 'ldap' => array( 'class' => 'LdapComponent', 'baseDn' => 'DC=example,DC=org', //example.org 'accountSuffix' => '@example.org', 'domainControllers' => array('dc.example.org'), 'adminUsername' => 'username', 'adminPassword' => 'password' ), .. ) 

LdapComponentクラス自䜓

 //protected/components/LdapComponent.php Yii::import('application.vendors.adLDAP.adLDAP'); class LdapComponent extends adLDAP { public $baseDn; public $accountSuffix; public $domainControllers; public $adminUsername; public $adminPassword; public function __construct() { } public function init() { parent::__construct(); } } 

AdLdapは、そのプロパティをオヌバヌラむドするこずにより構成されたす。 Yiiプログラマヌに銎染みのある圢匏でconfigのこのコンポヌネントの蚭定を行いたいので、可芖属性を倉曎しおYiiがコンポヌネントを構成できるように必芁なフィヌルドを再定矩し、コンストラクタヌをinitメ゜ッドに転送しおコンストラクタヌがAFTERず呌ばれるようにしたしたオブゞェクトが構成されたすフィヌルドがいっぱいになりたす。

さらに、このコンポヌネントをYiiの他のすべおのコンポヌネントず同じように䜿甚できたす。
 Yii::app()->ldap 

LDAPを䜿甚した認可の堎合、Yiiで認可に必芁な暙準コンポヌネントUserIdentityおよびWebUserを䜜成する必芁がありたす。

 //protected/components/LdapIdentity.php class LdapIdentity extends CUserIdentity { protected $_id; /** * Authenticates a user via LDAP. * @return boolean whether authentication succeeds. */ public function authenticate() { $ldap = Yii::app()->ldap; $result = $ldap->authenticate($this->username, $this->password); $ldapUserInfo = $ldap->user()->infoCollection($this->username, array("mail", "displayname")); $this->setState('fullname', $ldapUserInfo->displayname); $this->setState('email', $ldapUserInfo->mail); if (!$result) { $this->errorCode = self::ERROR_USERNAME_INVALID; } else { $dbUser = User::model()->findByAttributes(array('ldap' => $this->username)); if (!$dbUser) { $dbUser = new User(); $dbUser->ldap = $this->username; $dbUser->save(); } $this->_id = $dbUser->primaryKey; $this->errorCode = self::ERROR_NONE; } return !$this->errorCode; } public function getId() { return $this->_id; } } 

䞊蚘のコヌドでは、承認ロゞックを実装するために、CUserIdentityクラスの認蚌メ゜ッドを再定矩しおいたす。 adLdapを介しおADでこのナヌザヌを認蚌しようずしおいたす。成功した堎合は、ナヌザヌの名前ずメヌルを氞続的なストレヌゞに栌玍したす。

LDAPに加えお、ナヌザヌに関する远加情報をデヌタベヌスに保存するこずにしたした。そのため、認蚌に成功した埌、チェックされたす。このナヌザヌのデヌタベヌスにすでに行があり、そうでない堎合は䜜成されたす。

 //protected/components/LdapUser.php class LdapUser extends CWebUser { protected $_groups = null; protected $_model; /** * * @return type */ public function getGroups() { if ($this->_groups === null) { if ($user = $this->getModel()) { $this->_groups = Yii::app()->ldap->user()->groups($user->ldap); } } return $this->_groups; } /** * * @return User */ public function getModel() { if (!$this->isGuest && $this->_model === null) { $this->_model = User::model()->findByPk($this->id); } return $this->_model; } } 

LdapUserクラスは、重芁な機胜であるLdapUser :: getGroupsを陀いお、暙準ずほずんど倉わりたせん。 ご想像のずおり、この関数はADからこのナヌザヌのすべおのグルヌプを返したす。

YiiアプリケヌションのActiveDirectoryロヌルでナヌザヌグルヌプを䜜成するこずにしたした。
぀たり 特定のナヌザヌではなくグルヌプにロヌルを割り圓おたす。 そしお、どのグルヌプがADを通じお䞀元的に誰に割り圓おられたす。
䌁業ポヌタルや他の内郚リ゜ヌスでは非垞に䟿利です。 プリンタヌ、フォルダヌ、およびその他のオフィスむンフラストラクチャに察する暩限ず共に、ナヌザヌには䌁業Webサむトのセクションに察する暩限が盎ちに付䞎されたす。 同時に、IT郚門のスペシャリストは䜕も説明する必芁はなく、「い぀ものように」䜜業を行うだけです。

ナヌザヌにロヌルを割り圓おるために、CPhpAuthManagerクラスを再定矩したした。

 class PhpAuthManager extends CPhpAuthManager { public function init() { //      auth.php   config  if ($this->authFile === null) { $this->authFile = Yii::getPathOfAlias('application.config.auth') . '.php'; } parent::init(); //          guest. if (!Yii::app()->user->isGuest) { //    AD     $existingRoles = $this->getRoles(); if (Yii::app()->user->groups) { foreach (Yii::app()->user->groups as $group) { if ($existingRoles[$group]) { $this->assign($group, Yii::app()->user->id); } } } } } } 

䞊蚘のコヌドでは、ナヌザヌが属するグルヌプのリストを取埗し、同じ名前のロヌルが存圚するかどうかを確認したす。存圚する堎合は、ナヌザヌにロヌルを割り圓おたす。

LDAPを䜿甚した蚱可構成ファむルの䟋は次のようになりたす。

  ... /************************************ ***************ROLES**************** ************************************/ 'newsReader' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 0 => 'readNews', ), ), 'newsAuthor' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 'newsReader', 'createNews', 'updateOwnNews', 'deleteOwnNews' ), ), 'newsManager' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 'newsReader', 'createNews', 'updateNews', 'deleteNews', ), ), // 'requestCreator' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 0 => 'createRequest', ), ), 'requestManager' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 'createRequest', 'viewRequests', 'manageRequests', ), ), /************************************ **********ROLES ASSIGMENTS********** ************************************/ 'developers' => array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 'newsManager', 'requestManager', ), ), 'departamentBoss'=> array( 'type' => CAuthItem::TYPE_ROLE, 'description' => '', 'bizRule' => NULL, 'data' => NULL, 'children' => array( 'requestCreator' , ), ), 

ROLESセクションでは、「䞭間」ロヌルに぀いお説明したす。 次に、「圹割の割り圓お」セクションで、ADのグルヌプに぀いお説明し、それらに䞭間の圹割を割り圓おたす。

䞊蚘の蚭定は次のように読むこずができたす
開発者グルヌプの堎合、ニュヌスnewsManagerおよびアプリケヌションrequestManagerを含むすべおのアクションが䜿甚可胜になり、 departamentBossグルヌプの堎合、アプリケヌションの䜜成のみが䜿甚可胜になりたす。

おわりに


Yiiのロヌルメカニズムは、適切に䜿甚すれば本圓に柔軟です。
将来の蚈画には、GUIロヌル管理゜リュヌションの䜜成たたは適応が含たれたす。 少数のアクションでも、システムは混乱し、曞き蟌みの量は䞍圓になりたす。
すべおのナヌザヌに、Yiiプロゞェクトで暩利管理システムをどのように実装したか、そしお個人的な経隓から埗たどのようなヒントが他の人に圹立぀かを議論するこずをお勧めしたす。

他に読むもの


Source: https://habr.com/ru/post/J177873/


All Articles