How to access $user in custom Authorization

+
0
-

How can I access $user inside my Authorization class?
This is what I currently have, but when I try to access $this->getUser(), I get an error:

Call to undefined method App\Model\DrpAuthorization::getUser()

This is my class:

<?php

declare(strict_types=1);

namespace App\Model;

use \Nette\Security\User;

class DrpAuthorization implements \Nette\Security\IAuthorizator
{
        protected $db;

        public function __construct(\Nette\Database\Connection $db )
        {
                $this->db = $db;
        }

        public function isAllowed($role, $resource, $privilege): bool{

                $user = $this->getUser();

                $sql = "SELECT g.name AS role, r.name AS resource, e.name AS entitlement
                        FROM groups g
                        JOIN policy p ON g.id=p.group_id
                        JOIN resources r ON r.id=p.resource_id
                        JOIN entitlements e ON e.id=p.entitlement_id
                        JOIN user_group ug ON ug.group_id=g.id
                        JOIN users u ON u.id=ug.user_id
                        WHERE u.id=2";
                $roles = $this->db->query( $sql );

                $acl = new \Nette\Security\Permission;

                foreach( $roles as $r )
                {
                        if( !in_array( $r->role, $acl->getRoles() ) )
                        {
                                $acl->addRole( $r->role );
                                $acl->addResource( $r->resource );
                                $acl->allow( $r->role, $r->resource, $r->entitlement);
                        }
                }

                return true;
        }
}
?>
MajklNajt
Member | 363
+
+2
-

You do not need user in authorizator, you must authorize the role

+
0
-

MajklNajt wrote:

You do not need user in authorizator, you must authorize the role

So, once the roles, resources, and privileges are set, is there a function to check if a role has privilege on a resource..

In my Authorizator, what function needs to be called to determine if the role is authorized?

n3t
Member | 28
+
0
-

In authorizator method isAllowed have to return true/false if given role is authorized to resource – privilege.
You should propably load your definition in constructor, not in isAllowed method.

In presenter you can check right for example like this

$this->getUser()->isAllowed($resource, $privilege)
CZechBoY
Member | 3611
+
0
-

You implement that authorizer so you should implement the method.
But easier is to create permission factory which just fills (and returns) Nette\Security\Permission.

+
0
-

CZechBoY wrote:

You implement that authorizer so you should implement the method.
But easier is to create permission factory which just fills (and returns) Nette\Security\Permission.

A permission factory with Authorizator, or instead of Authorizator?

n3t
Member | 28
+
+1
-

See example in documentation

CZechBoY
Member | 3611
+
0
-

When I take your previous code I would end up with something like this.
I didn't test it, I didn't lint it, maybe there is bug/compile error – I just want to show you what you should write instead of reinventing authorizator.

declare(strict_types=1);

namespace App\Model;

use Nette\Security\IAuthorizator;
use Nette\Security\Permission;

class DrpAuthorizationFactory
{
        protected $db;

        public function __construct(\Nette\Database\Connection $db)
        {
                $this->db = $db;
        }

        public function createAuthorizator(): IAuthorizator
        {
                $sql = "SELECT g.name AS role, r.name AS resource, e.name AS entitlement
                        FROM groups g
                        JOIN policy p ON g.id=p.group_id
                        JOIN resources r ON r.id=p.resource_id
                        JOIN entitlements e ON e.id=p.entitlement_id
                        JOIN user_group ug ON ug.group_id=g.id
                        JOIN users u ON u.id=ug.user_id";
                $roles = $this->db->query($sql);

                $acl = new Permission();

                foreach($roles as $r)
                {
                    if(!in_array($r->role, $acl->getRoles(), true))
                    {
                        $acl->addRole($r->role);
                    }
                    if ( !in_array($r->resource, $acl->getResources(), true))
                    {
                        $acl->addResource($r->resource);
                    }
                    $acl->allow($r->role, $r->resource, $r->entitlement);
                }

                return $acl;
        }
}

and config

services:
    authorizatorFactory: AuthorizatorFactory
    authorizator: @authorizatorFactory::createAuthorizator()
+
0
-

CZechBoY wrote:

