Basic Authorization policy results and exception handling

I want to check if a user has been confirmed before being able to login.
if ($identity && $identity->canResult('login', $user)) below gives me MissingPolicyException, undefined policy for NULL, regardless my setup:

UserController.php

public function login()
{	
$this->Authorization->skipAuthorization();
$identity = $this->request->getAttribute('identity');
// debug($identity) confirms that this ID has field confirmed => true
    $this->request->allowMethod(['get', 'post']); 
$result=$identity->canResult('login', $user);
// regardless of POST or GET, redirect if user is logged in
if ($result->getStatus()) {
	// if (($identity && $identity->canResult('login', $user))->getStatus()){
		echo 'canResult';
		} 
	echo 'isValid result';

with UserPolicy.php

<?php
namespace App\Policy;

use App\Model\Entity\User;
use Authorization\IdentityInterface;
use Authorization\Policy\Result;

class UserPolicy
{
	
	public function canLogin(IdentityInterface $user){
		if ($user->confirmed == true) {
        return new Result(true);
		
    }
    return new Result(false, 'not-confirmed');			
}

I also added to the unauthorizedHandler in Application.php

'exceptions' => [
            ..,
			MissingPolicyException::class
        ],

How can I properly utilize the Authorization plugin to avoid Policy exceptions, and instead provide the user with a message that the account has not been confirmed yet? Thanks again in advance!

EDIT: I adapted my code since I noticed I was using the function of $result in two ways. This didn’t change the error though. The original was
$result = $this->Authentication->getResult();
if ($result->isValid()) {
if ($identity && $identity->canResult('login', $user)){
// e.g. echo 'canResult';
}
I have to check the different between isValid() and getSatus(), which seem to have similar functions.

You are referencing $user, but you have never set it to anything. So, it’s null, and the policy resolver doesn’t know what to do with that. Hence the error message about “undefined policy for NULL”.

I see. I assumed the Policy would recognize $user as reference to the UserPolicy.
I fixed it now by creating a $user after credentials are known, but it feels superfluous as $identity contains the necessary info already. However, it isn’t an Entity object of Users so canResult(‘login’, $identity) does not work. Is there a way to use $identity for this, or is the explicit Entity needed, like I have now:

public function login()
{	
$this->Authorization->skipAuthorization();
$this->request->allowMethod(['get', 'post']); 
    $result = $this->Authentication->getResult();
	if($result->isValid()){
	$identity = $this->request->getAttribute('identity');
	$user=$this->Users->findByEmail($identity['email'])->first();	
	$result2=$identity->canResult('login', $user);
	if (!$result2->getStatus()) {
	
	$this->Flash->error(__($result2->getReason()));
	$this->Authentication->logout();  }        
else{			
            //redirect to /articles after login success
            $redirect = $this->request->getQuery('redirect', [
                'controller' => 'Articles',
                'action' => 'index',
            ]);
    		return $this->redirect($redirect);
    	}	
    	}

The policy has no way to know what the name of your variable is in your function. It knows only what the contents of the variable is, and that’s null, because it doesn’t even exist. If you want your user class to also act as the identity class, you can implement the identity interface. Otherwise, what you have looks fine. Or, build a Policy to handle the Identity class and put the login check in there.

1 Like

I encountered two more issues, I hope I am missing something simple again.

1: the policy canEdit works, but canView gives a null error.
In OrdersController:

       {
         $order = $this->Orders->get($id, [
             'contain' => ['Countries'],
        ]);

     $this->set('order', $order);
     }
  public function edit($id = null)
      {
          $order = $this->Orders->get($id, [
              'contain' => [],
          ]);
          if ($this->request->is(['patch', 'post', 'put'])) {
              $order = $this->Orders->patchEntity($order, $this->request->getData());
              if ($this->Orders->save($order)) {
                  $this->Flash->success(__('The order has been saved.'));

                  return $this->redirect(['action' => 'index']);
              }
              $this->Flash->error(__('The order could not be saved. Please, try again.'));
          }
          $countries = $this->Orders->Countries->find('list', ['limit' => 200]);
    
          $this->set(compact('order', 'countries'));
      }

In OrderPolicy:
                                 public function canView(IdentityInterface $user, Order $order) 
                                               {$this->Flash->error(__(' canView run.'));
  		
  		debug($user);
  		return new Result(true);
      }
  public function canEdit(IdentityInterface $user, Order $order)
      {
          //logged in users can edit their own articles.
          return $this->isAuthor($user, $order);
  		
  		if ($user->id == $order->user_id) {
          return new Result(true);
      }
      // Results let you define a 'reason' for the failure.
      return new Result(false, 'not-owner'); 
  		
  		
      } 

Is there a clear catch?

I don’t see anything here that would indicate how the policies are being invoked. Where’s the ->authorize(...) call happening?

1 Like

For the formatting, put ``` on a line alone before and after the blocks you want formatted as code.

1 Like

That was indeed my mistake. I keep the ‘policy for NULL not defined’ however, even though I decided to keep this view out of authentication/authorization at all:

public function beforeFilter(\Cake\Event\EventInterface $event)
{
	parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['view']);
	
}

public function view($id = null)
    {
	$this->Authorization->skipAuthorization();

How could it still refer to a policy in this case?
I also removed canView() from the Policy since it would be of no use anymore (not being invoked anyway).

Is it working now? Not sure if your “How could it…” question is about what was going wrong before or something still going wrong now.

No it doesn’t work yet. My question is how Policy errors could still occur since I have added the view() to unAuthenticated and skipAuthorization. The authentication is indeed bypassed since I can access the view() whilst not being logged in. However, if I log in, the view prompts the policy for NULL error.

I’m not sure what other errors you might have, but both the policies your included; canEdit and canView are returning Result objects.

This would make them incapable of providing a FALSE answer for the policy check because:

can methods return boolean

canResult methods are the policies that return Result objects.

Good observation, it may relate to other errors. However, canEdit does already function correctly (while returning Results) and I removed canView, since I bypass authen and author.

EDIT: I have solved the problem, it was a stupid one - inside the view() template I still had a place where I used a copied policy for a therefore unknown Entity. Sorry.

I can’t be sure this error relates to your skipAuthZ setting.

The error you’ve referenced says ‘policy for NULL not defined’. That sounds like the policy resolver has received a null where it expected a value (probably and entity?) and as a result could find no matching policy.

Doesn’t your stack trace on the error lead you to where the offending policy request is being made?

Your code snippets indicate you believe the source is an automatic check. But any can(), canResult(), or applyScope() call that you make would also generate an error like this.

Also, if you are using the RequestAuthorizationMiddleware this would use policies before any of your controllers code has run. So this is another possible source of the error.

I suspect only an examination of the stack trace is going to identify the source.