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.