-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathaction-permission.ts
141 lines (114 loc) · 3.94 KB
/
action-permission.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import type { RawTreeWithSources } from './types';
import type { ForestAdminClientOptionsWithDefaults } from '../types';
import generateActionsFromPermissions, {
ActionPermissions,
} from './generate-actions-from-permissions';
import { ForestAdminServerInterface } from '../types';
import TTLCache from '../utils/ttl-cache';
export default class ActionPermissionService {
private permissionsCache: TTLCache<ActionPermissions>;
constructor(
private readonly options: ForestAdminClientOptionsWithDefaults,
private readonly forestAdminServerInterface: ForestAdminServerInterface,
) {
this.permissionsCache = new TTLCache(
this.fetchEnvironmentPermissions.bind(this),
this.options.permissionsCacheDurationInSeconds * 1000,
);
}
public async isDevelopmentPermission(): Promise<boolean> {
const permissions = await this.getPermissions();
// isDevelopment is true only for development environment
return permissions.isDevelopment;
}
public can(roleId: number, actionName: string): Promise<boolean> {
return this.hasPermissionOrRefetch({
roleId,
actionName,
// Only allow refetch when not using server events
allowRefetch: !this.options.instantCacheRefresh,
});
}
private async hasPermissionOrRefetch({
roleId,
actionName,
allowRefetch,
}: {
roleId: number;
actionName: string;
allowRefetch: boolean;
}): Promise<boolean> {
const permissions = await this.getPermissions();
const isAllowed = this.isAllowed({ permissions, actionName, roleId });
if (!isAllowed && allowRefetch) {
this.invalidateCache();
return this.hasPermissionOrRefetch({
roleId,
actionName,
allowRefetch: false,
});
}
this.options.logger(
'Debug',
`User ${roleId} is ${isAllowed ? '' : 'not '}allowed to perform ${actionName}`,
);
return isAllowed;
}
private isAllowed({
permissions,
actionName,
roleId,
}: {
permissions: ActionPermissions;
actionName: string;
roleId: number;
}): boolean {
// In development everything is allowed
return Boolean(
permissions.isDevelopment ||
permissions.actionsGloballyAllowed.has(actionName) ||
permissions.actionsByRole.get(actionName)?.allowedRoles.has(roleId),
);
}
private async getPermissions(): Promise<ActionPermissions> {
return this.permissionsCache.fetch('currentEnvironment');
}
private async fetchEnvironmentPermissions(): Promise<ActionPermissions> {
this.options.logger('Debug', 'Fetching environment permissions');
const rawPermissions = await this.forestAdminServerInterface.getEnvironmentPermissions(
this.options,
);
return generateActionsFromPermissions(rawPermissions);
}
public async getCustomActionCondition(
roleId: number,
actionName: string,
): Promise<RawTreeWithSources | undefined> {
const permissions = await this.getPermissions();
const conditionFilter = permissions.actionsByRole.get(actionName)?.conditionsByRole.get(roleId);
return conditionFilter;
}
public async getAllCustomActionConditions(
actionName: string,
): Promise<Map<number, RawTreeWithSources> | undefined> {
const permissions = await this.getPermissions();
return permissions.actionsByRole.get(actionName)?.conditionsByRole;
}
public async getRoleIdsAllowedToApproveWithoutConditions(
actionName: string,
): Promise<Array<number>> {
const permissions = await this.getPermissions();
const approvalPermission = permissions.actionsByRole.get(actionName);
if (!approvalPermission) {
return [];
}
// All allowed roles excluding the one with conditions
return Array.from(approvalPermission.allowedRoles).filter(
roleId => !approvalPermission.conditionsByRole?.has(roleId),
);
}
public invalidateCache() {
this.options.logger('Debug', 'Invalidating roles permissions cache..');
this.permissionsCache.clear();
}
}