Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@can directive improvements #2483

Merged
merged 23 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

- Split up `@can` directive into `@canFind`, `@canModel`, `@canQuery`, `@canResolved` and `@canRoot` https://github.com/nuwave/lighthouse/pull/2483
- Added `action` and `return_value` arguments to `@can*` family of directives https://github.com/nuwave/lighthouse/pull/2483
spawnia marked this conversation as resolved.
Show resolved Hide resolved
- Allows using any objects in `@can*` family of directives https://github.com/nuwave/lighthouse/pull/2483

## v6.26.0

### Added
Expand Down
30 changes: 30 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ It will prevent the following type of HTTP requests:
- `GET` requests
- `POST` requests that can be created using HTML forms

### `@can` directive is replaced with `@can*` directives

The `@can` directive was removed in favor of more specialized directives:
spawnia marked this conversation as resolved.
Show resolved Hide resolved
- with `find` field set: `@canFind`
- with `query` field set: `@canQuery`
- with `root` field set: `@canRoot`
- with `resolved` field set: `@canResolved`
- if none of the above are set: `@canModel`

```diff
type Mutation {
- createPost(input: PostInput): Post @can(ability: "create")
+ createPost(input: PostInput): Post @canModel(ability: "create")
- editPost(input: PostInput): Post @can(find: "input.id", ability: "edit")
+ editPost(input: PostInput): Post @canFind(find: "input.id", ability: "edit")
- deletePosts(ids: [ID!]! @whereKey): [Post!]! @can(query: true, ability: "delete") @delete
+ deletePosts(ids: [ID!]! @whereKey): [Post!]! @canQuery(ability: "delete") @delete
k0ka marked this conversation as resolved.
Show resolved Hide resolved
}

type Query {
- posts: [Post!]! @can(resolved: true, ability: "view") @paginate
+ posts: [Post!]! @canResolved(ability: "view") @paginate
}

type Post {
- sensitiveInformation: String @can(root: true, ability: "admin")
+ sensitiveInformation: String @canRoot(ability: "admin")
}
```

## v5 to v6

