@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
55 lines (54 loc) • 2.82 kB
JavaScript
import { getUnaliasedFieldKey } from '../../../utils/get-unaliased-field-key.js';
import { uniq } from 'lodash-es';
import { getCases } from './get-cases.js';
/**
* Mutates passed AST
*
* @param ast - Read query AST
* @param permissions - Expected to be filtered down for the policies and action already
*/
export function injectCases(ast, permissions) {
ast.cases = processChildren(ast.name, ast.children, permissions);
}
function processChildren(collection, children, permissions) {
// Use uniq here, since there might be multiple duplications due to aliases or functions
const requestedKeys = uniq(children.map(getUnaliasedFieldKey));
const { cases, caseMap, allowedFields } = getCases(collection, permissions, requestedKeys);
// TODO this can be optimized if there is only one rule to skip the whole case/where system,
// since fields that are not allowed at all are already filtered out
// TODO this can be optimized if all cases are the same for all requested keys, as those should be
//
for (const child of children) {
const fieldKey = getUnaliasedFieldKey(child);
const globalWhenCase = caseMap['*'];
const fieldWhenCase = caseMap[fieldKey];
// Validation should catch any fields that are attempted to be read that don't have any access control configured.
// When there are no access rules for this field, and no rules for "all" fields `*`, we missed something in the validation
// and should abort.
if (!globalWhenCase && !fieldWhenCase) {
throw new Error(`Cannot extract access permissions for field "${fieldKey}" in collection "${collection}"`);
}
// The case/when system only needs to take place if no full access is given on this field,
// otherwise we can skip and thus safe some query perf overhead
if (!allowedFields.has('*') && !allowedFields.has(fieldKey)) {
// Global and field can't both be undefined as per the error check prior
child.whenCase = [...(globalWhenCase ?? []), ...(fieldWhenCase ?? [])];
}
if (child.type === 'm2o') {
child.cases = processChildren(child.relation.related_collection, child.children, permissions);
}
if (child.type === 'o2m') {
child.cases = processChildren(child.relation.collection, child.children, permissions);
}
if (child.type === 'a2o') {
for (const collection of child.names) {
child.cases[collection] = processChildren(collection, child.children[collection] ?? [], permissions);
}
}
if (child.type === 'functionField') {
const { cases } = getCases(child.relatedCollection, permissions, []);
child.cases = cases;
}
}
return cases;
}