@via-profit/ability 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -0
- package/dist/AbilityPolicy.d.ts +78 -0
- package/dist/AbilityRule.d.ts +108 -0
- package/dist/AbilityService.d.ts +74 -0
- package/dist/AbilityStatement.d.ts +98 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +573 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# @via-profit/Ability
|
|
2
|
+
|
|
3
|
+
> Набор сервисов, частичyо реализующих принцип [Attribute Based Access Control](https://en.wikipedia.org/wiki/Attribute-based_access_control)
|
|
4
|
+
|
|
5
|
+
## Содержание
|
|
6
|
+
|
|
7
|
+
1. [Описание и общие принципы](#overview)
|
|
8
|
+
|
|
9
|
+
2. [Правила](#rules)
|
|
10
|
+
|
|
11
|
+
2.1 [Синтаксис правил](#rule-syntax)
|
|
12
|
+
|
|
13
|
+
2.2 [Примеры](#rule-recipes)
|
|
14
|
+
|
|
15
|
+
2.3 [Класс AbilityRule](#ability-rule-class)
|
|
16
|
+
|
|
17
|
+
3. [Политики](#policies)
|
|
18
|
+
|
|
19
|
+
3.1 [Синтаксис правил](#policy-syntax)
|
|
20
|
+
|
|
21
|
+
3.2 [Примеры](#policy-recipes)
|
|
22
|
+
|
|
23
|
+
3.3 [Класс AbilityPolicy](#ability-policy-class)
|
|
24
|
+
|
|
25
|
+
4. [Создание политики из конфига](#policy-config)
|
|
26
|
+
|
|
27
|
+
## Описание и общие принципы <a name="overview"></a>
|
|
28
|
+
|
|
29
|
+
Данный сервис позволяет создать правила или политики, а затем применить их по отношению к каким-либо данным для того чтобы проверить наличие доступа к этим данным.
|
|
30
|
+
|
|
31
|
+
Принцип работы основан на формировании правил, объединения их в политики и запуске политик и/или правил. Правила можно группировать в политики, а политики могут содержать вложенные политики.
|
|
32
|
+
|
|
33
|
+
После создания правил и политик их необходимо запустить для проверки соответствия передаваемым данным. Если переданные данные соответствуют всем правилам политики, то такая политика считается разрешенной.
|
|
34
|
+
|
|
35
|
+
## Правила <a name="rules"></a>
|
|
36
|
+
|
|
37
|
+
Предположим в системе имееются следующие данные: пользователь; отчеты.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// Пользователь
|
|
41
|
+
const user = {
|
|
42
|
+
id: '123',
|
|
43
|
+
name: 'Oleg',
|
|
44
|
+
age: 26,
|
|
45
|
+
departament: 'analytics',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Пользовательские отчёты
|
|
49
|
+
const reports = [
|
|
50
|
+
{
|
|
51
|
+
id: '1',
|
|
52
|
+
type: 'analytics',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: '2',
|
|
56
|
+
type: 'expenses',
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Пусть необходимо создать правило, которое будет разрешать доступ пользователю только к тем отчетам, тип которых соответствует его отделу. Тогда в правило будет записано что субъект имеет поле `departament` и оно должно быть равно значению поля `type` ресурса:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { AbilityRule } from '@via-profit/ability';
|
|
65
|
+
|
|
66
|
+
// Создание правила
|
|
67
|
+
const rule = new AbilityRule('Пользователь должен быть из отдела аналитики', [
|
|
68
|
+
'subject.departament', // субъект и адрес поля, в которое записано название отдела
|
|
69
|
+
'=', // оператор сравнения
|
|
70
|
+
'resource.type', // Ресурс и адрес поля, в которое записан тип отчета
|
|
71
|
+
]);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Адрес поля `subject.departament`, представляет собой запись в формате [dot notation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#dot_notation), которая обязательно должна начинаться с `subject`, что указывает, что для сравнения данных будет использоваться поле departament именно у субъекта (в данном примере субъект - это объект пользователя).
|
|
75
|
+
Для сравнения двух отделов будет использоваться оператор сравнения `=`.
|
|
76
|
+
Адрес поля `resource.type` тоже представляет собой декларацию пути с префиксом `resource` в формате [dot notation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#dot_notation).
|
|
77
|
+
|
|
78
|
+
Теперь, для того чтобы проверить правило, необходимо выполнить метод `check` передав необходимые субъект и ресурс. В данном примере субъект - это объект с данными пользователя, а ресурс - это один из отчетов.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const report = reports[0];
|
|
82
|
+
const isPermit = rule.check(user, report); // true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Метод `check` класса `AbilityRule` вернёт `permit` в случае, если переданные **user** и **report** отвечают требованием правила и `deny` в противном случае.
|
|
86
|
+
|
|
87
|
+
Полный листинг проверки правила:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { AbilityRule } from '@via-profit/ability';
|
|
91
|
+
|
|
92
|
+
// Создание правила
|
|
93
|
+
const rule = new AbilityRule('Пользователь должен быть из отдела аналитики', [
|
|
94
|
+
'subject.departament', // субъект и адрес поля, в которое записано название отдела
|
|
95
|
+
'=', // оператор сравнения
|
|
96
|
+
'resource.type', // Ресурс и адрес поля, в которое записан тип отчета
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
// Пользователь
|
|
100
|
+
const user = {
|
|
101
|
+
id: '123',
|
|
102
|
+
name: 'Oleg',
|
|
103
|
+
age: 26,
|
|
104
|
+
departament: 'analytics',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Пользовательские отчёты
|
|
108
|
+
const reports = [
|
|
109
|
+
{
|
|
110
|
+
id: '1',
|
|
111
|
+
type: 'analytics',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: '2',
|
|
115
|
+
type: 'expenses',
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
rule.check(user, reports[0]); // --> вернет true
|
|
120
|
+
rule.check(user, reports[1]); // --> вернет false, так как user.departament не соответствует значение поля type отчета
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Синтаксис правил <a name="rule-syntax"></a>
|
|
124
|
+
|
|
125
|
+
Для описания правил используется массив следующего типа:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
type AbilityRuleMatches = [
|
|
129
|
+
string, // dot notation путь до поля в субъекте или энвайронменте
|
|
130
|
+
'=' | '<>' | '>' | '<' | '<=' | '>=' | 'in', // оператор сравнения
|
|
131
|
+
string | number | boolean, // dot notation путь до поля в ресурсе или энвайронменте или просто значение
|
|
132
|
+
];
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Операторы сравнения <a name="rule-operators"></a>
|
|
136
|
+
|
|
137
|
+
`=` Прямое сравнение
|
|
138
|
+
`<>` Не равно
|
|
139
|
+
`>` Больше
|
|
140
|
+
`<` Меньше
|
|
141
|
+
`<=` Меньше или равно
|
|
142
|
+
`>=` Больше или равно
|
|
143
|
+
`in` Вхождение в массив. Позволяет проверять вхождение значения в массив
|
|
144
|
+
|
|
145
|
+
### Примеры правил <a name="rule-recipes"></a>
|
|
146
|
+
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Пользователь старше 21 года</summary>
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
const user = {
|
|
152
|
+
age: 18,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const isPermit = new AbilityRule('User age', ['subject.age', '>=', 21]).isPermit(user); // true
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
</details>
|
|
159
|
+
|
|
160
|
+
<details>
|
|
161
|
+
<summary>Пользователь имеет роль администратора</summary>
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const user = {
|
|
165
|
+
roles: ['administrator', 'manager'],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const isPermit = new AbilityRule('has role', ['subject.roles', 'in', 'administrator']).isPermit(
|
|
169
|
+
user,
|
|
170
|
+
); // true
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
</details>
|
|
174
|
+
|
|
175
|
+
<details>
|
|
176
|
+
<summary>Пользователь является владельцем заказа</summary>
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const user = {
|
|
180
|
+
id: '1',
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const order = {
|
|
184
|
+
owner: '1',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const isPermit = new AbilityRule('owner', ['subject.id', '=', 'resource.owner']).isPermit(
|
|
188
|
+
user,
|
|
189
|
+
order,
|
|
190
|
+
); // true
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
</details>
|
|
194
|
+
|
|
195
|
+
## Политики <a name="policies"></a>
|
|
196
|
+
|
|
197
|
+
Политики позволяют группировать правила или создавать вложенные друг в друга политики.
|
|
198
|
+
|
|
199
|
+
_Замечание: Одна политика может содержать либо правила, либо иметь вложенные политики. Одновременно иметь и правила и вложенные политики невозможно._
|
|
200
|
+
|
|
201
|
+
Предположим, необходимо создать правило, которое будет разрешать доступ к финансовому отчету только пользователю из отдела аналитики (`analytics`) и только при условии, что он является его владельцем. Таким образом нам необходимо сформировать политику, состоящую из двух обязательных правил.
|
|
202
|
+
|
|
203
|
+
_Псевдокод:_
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Политика (разрешить), если:
|
|
208
|
+
Правило 1 — пользователь.departament = analytics
|
|
209
|
+
(и)
|
|
210
|
+
Правило 2 — отчет.владелец = пользователь.id
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
_Реализация:_
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { Abilitypolicy, AbilityRule } from '@via-profit/ability';
|
|
218
|
+
|
|
219
|
+
const user = {
|
|
220
|
+
id: '1655',
|
|
221
|
+
departament: 'analytics',
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const report = {
|
|
225
|
+
id: '6',
|
|
226
|
+
owner: '1655',
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Создание первого правила
|
|
230
|
+
const departamentRule = new AbilityRule('Пользователь должен быть из отдела аналитики', [
|
|
231
|
+
'subject.departament',
|
|
232
|
+
'=',
|
|
233
|
+
'analytics',
|
|
234
|
+
]);
|
|
235
|
+
|
|
236
|
+
// Создание второго правила
|
|
237
|
+
const orderOwner = new AbilityRule('Пользователь должен быть владельцем отчета', [
|
|
238
|
+
'subject.id',
|
|
239
|
+
'=',
|
|
240
|
+
'resource.owner',
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
// Создание политики
|
|
244
|
+
const policy = new AbilityPolicy('Доступ к отчету', '1');
|
|
245
|
+
|
|
246
|
+
// Добавление правил в политику
|
|
247
|
+
// Второй аргумент («and») устанавливает, что
|
|
248
|
+
// политика будет разрешена только если переданные
|
|
249
|
+
// данные будут соответствовать обеим правилам сразу.
|
|
250
|
+
// Альтернативный вариант - «or». В таком случае, политика
|
|
251
|
+
// разрешится, если будет удовлетворено хотя бы одно правило
|
|
252
|
+
policy.addRules([departamentRule, orderOwner], 'and');
|
|
253
|
+
|
|
254
|
+
// запуск политики
|
|
255
|
+
// если данные, переданные в политику удовлетворяют ее правилам,
|
|
256
|
+
// то политика позволит выполниться коду, который следует за ней.
|
|
257
|
+
// в противном случае будет брошего исключение с сообщением
|
|
258
|
+
// о запрете доступа и перечислением правил, которые были нарушены
|
|
259
|
+
policy.enforce(user, report);
|
|
260
|
+
|
|
261
|
+
// какой-то код
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Вложенные политики
|
|
265
|
+
|
|
266
|
+
Политики могут состоять не из правил, а из других политик.
|
|
267
|
+
|
|
268
|
+
_Замечание: Одна политика может содержать либо правила, либо иметь вложенные политики. Одновременно иметь и правила и вложенные политики невозможно._
|
|
269
|
+
|
|
270
|
+
Например, Пользователь может установить статус заказа на «подтвержденный», но только в случаях, если: предыдущий статус заказа был «новый заказ», устанавливаемый статус заказа будет «подтвержденный заказ» и пользователь относится к отделу «manager» или в случае, если пользователь является старшим администратором
|
|
271
|
+
|
|
272
|
+
_Псевдокод:_
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Политика (разрешить), если:
|
|
277
|
+
Политика 1
|
|
278
|
+
Правило 1 — пользователь.departament = managers
|
|
279
|
+
(и)
|
|
280
|
+
Правило 2 — заказ.предыдущий статус = новый заказ
|
|
281
|
+
(и)
|
|
282
|
+
Правило 3 — заказ.новый статус = подтвержденный заказ
|
|
283
|
+
(или)
|
|
284
|
+
Политика 2
|
|
285
|
+
Правило 1 — пользователь.роль = administrator
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
_Реализация:_
|
|
290
|
+
|
|
291
|
+
Для создания политики будет задействован механизм парсинга конфиигурацонного файла. Подробнее см. раздел [Создание политики из конфига](#policy-config)
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import { Abilitypolicy, AbilityRule } from '@via-profit/ability';
|
|
295
|
+
|
|
296
|
+
const user = {
|
|
297
|
+
id: '1655',
|
|
298
|
+
departament: 'manager',
|
|
299
|
+
roles: ['super-admin', 'viewer'],
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const order = {
|
|
303
|
+
id: '6',
|
|
304
|
+
status: 'новый заказ',
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const policy = AbilityPolicy.parse({
|
|
308
|
+
name: 'Политика',
|
|
309
|
+
id: '1',
|
|
310
|
+
policiesCompareMethod: 'or',
|
|
311
|
+
policies: [
|
|
312
|
+
{
|
|
313
|
+
id: '2',
|
|
314
|
+
name: 'Только менеджер может сменить статус с «новый заказ» на «подтвержденный заказ»',
|
|
315
|
+
rulesCompareMethod: 'and',
|
|
316
|
+
rules: [
|
|
317
|
+
{
|
|
318
|
+
name: 'Пользователь должен быть из отдела менеджеров',
|
|
319
|
+
matches: ['subject.departament', '=', 'managers'],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'Предыдущий статус должен быть «новый заказ»',
|
|
323
|
+
matches: ['environment.prevStatus', '=', 'новый заказ'],
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'Устанавливаемый статус должен быть «подтвержденный заказ»',
|
|
327
|
+
matches: ['environment.nextStatus', '=', 'подтвержденный заказ'],
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: '3',
|
|
333
|
+
name: 'Пользователь должен быть старшим администратором',
|
|
334
|
+
rules: [
|
|
335
|
+
{
|
|
336
|
+
name: 'Пользователь должен быть старшим администратором',
|
|
337
|
+
matches: ['subject.rules', 'in', 'super-admin'],
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
policy.enforce(user, order, {
|
|
345
|
+
prevStatus: order.status,
|
|
346
|
+
nextStatus: 'подтвержденный заказ',
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Создание политики из конфига <a name="policy-config"></a>
|
|
351
|
+
|
|
352
|
+
Политику и правила можно создавать не только по средствам классов, но и при помощи конфигураций.
|
|
353
|
+
|
|
354
|
+
Структура конфигураций правила:
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
type AbilityRuleConfig = {
|
|
358
|
+
readonly name: string;
|
|
359
|
+
readonly effect?: AbilityRuleStatus;
|
|
360
|
+
readonly matches: AbilityRuleMatches;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
type AbilityRuleMatches = [
|
|
364
|
+
`${SubjectPrefix}${string}`,
|
|
365
|
+
'=' | '<>' | '>' | '<' | '<=' | '>=' | 'in'
|
|
366
|
+
string | number | boolean,
|
|
367
|
+
];
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Структура конфигураций политики:
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
type AbilityPolicyConfig = {
|
|
375
|
+
readonly id: string;
|
|
376
|
+
readonly name: string;
|
|
377
|
+
readonly description?: string;
|
|
378
|
+
readonly rulesCompareMethod?: 'or' | 'and';
|
|
379
|
+
readonly policiesCompareMethod?: 'or' | 'and';
|
|
380
|
+
readonly rules?: AbilityRuleConfig[] | null;
|
|
381
|
+
readonly policies?: AbilityPolicyConfig[] | null;
|
|
382
|
+
};
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Пример создания простой политики через конфигурацию:
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
import { AbilityPolicy } from '@via-profit/ability';
|
|
389
|
+
|
|
390
|
+
const policy = AbilityPolicy.parse({
|
|
391
|
+
name: 'Название политики',
|
|
392
|
+
id: '1',
|
|
393
|
+
policiesCompareMethod: 'or',
|
|
394
|
+
policies: [
|
|
395
|
+
{
|
|
396
|
+
id: '2',
|
|
397
|
+
name: 'Название вложенной политики',
|
|
398
|
+
rulesCompareMethod: 'and',
|
|
399
|
+
rules: [
|
|
400
|
+
{ name: 'Правило 1', matches: ['subject.id', '=', 'resource.creatable'] },
|
|
401
|
+
{ name: 'Правило 2', matches: ['environment.prevStatus', '=', 'unknown'] },
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
## Класс AbilityRule <a name="ability-rule-class"></a>
|
|
410
|
+
|
|
411
|
+
Класс `AbilityRule` предназначен для создания правил
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
## Класс AbilityPolicy <a name="ability-policy-class"></a>
|
|
415
|
+
|
|
416
|
+
Класс `AbilityPolicy` предназначен для создания политик
|
|
417
|
+
|
|
418
|
+
Метод `enforce`. В качестве текста сообщения об ошибке доступа, будет возвращено название политики, причем, только первой, политики, которая не прошла проверку.
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
### Синтаксис политик <a name="policy-syntax"></a>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import AbilityRule, { AbilityRuleStatus, AbilityRuleConfig } from './AbilityRule';
|
|
2
|
+
/**
|
|
3
|
+
* Compare method.\
|
|
4
|
+
* For the «and» method the entity (policy, rule or rule) will be permitted if all\
|
|
5
|
+
* entities will be returns «permit» status and for the «or» - if\
|
|
6
|
+
* one of the entities returns as «permit»
|
|
7
|
+
*/
|
|
8
|
+
export type AbilityCompareMethod = 'or' | 'and';
|
|
9
|
+
export type AbilityPolicyResult = {
|
|
10
|
+
readonly permission: AbilityRuleStatus;
|
|
11
|
+
readonly deniedRules: readonly AbilityRule[];
|
|
12
|
+
readonly deniedPolicies: readonly AbilityPolicy[];
|
|
13
|
+
};
|
|
14
|
+
export type AbilityPolicyConfig = {
|
|
15
|
+
readonly id?: string;
|
|
16
|
+
readonly name?: string;
|
|
17
|
+
readonly description?: string;
|
|
18
|
+
readonly rulesCompareMethod?: AbilityCompareMethod;
|
|
19
|
+
readonly policiesCompareMethod?: AbilityCompareMethod;
|
|
20
|
+
readonly rules?: AbilityRuleConfig[] | null;
|
|
21
|
+
readonly policies?: AbilityPolicyConfig[] | null;
|
|
22
|
+
};
|
|
23
|
+
export declare class AbilityPolicy<Subject = unknown, Resource = unknown, Environment = unknown> {
|
|
24
|
+
/**
|
|
25
|
+
* List of rules
|
|
26
|
+
*/
|
|
27
|
+
rules: AbilityRule[];
|
|
28
|
+
/**
|
|
29
|
+
* Nested policies
|
|
30
|
+
*/
|
|
31
|
+
policies: AbilityPolicy[];
|
|
32
|
+
/**
|
|
33
|
+
* Rules compare method.\
|
|
34
|
+
* For the «and» method the rule will be permitted if all\
|
|
35
|
+
* rules will be returns «permit» status and for the «or» - if\
|
|
36
|
+
* one of the rules returns as «permit»
|
|
37
|
+
*/
|
|
38
|
+
rulesCompareMethod: AbilityCompareMethod;
|
|
39
|
+
/**
|
|
40
|
+
* Policies compare method.\
|
|
41
|
+
* For the «and» method the policy will be permitted if all\
|
|
42
|
+
* policies will be returns «permit» status and for the «or» - if\
|
|
43
|
+
* one of the policies returns as «permit»
|
|
44
|
+
*/
|
|
45
|
+
policiesCompareMethod: AbilityCompareMethod;
|
|
46
|
+
/**
|
|
47
|
+
* Policy name
|
|
48
|
+
*/
|
|
49
|
+
name: string | symbol;
|
|
50
|
+
/**
|
|
51
|
+
* Policy description
|
|
52
|
+
*/
|
|
53
|
+
description: string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Policy ID
|
|
56
|
+
*/
|
|
57
|
+
id: string | symbol;
|
|
58
|
+
constructor(policyName: string | symbol | undefined, policyID: string | symbol | undefined, description?: string);
|
|
59
|
+
addRule(rule: AbilityRule, compareMethod?: AbilityCompareMethod): this;
|
|
60
|
+
addRules(rules: AbilityRule[], compareMethod?: AbilityCompareMethod): this;
|
|
61
|
+
addPolicy(policy: AbilityPolicy, compareMethod?: AbilityCompareMethod): this;
|
|
62
|
+
addPolicies(policies: AbilityPolicy[], compareMethod?: AbilityCompareMethod): this;
|
|
63
|
+
getName(): string | symbol;
|
|
64
|
+
getID(): string | symbol;
|
|
65
|
+
getPolicies(): AbilityPolicy<unknown, unknown, unknown>[];
|
|
66
|
+
getRules(): AbilityRule<unknown, unknown, unknown>[];
|
|
67
|
+
setDescription(description: string): this;
|
|
68
|
+
enforce(subject: Subject | null, resource?: Resource | null, environment?: Environment | null): void | never;
|
|
69
|
+
isPermit(subject: Subject, resource?: Resource, environment?: Environment): boolean;
|
|
70
|
+
isDeny(subject: Subject, resource?: Resource, environment?: Environment): boolean;
|
|
71
|
+
check(subject: Subject | null, resource?: Resource | null, environment?: Environment | null): AbilityPolicyResult;
|
|
72
|
+
/**
|
|
73
|
+
* Parse the config JSON format to Policy class instance
|
|
74
|
+
*/
|
|
75
|
+
static parse<Subject = unknown, Resource = unknown, Environment = unknown>(configOrJson: AbilityPolicyConfig | string): AbilityPolicy<Subject, Resource, Environment>;
|
|
76
|
+
static validatePolicy(policy: AbilityPolicy): void | never;
|
|
77
|
+
}
|
|
78
|
+
export default AbilityPolicy;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export type AbilityRuleStatus = 'permit' | 'deny';
|
|
2
|
+
export type SubjectPrefix = 'subject.' | 'environment.';
|
|
3
|
+
export type AbilityRuleMatches = [
|
|
4
|
+
`${SubjectPrefix}${string}`,
|
|
5
|
+
AbilityCondition,
|
|
6
|
+
string | number | boolean
|
|
7
|
+
];
|
|
8
|
+
export type AbilityCondition = '=' | '<>' | '>' | '<' | '<=' | '>=' | 'in';
|
|
9
|
+
export type AbilityRuleConfig = {
|
|
10
|
+
readonly name?: string | symbol;
|
|
11
|
+
readonly effect?: AbilityRuleStatus;
|
|
12
|
+
readonly matches: AbilityRuleMatches;
|
|
13
|
+
};
|
|
14
|
+
export declare class AbilityRule<Subject = unknown, Resource = unknown, Environment = unknown> {
|
|
15
|
+
matches: AbilityRuleMatches;
|
|
16
|
+
name: string | symbol;
|
|
17
|
+
effect: AbilityRuleStatus;
|
|
18
|
+
/**
|
|
19
|
+
* Create the rule to compare
|
|
20
|
+
*
|
|
21
|
+
* @param ruleName {string} - The rule name
|
|
22
|
+
* @param effect {AbilityRuleStatus} - Return value
|
|
23
|
+
* @param matches {AbilityRuleMatches} - The matching rule he matching rule can be on of the format:
|
|
24
|
+
* \
|
|
25
|
+
* For example, be compared two's data\
|
|
26
|
+
* \
|
|
27
|
+
* _The subject_
|
|
28
|
+
* ```json
|
|
29
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
30
|
+
* ```
|
|
31
|
+
* and _The resource_
|
|
32
|
+
* ```json
|
|
33
|
+
* {"departamentID": "154", "departamentName": "NBC"}
|
|
34
|
+
* ```
|
|
35
|
+
* \
|
|
36
|
+
* Now we can make the matching rule:
|
|
37
|
+
* ```json
|
|
38
|
+
* ["subject.userDepartament", "=", "resource.departamentName"]
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* \
|
|
42
|
+
* **Example 2.**\
|
|
43
|
+
* In this case will be compared resource and string:
|
|
44
|
+
* \
|
|
45
|
+
* _The subject_
|
|
46
|
+
* ```json
|
|
47
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
48
|
+
* ```
|
|
49
|
+
* and _The resource_ will be «undefined».\
|
|
50
|
+
* Now we can make the matching rule:
|
|
51
|
+
* ```json
|
|
52
|
+
* ["subject.userDepartament", "=", "NBC"]
|
|
53
|
+
* ```
|
|
54
|
+
* \
|
|
55
|
+
* **Example 3.**\
|
|
56
|
+
* In this case will be compared resource and array of string:\
|
|
57
|
+
* \
|
|
58
|
+
* _The subject_
|
|
59
|
+
* ```json
|
|
60
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
61
|
+
* ```
|
|
62
|
+
* and _The resource_
|
|
63
|
+
* ```json
|
|
64
|
+
* ["FOX", "NBC", "AONE"]
|
|
65
|
+
* ```
|
|
66
|
+
* \
|
|
67
|
+
* Now we can make the matching rule:
|
|
68
|
+
* ```json
|
|
69
|
+
* ["subject.userDepartament", "=", "resource"]
|
|
70
|
+
* ```
|
|
71
|
+
* **Note: In this rule whe set the resource field as the «resource» string.\
|
|
72
|
+
* This means that we will compare the entire resource as a whole,\
|
|
73
|
+
* and not search for it by field name.**
|
|
74
|
+
* \
|
|
75
|
+
* **Example 4.**\
|
|
76
|
+
* In this case will be compared resource and array of string:\
|
|
77
|
+
* \
|
|
78
|
+
* _The subject_
|
|
79
|
+
* ```json
|
|
80
|
+
* {"user": {"account": {"roles": ["admin", "viewer"]}}}
|
|
81
|
+
* ```
|
|
82
|
+
* and _The resource_
|
|
83
|
+
* ```json
|
|
84
|
+
* undefined
|
|
85
|
+
* ```
|
|
86
|
+
* \
|
|
87
|
+
* Now we can make the matching rule:
|
|
88
|
+
* ```json
|
|
89
|
+
* ["subject.user.account.roles", "in", "admin"]
|
|
90
|
+
*/
|
|
91
|
+
constructor(matches: AbilityRuleMatches, effect?: AbilityRuleStatus, ruleName?: string | symbol);
|
|
92
|
+
getName(): string | symbol;
|
|
93
|
+
getEffect(): AbilityRuleStatus;
|
|
94
|
+
isPermit(subject: Subject, resource?: Resource | undefined, environment?: Environment | undefined): boolean;
|
|
95
|
+
isDeny(subject: Subject, resource?: Resource | undefined, environment?: Environment | undefined): boolean;
|
|
96
|
+
check(subject: Subject, resource?: Resource | undefined, environment?: Environment | undefined): AbilityRuleStatus;
|
|
97
|
+
extractValues(sub: unknown, res?: unknown | undefined, env?: unknown | undefined): [
|
|
98
|
+
string | number | boolean | (string | number)[] | null | undefined,
|
|
99
|
+
string | number | boolean | (string | number)[] | null | undefined
|
|
100
|
+
];
|
|
101
|
+
getDotNotationValue(resource: unknown, desc: string): unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Parsing the rule config object or JSON string\
|
|
104
|
+
* of config and returns the AbilityRule class instance
|
|
105
|
+
*/
|
|
106
|
+
static parse<Subject = unknown, Resource = unknown, Environment = unknown>(configOrJson: AbilityRuleConfig | string): AbilityRule<Subject, Resource, Environment>;
|
|
107
|
+
}
|
|
108
|
+
export default AbilityRule;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import AbilityRule, { AbilityRuleStatus } from './AbilityRule';
|
|
2
|
+
import AbilityPolicy, { AbilityCompareMethod, AbilityPolicyResult } from './AbilityPolicy';
|
|
3
|
+
export type AbilityEnforceResult = {
|
|
4
|
+
readonly permission: AbilityRuleStatus;
|
|
5
|
+
readonly deniedRules: readonly AbilityRule[];
|
|
6
|
+
readonly deniedPolicies: readonly AbilityPolicy[];
|
|
7
|
+
};
|
|
8
|
+
declare class AbilityService {
|
|
9
|
+
/**
|
|
10
|
+
* Create the rule to compare
|
|
11
|
+
*
|
|
12
|
+
* @param ruleName {string} - The rule name
|
|
13
|
+
* @param effect {AbilityRuleStatus} - Return value
|
|
14
|
+
* @param matches {AbilityRuleMatches} - The matching rule he matching rule can be on of the format:
|
|
15
|
+
* \
|
|
16
|
+
* For example, be compared two's data\
|
|
17
|
+
* \
|
|
18
|
+
* _The subject_
|
|
19
|
+
* ```json
|
|
20
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
21
|
+
* ```
|
|
22
|
+
* and _The resource_
|
|
23
|
+
* ```json
|
|
24
|
+
* {"departamentID": "154", "departamentName": "NBC"}
|
|
25
|
+
* ```
|
|
26
|
+
* \
|
|
27
|
+
* Now we can make the matching rule:
|
|
28
|
+
* ```json
|
|
29
|
+
* ["subject.userDepartament", "=", "resource.departamentName"]
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* \
|
|
33
|
+
* **Example 2.**\
|
|
34
|
+
* In this case will be compared resource and string:
|
|
35
|
+
* \
|
|
36
|
+
* _The subject_
|
|
37
|
+
* ```json
|
|
38
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
39
|
+
* ```
|
|
40
|
+
* and _The resource_ will be «undefined».\
|
|
41
|
+
* Now we can make the matching rule:
|
|
42
|
+
* ```json
|
|
43
|
+
* ["subject.userDepartament", "=", "NBC"]
|
|
44
|
+
* ```
|
|
45
|
+
* \
|
|
46
|
+
* **Example 3.**\
|
|
47
|
+
* In this case will be compared resource and array of string:\
|
|
48
|
+
* \
|
|
49
|
+
* _The subject_
|
|
50
|
+
* ```json
|
|
51
|
+
* {"userID": "1", "userDepartament": "NBC"}
|
|
52
|
+
* ```
|
|
53
|
+
* and _The resource_
|
|
54
|
+
* ```json
|
|
55
|
+
* ["FOX", "NBC", "AONE"]
|
|
56
|
+
* ```
|
|
57
|
+
* \
|
|
58
|
+
* Now we can make the matching rule:
|
|
59
|
+
* ```json
|
|
60
|
+
* ["subject.userDepartament", "=", "resource"]
|
|
61
|
+
* ```
|
|
62
|
+
* **Note: In this rule whe set the resource field as the «resource» string.\
|
|
63
|
+
* This means that we will compare the entire resource as a whole,\
|
|
64
|
+
* and not search for it by field name.**
|
|
65
|
+
*/
|
|
66
|
+
createRule(...args: ConstructorParameters<typeof AbilityRule>): AbilityRule;
|
|
67
|
+
/**
|
|
68
|
+
* Create the Policy class instance
|
|
69
|
+
*/
|
|
70
|
+
createPolicy(...args: ConstructorParameters<typeof AbilityPolicy>): AbilityPolicy;
|
|
71
|
+
checkPolicies(policiesResult: readonly AbilityPolicyResult[], compareMethod?: AbilityCompareMethod | undefined): AbilityEnforceResult;
|
|
72
|
+
enforcePolicies(policiesResult: readonly AbilityPolicyResult[], compareMethod?: AbilityCompareMethod | undefined): void | never;
|
|
73
|
+
}
|
|
74
|
+
export default AbilityService;
|