UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

108 lines (107 loc) 4.22 kB
import { flatten, isEqual, merge, omit } from 'lodash-es'; import { mergeFields } from './merge-fields.js'; // Adapted from https://github.com/directus/directus/blob/141b8adbf4dd8e06530a7929f34e3fc68a522053/api/src/utils/merge-permissions.ts#L4 /** * Merges multiple permission lists into a flat list of unique permissions. * @param strategy `and` or `or` deduplicate permissions while `intersection` makes sure only common permissions across all lists are kept and overlapping permissions are merged through `and`. * @param permissions List of permission lists to merge. * @returns A flat list of unique permissions. */ export function mergePermissions(strategy, ...permissions) { let allPermissions; // Only keep permissions that are common to all lists if (strategy === 'intersection') { const permissionKeys = permissions.map((permissions) => { return new Set(permissions.map((permission) => `${permission.collection}__${permission.action}`)); }); const intersectionKeys = permissionKeys.reduce((acc, val) => { return new Set([...acc].filter((x) => val.has(x))); }, permissionKeys[0]); const deduplicateSubpermissions = permissions.map((permissions) => { return mergePermissions('or', permissions); }); allPermissions = flatten(deduplicateSubpermissions).filter((permission) => { return intersectionKeys.has(`${permission.collection}__${permission.action}`); }); strategy = 'and'; } else { allPermissions = flatten(permissions); } const mergedPermissions = allPermissions .reduce((acc, val) => { const key = `${val.collection}__${val.action}`; const current = acc.get(key); acc.set(key, current ? mergePermission(strategy, current, val) : val); return acc; }, new Map()) .values(); return Array.from(mergedPermissions); } export function mergePermission(strategy, currentPerm, newPerm) { const logicalKey = `_${strategy}`; let { permissions, validation, fields, presets } = currentPerm; if (newPerm.permissions) { if (currentPerm.permissions && Object.keys(currentPerm.permissions)[0] === logicalKey) { permissions = { [logicalKey]: [ ...currentPerm.permissions[logicalKey], newPerm.permissions, ], }; } else if (currentPerm.permissions) { // Empty {} supersedes other permissions in _OR merge if (strategy === 'or' && (isEqual(currentPerm.permissions, {}) || isEqual(newPerm.permissions, {}))) { permissions = {}; } else { permissions = { [logicalKey]: [currentPerm.permissions, newPerm.permissions], }; } } else { permissions = { [logicalKey]: [newPerm.permissions], }; } } if (newPerm.validation) { if (currentPerm.validation && Object.keys(currentPerm.validation)[0] === logicalKey) { validation = { [logicalKey]: [ ...currentPerm.validation[logicalKey], newPerm.validation, ], }; } else if (currentPerm.validation) { // Empty {} supersedes other validations in _OR merge if (strategy === 'or' && (isEqual(currentPerm.validation, {}) || isEqual(newPerm.validation, {}))) { validation = {}; } else { validation = { [logicalKey]: [currentPerm.validation, newPerm.validation], }; } } else { validation = { [logicalKey]: [newPerm.validation], }; } } fields = mergeFields(currentPerm.fields, newPerm.fields, strategy); if (newPerm.presets) { presets = merge({}, presets, newPerm.presets); } return omit({ ...currentPerm, permissions, validation, fields, presets, }, ['id', 'system']); }