#laravel #graphql #laravel-lighthouse
#laravel #graphql #laravel-lighthouse
Вопрос:
Я хотел бы защитить некоторые определенные поля типа контента, чтобы разрешить пользователю admin изменять значение, но разрешать пользователям получать к нему доступ.
Представьте, например, User
тип с is_admin
полем. Только администратор должен иметь возможность обновлять его, но каждый должен иметь возможность его читать.
type User {
id: ID!
name: String!
email: String!
is_admin: Boolean!
}
Директива can, похоже, не работает с полем при мутации. Сначала я попытался добавить @can(ability: "setAdmin")
с помощью пользовательской политики, но это не возымело никакого эффекта. Тот же can / policy, который использовался при мутации, «работал», но это было недостаточно детализировано.
Похоже, что ограничения пользовательских полей с помощью пользовательской директивы должны помочь, но это тоже, похоже, не работает на уровне поля в типе ввода мутации.
type mutation {
updateUser(
input: UpdateUserInput! @spread
): User @update @middleware(checks: ["auth:api"])
}
input UpdateUserInput {
id: ID!
name: String!
email: String!
is_admin: Boolean! @adminOnly
}
С помощью этой пользовательской директивы в app/GraphQL/Directives/AdminOnlyDirective.php
<?php
namespace AppGraphQLDirectives;
use Closure;
use GraphQLTypeDefinitionResolveInfo;
use NuwaveLighthouseExceptionsDefinitionException;
use NuwaveLighthouseSchemaDirectivesBaseDirective;
use NuwaveLighthouseSchemaValuesFieldValue;
use NuwaveLighthouseSupportContractsDefinedDirective;
use NuwaveLighthouseSupportContractsFieldMiddleware;
use NuwaveLighthouseSupportContractsGraphQLContext;
class AdminOnlyDirective extends BaseDirective implements FieldMiddleware, DefinedDirective
{
/**
* Name of the directive as used in the schema.
*
* @return string
*/
public function name(): string
{
return 'adminOnly';
}
public static function definition(): string
{
return /** @lang GraphQL */ <<<GRAPHQL
"""
Limit field update to only admin.
"""
directive @adminOnly() on FIELD_DEFINITION
GRAPHQL;
}
public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
{
$originalResolver = $fieldValue->getResolver();
return $next(
$fieldValue->setResolver(
function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($originalResolver) {
$user = $context->user();
if (
// Unauthenticated users don't get to see anything
! $user
// The user's role has to match have the required role
|| !$user->is_admin
) {
return null;
}
return $originalResolver($root, $args, $context, $resolveInfo);
}
)
);
}
}
Итак, есть ли способ предотвратить «обновление» определенных полей с помощью laravel lighthouse?
Ответ №1:
На данный момент вы можете использовать https://lighthouse-php.com/4.16/custom-directives/argument-directives.html#argtransformerdirective чтобы преобразовать это поле в null
перед вставкой базы данных или просто выдать ошибку, чтобы избежать изменений в вашем конкретном поле, это похоже на то, как ведет себя @trim;
В lighthouse v5 его класс ArgTransformerDirective
переименован в ArgSanitizerDirective
, а метод transform
— в sanitize
https://github.com/nuwave/lighthouse/blob/v5.0-alpha.3/src/Schema/Directives/TrimDirective.php
Дополнительно:
Я все еще пытаюсь понять, как работает @can, потому что мне все еще нужно удалить весь атрибут вместо передачи null в мою базу данных;
Обновление: @может применяться только к input
типу вместо input
типа
Комментарии:
1. > В lighthouse v5 его класс ArgTransformerDirective переименован в ArgSanitizerDirective, а метод transform для очистки, который не соответствует действительности.
ArgSanitizerDirective
Было добавлено иArgTransformerDirective
остается неизменным. Разница заключается в порядке выполнения: Проверка -> Validate -> Transform.2. Большое спасибо, это выглядит как наиболее гибкое решение, но как мы можем получить доступ к
$context->user()
из метода преобразования?3. @idFlood просто используйте Auth::user(), и все готово, вот несколько дополнительных обсуждений проблемы github.com/nuwave/lighthouse/issues/1559
Ответ №2:
Первая идея, которую я имею в виду здесь, заключается в создании двух разных входных данных и / или мутаций. Например. для администраторов, имеющих доступ к полю:
updateUserAsAdmin(
input: UpdateUserFullInput! @spread
): User @update
@middleware(checks: ["auth:api"])
@can("users.update.full")
И UpdateUserFullInput
содержит is_admin
поле.
Я также несколько раз сталкивался с этим обсуждением:https://github.com/nuwave/lighthouse/issues/325 Возможно, вы также сможете найти здесь несколько полезных идей.
Возможно, вы также захотите взглянуть на официальные документы: https://github.com/nuwave/lighthouse/blob/master/docs/master/security/authorization.md#custom-field-restrictions
Комментарии:
1. Спасибо, я, вероятно, буду использовать это решение с ответом от Су Сяо Тонга, поскольку они могут дополнять друг друга. Для моего варианта использования я переименую мутации в UpdateUser и updateOwnUser или что-то подобное.