title: Yii2 AccessControl and RBAC Update Own Record
body: <p>After following <a href="http://www.yiiframework.com/doc-2.0/guide-security-authorization.html" rel="nofollow noreferrer">this guide</a> I am unable to get the "update own record" type of functionality to work.  Here is what I have.</p>
  
  <p>Admin and Author roles. Author has the <code>imageUpdateOwn</code> permission which has the <code>imageUpdate</code> permission as a child. The <code>imageUpdateOwn</code> permission has the <code>isOwner</code> rule assigned to it. All other pertinent/related code is below.</p>
  
  <p><strong>Database</strong></p>
  
  <p><a href="https://i.stack.imgur.com/dhUtU.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/dhUtU.png" alt="database"></a></p>
  
  <p><strong>OwnerRule.php</strong></p>
  
  <p>The <code>Image</code> model has a <code>created_by</code> property which is the owner of the record.</p>
  
  <pre><code>public function execute($user, $item, $params)
  {
      return isset($params['model']) ? $params['model']-&gt;created_by == $user : false;
  }
  </code></pre>
  
  <p><strong>ImageController.php AccessControl</strong></p>
  
  <pre><code>public function behaviors()
  {
      return [
          'access' =&gt; [
              'class' =&gt; AccessControl::className(),
              'rules' =&gt; [
                  [
                      'allow' =&gt; true,
                      'actions' =&gt; ['update'],
                      'roles' =&gt; ['imageUpdate'],
                  ],
                  [
                      'allow' =&gt; true,
                      'actions' =&gt; ['delete'],
                      'roles' =&gt; ['imageDelete'],
                  ],
                  [
                      'allow' =&gt; true,
                      'actions' =&gt; ['index', 'view', 'create'],
                      'roles' =&gt; ['@'],
                  ],
                  [
                      'allow' =&gt; false,
                  ],
              ],
          ],
      ];
  }
  </code></pre>
  
  <p><strong>ImageController.php action</strong></p>
  
  <pre><code>public function actionUpdate($id)
  {
      $model = $this-&gt;findModel($id);
  
      if (\Yii::$app-&gt;user-&gt;can('imageUpdate', ['model' =&gt; $model])) {
          if ($model-&gt;load(Yii::$app-&gt;request-&gt;post()) &amp;&amp; $model-&gt;save()) {
              return $this-&gt;redirect(['view', 'id' =&gt; $model-&gt;id]);
          } else {
              return $this-&gt;render('update', [
                  'model' =&gt; $model,
              ]);
          }
      } else {
          throw new HttpException(403, 'You do not have permission to update this record.');
      }
  }
  </code></pre>
  
  <p>In my testing, I logged in as the author and uploaded image 1000 then logged in as the admin and uploaded image 1001. Performing the following quick test while logged in as each user yields the expected results.</p>
  
  <pre><code>$image1000 = \app\models\Image::findOne(1000); // author uploaded
  $image1001 = \app\models\Image::findOne(1001); // admin uploaded
  
  echo \Yii::$app-&gt;user-&gt;can('imageUpdate', ['model' =&gt; $image1000]) . "&lt;br&gt;";
  echo \Yii::$app-&gt;user-&gt;can('imageUpdate', ['model' =&gt; $image1001]);
  </code></pre>
  
  <p>When logged in as admin, I get "true" for each of the echo statements. When logged in as the author, I get "true" only for image 1000. This tells me the authorization is returning the correct permissions as expected with the isOwner rule I created.</p>
  
  <p>However, when I try to actually go to the update action, I can't even get past the access control.  Instead of getting the 403 error that I specified in the action (which is what I expect) I get the generic "You are not allowed to perform this action" error. This seems to be because I have the default access set to deny. If I set the default access to allow, I can get through to the action and I get the expected 403 error message. That tells me the action isn't allowing me through.</p>
  
  <p>I am not sure if the <code>imageUpdateOwn</code> role should be listed in the access control but having it there does not change the results (presumably because <code>imageUpdate</code> is a child of <code>imageUpdateOwn</code> so by having the latter, the author will also have the former).</p>
  
  <p>What am I missing in the above that is completely preventing me from accessing the update action when logged in as the author?</p>
  
  <p><strong>Edit 1</strong></p>
  
  <p>Perhaps because the model isn't loaded at the time the access control is being checked, the created_by user can't be evaluated. If that is the case, I'm confused since the author has <code>imageUpdate</code> by way of <code>imageUpdateOwn</code> it should be allowing the access to continue. Unless they only get <code>imageUpdate</code> when the rule associated with <code>imageUpdateOwn</code> passes.  In which case the author would never "get" the access associated with the <code>imageUpdate</code> role until the model can be evaluated. Meaning I would have to allow all authenticated users to access the <code>update</code> action and only check for the permission inside the action itself, once the model is loaded.</p>