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
вариант, так как по умолчанию это просто строка, а не массив, который будет сливаться с новой конфигурацией!
Смотрите также