When I take your previous code I would end up with something like this.
I didn't test it, I didn't lint it, maybe there is bug/compile error – I just want to show you what you should write instead of reinventing authorizator.

Thanks for the direction. With a little tweaking, it looks like this:

<?php

declare(strict_types=1);

namespace App\Model;

use Nette\Security\IAuthorizator;
use Nette\Security\Permission;

class DrpAuthorizationFactory
{
        protected $db;

        public function __construct(\Nette\Database\Connection $db)
        {
                        $this->db = $db;
        }

        public function createAuthorizator(): IAuthorizator
        {
                $sql = "SELECT g.name AS role, r.name AS resource, e.name AS entitlement
                        FROM groups g
                        JOIN policy p ON g.id=p.group_id
                        JOIN resources r ON r.id=p.resource_id
                        JOIN entitlements e ON e.id=p.entitlement_id
                        JOIN user_group ug ON ug.group_id=g.id
                                        JOIN users u ON u.id=ug.user_id";
                        $roles = $this->db->query($sql);

                        $acl = new Permission();

                        foreach($roles as $r)
                        {
                                if(!in_array($r->role, $acl->getRoles(), true))
                                {
                                        $acl->addRole($r->role);
                                }
                                if ( !in_array($r->resource, $acl->getResources(), true))
                                {
                                        $acl->addResource($r->resource);
                                }
                                $acl->allow($r->role, $r->resource, $r->entitlement);
                        }

                        return $acl;
        }

}
?>

And the config:

services:
        authorizatorFactory: App\Model\DrpAuthorizationFactory
        authorizator: @authorizatorFactory::createAuthorizator()

Then, in the presenter startup() function:

<?php
        protected function startup(): void
        {
                parent::startup();
                if (!$this->getUser()->isAllowed('admin')) {
                        throw new Nette\Application\ForbiddenRequestException;
                }
        }
?>

I think I am doing something wrong in calling $this->getUser()->isAllowed() function, because I get an error:

Nette\InvalidStateException
Role 'authenticated' does not exist

Which seems to be the state of the login, rather than a Authorizator thing.
What should I be calling here, to check if Authorizator is true/false?

CZechBoY
Member | 3611
+
0
-

send how do doing authentication, especialy part with new Identity(userid, roles, identity);

+
0
-

CZechBoY wrote:

send how do doing authentication, especialy part with new Identity(userid, roles, identity);

<?php

namespace App\Model;

use \Nette\Security\IIdentity;
use \Nette\Security\Passwords;
use \Nette\Security\IAuthenticator;
use \Nette\Database\ResultSet;

class DrpAuthenticator implements IAuthenticator
{
        private $context;

        private $passwords;

        public function __construct(\Nette\Database\Context $context, \Nette\Security\Passwords $passwords)
        {
                $this->context = $context;
                $this->passwords = $passwords;
        }

        public function authenticate(array $credentials): \Nette\Security\IIdentity
        {
                [$username, $password] = $credentials;

                $row = $this->context->table('users')
                        ->where('username', $username)->fetch();

                if (!$row) {
                        throw new Nette\Security\AuthenticationException('User not found.');
                }

                if (!$this->passwords->verify($password, $row->password)) {
                        throw new Nette\Security\AuthenticationException('Invalid password.');
                }

                $roles = [];
                $res = $this->context->query( 'SELECT g.name FROM groups g JOIN user_group ug ON g.id=ug.group_id JOIN users u ON ug.user_id=u.id' );
                foreach( $res as $r )
                {
                        if( !in_array( $r->name, $roles ) )
                        {
                                $role[] = $r->name;
                        }
                }

                return new \Nette\Security\Identity($row->id, $roles, ['username' => $row->username]);
        }
}
?>
n3t
Member | 28
+
0
-

Role autheticated is default user role, that is used automatically, when user does not have any role. Check if your user is assigned to some role, or try to logout / login, as identity is kept in session.

+
+1
-

n3t wrote:

Role autheticated is default user role, that is used automatically, when user does not have any role. Check if your user is assigned to some role, or try to logout / login, as identity is kept in session.

OK, a bit embarrassing, but
$role[] = $r->name;

should be

$roles[] = $r->name;