CakePHP 4.1 Пользовательский объект как поля, связанные с идентификатором авторизации

Я только что создал очень минимальный проект в CakePHP 4.1, в основном имитирующий руководство по CMS, и хочу реализовать довольно простую логику. ИспользуяAuthorization модуль, который я хочу разрешить пользователю A быть способным view Пользователь B если 1) они на самом деле один и тот же пользователь (A = B) ИЛИ 2) если A является админом.

Есть две таблицы БД - users а также user_types. users имеет внешний ключ user_type_id к user_types.

Эта связь отражена в коде как:

##### in UsersTable.php #####

class UsersTable extends Table {
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('users');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
        $this->belongsTo('UserTypes');

        $this->addBehavior('Timestamp');
    }

    //...
}



##### in UserTypesTable.php #####

class UserTypesTable extends Table {
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('user_types');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
        $this->hasMany('Users');
    }

    //...
}

В UsersController.php Я имею:

    public function view($id = null)
    {
        $user = $this->Users->get($id, [
            'contain' => ['UserTypes'],
        ]);

        $this->Authorization->authorize($user);

        $this->set(compact('user'));
    }

И в UserPolicy.php:

use App\Model\Entity\User;

class UserPolicy
{
    public function canView(User $user, User $resource)
    {
        // TODO: allow view if $user and $resource are the same User or if $user is an admin
        //
        // My problem is that here $user->user_type is NULL
        // while $resource->user_type is populated correctly
    }
}

Комментарий кода в приведенном выше отрывке показывает, в чем моя проблема. Я не знаю как получить$user иметь свой user_type поле заполняется, чтобы проверить, являются ли они администратором.

В рамках своих усилий я установил класс User как удостоверение авторизации, следуя этой статье: https://book.cakephp.org/authorization/2/en/middleware.html. С точки зрения кода это выглядит так:

##### relevant part of Application.php #####

$middlewareQueue
            ->add(new AuthenticationMiddleware($this))
            ->add(new AuthorizationMiddleware($this, [
                'identityDecorator' => function(\Authorization\AuthorizationServiceInterface $auth, \Authentication\IdentityInterface $user) {
                    return $user->getOriginalData()->setAuthorization($auth);
                }
            ]));





##### User.php #####

namespace App\Model\Entity;

use Authentication\PasswordHasher\DefaultPasswordHasher;
use Authorization\AuthorizationServiceInterface;
use Authorization\Policy\ResultInterface;
use Cake\ORM\Entity;

/**
 * User Entity
 *
 * @property int $id
 * @property string $email
 * @property string $password
 * @property string|null $name
 * @property \App\Model\Entity\UserType $user_type
 * @property \Cake\I18n\FrozenTime|null $created
 * @property \Cake\I18n\FrozenTime|null $modified
 * @property \Authorization\AuthorizationServiceInterface $authorization
 */
class User extends Entity implements \Authorization\IdentityInterface, \Authentication\IdentityInterface
{
    protected $_accessible = [
        'email' => true,
        'password' => true,
        'name' => true,
        'created' => true,
        'modified' => true,
    ];

    /**

    protected $_hidden = [
        'password',
    ];

    protected function _setPassword(string $password) : ?string
    {
        if (strlen($password) > 0) {
            return (new DefaultPasswordHasher())->hash($password);
        }
    }

    /**
     * @inheritDoc
     */
    public function can(string $action, $resource): bool
    {
        return $this->authorization->can($this, $action, $resource);
    }

    /**
     * @inheritDoc
     */
    public function canResult(string $action, $resource): ResultInterface
    {
        return $this->authorization->canResult($this, $action, $resource);
    }

    /**
     * @inheritDoc
     */
    public function applyScope(string $action, $resource)
    {
        return $this->authorization->applyScope($this, $action, $resource);
    }

    /**
     * @inheritDoc
     */
    public function getOriginalData()
    {
        return $this;
    }

    /**
     * Setter to be used by the middleware.
     * @param AuthorizationServiceInterface $service
     * @return User
     */
    public function setAuthorization(AuthorizationServiceInterface $service)
    {
        $this->authorization = $service;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getIdentifier()
    {
        return $this->id;
    }
}

Однако мне не удалось установить личность User в UserPolicy.php файл, чтобы иметь user_typeполе заполнено. Когда я звоню, кажется, что происходит какое-то внутреннее волшебство$this->Authorization->authorize() из контроллера, куда я явно передаю ресурс вместе с его типом пользователя (поскольку я создал его с 'contain' => ['UserTypes'] НО идентификатор пользователя заполняется автоматически Authorizationмодуль. Может ли кто-нибудь помочь мне найти способ перенести данные связанных таблиц в удостоверение пользователя политики авторизации?

ПРИМЕЧАНИЕ: я подделал код, чтобы он работал так:

##### in UserPolicy.php #####


use App\Model\Entity\User;

class UserPolicy
{
    public function canView(User $user, User $resource)
    {
        $user = \Cake\Datasource\FactoryLocator::get('Table')->get('Users')->get($user->id, ['contain' => ['UserTypes']]);

        // Now both $user->user_type and $resource->user_type are correctly populated
    }
}

ОДНАКО, это кажется ужасно "взломанным" и не таким, каким должно быть, поэтому мой первоначальный вопрос все еще в силе.

1 ответ

Решение

Идентификатор получает распознаватель задействованного идентификатора. В случае с учебником CMS этоPassword идентификатор, который по умолчанию использует ORM резольвер.

В ORM преобразователь можно настроить для использования настраиваемого поисковика в случае, если вам нужно управлять запросом для получения пользователя, именно здесь вы должны добавить вложение для своего UserTypes ассоциация.

В вашем UsersTable добавьте такую ​​поисковую систему:

public function findForAuthentication(\Cake\ORM\Query $query, array $options): \Cake\ORM\Query
{
    return $query->contain('UserTypes');
}

и настройте преобразователь идентификатора для использования этого средства поиска следующим образом:

$service->loadIdentifier('Authentication.Password', [
    'resolver' => [
        'className' => 'Authentication.Orm',
        'finder' => 'forAuthentication',
    ],
    'fields' => [
        'username' => 'email',
        'password' => 'password',
    ]
]);

Вам также необходимо указать имя класса преобразователя при переопределении resolver вариант, так как по умолчанию это просто строка, а не массив, который будет сливаться с новой конфигурацией!

Смотрите также

Другие вопросы по тегам