Laravel Lighthouse ограничивает поле мутации

#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 или что-то подобное.