GraphQL 隐式定义用户可以做什么
GraphQL define implicitly what a user can do
我正在使用 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 指令呢?您可以在查询、输入或字段上使用它。只需很少的修改就可以设置角色而不是权限。
第二个想法是根据角色为不同的经过身份验证的用户提供其他模式。
在这里看看我的回答:
我描述了针对不同目标的两种不同方法。最后,这两个允许我按照自己的意愿限制模式的每个部分。在基于角色的设置中完美运行
最后我写了一个自定义指令,类似于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
}
在这里,extraPayments 仅限于至少拥有这两种权利之一的人。通过限制扩展来限制一整套属性。
如果需要,突变也以同样的方式受到限制:
type Mutation {
test: String @restrict(rights: "baseWorkingTime")
}
我正在使用 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 指令呢?您可以在查询、输入或字段上使用它。只需很少的修改就可以设置角色而不是权限。
第二个想法是根据角色为不同的经过身份验证的用户提供其他模式。
在这里看看我的回答:
我描述了针对不同目标的两种不同方法。最后,这两个允许我按照自己的意愿限制模式的每个部分。在基于角色的设置中完美运行
最后我写了一个自定义指令,类似于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
}
在这里,extraPayments 仅限于至少拥有这两种权利之一的人。通过限制扩展来限制一整套属性。 如果需要,突变也以同样的方式受到限制:
type Mutation {
test: String @restrict(rights: "baseWorkingTime")
}