Skip to content
Permalink
Browse files
fix(keycloak-authz): make relationship field auth configurable at Gra…
…phQL field level

Relationship fields needed to be configured with the database field name

This fixes that so it can be configured with the model field name
  • Loading branch information
Enda Phelan committed Sep 22, 2020
1 parent 5bfb4b1 commit 525bc9a641fa7cb1818a0727a675564e6fa12dda
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 14 deletions.
@@ -57,24 +57,23 @@ With this configuration the following rules are in place.

## Relationships Autorization

Developers can also add authorization rules on sepecific relationships for data fetching purposes.
Developers can also add authorization rules on specific one-to-many relationships for data fetching purposes.
Relationship rules will be added on top of the existing rules that are defined for the individual objects.

To apply a relationship field rule to the one-to-many field `Task.users`, you must configure it on the `User.task` configuration object, the inverse field which `Task.users` maps to.

```ts
const authConfig = {
Task: {
User: {
relations: {
taskUsers: { roles: ['admin'] }
allTasksComments: { roles: ['commenter'] }
task: { roles: ['admin'] }
},
},
}
```
With this configuration the following rules are in place.
With this configuration the following authorization rule is set:
- Tasks `taskUsers` field has `admin` role applied. Fetching User object will require `admin` role for any user field fetched
- Tasks `allTasksComments` field has `commenter` role applied. Fetching `Comment` object will require `commenter` role for any user field fetched
- `User.relations.task.roles` applies the `admin` role on `Task.users`. Users must have the `admin` role in order to query `Task.users`.
:::info
Due to limitations of the Graphback `relations` authorization works only on `OneToMany` relationships.
@@ -1,5 +1,5 @@
import { isAuthorizedByRole, KeycloakContext, KeycloakContextBase } from "keycloak-connect-graphql";
import { GraphbackCRUDService, ResultList, GraphbackProxyService, GraphbackContext, QueryFilter, FindByArgs, getSelectedFieldsFromResolverInfo, ModelDefinition } from '@graphback/core';
import { GraphbackCRUDService, ResultList, GraphbackProxyService, GraphbackContext, QueryFilter, FindByArgs, getSelectedFieldsFromResolverInfo, ModelDefinition, FieldRelationshipMetadata } from '@graphback/core';
import { GraphQLResolveInfo } from 'graphql';
import { CrudServiceAuthConfig } from './KeycloakConfig';
import { getEmptyServiceConfig, UnauthorizedError, checkAuthRulesForInput, checkAuthRulesForSelections } from "./utils";
@@ -63,7 +63,7 @@ export class KeycloakCrudService<Type = any> extends GraphbackProxyService<Type>
throw new UnauthorizedError()
}
}

checkAuthRulesForInput(context, this.authConfig, Object.keys(data));

return super.update(data, context, info);
@@ -158,8 +158,10 @@ export class KeycloakCrudService<Type = any> extends GraphbackProxyService<Type>
}

public batchLoadData(relationField: string, id: string | number, filter: QueryFilter, context: GraphbackContext, info?: GraphQLResolveInfo) {
if (this.authConfig?.relations && this.authConfig?.relations[relationField]?.roles.length > 0) {
const { roles } = this.authConfig?.relations[relationField];
const relationshipMetadata = this.model.relationships.find((r: FieldRelationshipMetadata) => r.relationForeignKey === relationField);
const modelRelationshipField = relationshipMetadata ? relationshipMetadata.ownerField.name : relationField;
if (this.authConfig?.relations && this.authConfig?.relations[modelRelationshipField]?.roles.length > 0) {
const { roles } = this.authConfig?.relations[modelRelationshipField];
if (!isAuthorizedByRole(roles, context)) {
throw new UnauthorizedError()
}
@@ -313,7 +313,7 @@ test('Batching', async () => {
description: String
""" @oneToMany(field: 'task') """
comment: Comment
comments: [Comment]
}
"""@model"""

0 comments on commit 525bc9a

Please sign in to comment.