@flowerforce/flower-core 4.0.1-beta.0 → 4.0.1-beta.10
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/CHANGELOG.md +35 -0
- package/dist/index.cjs.js +171 -102
- package/dist/index.esm.js +162 -101
- package/dist/src/ABAC/abacEngine.d.ts +32 -0
- package/dist/src/ABAC/abacSingleton.d.ts +8 -0
- package/dist/src/ABAC/index.d.ts +2 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/interfaces/CoreInterface.d.ts +16 -23
- package/dist/src/interfaces/ReducerInterface.d.ts +26 -28
- package/dist/src/interfaces/RulesMatcherInterface.d.ts +11 -0
- package/dist/src/interfaces/SelectorsInterface.d.ts +17 -17
- package/dist/src/interfaces/Store.d.ts +1 -1
- package/dist/src/interfaces/UtilsInterface.d.ts +1 -1
- package/dist/src/rules-matcher/RulesMatcher.d.ts +1 -1
- package/dist/src/state-manager/state-functions/FlowerCoreMergedReducers.d.ts +2 -2
- package/dist/src/state-manager/state-functions/FlowerDataStateFunctions.d.ts +2 -13
- package/dist/src/state-manager/state-functions/index.d.ts +1 -1
- package/dist/src/state-manager/state-selectors/FlowerDataStateSelectors.d.ts +2 -0
- package/dist/src/state-manager/state-selectors/FlowerSelectorsMerged.d.ts +2 -2
- package/dist/src/state-manager/state-selectors/index.d.ts +2 -2
- package/dist/src/utils/FlowerCoreStateUtils.d.ts +1 -1
- package/dist/src/utils/FlowerDataCoreUtils.d.ts +2 -0
- package/dist/src/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/src/state-manager/state-selectors/FlowerFormStateSelectors.d.ts +0 -2
- package/dist/src/utils/FlowerFormCoreUtils.d.ts +0 -2
package/dist/index.esm.js
CHANGED
|
@@ -30,7 +30,7 @@ const devtoolState = {};
|
|
|
30
30
|
const FlowerStateUtils = {
|
|
31
31
|
getAllData: (state) => state &&
|
|
32
32
|
Object.entries(state ?? {}).reduce((acc, [k, v]) => ({ ...acc, [k]: v?.data ?? v }), {}),
|
|
33
|
-
|
|
33
|
+
selectFlowerDataNode: (name) => (state) => _get(state, name),
|
|
34
34
|
makeSelectCurrentNextRules: (name) => (state) => {
|
|
35
35
|
const nextRules = _get(state, [name, 'nextRules']);
|
|
36
36
|
const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(name)(state);
|
|
@@ -42,24 +42,107 @@ const FlowerStateUtils = {
|
|
|
42
42
|
return _get(subState, ['current']) || startId;
|
|
43
43
|
},
|
|
44
44
|
makeSelectNodeErrors: (name) => (state) => {
|
|
45
|
-
const
|
|
46
|
-
return
|
|
45
|
+
const data = FlowerStateUtils.selectFlowerDataNode(name)(state);
|
|
46
|
+
return generateData(data);
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
const
|
|
50
|
-
const validationErrors =
|
|
49
|
+
const generateData = (data) => {
|
|
50
|
+
const validationErrors = data && data.errors;
|
|
51
51
|
const allErrors = Object.values(validationErrors || {});
|
|
52
52
|
return {
|
|
53
|
-
isSubmitted:
|
|
54
|
-
isDirty: Object.values(
|
|
55
|
-
hasFocus:
|
|
56
|
-
errors:
|
|
57
|
-
customErrors:
|
|
58
|
-
isValidating:
|
|
53
|
+
isSubmitted: data?.isSubmitted || false,
|
|
54
|
+
isDirty: Object.values(data?.dirty || {}).some(Boolean) || false,
|
|
55
|
+
hasFocus: data?.hasFocus,
|
|
56
|
+
errors: data?.errors,
|
|
57
|
+
customErrors: data?.customErrors,
|
|
58
|
+
isValidating: data?.isValidating,
|
|
59
59
|
isValid: allErrors.flat().length === 0
|
|
60
60
|
};
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
class AbacEngine {
|
|
64
|
+
constructor(rules = []) {
|
|
65
|
+
// compile string conditions once
|
|
66
|
+
this.rules = (rules || [])
|
|
67
|
+
.map((r) => {
|
|
68
|
+
let conditionFn = undefined;
|
|
69
|
+
if (r.condition) {
|
|
70
|
+
try {
|
|
71
|
+
// NOTE: uses new Function -> ensure rules.json is trusted by the app owner
|
|
72
|
+
conditionFn = new Function('subject', 'resource', 'action', 'environment', `return (${r.condition});`);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error('ABAC: failed to compile rule condition', r.id, err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { ...r, conditionFn };
|
|
79
|
+
})
|
|
80
|
+
.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
|
81
|
+
}
|
|
82
|
+
decide(ctx) {
|
|
83
|
+
let anyPermit = false;
|
|
84
|
+
for (const r of this.rules) {
|
|
85
|
+
const matchesAction = Array.isArray(r.action)
|
|
86
|
+
? r.action.includes(ctx.action)
|
|
87
|
+
: r.action === ctx.action || r.action === '*';
|
|
88
|
+
if (!matchesAction)
|
|
89
|
+
continue;
|
|
90
|
+
let matches = true;
|
|
91
|
+
if (r.conditionFn) {
|
|
92
|
+
try {
|
|
93
|
+
matches = !!r.conditionFn(ctx.subject, ctx.resource, ctx.action, ctx.environment);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
matches = false;
|
|
97
|
+
console.error(`ABAC: rule ${r.id} threw while evaluating`, err);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!matches)
|
|
101
|
+
continue;
|
|
102
|
+
if (r.effect === 'Deny') {
|
|
103
|
+
// deny-overrides
|
|
104
|
+
return 'Deny';
|
|
105
|
+
}
|
|
106
|
+
if (r.effect === 'Permit') {
|
|
107
|
+
anyPermit = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return anyPermit ? 'Permit' : 'Deny';
|
|
111
|
+
}
|
|
112
|
+
// utility: checks multiple actions and returns those allowed
|
|
113
|
+
allowedActions(ctxBase, actions) {
|
|
114
|
+
return actions.filter((a) => this.decide({ ...ctxBase, action: a }) === 'Permit');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let engine = null;
|
|
119
|
+
let rawRules = null;
|
|
120
|
+
let currentSubject = null;
|
|
121
|
+
function initAbac(rules) {
|
|
122
|
+
engine = new AbacEngine(rules);
|
|
123
|
+
rawRules = rules;
|
|
124
|
+
}
|
|
125
|
+
function getAbacEngine() {
|
|
126
|
+
if (!engine)
|
|
127
|
+
console.warn('ABAC engine non inizializzato');
|
|
128
|
+
return engine;
|
|
129
|
+
}
|
|
130
|
+
function getRawRules() {
|
|
131
|
+
return rawRules;
|
|
132
|
+
}
|
|
133
|
+
function isAbacInitialized() {
|
|
134
|
+
return engine !== null;
|
|
135
|
+
}
|
|
136
|
+
function setSubject(subject) {
|
|
137
|
+
currentSubject = subject;
|
|
138
|
+
}
|
|
139
|
+
function getSubject() {
|
|
140
|
+
return currentSubject;
|
|
141
|
+
}
|
|
142
|
+
function clearSubject() {
|
|
143
|
+
currentSubject = null;
|
|
144
|
+
}
|
|
145
|
+
|
|
63
146
|
const EMPTY_STRING_REGEXP = /^\s*$/;
|
|
64
147
|
/**
|
|
65
148
|
* Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
|
|
@@ -83,6 +166,11 @@ const rulesMatcherUtils = {
|
|
|
83
166
|
if (!operators[op]) {
|
|
84
167
|
throw new Error(`Error missing operator:${op}`);
|
|
85
168
|
}
|
|
169
|
+
if (op === '$can') {
|
|
170
|
+
const subject = getSubject();
|
|
171
|
+
const valid = operators['$can'](subject ?? {}, _get(value, 'action'), _get(value, 'resource'));
|
|
172
|
+
return { valid, name: `${pathWithPrefix}___${name || op}` };
|
|
173
|
+
}
|
|
86
174
|
const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
|
|
87
175
|
return { valid, name: `${pathWithPrefix}___${name || op}` };
|
|
88
176
|
},
|
|
@@ -240,6 +328,11 @@ const rulesMatcherUtils = {
|
|
|
240
328
|
* Defines a set of comparison operators used for matching rules against user input.
|
|
241
329
|
*/
|
|
242
330
|
const operators = {
|
|
331
|
+
$can: (subject, action, resource) => getAbacEngine()?.decide({
|
|
332
|
+
subject: subject ?? {},
|
|
333
|
+
action,
|
|
334
|
+
resource
|
|
335
|
+
}) === 'Permit',
|
|
243
336
|
$exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
|
|
244
337
|
$eq: (a, b) => a === b,
|
|
245
338
|
$ne: (a, b) => a !== b,
|
|
@@ -265,19 +358,19 @@ const operators = {
|
|
|
265
358
|
.some((c) => c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))
|
|
266
359
|
};
|
|
267
360
|
|
|
268
|
-
const rulesMatcher = (rules,
|
|
361
|
+
const rulesMatcher = (rules, dataValue = {}, apply = true, options) => {
|
|
269
362
|
if (!rules)
|
|
270
363
|
return [apply];
|
|
271
364
|
// if (typeof rules !== 'object' && !Array.isArray(rules)) {
|
|
272
365
|
// throw new Error('Rules accept only array or object');
|
|
273
366
|
// }
|
|
274
367
|
if (typeof rules === 'function') {
|
|
275
|
-
return [rules(
|
|
368
|
+
return [rules(dataValue) === apply];
|
|
276
369
|
}
|
|
277
370
|
const conditions = Array.isArray(rules)
|
|
278
371
|
? { $and: rules }
|
|
279
372
|
: rules;
|
|
280
|
-
const valid = rulesMatcherUtils.checkRule(conditions,
|
|
373
|
+
const valid = rulesMatcherUtils.checkRule(conditions, dataValue, options ?? {});
|
|
281
374
|
return [valid === apply];
|
|
282
375
|
};
|
|
283
376
|
|
|
@@ -302,9 +395,6 @@ const flattenRules = (ob) => {
|
|
|
302
395
|
}
|
|
303
396
|
return result;
|
|
304
397
|
};
|
|
305
|
-
const getRulesExists = (rules) => {
|
|
306
|
-
return Object.keys(rules).length ? FlowUtils.mapEdge(rules) : undefined;
|
|
307
|
-
};
|
|
308
398
|
const FlowUtils = {
|
|
309
399
|
generateRulesName: (nextRules) => {
|
|
310
400
|
return nextRules.reduce((acc, inc) => {
|
|
@@ -361,23 +451,6 @@ const FlowUtils = {
|
|
|
361
451
|
return res;
|
|
362
452
|
},
|
|
363
453
|
makeRules: (rules) => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, { nodeId: k, rules: v }], []),
|
|
364
|
-
// TODO: This function is strictly related to React nodes, could make sense to move it in the flower-react folder
|
|
365
|
-
generateNodesForFlowerJson: (nodes) => nodes
|
|
366
|
-
.filter((e) => !!_get(e, 'props.id'))
|
|
367
|
-
.map((e) => {
|
|
368
|
-
const rules = FlowUtils.makeRules(e.props.to ?? {});
|
|
369
|
-
const nextRules = getRulesExists(rules);
|
|
370
|
-
const children = e.props.data?.children;
|
|
371
|
-
return {
|
|
372
|
-
nodeId: e.props.id,
|
|
373
|
-
nodeType: e.type.displayName || e.props.as || 'FlowerNode',
|
|
374
|
-
nodeTitle: _get(e.props, 'data.title'),
|
|
375
|
-
children,
|
|
376
|
-
nextRules,
|
|
377
|
-
retain: e.props.retain,
|
|
378
|
-
disabled: e.props.disabled
|
|
379
|
-
};
|
|
380
|
-
}),
|
|
381
454
|
allEqual: (arr, arr2) => arr.length === arr2.length && arr.every((v) => arr2.includes(v)),
|
|
382
455
|
findValidRule: (nextRules, value, prefix) => find(nextRules, (rule) => {
|
|
383
456
|
// fix per evitare di entrare in un nodo senza regole, ma con un name,
|
|
@@ -404,7 +477,7 @@ const FlowUtils = {
|
|
|
404
477
|
})
|
|
405
478
|
};
|
|
406
479
|
|
|
407
|
-
const
|
|
480
|
+
const DataUtils = {
|
|
408
481
|
cleanPath: (name, char = '^') => _trimStart(name, char),
|
|
409
482
|
getPath: (idValue) => {
|
|
410
483
|
if (!idValue) {
|
|
@@ -418,9 +491,9 @@ const FormUtils = {
|
|
|
418
491
|
};
|
|
419
492
|
}
|
|
420
493
|
if (idValue.indexOf('^') === 0) {
|
|
421
|
-
const [
|
|
494
|
+
const [rootName, ...rest] = DataUtils.cleanPath(idValue).split('.');
|
|
422
495
|
return {
|
|
423
|
-
|
|
496
|
+
rootName,
|
|
424
497
|
path: rest
|
|
425
498
|
};
|
|
426
499
|
}
|
|
@@ -435,7 +508,7 @@ const FormUtils = {
|
|
|
435
508
|
*/
|
|
436
509
|
const CoreUtils = {
|
|
437
510
|
...FlowUtils,
|
|
438
|
-
...
|
|
511
|
+
...DataUtils
|
|
439
512
|
};
|
|
440
513
|
|
|
441
514
|
const { generateNodes, hasNode, makeObjectRules, generateRulesName, findValidRule } = CoreUtils;
|
|
@@ -648,7 +721,7 @@ const FlowerCoreBaseReducers = {
|
|
|
648
721
|
payload: { name: flowName, node: nextNumberNode }
|
|
649
722
|
});
|
|
650
723
|
},
|
|
651
|
-
|
|
724
|
+
back: (state, { payload }) => {
|
|
652
725
|
const { name, flowName } = payload;
|
|
653
726
|
FlowerCoreBaseReducers.historyPop(state, {
|
|
654
727
|
type: 'historyPop',
|
|
@@ -672,75 +745,65 @@ const FlowerCoreBaseReducers = {
|
|
|
672
745
|
};
|
|
673
746
|
|
|
674
747
|
const { getPath } = CoreUtils;
|
|
675
|
-
/**
|
|
676
|
-
* formName => FlowerForm
|
|
677
|
-
* initialData => FlowerForm
|
|
678
|
-
* fieldName => FlowerField
|
|
679
|
-
* fieldValue => FlowerField
|
|
680
|
-
* errors => FlowerField
|
|
681
|
-
* customErrors => FlowerField
|
|
682
|
-
* fieldTouched => FlowerField
|
|
683
|
-
* fieldDirty => FlowerField
|
|
684
|
-
* fieldHasFocus => FlowerField
|
|
685
|
-
*/
|
|
686
748
|
const FlowerCoreDataReducers = {
|
|
687
|
-
|
|
688
|
-
|
|
749
|
+
setFormSubmitted: (state, { payload }) => {
|
|
750
|
+
const rootPath = typeof payload === 'string' ? payload : payload.rootName;
|
|
751
|
+
if (!_get(state, [rootPath])) {
|
|
689
752
|
return state;
|
|
690
753
|
}
|
|
691
|
-
_set(state,
|
|
754
|
+
_set(state, [rootPath, 'isSubmitted'], true);
|
|
692
755
|
return state;
|
|
693
756
|
},
|
|
694
|
-
|
|
695
|
-
_set(state, [payload.
|
|
757
|
+
addCustomDataErrors: (state, { payload }) => {
|
|
758
|
+
_set(state, [payload.rootName, 'customErrors', payload.id], payload.errors);
|
|
696
759
|
},
|
|
697
|
-
|
|
698
|
-
_set(state, [payload.
|
|
760
|
+
addDataErrors: (state, { payload }) => {
|
|
761
|
+
_set(state, [payload.rootName, 'errors', payload.id], payload.errors);
|
|
699
762
|
},
|
|
700
|
-
|
|
701
|
-
_unset(state, [payload.
|
|
702
|
-
_unset(state, [payload.
|
|
703
|
-
_unset(state, [payload.
|
|
763
|
+
removeDataErrors: (state, { payload }) => {
|
|
764
|
+
_unset(state, [payload.rootName, 'errors', payload.id]);
|
|
765
|
+
_unset(state, [payload.rootName, 'customErrors', payload.id]);
|
|
766
|
+
_unset(state, [payload.rootName, 'isValidating']);
|
|
704
767
|
},
|
|
705
|
-
|
|
706
|
-
_set(state, [payload.
|
|
768
|
+
fieldTouch: (state, { payload }) => {
|
|
769
|
+
_set(state, [payload.rootName, 'touches', payload.id], payload.touched);
|
|
707
770
|
},
|
|
708
|
-
|
|
709
|
-
_set(state, [payload.
|
|
771
|
+
fieldDirty: (state, { payload }) => {
|
|
772
|
+
_set(state, [payload.rootName, 'dirty', payload.id], payload.dirty);
|
|
710
773
|
},
|
|
711
|
-
|
|
774
|
+
fieldFocus: (state, { payload }) => {
|
|
712
775
|
if (!payload.focused) {
|
|
713
|
-
_unset(state, [payload.
|
|
776
|
+
_unset(state, [payload.rootName, 'hasFocus']);
|
|
714
777
|
return;
|
|
715
778
|
}
|
|
716
|
-
_set(state, [payload.
|
|
779
|
+
_set(state, [payload.rootName, 'hasFocus'], payload.id);
|
|
717
780
|
},
|
|
718
781
|
addData: (state, { payload }) => {
|
|
719
|
-
const prevData = _get(state, [payload.
|
|
720
|
-
_set(state, [payload.
|
|
782
|
+
const prevData = _get(state, [payload.rootName, 'data'], {});
|
|
783
|
+
_set(state, [payload.rootName, 'data'], { ...prevData, ...payload.value });
|
|
721
784
|
},
|
|
722
785
|
addDataByPath: (state, { payload }) => {
|
|
723
786
|
const { path: newpath } = getPath(payload.id);
|
|
724
787
|
if (payload.id && payload.id.length) {
|
|
725
|
-
_set(state, [payload.
|
|
788
|
+
_set(state, [payload.rootName, 'data', ...newpath], payload.value);
|
|
726
789
|
if (payload && payload.dirty) {
|
|
727
|
-
_set(state, [payload.
|
|
790
|
+
_set(state, [payload.rootName, 'dirty', payload.id], payload.dirty);
|
|
728
791
|
}
|
|
729
792
|
}
|
|
730
793
|
},
|
|
731
794
|
// TODO usato al momento solo il devtool
|
|
732
795
|
replaceData: /* istanbul ignore next */ (state, { payload }) => {
|
|
733
796
|
/* istanbul ignore next */
|
|
734
|
-
_set(state, [payload.
|
|
797
|
+
_set(state, [payload.rootName, 'data'], payload.value);
|
|
735
798
|
},
|
|
736
799
|
unsetData: (state, { payload }) => {
|
|
737
|
-
_unset(state, [payload.
|
|
800
|
+
_unset(state, [payload.rootName, 'data', ...payload.id]);
|
|
738
801
|
},
|
|
739
|
-
|
|
740
|
-
_set(state, [payload.
|
|
802
|
+
setIsDataValidating: (state, { payload }) => {
|
|
803
|
+
_set(state, [payload.rootName, 'isValidating'], payload.isValidating);
|
|
741
804
|
},
|
|
742
|
-
|
|
743
|
-
const touchedFields = _get(state, [
|
|
805
|
+
resetData: (state, { payload: { rootName, initialData } }) => {
|
|
806
|
+
const touchedFields = _get(state, [rootName, 'errors'], {});
|
|
744
807
|
const newStateData = initialData
|
|
745
808
|
? Object.keys(touchedFields).reduce((acc, key) => {
|
|
746
809
|
const { path } = getPath(key);
|
|
@@ -749,13 +812,13 @@ const FlowerCoreDataReducers = {
|
|
|
749
812
|
return acc;
|
|
750
813
|
}, {})
|
|
751
814
|
: {};
|
|
752
|
-
_set(state, [
|
|
753
|
-
_unset(state, [
|
|
754
|
-
_unset(state, [
|
|
755
|
-
_unset(state, [
|
|
815
|
+
_set(state, [rootName, 'data'], newStateData);
|
|
816
|
+
_unset(state, [rootName, 'touches']);
|
|
817
|
+
_unset(state, [rootName, 'dirty']);
|
|
818
|
+
_unset(state, [rootName, 'isSubmitted']);
|
|
756
819
|
},
|
|
757
|
-
|
|
758
|
-
_set(state, [
|
|
820
|
+
initData: (state, { payload: { rootName, initialData } }) => {
|
|
821
|
+
_set(state, [rootName, 'data'], initialData);
|
|
759
822
|
}
|
|
760
823
|
};
|
|
761
824
|
|
|
@@ -764,8 +827,6 @@ const FlowerCoreReducers = { ...FlowerCoreBaseReducers, ...FlowerCoreDataReducer
|
|
|
764
827
|
const FlowerCoreStateBaseSelectors = {
|
|
765
828
|
selectGlobal: (state) => state && state[REDUCER_NAME.FLOWER_FLOW],
|
|
766
829
|
selectFlower: (name) => (state) => _get(state, [name]),
|
|
767
|
-
// selectFlowerFormNode: (id) => (state) =>
|
|
768
|
-
// _get(state, [REDUCER_NAME.FLOWER_DATA, id]),
|
|
769
830
|
selectFlowerHistory: (flower) => _get(flower, ['history'], []),
|
|
770
831
|
makeSelectNodesIds: (flower) => _get(flower, ['nodes']),
|
|
771
832
|
makeSelectStartNodeId: (flower) => _get(flower, ['startId']),
|
|
@@ -794,24 +855,24 @@ const FlowerCoreStateBaseSelectors = {
|
|
|
794
855
|
|
|
795
856
|
const FlowerCoreStateDataSelectors = {
|
|
796
857
|
selectGlobalReducerByName: (name) => (state) => state[name] ?? state[REDUCER_NAME.FLOWER_DATA][name],
|
|
797
|
-
|
|
858
|
+
selectGlobalData: (state) => {
|
|
798
859
|
return state && state[REDUCER_NAME.FLOWER_DATA];
|
|
799
860
|
},
|
|
800
861
|
// getDataByFlow: (flower) => _get(flower, 'data') ?? {},
|
|
801
862
|
getDataFromState: (id) => (data) => (id === '*' ? data : _get(data, id)),
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
return
|
|
806
|
-
},
|
|
807
|
-
|
|
808
|
-
makeSelectNodeErrors:
|
|
809
|
-
makeSelectFieldError: (name, id, validate) => (
|
|
810
|
-
const customErrors = Object.entries((
|
|
863
|
+
makeSelectNodeDataSubmitted: (data) => data && data.isSubmitted,
|
|
864
|
+
makeSelectNodeDataFieldTouched: (id) => (data) => data && data.touches && data.touches[id],
|
|
865
|
+
makeSelectNodeDataFieldFocused: (id) => (data) => {
|
|
866
|
+
return data && data.hasFocus === id ? id : undefined;
|
|
867
|
+
},
|
|
868
|
+
makeSelectNodeDataFieldDirty: (id) => (data) => data && data.dirty && data.dirty[id],
|
|
869
|
+
makeSelectNodeErrors: generateData,
|
|
870
|
+
makeSelectFieldError: (name, id, validate) => (globalData, data) => {
|
|
871
|
+
const customErrors = Object.entries((data && data.customErrors) || {})
|
|
811
872
|
.filter(([k]) => k === id)
|
|
812
873
|
.map(([, v]) => v)
|
|
813
874
|
.flat();
|
|
814
|
-
if (!validate || !
|
|
875
|
+
if (!validate || !globalData)
|
|
815
876
|
return [];
|
|
816
877
|
const errors = validate.filter((rule) => {
|
|
817
878
|
if (!rule)
|
|
@@ -819,7 +880,7 @@ const FlowerCoreStateDataSelectors = {
|
|
|
819
880
|
if (!rule.rules)
|
|
820
881
|
return true;
|
|
821
882
|
const transformSelf = CoreUtils.mapKeysDeepLodash(rule.rules, (v, key) => key === '$self' ? id : key);
|
|
822
|
-
const [hasError] = rulesMatcher(transformSelf,
|
|
883
|
+
const [hasError] = rulesMatcher(transformSelf, globalData, false, {
|
|
823
884
|
prefix: name
|
|
824
885
|
});
|
|
825
886
|
return hasError;
|
|
@@ -827,8 +888,8 @@ const FlowerCoreStateDataSelectors = {
|
|
|
827
888
|
const result = errors.map((r) => (r && r.message) || 'error');
|
|
828
889
|
return [...customErrors, ...(result.length === 0 ? [] : result)];
|
|
829
890
|
},
|
|
830
|
-
selectorRulesDisabled: (id, rules, keys, flowName, value) => (
|
|
831
|
-
const newState = { ...
|
|
891
|
+
selectorRulesDisabled: (id, rules, keys, flowName, value) => (globalData, data) => {
|
|
892
|
+
const newState = { ...globalData, ...value, $data: data, $form: data };
|
|
832
893
|
const state = Object.assign(newState, id ? { $self: _get(newState, [flowName, ...id.split('.')]) } : {});
|
|
833
894
|
if (!rules)
|
|
834
895
|
return false;
|
|
@@ -853,4 +914,4 @@ const FlowerCoreStateSelectors = {
|
|
|
853
914
|
...FlowerCoreStateDataSelectors
|
|
854
915
|
};
|
|
855
916
|
|
|
856
|
-
export { CoreUtils, Emitter, FlowUtils, FlowerCoreBaseReducers, FlowerCoreDataReducers, FlowerCoreReducers, FlowerCoreStateBaseSelectors, FlowerCoreStateDataSelectors, FlowerCoreStateSelectors, FlowerStateUtils,
|
|
917
|
+
export { AbacEngine, CoreUtils, DataUtils, Emitter, FlowUtils, FlowerCoreBaseReducers, FlowerCoreDataReducers, FlowerCoreReducers, FlowerCoreStateBaseSelectors, FlowerCoreStateDataSelectors, FlowerCoreStateSelectors, FlowerStateUtils, REDUCER_NAME, clearSubject, devtoolState, flattenRules, generateData, getAbacEngine, getRawRules, getSubject, initAbac, isAbacInitialized, rulesMatcher, rulesMatcherUtils, setSubject };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type Action = string;
|
|
2
|
+
export type Subject = Record<string, any>;
|
|
3
|
+
export type Resource = Record<string, any>;
|
|
4
|
+
export type Environment = Record<string, any>;
|
|
5
|
+
export type AbacCtx = {
|
|
6
|
+
subject: Subject;
|
|
7
|
+
resource?: Resource;
|
|
8
|
+
action: Action;
|
|
9
|
+
environment?: Environment;
|
|
10
|
+
};
|
|
11
|
+
export type RuleInput = {
|
|
12
|
+
id: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
effect: 'Permit' | 'Deny';
|
|
15
|
+
priority?: number;
|
|
16
|
+
action?: string | string[];
|
|
17
|
+
/**
|
|
18
|
+
* condition: string JS expression that returns boolean.
|
|
19
|
+
* It can reference `subject`, `resource`, `action`, `environment`.
|
|
20
|
+
* Example: "action === 'read' && (!resource.confidential || resource.ownerID === subject.id)"
|
|
21
|
+
*/
|
|
22
|
+
condition?: string;
|
|
23
|
+
};
|
|
24
|
+
export type CompiledRule = RuleInput & {
|
|
25
|
+
conditionFn?: (subject: Subject, resource?: Resource, action?: Action, environment?: Environment) => boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare class AbacEngine {
|
|
28
|
+
private rules;
|
|
29
|
+
constructor(rules?: RuleInput[]);
|
|
30
|
+
decide(ctx: AbacCtx): 'Permit' | 'Deny';
|
|
31
|
+
allowedActions(ctxBase: Omit<AbacCtx, 'action'>, actions: Action[]): string[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AbacEngine, RuleInput, Subject } from './abacEngine';
|
|
2
|
+
export declare function initAbac(rules: RuleInput[]): void;
|
|
3
|
+
export declare function getAbacEngine(): AbacEngine | null;
|
|
4
|
+
export declare function getRawRules(): RuleInput[] | null;
|
|
5
|
+
export declare function isAbacInitialized(): boolean;
|
|
6
|
+
export declare function setSubject(subject: Subject): void;
|
|
7
|
+
export declare function getSubject(): Subject | null;
|
|
8
|
+
export declare function clearSubject(): void;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare enum RulesOperators {
|
|
2
2
|
$exists = "$exists",
|
|
3
|
+
$can = "$can",
|
|
3
4
|
$eq = "$eq",
|
|
4
5
|
$ne = "$ne",
|
|
5
6
|
$gt = "$gt",
|
|
@@ -26,6 +27,15 @@ export type Edge<T = object> = {
|
|
|
26
27
|
rules: RulesObject<T>;
|
|
27
28
|
};
|
|
28
29
|
};
|
|
30
|
+
export type NodeConfig = {
|
|
31
|
+
nodeId: string | undefined;
|
|
32
|
+
nodeType: string;
|
|
33
|
+
nodeTitle: string;
|
|
34
|
+
children: Node['children'];
|
|
35
|
+
nextRules: ReturnType<MapEdge> | undefined;
|
|
36
|
+
retain: boolean;
|
|
37
|
+
disabled: boolean;
|
|
38
|
+
};
|
|
29
39
|
export type Node = {
|
|
30
40
|
nodeId: string | undefined;
|
|
31
41
|
nodeType: string;
|
|
@@ -76,7 +86,7 @@ export type RulesObject<T> = RulesValuesType<T> | {
|
|
|
76
86
|
export type CleanPath = (name: string, char?: string) => string;
|
|
77
87
|
export type GetPath = (idValue?: string) => {
|
|
78
88
|
path: string | string[];
|
|
79
|
-
|
|
89
|
+
rootName?: string;
|
|
80
90
|
};
|
|
81
91
|
export type AllEqual = (...args: Array<number | string | boolean>[]) => boolean;
|
|
82
92
|
export type FindValidRule<T = Rules<RulesObject<any>>> = (nextRules: {
|
|
@@ -93,22 +103,12 @@ export type MapEdge<K = RulesByNodeId<any>, T = K[]> = (nextNode: T) => Array<K>
|
|
|
93
103
|
export type MakeRules<T extends Record<string, any> = {
|
|
94
104
|
rules: RulesObject<any> | object;
|
|
95
105
|
}> = (rules: T) => Array<RulesByNodeId<T>>;
|
|
96
|
-
export type GetRulesExists = (rules: RulesByNodeId<any>[]) => ReturnType<MapEdge> | undefined;
|
|
97
|
-
export type GenerateNodesForFlowerJson = (nodes: Node[], edges?: Edge[]) => {
|
|
98
|
-
nodeId: string | undefined;
|
|
99
|
-
nodeType: string;
|
|
100
|
-
nodeTitle: string;
|
|
101
|
-
children: Node['children'];
|
|
102
|
-
nextRules: ReturnType<GetRulesExists>;
|
|
103
|
-
retain: boolean;
|
|
104
|
-
disabled: boolean;
|
|
105
|
-
}[];
|
|
106
106
|
export type HasNode = (state: Record<string, any>, name: string, node: string) => boolean;
|
|
107
|
-
export type MakeObjectRules = (nodes:
|
|
107
|
+
export type MakeObjectRules = (nodes: NodeConfig[]) => {
|
|
108
108
|
[x: string]: Node['nextRules'];
|
|
109
109
|
};
|
|
110
|
-
export type GenerateNodes = (nodes:
|
|
111
|
-
[x: string]:
|
|
110
|
+
export type GenerateNodes = (nodes: NodeConfig[]) => {
|
|
111
|
+
[x: string]: Partial<Node>;
|
|
112
112
|
};
|
|
113
113
|
export type MapKeysDeepLodash = (obj: Record<string, any>, cb: (...args: any) => any, isRecursive?: boolean) => Record<string, any>;
|
|
114
114
|
export type GenerateRulesName = (nextRules: RulesWithName[]) => {
|
|
@@ -176,13 +176,6 @@ export interface FlowUtilitiesFunctions {
|
|
|
176
176
|
* @returns
|
|
177
177
|
*/
|
|
178
178
|
makeRules: MakeRules;
|
|
179
|
-
/**
|
|
180
|
-
* Generates nodes for a flower JSON structure, extracting rules and other properties.
|
|
181
|
-
* @param nodes
|
|
182
|
-
*
|
|
183
|
-
* @returns
|
|
184
|
-
*/
|
|
185
|
-
generateNodesForFlowerJson: GenerateNodesForFlowerJson;
|
|
186
179
|
/**
|
|
187
180
|
* Checks if two arrays are equal in length and have the same elements.
|
|
188
181
|
* @param arr
|
|
@@ -201,7 +194,7 @@ export interface FlowUtilitiesFunctions {
|
|
|
201
194
|
*/
|
|
202
195
|
findValidRule: FindValidRule;
|
|
203
196
|
}
|
|
204
|
-
export interface
|
|
197
|
+
export interface DataUtilitiesFunctions {
|
|
205
198
|
/**
|
|
206
199
|
* Removes specified characters from the beginning of a string (default char -> '^').
|
|
207
200
|
* @param name
|
|
@@ -218,6 +211,6 @@ export interface FormUtilitiesFunctions {
|
|
|
218
211
|
*/
|
|
219
212
|
getPath: GetPath;
|
|
220
213
|
}
|
|
221
|
-
export interface CoreUtilitiesFunctions extends
|
|
214
|
+
export interface CoreUtilitiesFunctions extends DataUtilitiesFunctions, FlowUtilitiesFunctions {
|
|
222
215
|
}
|
|
223
216
|
export {};
|