directus

Форк
0
/
get-permissions.ts 
208 строк · 6.6 Кб
1
import { useEnv } from '@directus/env';
2
import type { Accountability, Permission, SchemaOverview } from '@directus/types';
3
import { deepMap, parseFilter, parseJSON, parsePreset } from '@directus/utils';
4
import { cloneDeep } from 'lodash-es';
5
import hash from 'object-hash';
6
import { getCache, getCacheValue, getSystemCache, setCacheValue, setSystemCache } from '../cache.js';
7
import getDatabase from '../database/index.js';
8
import { appAccessMinimalPermissions } from '@directus/system-data';
9
import { useLogger } from '../logger.js';
10
import { RolesService } from '../services/roles.js';
11
import { UsersService } from '../services/users.js';
12
import { mergePermissionsForShare } from './merge-permissions-for-share.js';
13
import { mergePermissions } from './merge-permissions.js';
14

15
export async function getPermissions(accountability: Accountability, schema: SchemaOverview) {
16
	const database = getDatabase();
17
	const { cache } = getCache();
18
	const env = useEnv();
19
	const logger = useLogger();
20

21
	let permissions: Permission[] = [];
22

23
	const { user, role, app, admin, share_scope } = accountability;
24
	const cacheKey = `permissions-${hash({ user, role, app, admin, share_scope })}`;
25

26
	if (cache && env['CACHE_PERMISSIONS'] !== false) {
27
		let cachedPermissions;
28

29
		try {
30
			cachedPermissions = await getSystemCache(cacheKey);
31
		} catch (err: any) {
32
			logger.warn(err, `[cache] Couldn't read key ${cacheKey}. ${err.message}`);
33
		}
34

35
		if (cachedPermissions) {
36
			if (!cachedPermissions['containDynamicData']) {
37
				return processPermissions(accountability, cachedPermissions['permissions'], {});
38
			}
39

40
			const cachedFilterContext = await getCacheValue(
41
				cache,
42
				`filterContext-${hash({ user, role, permissions: cachedPermissions['permissions'] })}`,
43
			);
44

45
			if (cachedFilterContext) {
46
				return processPermissions(accountability, cachedPermissions['permissions'], cachedFilterContext);
47
			} else {
48
				const {
49
					permissions: parsedPermissions,
50
					requiredPermissionData,
51
					containDynamicData,
52
				} = parsePermissions(cachedPermissions['permissions']);
53

54
				permissions = parsedPermissions;
55

56
				const filterContext = containDynamicData
57
					? await getFilterContext(schema, accountability, requiredPermissionData)
58
					: {};
59

60
				if (containDynamicData && env['CACHE_ENABLED'] !== false) {
61
					await setCacheValue(cache, `filterContext-${hash({ user, role, permissions })}`, filterContext);
62
				}
63

64
				return processPermissions(accountability, permissions, filterContext);
65
			}
66
		}
67
	}
68

69
	if (accountability.admin !== true) {
70
		const query = database.select('*').from('directus_permissions');
71

72
		if (accountability.role) {
73
			query.where({ role: accountability.role });
74
		} else {
75
			query.whereNull('role');
76
		}
77

78
		const permissionsForRole = await query;
79

80
		const {
81
			permissions: parsedPermissions,
82
			requiredPermissionData,
83
			containDynamicData,
84
		} = parsePermissions(permissionsForRole);
85

86
		permissions = parsedPermissions;
87

88
		if (accountability.app === true) {
89
			permissions = mergePermissions(
90
				'or',
91
				permissions,
92
				appAccessMinimalPermissions.map((perm) => ({ ...perm, role: accountability.role })),
93
			);
94
		}
95

96
		if (accountability.share_scope) {
97
			permissions = mergePermissionsForShare(permissions, accountability, schema);
98
		}
99

100
		const filterContext = containDynamicData
101
			? await getFilterContext(schema, accountability, requiredPermissionData)
102
			: {};
103

104
		if (cache && env['CACHE_PERMISSIONS'] !== false) {
105
			await setSystemCache(cacheKey, { permissions, containDynamicData });
106

107
			if (containDynamicData && env['CACHE_ENABLED'] !== false) {
108
				await setCacheValue(cache, `filterContext-${hash({ user, role, permissions })}`, filterContext);
109
			}
110
		}
111

112
		return processPermissions(accountability, permissions, filterContext);
113
	}
114

115
	return permissions;
116
}
117

118
function parsePermissions(permissions: any[]) {
119
	const requiredPermissionData = {
120
		$CURRENT_USER: [] as string[],
121
		$CURRENT_ROLE: [] as string[],
122
	};
123

124
	let containDynamicData = false;
125

126
	permissions = permissions.map((permissionRaw) => {
127
		const permission = cloneDeep(permissionRaw);
128

129
		if (permission.permissions && typeof permission.permissions === 'string') {
130
			permission.permissions = parseJSON(permission.permissions);
131
		}
132

133
		if (permission.validation && typeof permission.validation === 'string') {
134
			permission.validation = parseJSON(permission.validation);
135
		} else if (permission.validation === null) {
136
			permission.validation = {};
137
		}
138

139
		if (permission.presets && typeof permission.presets === 'string') {
140
			permission.presets = parseJSON(permission.presets);
141
		} else if (permission.presets === null) {
142
			permission.presets = {};
143
		}
144

145
		if (permission.fields && typeof permission.fields === 'string') {
146
			permission.fields = permission.fields.split(',');
147
		} else if (permission.fields === null) {
148
			permission.fields = [];
149
		}
150

151
		const extractPermissionData = (val: any) => {
152
			if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
153
				requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
154
				containDynamicData = true;
155
			}
156

157
			if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
158
				requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
159
				containDynamicData = true;
160
			}
161

162
			return val;
163
		};
164

165
		deepMap(permission.permissions, extractPermissionData);
166
		deepMap(permission.validation, extractPermissionData);
167
		deepMap(permission.presets, extractPermissionData);
168

169
		return permission;
170
	});
171

172
	return { permissions, requiredPermissionData, containDynamicData };
173
}
174

175
async function getFilterContext(schema: SchemaOverview, accountability: Accountability, requiredPermissionData: any) {
176
	const usersService = new UsersService({ schema });
177
	const rolesService = new RolesService({ schema });
178

179
	const filterContext: Record<string, any> = {};
180

181
	if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
182
		filterContext['$CURRENT_USER'] = await usersService.readOne(accountability.user, {
183
			fields: requiredPermissionData.$CURRENT_USER,
184
		});
185
	}
186

187
	if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
188
		filterContext['$CURRENT_ROLE'] = await rolesService.readOne(accountability.role, {
189
			fields: requiredPermissionData.$CURRENT_ROLE,
190
		});
191
	}
192

193
	return filterContext;
194
}
195

196
function processPermissions(
197
	accountability: Accountability,
198
	permissions: Permission[],
199
	filterContext: Record<string, any>,
200
) {
201
	return permissions.map((permission) => {
202
		permission.permissions = parseFilter(permission.permissions, accountability!, filterContext);
203
		permission.validation = parseFilter(permission.validation, accountability!, filterContext);
204
		permission.presets = parsePreset(permission.presets, accountability!, filterContext);
205

206
		return permission;
207
	});
208
}
209

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.