Skip to content

Commit

Permalink
relay: update access control policies
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasgv committed Dec 17, 2024
1 parent 6d5d1cf commit c3d5dd3
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 29 deletions.
31 changes: 21 additions & 10 deletions docs/frameworks/relay/admin/access_control.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,20 @@ policies in the form of one-line conditional statements (like ```if``` statement
(usually means "access granted") or ```false``` (usually means "access denied).

Relay bundles that come with access control declare a predefined set of policies in their bundle configuration.
The policy's name and info describe the action and the resource it grants (or denies) access to.
The policy's name and info describe the action, and optionally the resource it grants (or denies) access to.

The following variables can be addressed in policy statements:
There are currently two types of policies:
* **Roles**
* **Resource Permissions**

Resource **Permissions** always define a permission granted on one concrete resource item (instance), e.g. `READ_DOCUMENT`, where **Roles** are
resource item independent. They can be defined for a resource class (e.g. `CREATE_DOCUMENTS`) or globally (e.g. `ROLE_ADMIN`).

The following variable can be accessed in all policy statements:
* ```user``` The authenticated API user (see [The User Object](#the-user-object)).
* ```object``` The requested resource (may be ```null```) (see [The Resource Object](#the-resource-object))

The following variable can be accessed in **Resource Permision** statements only:
* ```resource``` The requested resource (see [The Resource Object](#the-resource-object))

## Access Control Attributes

Expand All @@ -44,28 +52,31 @@ declared primitive, array, or object type. They allow access control relevant at
to be used in the API code to be customized by administrators.

The following variables can be addressed in attribute statements:

* ```user``` The authenticated API user (see [The User Object](#the-user-object)).

### The User Object

The ```user``` object represents the authenticated API user and provides the following set of methods:

* ```getIdentifier(): ?string``` Returns the user identifier or ```null``` for system accounts.
* ```get(string $attributeName, $defaultValue = null): mixed|null``` Gets the value of a [user attribute](#user-attributes).
* ```get(string $attributeName, mixed $defaultValue = null): mixed``` Gets the value of a [user attribute](#user-attributes).
Throws an exception, if the attribute is not declared. If no value is provided for the user, ```$defailtValue``` is
returned.
* ```isGranted(string $policyName, $resource)``` Evaluates the access control policy ```$policyName```
* ```isGrantedRole(string $roleName): bool``` Evaluates the **Role** expression ```$roleName```for the current user.
Allows the evaluation of policies within other policy or attribute
statements of the same bundle. Throws an exception if the policy is not declared in the bundle or an infinite loop
is detected.
* ```isGrantedResourcePersmission(string $resourcePermissionName, object $resource): bool``` Evaluates the **Resource Permision**
expression ```$resourcePermissionName```
with the resource ```$resource``` for the current user. Allows the evaluation of policies within other policy or attribute
statements of the same bundle. Throws an exception if the policy is not declared in the bundle or an infinite loop
is detected.
* ```getAttribute(string $attributeName, $defaultValue = null): mixed|null``` Evaluates the access control attribute
* ```getAttribute(string $attributeName, $defaultValue = null): mixed``` Evaluates the access control attribute
```$attributeName``` for the current user and returns its return value. Allows the evaluation of attributes within
other or attribute statements of the same bundle. Throws an exception if the attribute is not declared
in the bundle or an infinite loop is detected.

### The Resource Object

The resource ```object``` represents one of possible many resources that the user requested access to. Its type depends
on the context and is part of the documentation of the respective [access control policy](#access-control-policies). It
may also be ```null```, in case it the policy is not associated with a resource (e.g. policies like 'MAY_USE_API')
The Resource Object represents a resources item (instance) that the user requested access to. Its type (resource class) depends
on the context and is part of the documentation of the respective **Resource Permission** (see [Access Control Policies](#access-control-policies)).
60 changes: 41 additions & 19 deletions docs/frameworks/relay/dev/access_control.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ for details):
class Configuration implements ConfigurationInterface
{
public const MAY_READ_BLOG_POST = 'MAY_READ_BLOG_POST';
public const MAY_ADD_BLOG_POST = 'MAY_ADD_BLOG_POST';
public const MAY_ADD_BLOG_POSTS = 'MAY_ADD_BLOG_POSTS';
public const USER_GROUPS = 'USER_GROUPS';

public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('my_app');
$treeBuilder->getRootNode()->append(
AuthorizationConfigDefinition::create()
->addPolicy(self::MAY_READ_BLOG_POST, 'false', 'Returns true if the authenticated user may read the given blog post.')
->addPolicy(self::MAY_ADD_BLOG_POST, 'false', 'Returns true if the authenticated user may add the given blog post.')
->addAttribute(self::USER_GROUPS, '[]', 'Returns an array of group IDs the authenticated user is part of')
->addResourcePermission(self::MAY_READ_BLOG_POST, 'false', 'Returns true if the authenticated user may read the given blog post.')
->addRole(self::MAY_ADD_BLOG_POSTS, 'false', 'Returns true if the authenticated user may add blog posts.')
->addAttribute(self::USER_GROUPS, '[]', 'Returns an array of group IDs the authenticated user is member of')
->getNodeDefinition()
);

Expand All @@ -42,10 +42,16 @@ public static function create()
Creates a new instance of ```AuthorizationConfigDefinition```.

```php
public function addPolicy(string $policyName, string $defaultExpression = 'false', string $info = ''): AuthorizationConfigDefinition
public function addRole(string $roleName, string $defaultExpression = 'false', string $info = ''): AuthorizationConfigDefinition
```
Appends a new node definition for the policy ```$policyName```, with the default expression ```$defaultExpression```
(```'false'``` meaning that nobody is granted access) and the policy description ```$info```.
Appends a new node definition for the role ```$roleName```, with the default expression ```$defaultExpression```
(```'false'``` meaning that nobody is granted access) and the role description ```$info```.

```php
public function addResourcePermission(string $resourcePermissionName, string $defaultExpression = 'false', string $info = ''): AuthorizationConfigDefinition
```
Appends a new node definition for the resource permission ```$resourcePermissionName```, with the default expression ```$defaultExpression```
(```'false'``` meaning that nobody is granted access) and the policy description ```$info```.

```php
public function addAttribute(string $attributeName, string $defaultExpression = 'false', string $info = ''): AuthorizationConfigDefinition
Expand All @@ -65,9 +71,12 @@ The config definition example above yields the following default config:
```yaml
my_app:
authorization:
MAY_READ_BLOG_POST: 'false'
MAY_ADD_BLOG_POST: 'false'
USER_GROUPS: '[]'
resource_permissions:
MAY_READ_BLOG_POST: 'false'
roles:
MAY_ADD_BLOG_POSTS: 'false'
attributes:
USER_GROUPS: '[]'
```
## Creating Your Authorization Service
Expand Down Expand Up @@ -112,33 +121,46 @@ Now you are ready to use your access control policies and attributes at runtime,
```php
class MyAppController
{
private $authorizationService;
public function __contruct(MyAuthorizationService $authorizationService)
public function __construct(
private readonly MyAuthorizationService $authorizationService)
{
$this->authorizationService = $authorizationService;
}
/**
* @throws ApiError throws a 403 'forbidden' exception if the current user is not authorized to add $blogPost
* @throws ApiError throws a 403 'forbidden' exception if the current user is not authorized to add blog posts
*/
public function addBlogPost(BlogPost $blogPost)
{
// if you just want to check without an exception being thrown, use $this->authorizationService->isGranted(...)
$this->authorizationService->denyAccessUnlessIsGranted(Configuration::MAY_ADD_BLOG_POST, $blogPost);
// if you just want to check without an exception being thrown,
// use $this->authorizationService->isGrantedRole(...)
$this->authorizationService->denyAccessUnlessIsGrantedRole(
Configuration::MAY_ADD_BLOG_POSTS);
// add the blog post
}
/**
* @throws ApiError throws a 403 'forbidden' exception if the current user is not authorized to read $blogPost
*/
public function getBlogPost(BlogPost $blogPost)
{
// if you just want to check without an exception being thrown,
// use $this->authorizationService->denyAccessUnlessIsGrantedResourcePermission(...)
$this->authorizationService->denyAccessUnlessIsGrantedResourcePermission(
Configuration::MAY_READ_BLOG_POST, $blogPost);
return $blogPost;
}
public function getUserGroups(): array
{
return $this->authorizationService->getAttribute(Configuration::USER_GROUPS);
}
}
```

Note that the ```denyAccessUnlessIsGranted``` method gets passed the blog post as a parameter. It is available
in your policy expression as ```object``` variable
Note that the ```denyAccessUnlessIsGrantedResourcePermission``` method gets passed the blog post as a parameter.
It is available in your policy expression as ```resource``` variable
(see [The Resource Object](../admin/access_control.md#the-resource-object)).

## Symfony Access Control (Deprecated)
Expand Down

0 comments on commit c3d5dd3

Please sign in to comment.