use*_*558 2 orm entity cakephp cakephp-4.x
我刚刚在 CakePHP 4.1 中创建了一个非常小的项目,主要模仿 CMS 教程,并且想要实现一个相当简单的逻辑。使用该Authorization模块,我希望允许用户A能够访问view用户,B如果 1) 他们实际上是同一用户 ( A = B) 或 2) 如果A是管理员。
有两个数据库表 -users和user_types。有users一个外键.user_type_iduser_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');
}
//...
}
Run Code Online (Sandbox Code Playgroud)
在UsersController.php我有:
public function view($id = null)
{
$user = $this->Users->get($id, [
'contain' => ['UserTypes'],
]);
$this->Authorization->authorize($user);
$this->set(compact('user'));
}
Run Code Online (Sandbox Code Playgroud)
并在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
}
}
Run Code Online (Sandbox Code Playgroud)
上面摘录中的代码注释显示了我的问题所在。我不知道如何$user填充其user_type字段以检查他们是否是管理员。
作为我努力的一部分,我已按照本文将 User 类设置为授权身份: https: //book.cakephp.org/authorization/2/en/middleware.html#using-your-user-class -作为身份。从代码角度来看,这看起来像:
##### 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;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我无法获取文件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
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这感觉非常“hacky”,而不是它应该的方式,所以我原来的问题仍然存在。
相关标识符的解析器正在获取身份。在 CMS 教程中,这是Password默认使用ORM解析器的标识符。
解析器ORM可以配置为使用自定义查找器,以防您需要控制获取用户的查询,您应该在其中添加关联的包含UserTypes。
在您UsersTable添加一个像这样的查找器:
public function findForAuthentication(\Cake\ORM\Query $query, array $options): \Cake\ORM\Query
{
return $query->contain('UserTypes');
}
Run Code Online (Sandbox Code Playgroud)
并配置标识符的解析器以使用该查找器,如下所示:
$service->loadIdentifier('Authentication.Password', [
'resolver' => [
'className' => 'Authentication.Orm',
'finder' => 'forAuthentication',
],
'fields' => [
'username' => 'email',
'password' => 'password',
]
]);
Run Code Online (Sandbox Code Playgroud)
在覆盖选项时,您还需要指定解析器类名resolver,因为默认情况下它只是一个字符串,而不是与新配置合并的数组!
也可以看看