|
| 1 | +--- |
| 2 | +id: fine-grained-security |
| 3 | +title: Fine grained security |
| 4 | +sidebar_label: Fine grained security |
| 5 | +--- |
| 6 | + |
| 7 | +If the [`@Logged` and `@Right` annotations](authentication_authorization.md#logged-and-right-annotations) are not |
| 8 | +granular enough for your needs, you can use the advanced `@Security` annotation. |
| 9 | + |
| 10 | +Using the `@Security` annotation, you can write an *expression* that can contain custom logic. For instance: |
| 11 | + |
| 12 | +- Check that a user can access a given resource |
| 13 | +- Check that a user has one right or another right |
| 14 | +- ... |
| 15 | + |
| 16 | +## Using the @Security annotation |
| 17 | + |
| 18 | +The `@Security` annotation is very flexible: it allows you to pass an expression that can contains custom logic: |
| 19 | + |
| 20 | +```php |
| 21 | +use TheCodingMachine\GraphQLite\Annotations\Security; |
| 22 | + |
| 23 | +// ... |
| 24 | + |
| 25 | +/** |
| 26 | + * @Query |
| 27 | + * @Security("is_granted('ROLE_ADMIN') or is_granted('POST_SHOW', post)") |
| 28 | + */ |
| 29 | +public function getPost(Post $post): array |
| 30 | +{ |
| 31 | + // ... |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +The *expression* defined in the `@Security` annotation must conform to [Symfony's Expression Language syntax](https://symfony.com/doc/4.4/components/expression_language/syntax.html) |
| 36 | + |
| 37 | +<div class="alert alert-info"> |
| 38 | + If you are a Symfony user, you might already be used to the <code>@Security</code> annotation. Most of the inspiration |
| 39 | + of this annotation comes from Symfony. Warning though! GraphQLite's <code>@Security</code> annotation and |
| 40 | + Symfony's <code>@Security</code> annotation are slightly different. Especially, the two annotations do not live |
| 41 | + in the same namespace! |
| 42 | +</div> |
| 43 | + |
| 44 | +## The `is_granted` function |
| 45 | + |
| 46 | +Use the `is_granted` function to check if a user has a special right. |
| 47 | + |
| 48 | +```php |
| 49 | +@Security("is_granted('ROLE_ADMIN')") |
| 50 | +``` |
| 51 | + |
| 52 | +is similar to |
| 53 | + |
| 54 | +```php |
| 55 | +@Right("ROLE_ADMIN") |
| 56 | +``` |
| 57 | + |
| 58 | +In addition, the `is_granted` function accepts a second optional parameter: the "scope" of the right. |
| 59 | + |
| 60 | +```php |
| 61 | +/** |
| 62 | + * @Query |
| 63 | + * @Security("is_granted('POST_SHOW', post)") |
| 64 | + */ |
| 65 | +public function getPost(Post $post): array |
| 66 | +{ |
| 67 | + // ... |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +In the example above, the `getPost` method can be called only if the logged user has the 'POST_SHOW' permission on the |
| 72 | +`$post` object. You can notice that the `$post` object comes from the parameters. |
| 73 | + |
| 74 | +## Accessing method parameters |
| 75 | + |
| 76 | +All parameters passed to the method can be accessed in the `@Security` expression. |
| 77 | + |
| 78 | +```php |
| 79 | +/** |
| 80 | + * @Query |
| 81 | + * @Security("startDate < endDate", statusCode=400, message="End date must be after start date") |
| 82 | + */ |
| 83 | +public function getPosts(DateTimeImmutable $startDate, DateTimeImmutable $endDate): array |
| 84 | +{ |
| 85 | + // ... |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +In the example above, we tweak a bit the Security annotation purpose to do simple input validation. |
| 90 | + |
| 91 | +## Setting HTTP code and error message |
| 92 | + |
| 93 | +You can use the `statusCode` and `message` attributes to set the HTTP code and GraphQL error message. |
| 94 | + |
| 95 | +```php |
| 96 | +/** |
| 97 | + * @Query |
| 98 | + * @Security("is_granted('POST_SHOW', post)", statusCode=404, message="Post not found (let's pretend the post does not exists!)") |
| 99 | + */ |
| 100 | +public function getPost(Post $post): array |
| 101 | +{ |
| 102 | + // ... |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +Note: since a single GraphQL call contain many errors, 2 errors might have conflicting HTTP status code. |
| 107 | +The resulting status code is up to the GraphQL middleware you use. Most of the time, the status code with the |
| 108 | +higher error code will be returned. |
| 109 | + |
| 110 | +## Accessing the user |
| 111 | + |
| 112 | +You can use the `user` variable to access the currently logged user. |
| 113 | +You can use the `is_logged()` function to check if a user is logged or not. |
| 114 | + |
| 115 | +```php |
| 116 | +/** |
| 117 | + * @Query |
| 118 | + * @Security("is_logged() && user.age > 18") |
| 119 | + */ |
| 120 | +public function getNSFWImages(): array |
| 121 | +{ |
| 122 | + // ... |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +## Accessing the current object |
| 127 | + |
| 128 | +You can use the `this` variable to access any (public) property / method of the current class. |
| 129 | + |
| 130 | +```php |
| 131 | +class Post { |
| 132 | + /** |
| 133 | + * @Query |
| 134 | + * @Security("this.canAccess(post, user)") |
| 135 | + */ |
| 136 | + public function getPost(Post $post): array |
| 137 | + { |
| 138 | + // ... |
| 139 | + } |
| 140 | + |
| 141 | + public function canAccess(Post $post, User $user): bool |
| 142 | + { |
| 143 | + // Some custom logic here |
| 144 | + } |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +## Available scope |
| 149 | + |
| 150 | +The `@Security` annotation can be used in any query, mutation or field, so anywhere you have a `@Query`, `@Mutation` |
| 151 | +or `@Field` annotation. |
| 152 | + |
| 153 | +## How to restrict a given resource |
| 154 | + |
| 155 | +The `is_granted` method can be used to restrict access to a specific resource. |
| 156 | + |
| 157 | +```php |
| 158 | +@Security("is_granted('POST_SHOW', post)") |
| 159 | +``` |
| 160 | + |
| 161 | +If you are wondering how to configure these fine-grained permissions, this is not something that GraphQLite handles |
| 162 | +itself. Instead, this depends on the framework you are using. |
| 163 | + |
| 164 | +If you are using Symfony, you will [create a custom voter](https://symfony.com/doc/current/security/voters.html). |
| 165 | + |
| 166 | +If you are using Laravel, you will [create a Gate or a Policy](https://laravel.com/docs/6.x/authorization). |
| 167 | + |
| 168 | +If you are using another framework, you need to know that the `is_granted` function simply forwards the call to |
| 169 | +the `isAllowed` method of the configured `AuthorizationSerice`. See [Connecting GraphQLite to your framework's security module |
| 170 | +](implementing-security.md) for more details |
0 commit comments