### `messages` on `@rules` and `@rulesForArray`
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@
"thecodingmachine/phpstan-safe-rule": "^1.2"
},
"suggest": {
"ext-protobuf": "Improve protobuf serialization performance (used for tracing)",
"bensampo/laravel-enum": "Convenient enum definitions that can easily be registered in your Schema",
"google/protobuf": "Required when using the tracing driver federated-tracing",
"laravel/pennant": "Required for the @feature directive",
"laravel/scout": "Required for the @search directive",
"mll-lab/graphql-php-scalars": "Useful scalar types, required for @whereConditions",
"mll-lab/laravel-graphiql": "A graphical interactive in-browser GraphQL IDE - integrated with Laravel",
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver",
"google/protobuf": "Required when using the tracing driver federated-tracing",
"ext-protobuf": "Improve protobuf serialization performance (used for tracing)"
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver"
},
"minimum-stability": "dev",
"prefer-stable": true,
Expand Down
172 changes: 109 additions & 63 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -604,109 +604,155 @@ You can find usage examples of this directive in [the caching docs](../performan

## @can

Deprecated. Use the [@can* family of directives](#can-family-of-directives) instead.

## @can* family of directives

All @can* directives have common arguments. These arguments specify how gates are checked and what to do if the user is not authorized.
Each directive has its own set of arguments that specify what to check against.

```graphql
"""
The ability to check permissions for.
"""
Check a Laravel Policy to ensure the current user is authorized to access a field.
ability: String!

When `injectArgs` and `args` are used together, the client given
arguments will be passed before the static args.
"""
directive @can(
"""
The ability to check permissions for.
"""
ability: String!
Pass along the client given input data as arguments to `Gate::check`.
"""
injectArgs: Boolean! = false

"""
Check the policy against the model instances returned by the field resolver.
Only use this if the field does not mutate data, it is run before checking.
"""
Statically defined arguments that are passed to `Gate::check`.

Mutually exclusive with `query`, `find`, and `root`.
"""
resolved: Boolean! = false
You may pass arbitrary GraphQL literals,
e.g.: [1, 2, 3] or { foo: "bar" }
"""
args: CanArgs

"""
Specify the class name of the model to use.
This is only needed when the default model detection does not work.
"""
model: String
"""
Action to do if the user is not authorized.
"""
action: CanAction! = EXCEPTION_PASS

"""
Pass along the client given input data as arguments to `Gate::check`.
"""
injectArgs: Boolean! = false
"""
Value to return if the user is not authorized and `action` is `RETURN_VALUE`.
"""
return_value: CanArgs
k0ka marked this conversation as resolved.
Show resolved Hide resolved
"""
```

"""
Statically defined arguments that are passed to `Gate::check`.
Types are specified as:
```graphql
"""
Any constant literal value: https://graphql.github.io/graphql-spec/draft/#sec-Input-Values
"""
scalar CanArgs

You may pass arbitrary GraphQL literals,
e.g.: [1, 2, 3] or { foo: "bar" }
"""
args: CanArgs
enum CanAction {
"""
Pass exception to the client.
"""
EXCEPTION_PASS

"""
Query for specific model instances to check the policy against, using arguments
with directives that add constraints to the query builder, such as `@eq`.
"""
Throw generic "not authorized" exception to conceal the real error.
"""
EXCEPTION_NOT_AUTHORIZED

Mutually exclusive with `resolved`, `find`, and `root`.
"""
query: Boolean! = false
"""
Return the value specified in `value` argument to conceal the real error.
k0ka marked this conversation as resolved.
Show resolved Hide resolved
"""
RETURN_VALUE
}
```

"""
Apply scopes to the underlying query.
"""
scopes: [String!]
You can find usage examples of these directives in [the authorization docs](../security/authorization.md#restrict-fields-through-policies).

### @canFind

```graphql
"""
Check a Laravel Policy to ensure the current user is authorized to access a field.

Query for specific model instances to check the policy against, using primary key(s) from specified argument.
"""
directive @canFind(
"""
If your policy checks against specific model instances, specify
the name of the field argument that contains its primary key(s).
Specify the name of the field argument that contains its primary key(s).

You may pass the string in dot notation to use nested inputs.

Mutually exclusive with `resolved`, `query`, and `root`.
"""
find: String
find: String!

"""
Should the query fail when the models of `find` were not found?
"""
findOrFail: Boolean! = true

"""
If your policy should check against the root value.

Mutually exclusive with `resolved`, `query`, and `find`.
Apply scopes to the underlying query.
"""
root: Boolean! = false
scopes: [String!]
) repeatable on FIELD_DEFINITION
```

### canModel

```graphql
"""
Any constant literal value: https://graphql.github.io/graphql-spec/draft/#sec-Input-Values
Check a Laravel Policy to ensure the current user is authorized to access a field.

Check the policy against the root model.
"""
scalar CanArgs
directive @canRoot(
"""
The model name to check against.
"""
model: String

) repeatable on FIELD_DEFINITION
```

The name of the returned Type `Post` is used as the Model class, however you may overwrite this by
passing the `model` argument:
### @canQuery

```graphql
type Mutation {
createBlogPost(input: PostInput!): BlogPost
@can(ability: "create", model: "App\\Post")
}
"""
Check a Laravel Policy to ensure the current user is authorized to access a field.

Query for specific model instances to check the policy against, using arguments
with directives that add constraints to the query builder, such as `@eq`.
"""
directive @canQuery(
"""
Apply scopes to the underlying query.
"""
scopes: [String!]
) repeatable on FIELD_DEFINITION
```

Check the policy against the resolved model instances with the `resolved` argument:
### @canResolved

```graphql
type Query {
fetchUserByEmail(email: String! @eq): User
@can(ability: "view", resolved: true)
@find
}
"""
Check a Laravel Policy to ensure the current user is authorized to access a field.

Check the policy against the model instances returned by the field resolver.
Only use this if the field does not mutate data, it is run before checking.
"""
directive @canResolved repeatable on FIELD_DEFINITION
```

You can find usage examples of this directive in [the authorization docs](../security/authorization.md#restrict-fields-through-policies).
### @canRoot

```graphql
"""
Check a Laravel Policy to ensure the current user is authorized to access a field.

Check the policy against the root object.
"""
directive @canRoot repeatable on FIELD_DEFINITION
```

## @clearCache

Expand Down
2 changes: 1 addition & 1 deletion docs/master/eloquent/nested-mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ See [this issue](https://github.com/nuwave/lighthouse/issues/900) for further di
## Security considerations

Lighthouse has no mechanism for fine-grained permissions of nested mutation operations.
Field directives such as [@can](../api-reference/directives.md#can) apply to the whole field.
Field directives such as [@can*](../api-reference/directives.md#can-family-of-directives) apply to the whole field.

Make sure that fields with nested mutations are only available to users who are allowed
to execute all reachable nested mutations.
Expand Down
Loading
Loading