mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Separate caching of dynamic user vars from permissions (#9957)
* Separate caching of dynamic user vars from permissions * Cache filterContext only when permissions are cached * Reset merge * Reapply changes * Reduce nesting * Add missing assignment Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import { SchemaOverview } from '../types';
|
||||
|
||||
export async function getPermissions(accountability: Accountability, schema: SchemaOverview) {
|
||||
const database = getDatabase();
|
||||
const { systemCache } = getCache();
|
||||
const { systemCache, cache } = getCache();
|
||||
|
||||
let permissions: Permission[] = [];
|
||||
|
||||
@@ -24,7 +24,31 @@ export async function getPermissions(accountability: Accountability, schema: Sch
|
||||
const cachedPermissions = await systemCache.get(cacheKey);
|
||||
|
||||
if (cachedPermissions) {
|
||||
return cachedPermissions;
|
||||
if (!cachedPermissions.containDynamicData) {
|
||||
return processPermissions(accountability, cachedPermissions.permissions, {});
|
||||
}
|
||||
|
||||
const cachedFilterContext = await cache?.get(
|
||||
`filterContext-${hash({ user, role, permissions: cachedPermissions.permissions })}`
|
||||
);
|
||||
|
||||
if (cachedFilterContext) {
|
||||
return processPermissions(accountability, cachedPermissions.permissions, cachedFilterContext);
|
||||
} else {
|
||||
let requiredPermissionData, containDynamicData;
|
||||
|
||||
({ permissions, requiredPermissionData, containDynamicData } = parsePermissions(cachedPermissions.permissions));
|
||||
|
||||
const filterContext = containDynamicData
|
||||
? await getFilterContext(schema, accountability, requiredPermissionData)
|
||||
: {};
|
||||
|
||||
if (containDynamicData && env.CACHE_ENABLED !== false) {
|
||||
await cache?.set(`filterContext-${hash({ user, role, permissions })}`, filterContext);
|
||||
}
|
||||
|
||||
return processPermissions(accountability, permissions, filterContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,56 +58,9 @@ export async function getPermissions(accountability: Accountability, schema: Sch
|
||||
.from('directus_permissions')
|
||||
.where({ role: accountability.role });
|
||||
|
||||
const requiredPermissionData = {
|
||||
$CURRENT_USER: [] as string[],
|
||||
$CURRENT_ROLE: [] as string[],
|
||||
};
|
||||
let requiredPermissionData, containDynamicData;
|
||||
|
||||
permissions = permissionsForRole.map((permissionRaw) => {
|
||||
const permission = cloneDeep(permissionRaw);
|
||||
|
||||
if (permission.permissions && typeof permission.permissions === 'string') {
|
||||
permission.permissions = JSON.parse(permission.permissions);
|
||||
} else if (permission.permissions === null) {
|
||||
permission.permissions = {};
|
||||
}
|
||||
|
||||
if (permission.validation && typeof permission.validation === 'string') {
|
||||
permission.validation = JSON.parse(permission.validation);
|
||||
} else if (permission.validation === null) {
|
||||
permission.validation = {};
|
||||
}
|
||||
|
||||
if (permission.presets && typeof permission.presets === 'string') {
|
||||
permission.presets = JSON.parse(permission.presets);
|
||||
} else if (permission.presets === null) {
|
||||
permission.presets = {};
|
||||
}
|
||||
|
||||
if (permission.fields && typeof permission.fields === 'string') {
|
||||
permission.fields = permission.fields.split(',');
|
||||
} else if (permission.fields === null) {
|
||||
permission.fields = [];
|
||||
}
|
||||
|
||||
const extractPermissionData = (val: any) => {
|
||||
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
||||
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
||||
}
|
||||
|
||||
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
||||
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
deepMap(permission.permissions, extractPermissionData);
|
||||
deepMap(permission.validation, extractPermissionData);
|
||||
deepMap(permission.presets, extractPermissionData);
|
||||
|
||||
return permission;
|
||||
});
|
||||
({ permissions, requiredPermissionData, containDynamicData } = parsePermissions(permissionsForRole));
|
||||
|
||||
if (accountability.app === true) {
|
||||
permissions = mergePermissions(
|
||||
@@ -92,35 +69,114 @@ export async function getPermissions(accountability: Accountability, schema: Sch
|
||||
);
|
||||
}
|
||||
|
||||
const usersService = new UsersService({ schema });
|
||||
const rolesService = new RolesService({ schema });
|
||||
|
||||
const filterContext: Record<string, any> = {};
|
||||
|
||||
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
||||
filterContext.$CURRENT_USER = await usersService.readOne(accountability.user, {
|
||||
fields: requiredPermissionData.$CURRENT_USER,
|
||||
});
|
||||
}
|
||||
|
||||
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
||||
filterContext.$CURRENT_ROLE = await rolesService.readOne(accountability.role, {
|
||||
fields: requiredPermissionData.$CURRENT_ROLE,
|
||||
});
|
||||
}
|
||||
|
||||
permissions = permissions.map((permission) => {
|
||||
permission.permissions = parseFilter(permission.permissions, accountability!, filterContext);
|
||||
permission.validation = parseFilter(permission.validation, accountability!, filterContext);
|
||||
permission.presets = parseFilter(permission.presets, accountability!, filterContext);
|
||||
|
||||
return permission;
|
||||
});
|
||||
const filterContext = containDynamicData
|
||||
? await getFilterContext(schema, accountability, requiredPermissionData)
|
||||
: {};
|
||||
|
||||
if (env.CACHE_PERMISSIONS !== false) {
|
||||
await systemCache.set(cacheKey, permissions);
|
||||
await systemCache.set(cacheKey, { permissions, containDynamicData });
|
||||
|
||||
if (containDynamicData && env.CACHE_ENABLED !== false) {
|
||||
await cache?.set(`filterContext-${hash({ user, role, permissions })}`, filterContext);
|
||||
}
|
||||
}
|
||||
|
||||
return processPermissions(accountability, permissions, filterContext);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
function parsePermissions(permissions: any[]) {
|
||||
const requiredPermissionData = {
|
||||
$CURRENT_USER: [] as string[],
|
||||
$CURRENT_ROLE: [] as string[],
|
||||
};
|
||||
|
||||
let containDynamicData = false;
|
||||
|
||||
permissions = permissions.map((permissionRaw) => {
|
||||
const permission = cloneDeep(permissionRaw);
|
||||
|
||||
if (permission.permissions && typeof permission.permissions === 'string') {
|
||||
permission.permissions = JSON.parse(permission.permissions);
|
||||
} else if (permission.permissions === null) {
|
||||
permission.permissions = {};
|
||||
}
|
||||
|
||||
if (permission.validation && typeof permission.validation === 'string') {
|
||||
permission.validation = JSON.parse(permission.validation);
|
||||
} else if (permission.validation === null) {
|
||||
permission.validation = {};
|
||||
}
|
||||
|
||||
if (permission.presets && typeof permission.presets === 'string') {
|
||||
permission.presets = JSON.parse(permission.presets);
|
||||
} else if (permission.presets === null) {
|
||||
permission.presets = {};
|
||||
}
|
||||
|
||||
if (permission.fields && typeof permission.fields === 'string') {
|
||||
permission.fields = permission.fields.split(',');
|
||||
} else if (permission.fields === null) {
|
||||
permission.fields = [];
|
||||
}
|
||||
|
||||
const extractPermissionData = (val: any) => {
|
||||
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
||||
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
||||
containDynamicData = true;
|
||||
}
|
||||
|
||||
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
||||
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
||||
containDynamicData = true;
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
deepMap(permission.permissions, extractPermissionData);
|
||||
deepMap(permission.validation, extractPermissionData);
|
||||
deepMap(permission.presets, extractPermissionData);
|
||||
|
||||
return permission;
|
||||
});
|
||||
|
||||
return { permissions, requiredPermissionData, containDynamicData };
|
||||
}
|
||||
|
||||
async function getFilterContext(schema: SchemaOverview, accountability: Accountability, requiredPermissionData: any) {
|
||||
const usersService = new UsersService({ schema });
|
||||
const rolesService = new RolesService({ schema });
|
||||
|
||||
const filterContext: Record<string, any> = {};
|
||||
|
||||
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
||||
filterContext.$CURRENT_USER = await usersService.readOne(accountability.user, {
|
||||
fields: requiredPermissionData.$CURRENT_USER,
|
||||
});
|
||||
}
|
||||
|
||||
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
||||
filterContext.$CURRENT_ROLE = await rolesService.readOne(accountability.role, {
|
||||
fields: requiredPermissionData.$CURRENT_ROLE,
|
||||
});
|
||||
}
|
||||
|
||||
return filterContext;
|
||||
}
|
||||
|
||||
function processPermissions(
|
||||
accountability: Accountability,
|
||||
permissions: Permission[],
|
||||
filterContext: Record<string, any>
|
||||
) {
|
||||
return permissions.map((permission) => {
|
||||
permission.permissions = parseFilter(permission.permissions, accountability!, filterContext);
|
||||
permission.validation = parseFilter(permission.validation, accountability!, filterContext);
|
||||
permission.presets = parseFilter(permission.presets, accountability!, filterContext);
|
||||
|
||||
return permission;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user