Accessing controller authorization through template View

I would like to only display the edit action of an article if the user is authorized. I tried to access the controller authorization in the template file like this:

\templates\Articles\index.php

<td class="actions">
                       ..
                        <?= $this->controller->Authorization->authorize($article) ? $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) : '' ; ?> 
.. </td>

resulting in MissingHelper error. What do I need to understand to create a proper way of accessing the authorization function within a template/View?

Maybe help.

<?php if ($this->Authorize->verify($current_user, 'Articles, 'index')): ?>
    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) ?>
<?php endif ?>

That results in the same MissingHelper error.

I tried both variations with syntax errors
<?php $this->Authorize->verify($current_user, 'Articles', 'index') ? echo $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) : '' ;?>
error echo unexpected.

<?php if($this->Authorize->verify($current_user, 'Articles', 'index')) 
						{ echo $this->Html->link(__('Edit'), ['action' => 'edit', $article->id])} ;?> 

error ā€˜{ā€™ unexpected, expecting ā€˜;ā€™ or ā€˜,ā€™

Did you load the Authorize helper? Is there even such a thing? Why did you think that this would work?

For my application, I built a little Authorize helper. With that loaded, I can call $this->Authorize->can(...) in my views to check permissions on individual actions. I have at times had to work around limitations in the plugin, and am therefore currently using a modified fork of it, so I canā€™t guarantee that this will work for you out of the box, but it should be close enough to give the general idea.

As @Zuluru noted, at the very top of your template file, load the Authorization helper:

$this->loadHelper('Authentication.Identity');

You can then:

<?php if ($this->Identity->isLoggedIn()): ?>
<!-- do the things for logged in folk -->
<?php endif ?>

Authentication and authorization are different. Authentication helper can get you details about who is logged in. Checking whether they have permission to do a particular thing is authorization, which doesnā€™t appear to have a stock helper.

The Identity that is stored in the session for the Authenticated user gains an additional interface when Authorization is used also.

If all that is set up properly you can do the policy checks directly from the Identity object too.

if ($identity->can('edit', $article)){
   echo $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]);
}
1 Like

Wonder if that was added after I adopted the plugin.

Donā€™t know. This now starts with a discussion of the Identity.

I ā€œgot inspiredā€ by $this->Authorization->authorize($article); in the ArticleController.php since it works there, without explicit loading a corresponding helper.

Thatā€™s because controllers donā€™t use helpers, they use components. You presumably have loaded the authorization component.

the can(ā€¦) function is unknown. From the documentation I am not sure if I have to change the UserEntity.php, since I use $id = $this->request->getAttribute('identity'); instead of user_id
I added use Authorization\Identity; to the Application.php since

"If your application uses the cakephp/authentication plugin then the Authorization\Identity class will be used. This class implements the Authentication\IdentityInterface` in addition to the Authorization\IdentityInterface . "

This seems redundant though, as it states it already will be used automatically?
I followed the authorization tutorial so it is loaded in my Apllication.php and its middleware function. Although I miss the ResponseInterface $response in the public function getAuthorizationService().

So $id is the attribute identity now. What type of object is this?

Thatā€™s why I tried refering to the controller:
$this->controller->
But there I guess the View object (?) of $this in the template doesnā€™t have this reference.

Yeah, separation of concerns. You should never (read: extremely rarely) need to access the controller from a view/template or a model.

Iā€™m not sure of the ā€˜main lineā€™ procedure here.

In my application I wanted a more feature rich Identity than the one created by Authentication/Authorization so I had to write code to insure my substitute object implemented the two required interfaces.

It is the Authorization\IdentityInterface the provides the can() method. So, it sounds like your object isnā€™t properly implementing that.

Since I manually built my object to have the required features Iā€™m not clear on what solution to suggest in your out-of-the-box case.

EDIT: I may have overlooked or the error has changed, but can() seems not to be the problem, but the $article?:

Call to a member function can() on null

OP (how to spoiler this text?):
I donā€™t know what could be wrong with my setup. I checked with the files of lukec8 and it seems OK (the same components setup and some differences in ArticlePolicy and ArticleController though).

So I guess I have to dig a little deeper. What object is the $id below? And to make sure, $this is a View? Is there a console where I can test with gettype()? Then I may find out where $id is missing the link with IdentityInterface.

$id = $this->request->getAttribute('identity');

How are you calling can?

In a controller, this will output Important details of your class:

        echo '<pre>';
        $identity = $this->request->getAttribute('identity');

        echo 'My Identity class' . PHP_EOL;
        echo get_class($identity) . PHP_EOL . PHP_EOL;

        echo 'Parents of my class' . PHP_EOL;
        echo var_export(class_parents($identity)) . PHP_EOL. PHP_EOL;

        echo 'My class implements theses required interfaces' . PHP_EOL;
        echo var_export(class_implements($identity)) . PHP_EOL . PHP_EOL;
        echo '</pre>';

This is what I get.

My Identity class
App\Model\Entity\UserIdentity

Parents of my class
array (
  'Authentication\\Identity' => 'Authentication\\Identity',
)

My class implements theses required interfaces
array (
  'ArrayAccess' => 'ArrayAccess',
  'Authentication\\IdentityInterface' => 'Authentication\\IdentityInterface',
  'Authorization\\IdentityInterface' => 'Authorization\\IdentityInterface',
)

As I said earlier, my Identity object is different (UserIdentity) but it extends Identity and implements all the required interfaces.

If yours implements those interfaces, you are on the right path.

Here are some other php introspection tools that may come in handy when debugging.

As dreamingmind suggested:

in <?php foreach ($articles as $article): ?> <tr>.. <td class="actions">... of edit.php (Article template)
and initiating $identity in the top of that template:
$id = $this->request->getAttribute('identity');

It gives me the following error:

[ Warning (2)](javascript:void(0);): get_class() expects parameter 1 to be object, null given

But you say this should be in a controller, while I want to implement this in a View template. Or should your code work the same nevertheless?