问题描述
我正在使用Laravel Lighthouse。
以下情况: 我有用户,应该允许这些用户访问不同的数据集并运行不同的变异。
我的解决方案: 为用户分配了角色,这些角色定义了用户可以访问哪些数据集以及可以运行哪些变异。
我对实现感到困惑。我所能做的就是在架构中写下我所有的查询和变异,并制定限制访问它们的策略。 我更希望有一种从架构中查看哪个角色有权访问什么的方法。
我的想法: 每个角色都有一个类型,并且在该类型中关联可以访问哪些数据以及可以运行哪些变异
type Query {
me: User @auth
}
type User {
id: ID
username: String
first_name: String
wage: Float
password: String
roles: [Role]
role(name: String! @eq): Role @find
}
type Role {
id: ID
name: String
}
type AdminRole {
#set of users whose data the admin has access to
#also directly restrict the amount of attributes that are accessible (e.g. password is not accessible)
#this is invalid Syntax,I kNow
users: [Users] @all {
id
first_name
wage
}
#a mutation the admin has access to
updateUser(id: ID!,wage: Float): User @update
}
query {
me {
role(name: "AdminRole") {
users {
wage
}
}
}
}
mutation {
me {
role(name: "AdminRole") {
updateUser(id: 7,wage: 10.00) {
id
wage
}
}
}
}
因此,与其编写限制访问权限的策略,不如将所有内容隐式定义在架构中。这样可以定义并回答“管理员可以做什么?”更直观,更容易理解,因为它是在一个位置而不是多个策略中记录下来的。
我认为按照我上面描述的方式是不可能的。最接近的是什么?还是这种方法有问题?
解决方法
@can指令呢?您可以在查询,输入或字段上使用它。只需很少的修改,就可以设置角色而不是权限。
第二个想法是根据角色向其他经过身份验证的用户提供其他模式。
,在这里查看我的答案:https://stackoverflow.com/a/63405046/2397915
我描述了针对不同目标的两种不同方法。最后,这两点使我能够限制自己的模式的每个部分。在基于角色的设置中完美工作
,最后,我写了一个自定义指令,类似于lorado提到的,但是有点简单:
<?php
namespace App\GraphQL\Directives;
use Closure;
use GraphQL\Language\AST\TypeExtensionNode;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Exceptions\AuthorizationException;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use Nuwave\Lighthouse\Support\Contracts\TypeExtensionManipulator;
class RestrictDirective extends BaseDirective implements FieldMiddleware,TypeExtensionManipulator {
public function name() {
return "restrict";
}
public static function definition(): string {
return /** @lang GraphQL */ <<<'SDL'
directive @restrict(
roles: Mixed!
) on FIELD_DEFINITION | OBJECT
SDL;
}
public function handleField(FieldValue $fieldValue,Closure $next): FieldValue {
$resolver = $fieldValue->getResolver();
$fieldValue->setResolver(function ($root,array $args,GraphQLContext $context,ResolveInfo $resolveInfo) use ($resolver) {
//get the passed rights
$rights = $this->directiveArgValue("rights");
if ($rights === null) throw new DefinitionException("Missing argument 'rights' for directive '@restrict'.");
//allow both a single string and an array as input
if (!is_array($rights)) $rights = [$rights];
//current user,must be logged in
$user = $context->user();
if (!$user) $this->no();
//returns an array of strings
$user_rights = $user->getAllRightNames();
//this is the part where we check whether the user has the rights or not
if (empty(array_intersect($user_rights,$rights))) $this->no();
return $resolver($root,$args,$context,$resolveInfo);
});
return $next($fieldValue);
}
public function no() {
throw new AuthorizationException("You are not authorized to access {$this->nodeName()}");
}
public function manipulateTypeExtension(DocumentAST &$documentAST,TypeExtensionNode &$typeExtension) {
ASTHelper::addDirectiveToFields($this->directiveNode,$typeExtension);
}
}
原样使用:
type User {
id: ID!
username: String
extraPayments: [ExtraPayment] @restrict(rights: ["baseWorkingTime","someOtherRight"])
}
#how to easily restrict a subset of attributes
extend type User @restrict(rights: "baseWorkingTime") {
wage: Float
password: String
}
在这里,额外付款仅限于拥有两项权利中至少一项的人。通过限制扩展来限制整个属性集。 如果需要,可以以相同的方式限制突变:
type Mutation {
test: String @restrict(rights: "baseWorkingTime")
}