mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Merge pull request #549 from directus/relation-permissions
Only return relations for collections/fields you have access to read
This commit is contained in:
@@ -1,8 +1,40 @@
|
||||
import { AbstractServiceOptions } from '../types';
|
||||
import { AbstractServiceOptions, PermissionsAction } from '../types';
|
||||
import { ItemsService } from '../services/items';
|
||||
|
||||
export class PermissionsService extends ItemsService {
|
||||
constructor(options?: AbstractServiceOptions) {
|
||||
super('directus_permissions', options);
|
||||
}
|
||||
|
||||
async getAllowedCollections(role: string | null, action: PermissionsAction) {
|
||||
const query = this.knex
|
||||
.select('collection')
|
||||
.from('directus_permissions')
|
||||
.where({ role, action });
|
||||
const results = await query;
|
||||
return results.map((result) => result.collection);
|
||||
}
|
||||
|
||||
async getAllowedFields(role: string | null, action: PermissionsAction, collection?: string) {
|
||||
const query = this.knex
|
||||
.select('collection', 'fields')
|
||||
.from('directus_permissions')
|
||||
.where({ role, action });
|
||||
|
||||
if (collection) {
|
||||
query.andWhere({ collection });
|
||||
}
|
||||
|
||||
const results = await query;
|
||||
|
||||
const fieldsPerCollection: Record<string, string[]> = {};
|
||||
|
||||
for (const result of results) {
|
||||
const { collection, fields } = result;
|
||||
if (!fieldsPerCollection[collection]) fieldsPerCollection[collection] = [];
|
||||
fieldsPerCollection[collection].push(...(fields || '').split(','));
|
||||
}
|
||||
|
||||
return fieldsPerCollection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,80 @@
|
||||
import { ItemsService } from './items';
|
||||
import { AbstractServiceOptions } from '../types';
|
||||
import {
|
||||
AbstractServiceOptions,
|
||||
Query,
|
||||
Item,
|
||||
PrimaryKey,
|
||||
PermissionsAction,
|
||||
Relation,
|
||||
} from '../types';
|
||||
import { PermissionsService } from './permissions';
|
||||
|
||||
/**
|
||||
* @TODO update foreign key constraints when relations are updated
|
||||
*/
|
||||
|
||||
export class RelationsService extends ItemsService {
|
||||
permissionsService: PermissionsService;
|
||||
|
||||
constructor(options?: AbstractServiceOptions) {
|
||||
super('directus_relations', options);
|
||||
this.permissionsService = new PermissionsService(options);
|
||||
}
|
||||
|
||||
async readByQuery(query: Query): Promise<null | Item | Item[]> {
|
||||
const results = (await super.readByQuery(query)) as Relation | Relation[] | null;
|
||||
const filteredResults = await this.filterForbidden(results);
|
||||
return filteredResults;
|
||||
}
|
||||
|
||||
readByKey(
|
||||
keys: PrimaryKey[],
|
||||
query?: Query,
|
||||
action?: PermissionsAction
|
||||
): Promise<null | Item[]>;
|
||||
readByKey(key: PrimaryKey, query?: Query, action?: PermissionsAction): Promise<null | Item>;
|
||||
async readByKey(
|
||||
key: PrimaryKey | PrimaryKey[],
|
||||
query: Query = {},
|
||||
action: PermissionsAction = 'read'
|
||||
): Promise<null | Item | Item[]> {
|
||||
const results = (await super.readByKey(key as any, query, action)) as
|
||||
| Relation
|
||||
| Relation[]
|
||||
| null;
|
||||
const filteredResults = await this.filterForbidden(results);
|
||||
return filteredResults;
|
||||
}
|
||||
|
||||
private async filterForbidden(relations: Relation | Relation[] | null) {
|
||||
if (relations === null) return null;
|
||||
if (this.accountability === null || this.accountability?.admin === true) return relations;
|
||||
|
||||
const allowedCollections = await this.permissionsService.getAllowedCollections(
|
||||
this.accountability?.role || null,
|
||||
'read'
|
||||
);
|
||||
const allowedFields = await this.permissionsService.getAllowedFields(
|
||||
this.accountability?.role || null,
|
||||
'read'
|
||||
);
|
||||
|
||||
relations = Array.isArray(relations) ? relations : [relations];
|
||||
|
||||
return relations.filter((relation) => {
|
||||
const collectionsAllowed =
|
||||
allowedCollections.includes(relation.many_collection) &&
|
||||
allowedCollections.includes(relation.one_collection);
|
||||
|
||||
const fieldsAllowed =
|
||||
allowedFields[relation.one_collection] &&
|
||||
allowedFields[relation.many_collection] &&
|
||||
(allowedFields[relation.many_collection].includes('*') ||
|
||||
allowedFields[relation.many_collection].includes(relation.many_field)) &&
|
||||
(allowedFields[relation.one_collection].includes('*') ||
|
||||
allowedFields[relation.one_collection].includes(relation.one_field));
|
||||
|
||||
return collectionsAllowed && fieldsAllowed;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user