@frontegg/entitlements-javascript-commons 1.0.0-alpha.9 → 1.0.0

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.
Files changed (42) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +4 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/operations/date/sanitizers.d.ts +2 -0
  5. package/dist/operations/date/sanitizers.js +16 -5
  6. package/dist/operations/date/sanitizers.js.map +1 -1
  7. package/dist/operations/numeric/sanitizers.js +2 -2
  8. package/dist/operations/string/sanitizers.js +2 -2
  9. package/dist/operations/string/sanitizers.js.map +1 -1
  10. package/dist/user-entitlements/attributes.utils.d.ts +11 -0
  11. package/dist/user-entitlements/attributes.utils.js +43 -0
  12. package/dist/user-entitlements/attributes.utils.js.map +1 -0
  13. package/dist/user-entitlements/flatten.utils.d.ts +8 -0
  14. package/dist/user-entitlements/flatten.utils.js +39 -0
  15. package/dist/user-entitlements/flatten.utils.js.map +1 -0
  16. package/dist/user-entitlements/index.d.ts +2 -0
  17. package/dist/user-entitlements/index.js +2 -0
  18. package/dist/user-entitlements/index.js.map +1 -1
  19. package/dist/user-entitlements/is-entitled.evaluator.d.ts +0 -8
  20. package/dist/user-entitlements/is-entitled.evaluator.js +8 -24
  21. package/dist/user-entitlements/is-entitled.evaluator.js.map +1 -1
  22. package/dist/user-entitlements/permissions.utils.d.ts +3 -0
  23. package/dist/user-entitlements/permissions.utils.js +12 -0
  24. package/dist/user-entitlements/permissions.utils.js.map +1 -0
  25. package/dist/user-entitlements/types.d.ts +5 -4
  26. package/dist/user-entitlements/types.js.map +1 -1
  27. package/docs/CHANGELOG.md +89 -0
  28. package/package.json +1 -1
  29. package/src/index.ts +6 -0
  30. package/src/operations/date/sanitizers.ts +19 -4
  31. package/src/operations/date/tests/sanitizers.spec.ts +18 -1
  32. package/src/operations/numeric/sanitizers.ts +2 -2
  33. package/src/operations/string/sanitizers.ts +2 -2
  34. package/src/user-entitlements/attributes.utils.ts +43 -0
  35. package/src/user-entitlements/flatten.utils.ts +52 -0
  36. package/src/user-entitlements/index.ts +2 -0
  37. package/src/user-entitlements/is-entitled.evaluator.ts +8 -25
  38. package/src/user-entitlements/permissions.utils.ts +10 -0
  39. package/src/user-entitlements/tests/attributes.utils.spec.ts +75 -0
  40. package/src/user-entitlements/tests/is-entitled.evaluator.spec.ts +12 -29
  41. package/src/user-entitlements/tests/permissions.utils.spec.ts +21 -0
  42. package/src/user-entitlements/types.ts +5 -6
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { FeatureFlagEvaluationResult, FeatureFlag, evaluateFeatureFlag } from '.
2
2
  export { TreatmentEnum, Rule } from './rules';
3
3
  export { Condition } from './conditions';
4
4
  export { OperationEnum, ConditionValue } from './operations/types';
5
- export { evaluateIsEntitledToFeature, evaluateIsEntitledToPermissions, CustomAttributes, FronteggAttributes, NotEntitledJustification, UserEntitlementsContext, EntitlementResult, } from './user-entitlements';
5
+ export { evaluateIsEntitledToFeature, evaluateIsEntitledToPermissions, prepareAttributes, checkPermission, createPermissionCheckRegex, Permissions, JwtAttributes, CustomAttributes, FronteggAttributes, NotEntitledJustification, UserEntitlementsContext, EntitlementResult, Attributes, } from './user-entitlements';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NotEntitledJustification = exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = exports.OperationEnum = exports.TreatmentEnum = exports.evaluateFeatureFlag = void 0;
3
+ exports.NotEntitledJustification = exports.createPermissionCheckRegex = exports.checkPermission = exports.prepareAttributes = exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = exports.OperationEnum = exports.TreatmentEnum = exports.evaluateFeatureFlag = void 0;
4
4
  var feature_flags_1 = require("./feature-flags");
5
5
  Object.defineProperty(exports, "evaluateFeatureFlag", { enumerable: true, get: function () { return feature_flags_1.evaluateFeatureFlag; } });
6
6
  var rules_1 = require("./rules");
@@ -10,5 +10,8 @@ Object.defineProperty(exports, "OperationEnum", { enumerable: true, get: functio
10
10
  var user_entitlements_1 = require("./user-entitlements");
11
11
  Object.defineProperty(exports, "evaluateIsEntitledToFeature", { enumerable: true, get: function () { return user_entitlements_1.evaluateIsEntitledToFeature; } });
12
12
  Object.defineProperty(exports, "evaluateIsEntitledToPermissions", { enumerable: true, get: function () { return user_entitlements_1.evaluateIsEntitledToPermissions; } });
13
+ Object.defineProperty(exports, "prepareAttributes", { enumerable: true, get: function () { return user_entitlements_1.prepareAttributes; } });
14
+ Object.defineProperty(exports, "checkPermission", { enumerable: true, get: function () { return user_entitlements_1.checkPermission; } });
15
+ Object.defineProperty(exports, "createPermissionCheckRegex", { enumerable: true, get: function () { return user_entitlements_1.createPermissionCheckRegex; } });
13
16
  Object.defineProperty(exports, "NotEntitledJustification", { enumerable: true, get: function () { return user_entitlements_1.NotEntitledJustification; } });
14
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAgG;AAA7C,oHAAA,mBAAmB,OAAA;AACtE,iCAA8C;AAArC,sGAAA,aAAa,OAAA;AAEtB,4CAAmE;AAA1D,sGAAA,aAAa,OAAA;AACtB,yDAQ6B;AAP3B,gIAAA,2BAA2B,OAAA;AAC3B,oIAAA,+BAA+B,OAAA;AAG/B,6HAAA,wBAAwB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAgG;AAA7C,oHAAA,mBAAmB,OAAA;AACtE,iCAA8C;AAArC,sGAAA,aAAa,OAAA;AAEtB,4CAAmE;AAA1D,sGAAA,aAAa,OAAA;AACtB,yDAc6B;AAb3B,gIAAA,2BAA2B,OAAA;AAC3B,oIAAA,+BAA+B,OAAA;AAC/B,sHAAA,iBAAiB,OAAA;AACjB,oHAAA,eAAe,OAAA;AACf,+HAAA,0BAA0B,OAAA;AAK1B,6HAAA,wBAAwB,OAAA"}
@@ -1,5 +1,7 @@
1
1
  import { Sanitizer, SanitizersMapper } from '../types';
2
2
  import { BetweenDateOperationPayload, DateOperationPayload, SingleDateOperationPayload } from './types';
3
+ export declare const sanitizeDateValue: (value: Date | string | number) => Date;
4
+ export declare const isValidDate: (value: unknown) => boolean;
3
5
  export declare const sanitizeSingleDate: Sanitizer<SingleDateOperationPayload>;
4
6
  export declare const sanitizeDateRange: Sanitizer<BetweenDateOperationPayload>;
5
7
  export declare const DateSanitizersMapper: SanitizersMapper<DateOperationPayload>;
@@ -1,9 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DateSanitizersMapper = exports.sanitizeDateRange = exports.sanitizeSingleDate = void 0;
3
+ exports.DateSanitizersMapper = exports.sanitizeDateRange = exports.sanitizeSingleDate = exports.isValidDate = exports.sanitizeDateValue = void 0;
4
4
  const types_1 = require("../types");
5
+ const sanitizeDateValue = (value) => {
6
+ return new Date(value);
7
+ };
8
+ exports.sanitizeDateValue = sanitizeDateValue;
9
+ const isValidDate = (value) => value instanceof Date && !isNaN(value);
10
+ exports.isValidDate = isValidDate;
5
11
  const sanitizeSingleDate = (value) => {
6
- const sanitizedValue = value.date ? { date: value.date } : undefined;
12
+ const sanitizedDateValue = value.date ? (0, exports.sanitizeDateValue)(value.date) : undefined;
13
+ const sanitizedValue = sanitizedDateValue && (0, exports.isValidDate)(sanitizedDateValue) ? { date: sanitizedDateValue } : undefined;
7
14
  return {
8
15
  isSanitized: !!sanitizedValue,
9
16
  sanitizedValue,
@@ -11,7 +18,11 @@ const sanitizeSingleDate = (value) => {
11
18
  };
12
19
  exports.sanitizeSingleDate = sanitizeSingleDate;
13
20
  const sanitizeDateRange = (value) => {
14
- const sanitizedValue = value.start && value.end ? { start: value.start, end: value.end } : undefined;
21
+ const sanitizedStartValue = value.start ? (0, exports.sanitizeDateValue)(value.start) : undefined;
22
+ const sanitizedEndValue = value.end ? (0, exports.sanitizeDateValue)(value.end) : undefined;
23
+ const sanitizedValue = sanitizedStartValue && sanitizedEndValue && (0, exports.isValidDate)(sanitizedStartValue) && (0, exports.isValidDate)(sanitizedEndValue)
24
+ ? { start: sanitizedStartValue, end: sanitizedEndValue }
25
+ : undefined;
15
26
  return {
16
27
  isSanitized: !!sanitizedValue,
17
28
  sanitizedValue,
@@ -20,8 +31,8 @@ const sanitizeDateRange = (value) => {
20
31
  exports.sanitizeDateRange = sanitizeDateRange;
21
32
  exports.DateSanitizersMapper = {
22
33
  [types_1.OperationEnum.On]: exports.sanitizeSingleDate,
23
- [types_1.OperationEnum.OnOrAfter]: exports.sanitizeDateRange,
34
+ [types_1.OperationEnum.OnOrAfter]: exports.sanitizeSingleDate,
24
35
  [types_1.OperationEnum.OnOrBefore]: exports.sanitizeSingleDate,
25
- [types_1.OperationEnum.BetweenDate]: exports.sanitizeSingleDate,
36
+ [types_1.OperationEnum.BetweenDate]: exports.sanitizeDateRange,
26
37
  };
27
38
  //# sourceMappingURL=sanitizers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../../src/operations/date/sanitizers.ts"],"names":[],"mappings":";;;AAAA,oCAAsE;AAG/D,MAAM,kBAAkB,GAA0C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAY,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7E,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAPW,QAAA,kBAAkB,sBAO7B;AAEK,MAAM,iBAAiB,GAA2C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAa,EAAE,GAAG,EAAE,KAAK,CAAC,GAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAErH,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAPW,QAAA,iBAAiB,qBAO5B;AAEW,QAAA,oBAAoB,GAA2C;IAC1E,CAAC,qBAAa,CAAC,EAAE,CAAC,EAAE,0BAAkB;IACtC,CAAC,qBAAa,CAAC,SAAS,CAAC,EAAE,yBAAiB;IAC5C,CAAC,qBAAa,CAAC,UAAU,CAAC,EAAE,0BAAkB;IAC9C,CAAC,qBAAa,CAAC,WAAW,CAAC,EAAE,0BAAkB;CAChD,CAAC"}
1
+ {"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../../src/operations/date/sanitizers.ts"],"names":[],"mappings":";;;AAAA,oCAAsE;AAK/D,MAAM,iBAAiB,GAAG,CAAC,KAA6B,EAAQ,EAAE;IACvE,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC,CAAC;AAFW,QAAA,iBAAiB,qBAE5B;AAEK,MAAM,WAAW,GAAG,CAAC,KAAc,EAAW,EAAE,CAAC,KAAK,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,KAA0B,CAAC,CAAC;AAAvG,QAAA,WAAW,eAA4F;AAE7G,MAAM,kBAAkB,GAA0C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,yBAAiB,EAAC,KAAK,CAAC,IAAoB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,MAAM,cAAc,GAClB,kBAAkB,IAAI,IAAA,mBAAW,EAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AATW,QAAA,kBAAkB,sBAS7B;AAEK,MAAM,iBAAiB,GAA2C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,mBAAmB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,yBAAiB,EAAC,KAAK,CAAC,KAAqB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrG,MAAM,iBAAiB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAA,yBAAiB,EAAC,KAAK,CAAC,GAAmB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/F,MAAM,cAAc,GAClB,mBAAmB,IAAI,iBAAiB,IAAI,IAAA,mBAAW,EAAC,mBAAmB,CAAC,IAAI,IAAA,mBAAW,EAAC,iBAAiB,CAAC;QAC5G,CAAC,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,iBAAiB,EAAE;QACxD,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,iBAAiB,qBAY5B;AAEW,QAAA,oBAAoB,GAA2C;IAC1E,CAAC,qBAAa,CAAC,EAAE,CAAC,EAAE,0BAAkB;IACtC,CAAC,qBAAa,CAAC,SAAS,CAAC,EAAE,0BAAkB;IAC7C,CAAC,qBAAa,CAAC,UAAU,CAAC,EAAE,0BAAkB;IAC9C,CAAC,qBAAa,CAAC,WAAW,CAAC,EAAE,yBAAiB;CAC/C,CAAC"}
@@ -23,10 +23,10 @@ const sanitizeNumericRange = (value) => {
23
23
  exports.sanitizeNumericRange = sanitizeNumericRange;
24
24
  exports.NumericSanitizersMapper = {
25
25
  [types_1.OperationEnum.Equal]: exports.sanitizeSingleNumber,
26
- [types_1.OperationEnum.GreaterThan]: exports.sanitizeNumericRange,
26
+ [types_1.OperationEnum.GreaterThan]: exports.sanitizeSingleNumber,
27
27
  [types_1.OperationEnum.GreaterThanEqual]: exports.sanitizeSingleNumber,
28
28
  [types_1.OperationEnum.LesserThan]: exports.sanitizeSingleNumber,
29
- [types_1.OperationEnum.LesserThanEqual]: exports.sanitizeNumericRange,
29
+ [types_1.OperationEnum.LesserThanEqual]: exports.sanitizeSingleNumber,
30
30
  [types_1.OperationEnum.BetweenNumeric]: exports.sanitizeNumericRange,
31
31
  };
32
32
  //# sourceMappingURL=sanitizers.js.map
@@ -24,8 +24,8 @@ exports.sanitizeListString = sanitizeListString;
24
24
  exports.StringSanitizersMapper = {
25
25
  [types_1.OperationEnum.Matches]: exports.sanitizeSingleString,
26
26
  [types_1.OperationEnum.Contains]: exports.sanitizeListString,
27
- [types_1.OperationEnum.StartsWith]: exports.sanitizeSingleString,
28
- [types_1.OperationEnum.EndsWith]: exports.sanitizeSingleString,
27
+ [types_1.OperationEnum.StartsWith]: exports.sanitizeListString,
28
+ [types_1.OperationEnum.EndsWith]: exports.sanitizeListString,
29
29
  [types_1.OperationEnum.InList]: exports.sanitizeListString,
30
30
  };
31
31
  //# sourceMappingURL=sanitizers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../../src/operations/string/sanitizers.ts"],"names":[],"mappings":";;;AAAA,oCAAsE;AAGtE,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;AAEzE,MAAM,oBAAoB,GAA4C,CAAC,KAAK,EAAE,EAAE;IACrF,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnH,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAPW,QAAA,oBAAoB,wBAO/B;AAEK,MAAM,kBAAkB,GAA0C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,cAAc,GAClB,KAAK,CAAC,IAAI,KAAK,SAAS,IAAgB,KAAK,CAAC,IAAK,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC/E,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAgB,EAAE;QAClC,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAVW,QAAA,kBAAkB,sBAU7B;AAEW,QAAA,sBAAsB,GAA6C;IAC9E,CAAC,qBAAa,CAAC,OAAO,CAAC,EAAE,4BAAoB;IAC7C,CAAC,qBAAa,CAAC,QAAQ,CAAC,EAAE,0BAAkB;IAC5C,CAAC,qBAAa,CAAC,UAAU,CAAC,EAAE,4BAAoB;IAChD,CAAC,qBAAa,CAAC,QAAQ,CAAC,EAAE,4BAAoB;IAC9C,CAAC,qBAAa,CAAC,MAAM,CAAC,EAAE,0BAAkB;CAC3C,CAAC"}
1
+ {"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../../src/operations/string/sanitizers.ts"],"names":[],"mappings":";;;AAAA,oCAAsE;AAGtE,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;AAEzE,MAAM,oBAAoB,GAA4C,CAAC,KAAK,EAAE,EAAE;IACrF,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnH,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAPW,QAAA,oBAAoB,wBAO/B;AAEK,MAAM,kBAAkB,GAA0C,CAAC,KAAK,EAAE,EAAE;IACjF,MAAM,cAAc,GAClB,KAAK,CAAC,IAAI,KAAK,SAAS,IAAgB,KAAK,CAAC,IAAK,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC/E,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAgB,EAAE;QAClC,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,cAAc;QAC7B,cAAc;KACf,CAAC;AACJ,CAAC,CAAC;AAVW,QAAA,kBAAkB,sBAU7B;AAEW,QAAA,sBAAsB,GAA6C;IAC9E,CAAC,qBAAa,CAAC,OAAO,CAAC,EAAE,4BAAoB;IAC7C,CAAC,qBAAa,CAAC,QAAQ,CAAC,EAAE,0BAAkB;IAC5C,CAAC,qBAAa,CAAC,UAAU,CAAC,EAAE,0BAAkB;IAC9C,CAAC,qBAAa,CAAC,QAAQ,CAAC,EAAE,0BAAkB;IAC5C,CAAC,qBAAa,CAAC,MAAM,CAAC,EAAE,0BAAkB;CAC3C,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Attributes, JwtAttributes, FronteggAttributes } from './types';
2
+ /**
3
+ * Merges both `custom` and `jwt` records, map Frontegg attributes and modifies record keys with corrisponding prefixes
4
+ *
5
+ * Example:
6
+ * Input: { 'custom': { 'customAttribute': 'someValue' }, 'jwt': { 'email': 'user@email.com', other: 'some-vaule' } }
7
+ * Output: { 'customAttribute': 'someValue', 'frontegg.email': 'user@email.com', 'jwt.email': 'user@email.com', 'jwt.other': 'some-vaule' }
8
+ */
9
+ export declare function prepareAttributes(attributes?: Attributes, customFronteggAttributesMapper?: (jwtAttributes: JwtAttributes) => FronteggAttributes): Record<string, unknown>;
10
+ export declare function defaultFronteggAttributesMapper(jwt: JwtAttributes): FronteggAttributes;
11
+ export declare function modifyObjectKeysWithPrefix(object: Record<string, unknown>, prefix: string): Record<string, unknown>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modifyObjectKeysWithPrefix = exports.defaultFronteggAttributesMapper = exports.prepareAttributes = void 0;
4
+ const flatten_utils_1 = require("./flatten.utils");
5
+ /**
6
+ * Merges both `custom` and `jwt` records, map Frontegg attributes and modifies record keys with corrisponding prefixes
7
+ *
8
+ * Example:
9
+ * Input: { 'custom': { 'customAttribute': 'someValue' }, 'jwt': { 'email': 'user@email.com', other: 'some-vaule' } }
10
+ * Output: { 'customAttribute': 'someValue', 'frontegg.email': 'user@email.com', 'jwt.email': 'user@email.com', 'jwt.other': 'some-vaule' }
11
+ */
12
+ function prepareAttributes(attributes = {}, customFronteggAttributesMapper) {
13
+ const { custom = {}, jwt = {} } = attributes;
14
+ const flatJwtAttributes = (0, flatten_utils_1.flatten)(jwt);
15
+ const fronteggAttributes = customFronteggAttributesMapper
16
+ ? customFronteggAttributesMapper(jwt)
17
+ : defaultFronteggAttributesMapper(jwt);
18
+ const fronteggAttributesPrefix = 'frontegg.';
19
+ const jwtAttributesPrefix = 'jwt.';
20
+ return {
21
+ ...custom,
22
+ ...modifyObjectKeysWithPrefix(fronteggAttributes, fronteggAttributesPrefix),
23
+ ...modifyObjectKeysWithPrefix(flatJwtAttributes, jwtAttributesPrefix),
24
+ };
25
+ }
26
+ exports.prepareAttributes = prepareAttributes;
27
+ function defaultFronteggAttributesMapper(jwt) {
28
+ return {
29
+ email: jwt.email,
30
+ emailVerified: jwt.email_verified,
31
+ tenantId: jwt.tenantId,
32
+ userId: jwt.userId,
33
+ };
34
+ }
35
+ exports.defaultFronteggAttributesMapper = defaultFronteggAttributesMapper;
36
+ function modifyObjectKeysWithPrefix(object, prefix) {
37
+ return Object.keys(object).reduce((modifiedObject, currentKey) => {
38
+ modifiedObject[`${prefix}${currentKey}`] = object[currentKey];
39
+ return modifiedObject;
40
+ }, {});
41
+ }
42
+ exports.modifyObjectKeysWithPrefix = modifyObjectKeysWithPrefix;
43
+ //# sourceMappingURL=attributes.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.utils.js","sourceRoot":"","sources":["../../src/user-entitlements/attributes.utils.ts"],"names":[],"mappings":";;;AACA,mDAA0C;AAC1C;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAC/B,aAAyB,EAAE,EAC3B,8BAAqF;IAErF,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC;IAC7C,MAAM,iBAAiB,GAAG,IAAA,uBAAO,EAA+B,GAAG,CAAC,CAAC;IACrE,MAAM,kBAAkB,GAAG,8BAA8B;QACvD,CAAC,CAAC,8BAA8B,CAAC,GAAG,CAAC;QACrC,CAAC,CAAC,+BAA+B,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,wBAAwB,GAAG,WAAW,CAAC;IAC7C,MAAM,mBAAmB,GAAG,MAAM,CAAC;IAEnC,OAAO;QACL,GAAG,MAAM;QACT,GAAG,0BAA0B,CAAC,kBAAkB,EAAE,wBAAwB,CAAC;QAC3E,GAAG,0BAA0B,CAAC,iBAAiB,EAAE,mBAAmB,CAAC;KACtE,CAAC;AACJ,CAAC;AAjBD,8CAiBC;AAED,SAAgB,+BAA+B,CAAC,GAAkB;IAChE,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,KAAe;QAC1B,aAAa,EAAE,GAAG,CAAC,cAAyB;QAC5C,QAAQ,EAAE,GAAG,CAAC,QAAkB;QAChC,MAAM,EAAE,GAAG,CAAC,MAAgB;KAC7B,CAAC;AACJ,CAAC;AAPD,0EAOC;AAED,SAAgB,0BAA0B,CAAC,MAA+B,EAAE,MAAc;IACxF,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE;QAC/D,cAAc,CAAC,GAAG,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9D,OAAO,cAAc,CAAC;IACxB,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AALD,gEAKC"}
@@ -0,0 +1,8 @@
1
+ /** DISCLAIMER - THIS CODE BELONGS TO https://github.com/hughsk/flat */
2
+ export interface FlattenOptions {
3
+ delimiter?: string;
4
+ maxDepth?: number;
5
+ safe?: boolean;
6
+ transformKey?: (key: string) => string;
7
+ }
8
+ export declare function flatten<TTraget, TResult>(target: TTraget, opts?: FlattenOptions): TResult;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flatten = void 0;
4
+ function flatten(target, opts) {
5
+ opts = opts || {};
6
+ const delimiter = (opts === null || opts === void 0 ? void 0 : opts.delimiter) || '.';
7
+ const maxDepth = opts === null || opts === void 0 ? void 0 : opts.maxDepth;
8
+ const transformKey = (opts === null || opts === void 0 ? void 0 : opts.transformKey) || keyIdentity;
9
+ const output = {};
10
+ function step(object, prev, currentDepth) {
11
+ currentDepth = currentDepth || 1;
12
+ Object.keys(object).forEach(function (key) {
13
+ const value = object[key];
14
+ const isarray = (opts === null || opts === void 0 ? void 0 : opts.safe) && Array.isArray(value);
15
+ const type = Object.prototype.toString.call(value);
16
+ const isbuffer = isBuffer(value);
17
+ const isobject = type === '[object Object]' || type === '[object Array]';
18
+ const newKey = prev ? prev + delimiter + transformKey(key) : transformKey(key);
19
+ if (!isarray &&
20
+ !isbuffer &&
21
+ isobject &&
22
+ Object.keys(value).length &&
23
+ (!(opts === null || opts === void 0 ? void 0 : opts.maxDepth) || (maxDepth && currentDepth < maxDepth))) {
24
+ return step(value, newKey, currentDepth + 1);
25
+ }
26
+ output[newKey] = value;
27
+ });
28
+ }
29
+ step(target);
30
+ return output;
31
+ }
32
+ exports.flatten = flatten;
33
+ function isBuffer(obj) {
34
+ return obj && obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj);
35
+ }
36
+ function keyIdentity(key) {
37
+ return key;
38
+ }
39
+ //# sourceMappingURL=flatten.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten.utils.js","sourceRoot":"","sources":["../../src/user-entitlements/flatten.utils.ts"],"names":[],"mappings":";;;AAQA,SAAgB,OAAO,CAAmB,MAAe,EAAE,IAAqB;IAC9E,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IAElB,MAAM,SAAS,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,SAAS,KAAI,GAAG,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,QAAQ,CAAC;IAChC,MAAM,YAAY,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,YAAY,KAAI,WAAW,CAAC;IACvD,MAAM,MAAM,GAAG,EAAE,CAAC;IAElB,SAAS,IAAI,CAAC,MAAM,EAAE,IAAK,EAAE,YAAa;QACxC,YAAY,GAAG,YAAY,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,GAAG;YACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,MAAM,OAAO,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,KAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,QAAQ,GAAG,IAAI,KAAK,iBAAiB,IAAI,IAAI,KAAK,gBAAgB,CAAC;YAEzE,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC/E,IACE,CAAC,OAAO;gBACR,CAAC,QAAQ;gBACT,QAAQ;gBACR,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM;gBACzB,CAAC,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,QAAQ,CAAA,IAAI,CAAC,QAAQ,IAAI,YAAY,GAAG,QAAQ,CAAC,CAAC,EAC1D;gBACA,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;aAC9C;YAED,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,CAAC;IAEb,OAAO,MAAiB,CAAC;AAC3B,CAAC;AAnCD,0BAmCC;AAED,SAAS,QAAQ,CAAC,GAAG;IACnB,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,IAAI,OAAO,GAAG,CAAC,WAAW,CAAC,QAAQ,KAAK,UAAU,IAAI,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACnH,CAAC;AAED,SAAS,WAAW,CAAC,GAAG;IACtB,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -1,2 +1,4 @@
1
1
  export * from './is-entitled.evaluator';
2
2
  export * from './types';
3
+ export * from './attributes.utils';
4
+ export * from './permissions.utils';
@@ -16,4 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./is-entitled.evaluator"), exports);
18
18
  __exportStar(require("./types"), exports);
19
+ __exportStar(require("./attributes.utils"), exports);
20
+ __exportStar(require("./permissions.utils"), exports);
19
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user-entitlements/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,0CAAwB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user-entitlements/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,0CAAwB;AACxB,qDAAmC;AACnC,sDAAoC"}
@@ -1,11 +1,3 @@
1
1
  import { EntitlementResult, UserEntitlementsContext, Attributes } from './types';
2
2
  export declare function evaluateIsEntitledToFeature(featureKey: string, userEntitlementsContext: UserEntitlementsContext, attributes?: Attributes): EntitlementResult;
3
3
  export declare function evaluateIsEntitledToPermissions(permissionKey: string, userEntitlementsContext: UserEntitlementsContext, attributes?: Attributes): EntitlementResult;
4
- /**
5
- * Merges the `custom` and `frontegg` Records into a single Record,
6
- * Alters the `frontegg` Record keys with a prefix
7
- * Example:
8
- * Input: { 'custom': { 'customAttribute': 'someValue' }, 'frontegg': { 'email': 'user@email.com' } }
9
- * Output: { 'customAttribute': 'someValue', 'imported.email': 'user@email.com' }
10
- */
11
- export declare function prepareAttributes(attributes?: Attributes): Record<string, unknown>;
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prepareAttributes = exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = void 0;
3
+ exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = void 0;
4
4
  const types_1 = require("./types");
5
5
  const feature_flags_1 = require("../feature-flags");
6
+ const attributes_utils_1 = require("./attributes.utils");
6
7
  const rules_1 = require("../rules");
7
- function evaluateIsEntitledToFeature(featureKey, userEntitlementsContext, attributes) {
8
+ const permissions_utils_1 = require("./permissions.utils");
9
+ function evaluateIsEntitledToFeature(featureKey, userEntitlementsContext, attributes = {}) {
8
10
  const feature = userEntitlementsContext.features[featureKey];
9
11
  let hasExpired = false;
10
12
  if (feature && feature.expireTime !== null) {
@@ -14,7 +16,8 @@ function evaluateIsEntitledToFeature(featureKey, userEntitlementsContext, attrib
14
16
  }
15
17
  }
16
18
  if (feature && feature.featureFlag) {
17
- const { treatment } = (0, feature_flags_1.evaluateFeatureFlag)(feature.featureFlag, prepareAttributes(attributes));
19
+ const preparedAttributes = (0, attributes_utils_1.prepareAttributes)(attributes);
20
+ const { treatment } = (0, feature_flags_1.evaluateFeatureFlag)(feature.featureFlag, preparedAttributes);
18
21
  if (treatment === rules_1.TreatmentEnum.True) {
19
22
  return { isEntitled: true };
20
23
  }
@@ -26,8 +29,8 @@ function evaluateIsEntitledToFeature(featureKey, userEntitlementsContext, attrib
26
29
  }
27
30
  exports.evaluateIsEntitledToFeature = evaluateIsEntitledToFeature;
28
31
  function evaluateIsEntitledToPermissions(permissionKey, userEntitlementsContext, attributes) {
29
- const permission = userEntitlementsContext.permissions[permissionKey];
30
- if (!permission) {
32
+ const hasPermission = (0, permissions_utils_1.checkPermission)(userEntitlementsContext.permissions, permissionKey);
33
+ if (!hasPermission) {
31
34
  return { isEntitled: false, justification: types_1.NotEntitledJustification.MISSING_PERMISSION };
32
35
  }
33
36
  const linkedFeatures = getLinkedFeatures(permissionKey, userEntitlementsContext);
@@ -53,23 +56,4 @@ exports.evaluateIsEntitledToPermissions = evaluateIsEntitledToPermissions;
53
56
  function getLinkedFeatures(permissionKey, userEntitlementsContext) {
54
57
  return Object.keys(userEntitlementsContext.features).filter((featureKey) => userEntitlementsContext.features[featureKey].linkedPermissions.includes(permissionKey));
55
58
  }
56
- /**
57
- * Merges the `custom` and `frontegg` Records into a single Record,
58
- * Alters the `frontegg` Record keys with a prefix
59
- * Example:
60
- * Input: { 'custom': { 'customAttribute': 'someValue' }, 'frontegg': { 'email': 'user@email.com' } }
61
- * Output: { 'customAttribute': 'someValue', 'imported.email': 'user@email.com' }
62
- */
63
- function prepareAttributes(attributes = {}) {
64
- const { custom = {}, frontegg = {} } = attributes;
65
- const importedAttributesPrefix = 'imported.'; // Not Final
66
- return {
67
- ...custom,
68
- ...Object.keys(frontegg).reduce((modifiedImportedAttributes, key) => {
69
- modifiedImportedAttributes[`${importedAttributesPrefix}${key}`] = frontegg[key];
70
- return modifiedImportedAttributes;
71
- }, {}),
72
- };
73
- }
74
- exports.prepareAttributes = prepareAttributes;
75
59
  //# sourceMappingURL=is-entitled.evaluator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"is-entitled.evaluator.js","sourceRoot":"","sources":["../../src/user-entitlements/is-entitled.evaluator.ts"],"names":[],"mappings":";;;AAAA,mCAMiB;AACjB,oDAAuD;AACvD,oCAAyC;AACzC,SAAgB,2BAA2B,CACzC,UAAkB,EAClB,uBAAgD,EAChD,UAAuB;IAEvB,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,EAAE;QAC1C,UAAU,GAAG,OAAO,CAAC,UAAU,KAAK,0BAAkB,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE1F,IAAI,CAAC,UAAU,EAAE;YACf,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;KACF;IAED,IAAI,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE;QAClC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAA,mCAAmB,EAAC,OAAO,CAAC,WAAW,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;QAC9F,IAAI,SAAS,KAAK,qBAAa,CAAC,IAAI,EAAE;YACpC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;KACF;IAED,OAAO;QACL,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,gCAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,gCAAwB,CAAC,eAAe;KAC/G,CAAC;AACJ,CAAC;AA1BD,kEA0BC;AAED,SAAgB,+BAA+B,CAC7C,aAAqB,EACrB,uBAAgD,EAChD,UAAuB;IAEvB,MAAM,UAAU,GAAG,uBAAuB,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAEtE,IAAI,CAAC,UAAU,EAAE;QACf,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,gCAAwB,CAAC,kBAAkB,EAAE,CAAC;KAC1F;IAED,MAAM,cAAc,GAAG,iBAAiB,CAAC,aAAa,EAAE,uBAAuB,CAAC,CAAC;IAEjF,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;QAC1B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;KAC7B;IAED,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,KAAK,MAAM,UAAU,IAAI,cAAc,EAAE;QACvC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,2BAA2B,CAAC,UAAU,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;QAEnH,IAAI,UAAU,EAAE;YACd,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;QAED,IAAI,aAAa,KAAK,gCAAwB,CAAC,cAAc,EAAE;YAC7D,UAAU,GAAG,IAAI,CAAC;SACnB;KACF;IAED,OAAO;QACL,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,gCAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,gCAAwB,CAAC,eAAe;KAC/G,CAAC;AACJ,CAAC;AAnCD,0EAmCC;AAED,SAAS,iBAAiB,CAAC,aAAqB,EAAE,uBAAgD;IAChG,OAAO,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CACzE,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,aAAa,CAAC,CACvF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,aAAyB,EAAE;IAC3D,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC;IAClD,MAAM,wBAAwB,GAAG,WAAW,CAAC,CAAC,YAAY;IAE1D,OAAO;QACL,GAAG,MAAM;QACT,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,0BAA0B,EAAE,GAAG,EAAE,EAAE;YAClE,0BAA0B,CAAC,GAAG,wBAAwB,GAAG,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAChF,OAAO,0BAA0B,CAAC;QACpC,CAAC,EAAE,EAAE,CAAC;KACP,CAAC;AACJ,CAAC;AAXD,8CAWC"}
1
+ {"version":3,"file":"is-entitled.evaluator.js","sourceRoot":"","sources":["../../src/user-entitlements/is-entitled.evaluator.ts"],"names":[],"mappings":";;;AAAA,mCAMiB;AAEjB,oDAAuD;AACvD,yDAAuD;AACvD,oCAAyC;AACzC,2DAAsD;AACtD,SAAgB,2BAA2B,CACzC,UAAkB,EAClB,uBAAgD,EAChD,aAAyB,EAAE;IAE3B,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,EAAE;QAC1C,UAAU,GAAG,OAAO,CAAC,UAAU,KAAK,0BAAkB,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE1F,IAAI,CAAC,UAAU,EAAE;YACf,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;KACF;IAED,IAAI,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE;QAClC,MAAM,kBAAkB,GAAG,IAAA,oCAAiB,EAAC,UAAU,CAAC,CAAC;QACzD,MAAM,EAAE,SAAS,EAAE,GAAG,IAAA,mCAAmB,EAAC,OAAO,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;QACnF,IAAI,SAAS,KAAK,qBAAa,CAAC,IAAI,EAAE;YACpC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;KACF;IAED,OAAO;QACL,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,gCAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,gCAAwB,CAAC,eAAe;KAC/G,CAAC;AACJ,CAAC;AA3BD,kEA2BC;AAED,SAAgB,+BAA+B,CAC7C,aAAqB,EACrB,uBAAgD,EAChD,UAAuB;IAEvB,MAAM,aAAa,GAAG,IAAA,mCAAe,EAAC,uBAAuB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC1F,IAAI,CAAC,aAAa,EAAE;QAClB,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,gCAAwB,CAAC,kBAAkB,EAAE,CAAC;KAC1F;IAED,MAAM,cAAc,GAAG,iBAAiB,CAAC,aAAa,EAAE,uBAAuB,CAAC,CAAC;IAEjF,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;QAC1B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;KAC7B;IAED,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,KAAK,MAAM,UAAU,IAAI,cAAc,EAAE;QACvC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,2BAA2B,CAAC,UAAU,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;QAEnH,IAAI,UAAU,EAAE;YACd,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;SAC7B;QAED,IAAI,aAAa,KAAK,gCAAwB,CAAC,cAAc,EAAE;YAC7D,UAAU,GAAG,IAAI,CAAC;SACnB;KACF;IAED,OAAO;QACL,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,gCAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,gCAAwB,CAAC,eAAe;KAC/G,CAAC;AACJ,CAAC;AAlCD,0EAkCC;AAED,SAAS,iBAAiB,CAAC,aAAqB,EAAE,uBAAgD;IAChG,OAAO,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CACzE,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,aAAa,CAAC,CACvF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Permissions } from './types';
2
+ export declare function checkPermission(permissions: Permissions, requiredPermission: string): boolean;
3
+ export declare function createPermissionCheckRegex(permissionKey: string): RegExp;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPermissionCheckRegex = exports.checkPermission = void 0;
4
+ function checkPermission(permissions, requiredPermission) {
5
+ return Object.keys(permissions).some((permissionKey) => createPermissionCheckRegex(permissionKey).test(requiredPermission));
6
+ }
7
+ exports.checkPermission = checkPermission;
8
+ function createPermissionCheckRegex(permissionKey) {
9
+ return new RegExp('^' + permissionKey.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$', 'gs');
10
+ }
11
+ exports.createPermissionCheckRegex = createPermissionCheckRegex;
12
+ //# sourceMappingURL=permissions.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.utils.js","sourceRoot":"","sources":["../../src/user-entitlements/permissions.utils.ts"],"names":[],"mappings":";;;AACA,SAAgB,eAAe,CAAC,WAAwB,EAAE,kBAA0B;IAClF,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE,CACrD,0BAA0B,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CACnE,CAAC;AACJ,CAAC;AAJD,0CAIC;AAED,SAAgB,0BAA0B,CAAC,aAAqB;IAC9D,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;AAChG,CAAC;AAFD,gEAEC"}
@@ -5,7 +5,7 @@ export type UserEntitlementsContext = {
5
5
  linkedPermissions: string[];
6
6
  featureFlag?: FeatureFlag;
7
7
  }>;
8
- permissions: Record<string, true>;
8
+ permissions: Permissions;
9
9
  };
10
10
  export type EntitlementResult = {
11
11
  isEntitled: boolean;
@@ -21,11 +21,12 @@ export type FronteggAttributes = {
21
21
  tenantId?: string;
22
22
  userId?: string;
23
23
  email?: string;
24
- email_verified?: boolean;
25
- [unmappedAttribute: string]: unknown;
24
+ emailVerified?: boolean;
26
25
  };
26
+ export type Permissions = Record<string, true>;
27
27
  export type Attributes = {
28
28
  custom?: CustomAttributes;
29
- frontegg?: FronteggAttributes;
29
+ jwt?: JwtAttributes;
30
30
  };
31
+ export type JwtAttributes = Record<string, unknown>;
31
32
  export declare const NO_EXPIRATION_TIME = -1;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/user-entitlements/types.ts"],"names":[],"mappings":";;;AAkBA,IAAY,wBAIX;AAJD,WAAY,wBAAwB;IAClC,+DAAmC,CAAA;IACnC,qEAAyC,CAAA;IACzC,6DAAiC,CAAA;AACnC,CAAC,EAJW,wBAAwB,GAAxB,gCAAwB,KAAxB,gCAAwB,QAInC;AAcY,QAAA,kBAAkB,GAAG,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/user-entitlements/types.ts"],"names":[],"mappings":";;;AAkBA,IAAY,wBAIX;AAJD,WAAY,wBAAwB;IAClC,+DAAmC,CAAA;IACnC,qEAAyC,CAAA;IACzC,6DAAiC,CAAA;AACnC,CAAC,EAJW,wBAAwB,GAAxB,gCAAwB,KAAxB,gCAAwB,QAInC;AAaY,QAAA,kBAAkB,GAAG,CAAC,CAAC,CAAC"}
package/docs/CHANGELOG.md CHANGED
@@ -1,3 +1,92 @@
1
+ # 1.0.0 (2023-10-31)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix export order ([#16](https://github.com/frontegg/entitlements-javascript-commons/issues/16)) ([d7fafa4](https://github.com/frontegg/entitlements-javascript-commons/commit/d7fafa427e5f99fffe541f9fa78fd9b7507045c3))
7
+ * **date:** fix incorrect sanitizers mapping ([#25](https://github.com/frontegg/entitlements-javascript-commons/issues/25)) ([da08f16](https://github.com/frontegg/entitlements-javascript-commons/commit/da08f1628753b1794422c83327670ae09242ec11))
8
+ * **dep:** remove flat dependency ([e8a2daf](https://github.com/frontegg/entitlements-javascript-commons/commit/e8a2dafde0612ebfacc8e2d7e346d189276e5f72))
9
+ * **sanitizers:** fix numeric and date sanitizers ([#24](https://github.com/frontegg/entitlements-javascript-commons/issues/24)) ([8f5717a](https://github.com/frontegg/entitlements-javascript-commons/commit/8f5717acdcc0a1eccae84b272c7a02e315131a01))
10
+ * **string:** fix string sanitizers type to match opertaions ([#21](https://github.com/frontegg/entitlements-javascript-commons/issues/21)) ([4461155](https://github.com/frontegg/entitlements-javascript-commons/commit/4461155e53c0ef6f647ff4bc0215804667b52928))
11
+ * **user-entitlements:** exported utility function ([4d81b3e](https://github.com/frontegg/entitlements-javascript-commons/commit/4d81b3e04b1de59047c6b5911f09391a4ccafcb9))
12
+
13
+
14
+ ### Features
15
+
16
+ * additional (handy) exports that SDKs might use to interact with feature flags (FR-13491) ([a7d1fc2](https://github.com/frontegg/entitlements-javascript-commons/commit/a7d1fc2f596cf379d10cb34ae7aa55a7111c1244))
17
+ * create pipeline and release infra ([16fd6d1](https://github.com/frontegg/entitlements-javascript-commons/commit/16fd6d165cff4c3ae28e2392db2480d41dd591b1))
18
+ * **conditions:** add condition evaluator ([#5](https://github.com/frontegg/entitlements-javascript-commons/issues/5)) ([5ca2446](https://github.com/frontegg/entitlements-javascript-commons/commit/5ca24465a76b9fa103977e5600a6d870da5520cb))
19
+ * **feature-flags:** add feature flag evaluation ([#11](https://github.com/frontegg/entitlements-javascript-commons/issues/11)) ([94679a1](https://github.com/frontegg/entitlements-javascript-commons/commit/94679a123581cd4977fd9f2087adc9e2532a638c))
20
+ * **operations:** add numeric operations ([#3](https://github.com/frontegg/entitlements-javascript-commons/issues/3)) ([#10](https://github.com/frontegg/entitlements-javascript-commons/issues/10)) ([0b3de2f](https://github.com/frontegg/entitlements-javascript-commons/commit/0b3de2f7f1aede036ec63e4fadf898dcf5ad32a4))
21
+ * **operations:** add string operations ([#2](https://github.com/frontegg/entitlements-javascript-commons/issues/2)) ([e2e63a7](https://github.com/frontegg/entitlements-javascript-commons/commit/e2e63a74211a723dc326918e42e2093fcca86779))
22
+ * **rule:** add rule evaluator ([#6](https://github.com/frontegg/entitlements-javascript-commons/issues/6)) ([5fbf4da](https://github.com/frontegg/entitlements-javascript-commons/commit/5fbf4da00a3d9df2908d8899723a64b1bd80a7c2))
23
+ * **sanitizers:** add value sanitizers ([#12](https://github.com/frontegg/entitlements-javascript-commons/issues/12)) ([1decf2c](https://github.com/frontegg/entitlements-javascript-commons/commit/1decf2c01e0a86055bc856db5ff115a2318f1c59))
24
+ * **user-entitlements:** add frontegg attributes ([878f4aa](https://github.com/frontegg/entitlements-javascript-commons/commit/878f4aa5f284fc2dd27ffae63ed9c0c74b2c8adb))
25
+ * **user-entitlements:** add frontegg attributes ([b1c3e30](https://github.com/frontegg/entitlements-javascript-commons/commit/b1c3e30e420e8972167087c5745ef49ac0c6b859))
26
+ * **user-entitlements:** add missing types ([fd375c1](https://github.com/frontegg/entitlements-javascript-commons/commit/fd375c15fea40ddbf8e259a73f8831fa9dbb763e))
27
+ * **user-entitlements:** add user entitlements evaluation logic ([d924a05](https://github.com/frontegg/entitlements-javascript-commons/commit/d924a056498b4b040dd765b262cde42201644653))
28
+ * **user-entitlements:** export function signature parameters types ([2207986](https://github.com/frontegg/entitlements-javascript-commons/commit/220798611a91875e594c83f76baed6c18f5ac19a))
29
+ * **user-entitlements:** export types ([06f6f13](https://github.com/frontegg/entitlements-javascript-commons/commit/06f6f13263bcfecc098290da35b310393bcecda4))
30
+ * **user-entitlements:** handle jwt attributes ([cc0ce70](https://github.com/frontegg/entitlements-javascript-commons/commit/cc0ce70f4c3ebf2b3b410865d740c23ab1d0d9ed))
31
+ * **user-entitlements:** introduce wildcard to permission check ([986ba21](https://github.com/frontegg/entitlements-javascript-commons/commit/986ba213cbf88aef99495e3fd6fc2bf1203ec7e6))
32
+
33
+ # [1.0.0-alpha.17](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.16...v-1.0.0-alpha.17) (2023-10-30)
34
+
35
+
36
+ ### Bug Fixes
37
+
38
+ * **user-entitlements:** exported utility function ([4d81b3e](https://github.com/frontegg/entitlements-javascript-commons/commit/4d81b3e04b1de59047c6b5911f09391a4ccafcb9))
39
+
40
+ # [1.0.0-alpha.16](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.15...v-1.0.0-alpha.16) (2023-10-30)
41
+
42
+
43
+ ### Features
44
+
45
+ * **user-entitlements:** introduce wildcard to permission check ([986ba21](https://github.com/frontegg/entitlements-javascript-commons/commit/986ba213cbf88aef99495e3fd6fc2bf1203ec7e6))
46
+
47
+ # [1.0.0-alpha.15](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.14...v-1.0.0-alpha.15) (2023-10-25)
48
+
49
+
50
+ ### Bug Fixes
51
+
52
+ * **date:** fix incorrect sanitizers mapping ([#25](https://github.com/frontegg/entitlements-javascript-commons/issues/25)) ([da08f16](https://github.com/frontegg/entitlements-javascript-commons/commit/da08f1628753b1794422c83327670ae09242ec11))
53
+
54
+ # [1.0.0-alpha.14](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.13...v-1.0.0-alpha.14) (2023-10-24)
55
+
56
+
57
+ ### Bug Fixes
58
+
59
+ * **sanitizers:** fix numeric and date sanitizers ([#24](https://github.com/frontegg/entitlements-javascript-commons/issues/24)) ([8f5717a](https://github.com/frontegg/entitlements-javascript-commons/commit/8f5717acdcc0a1eccae84b272c7a02e315131a01))
60
+
61
+ # [1.0.0-alpha.13](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.12...v-1.0.0-alpha.13) (2023-10-22)
62
+
63
+
64
+ ### Bug Fixes
65
+
66
+ * **dep:** remove flat dependency ([e8a2daf](https://github.com/frontegg/entitlements-javascript-commons/commit/e8a2dafde0612ebfacc8e2d7e346d189276e5f72))
67
+
68
+ # [1.0.0-alpha.12](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.11...v-1.0.0-alpha.12) (2023-10-18)
69
+
70
+
71
+ ### Bug Fixes
72
+
73
+ * **string:** fix string sanitizers type to match opertaions ([#21](https://github.com/frontegg/entitlements-javascript-commons/issues/21)) ([4461155](https://github.com/frontegg/entitlements-javascript-commons/commit/4461155e53c0ef6f647ff4bc0215804667b52928))
74
+
75
+ # [1.0.0-alpha.11](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.10...v-1.0.0-alpha.11) (2023-10-17)
76
+
77
+
78
+ ### Features
79
+
80
+ * **user-entitlements:** handle jwt attributes ([cc0ce70](https://github.com/frontegg/entitlements-javascript-commons/commit/cc0ce70f4c3ebf2b3b410865d740c23ab1d0d9ed))
81
+
82
+ # [1.0.0-alpha.10](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.9...v-1.0.0-alpha.10) (2023-10-12)
83
+
84
+
85
+ ### Features
86
+
87
+ * **user-entitlements:** add missing types ([fd375c1](https://github.com/frontegg/entitlements-javascript-commons/commit/fd375c15fea40ddbf8e259a73f8831fa9dbb763e))
88
+ * **user-entitlements:** export types ([06f6f13](https://github.com/frontegg/entitlements-javascript-commons/commit/06f6f13263bcfecc098290da35b310393bcecda4))
89
+
1
90
  # [1.0.0-alpha.9](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.8...v-1.0.0-alpha.9) (2023-10-12)
2
91
 
3
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontegg/entitlements-javascript-commons",
3
- "version": "1.0.0-alpha.9",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -5,9 +5,15 @@ export { OperationEnum, ConditionValue } from './operations/types';
5
5
  export {
6
6
  evaluateIsEntitledToFeature,
7
7
  evaluateIsEntitledToPermissions,
8
+ prepareAttributes,
9
+ checkPermission,
10
+ createPermissionCheckRegex,
11
+ Permissions,
12
+ JwtAttributes,
8
13
  CustomAttributes,
9
14
  FronteggAttributes,
10
15
  NotEntitledJustification,
11
16
  UserEntitlementsContext,
12
17
  EntitlementResult,
18
+ Attributes,
13
19
  } from './user-entitlements';
@@ -1,8 +1,18 @@
1
1
  import { OperationEnum, Sanitizer, SanitizersMapper } from '../types';
2
2
  import { BetweenDateOperationPayload, DateOperationPayload, SingleDateOperationPayload } from './types';
3
3
 
4
+ type RawDateValue = Date | string | number;
5
+
6
+ export const sanitizeDateValue = (value: Date | string | number): Date => {
7
+ return new Date(value);
8
+ };
9
+
10
+ export const isValidDate = (value: unknown): boolean => value instanceof Date && !isNaN(value as unknown as number);
11
+
4
12
  export const sanitizeSingleDate: Sanitizer<SingleDateOperationPayload> = (value) => {
5
- const sanitizedValue = value.date ? { date: value.date as Date } : undefined;
13
+ const sanitizedDateValue = value.date ? sanitizeDateValue(value.date as RawDateValue) : undefined;
14
+ const sanitizedValue =
15
+ sanitizedDateValue && isValidDate(sanitizedDateValue) ? { date: sanitizedDateValue } : undefined;
6
16
 
7
17
  return {
8
18
  isSanitized: !!sanitizedValue,
@@ -11,7 +21,12 @@ export const sanitizeSingleDate: Sanitizer<SingleDateOperationPayload> = (value)
11
21
  };
12
22
 
13
23
  export const sanitizeDateRange: Sanitizer<BetweenDateOperationPayload> = (value) => {
14
- const sanitizedValue = value.start && value.end ? { start: value.start as Date, end: value.end as Date } : undefined;
24
+ const sanitizedStartValue = value.start ? sanitizeDateValue(value.start as RawDateValue) : undefined;
25
+ const sanitizedEndValue = value.end ? sanitizeDateValue(value.end as RawDateValue) : undefined;
26
+ const sanitizedValue =
27
+ sanitizedStartValue && sanitizedEndValue && isValidDate(sanitizedStartValue) && isValidDate(sanitizedEndValue)
28
+ ? { start: sanitizedStartValue, end: sanitizedEndValue }
29
+ : undefined;
15
30
 
16
31
  return {
17
32
  isSanitized: !!sanitizedValue,
@@ -21,7 +36,7 @@ export const sanitizeDateRange: Sanitizer<BetweenDateOperationPayload> = (value)
21
36
 
22
37
  export const DateSanitizersMapper: SanitizersMapper<DateOperationPayload> = {
23
38
  [OperationEnum.On]: sanitizeSingleDate,
24
- [OperationEnum.OnOrAfter]: sanitizeDateRange,
39
+ [OperationEnum.OnOrAfter]: sanitizeSingleDate,
25
40
  [OperationEnum.OnOrBefore]: sanitizeSingleDate,
26
- [OperationEnum.BetweenDate]: sanitizeSingleDate,
41
+ [OperationEnum.BetweenDate]: sanitizeDateRange,
27
42
  };
@@ -1,7 +1,24 @@
1
1
  import { fc, test } from '@fast-check/jest';
2
- import { sanitizeDateRange, sanitizeSingleDate } from '../sanitizers';
2
+ import { isValidDate, sanitizeDateRange, sanitizeDateValue, sanitizeSingleDate } from '../sanitizers';
3
3
 
4
4
  describe('Date sanitizers', () => {
5
+ it.each([['2021-01-01'], ['2023-10-23T12:09:17.502Z'], [123432443], [new Date()]])(
6
+ 'should return sanitized date when value is a string',
7
+ (value) => {
8
+ const sanitizationResult = sanitizeDateValue(value);
9
+
10
+ expect(sanitizationResult).toBeDefined();
11
+ expect(sanitizationResult).toEqual(new Date(value));
12
+ },
13
+ );
14
+
15
+ it.each([['not a date'], [], {}, [undefined]])('should return invalid date when value is not a date', (value) => {
16
+ const sanitizationResult = sanitizeDateValue(value as any);
17
+
18
+ expect(sanitizationResult).toBeDefined();
19
+ expect(isValidDate(sanitizationResult)).toBe(false);
20
+ });
21
+
5
22
  test.prop([fc.record({ date: fc.date() })], { verbose: true })(
6
23
  'should return sanitized when date value exists',
7
24
  (value) => {
@@ -26,9 +26,9 @@ export const sanitizeNumericRange: Sanitizer<BetweenNumericOperationPayload> = (
26
26
 
27
27
  export const NumericSanitizersMapper: SanitizersMapper<NumericOperationPayload> = {
28
28
  [OperationEnum.Equal]: sanitizeSingleNumber,
29
- [OperationEnum.GreaterThan]: sanitizeNumericRange,
29
+ [OperationEnum.GreaterThan]: sanitizeSingleNumber,
30
30
  [OperationEnum.GreaterThanEqual]: sanitizeSingleNumber,
31
31
  [OperationEnum.LesserThan]: sanitizeSingleNumber,
32
- [OperationEnum.LesserThanEqual]: sanitizeNumericRange,
32
+ [OperationEnum.LesserThanEqual]: sanitizeSingleNumber,
33
33
  [OperationEnum.BetweenNumeric]: sanitizeNumericRange,
34
34
  };
@@ -27,7 +27,7 @@ export const sanitizeListString: Sanitizer<ListStringOperationPayload> = (value)
27
27
  export const StringSanitizersMapper: SanitizersMapper<StringOperationPayload> = {
28
28
  [OperationEnum.Matches]: sanitizeSingleString,
29
29
  [OperationEnum.Contains]: sanitizeListString,
30
- [OperationEnum.StartsWith]: sanitizeSingleString,
31
- [OperationEnum.EndsWith]: sanitizeSingleString,
30
+ [OperationEnum.StartsWith]: sanitizeListString,
31
+ [OperationEnum.EndsWith]: sanitizeListString,
32
32
  [OperationEnum.InList]: sanitizeListString,
33
33
  };
@@ -0,0 +1,43 @@
1
+ import { Attributes, JwtAttributes, FronteggAttributes } from './types';
2
+ import { flatten } from './flatten.utils';
3
+ /**
4
+ * Merges both `custom` and `jwt` records, map Frontegg attributes and modifies record keys with corrisponding prefixes
5
+ *
6
+ * Example:
7
+ * Input: { 'custom': { 'customAttribute': 'someValue' }, 'jwt': { 'email': 'user@email.com', other: 'some-vaule' } }
8
+ * Output: { 'customAttribute': 'someValue', 'frontegg.email': 'user@email.com', 'jwt.email': 'user@email.com', 'jwt.other': 'some-vaule' }
9
+ */
10
+ export function prepareAttributes(
11
+ attributes: Attributes = {},
12
+ customFronteggAttributesMapper?: (jwtAttributes: JwtAttributes) => FronteggAttributes,
13
+ ): Record<string, unknown> {
14
+ const { custom = {}, jwt = {} } = attributes;
15
+ const flatJwtAttributes = flatten<JwtAttributes, JwtAttributes>(jwt);
16
+ const fronteggAttributes = customFronteggAttributesMapper
17
+ ? customFronteggAttributesMapper(jwt)
18
+ : defaultFronteggAttributesMapper(jwt);
19
+ const fronteggAttributesPrefix = 'frontegg.';
20
+ const jwtAttributesPrefix = 'jwt.';
21
+
22
+ return {
23
+ ...custom,
24
+ ...modifyObjectKeysWithPrefix(fronteggAttributes, fronteggAttributesPrefix),
25
+ ...modifyObjectKeysWithPrefix(flatJwtAttributes, jwtAttributesPrefix),
26
+ };
27
+ }
28
+
29
+ export function defaultFronteggAttributesMapper(jwt: JwtAttributes): FronteggAttributes {
30
+ return {
31
+ email: jwt.email as string,
32
+ emailVerified: jwt.email_verified as boolean,
33
+ tenantId: jwt.tenantId as string,
34
+ userId: jwt.userId as string,
35
+ };
36
+ }
37
+
38
+ export function modifyObjectKeysWithPrefix(object: Record<string, unknown>, prefix: string): Record<string, unknown> {
39
+ return Object.keys(object).reduce((modifiedObject, currentKey) => {
40
+ modifiedObject[`${prefix}${currentKey}`] = object[currentKey];
41
+ return modifiedObject;
42
+ }, {});
43
+ }
@@ -0,0 +1,52 @@
1
+ /** DISCLAIMER - THIS CODE BELONGS TO https://github.com/hughsk/flat */
2
+ export interface FlattenOptions {
3
+ delimiter?: string;
4
+ maxDepth?: number;
5
+ safe?: boolean;
6
+ transformKey?: (key: string) => string;
7
+ }
8
+
9
+ export function flatten<TTraget, TResult>(target: TTraget, opts?: FlattenOptions): TResult {
10
+ opts = opts || {};
11
+
12
+ const delimiter = opts?.delimiter || '.';
13
+ const maxDepth = opts?.maxDepth;
14
+ const transformKey = opts?.transformKey || keyIdentity;
15
+ const output = {};
16
+
17
+ function step(object, prev?, currentDepth?) {
18
+ currentDepth = currentDepth || 1;
19
+ Object.keys(object).forEach(function (key) {
20
+ const value = object[key];
21
+ const isarray = opts?.safe && Array.isArray(value);
22
+ const type = Object.prototype.toString.call(value);
23
+ const isbuffer = isBuffer(value);
24
+ const isobject = type === '[object Object]' || type === '[object Array]';
25
+
26
+ const newKey = prev ? prev + delimiter + transformKey(key) : transformKey(key);
27
+ if (
28
+ !isarray &&
29
+ !isbuffer &&
30
+ isobject &&
31
+ Object.keys(value).length &&
32
+ (!opts?.maxDepth || (maxDepth && currentDepth < maxDepth))
33
+ ) {
34
+ return step(value, newKey, currentDepth + 1);
35
+ }
36
+
37
+ output[newKey] = value;
38
+ });
39
+ }
40
+
41
+ step(target);
42
+
43
+ return output as TResult;
44
+ }
45
+
46
+ function isBuffer(obj) {
47
+ return obj && obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj);
48
+ }
49
+
50
+ function keyIdentity(key) {
51
+ return key;
52
+ }
@@ -1,2 +1,4 @@
1
1
  export * from './is-entitled.evaluator';
2
2
  export * from './types';
3
+ export * from './attributes.utils';
4
+ export * from './permissions.utils';
@@ -5,12 +5,15 @@ import {
5
5
  UserEntitlementsContext,
6
6
  Attributes,
7
7
  } from './types';
8
+
8
9
  import { evaluateFeatureFlag } from '../feature-flags';
10
+ import { prepareAttributes } from './attributes.utils';
9
11
  import { TreatmentEnum } from '../rules';
12
+ import { checkPermission } from './permissions.utils';
10
13
  export function evaluateIsEntitledToFeature(
11
14
  featureKey: string,
12
15
  userEntitlementsContext: UserEntitlementsContext,
13
- attributes?: Attributes,
16
+ attributes: Attributes = {},
14
17
  ): EntitlementResult {
15
18
  const feature = userEntitlementsContext.features[featureKey];
16
19
  let hasExpired = false;
@@ -23,7 +26,8 @@ export function evaluateIsEntitledToFeature(
23
26
  }
24
27
 
25
28
  if (feature && feature.featureFlag) {
26
- const { treatment } = evaluateFeatureFlag(feature.featureFlag, prepareAttributes(attributes));
29
+ const preparedAttributes = prepareAttributes(attributes);
30
+ const { treatment } = evaluateFeatureFlag(feature.featureFlag, preparedAttributes);
27
31
  if (treatment === TreatmentEnum.True) {
28
32
  return { isEntitled: true };
29
33
  }
@@ -40,9 +44,8 @@ export function evaluateIsEntitledToPermissions(
40
44
  userEntitlementsContext: UserEntitlementsContext,
41
45
  attributes?: Attributes,
42
46
  ): EntitlementResult {
43
- const permission = userEntitlementsContext.permissions[permissionKey];
44
-
45
- if (!permission) {
47
+ const hasPermission = checkPermission(userEntitlementsContext.permissions, permissionKey);
48
+ if (!hasPermission) {
46
49
  return { isEntitled: false, justification: NotEntitledJustification.MISSING_PERMISSION };
47
50
  }
48
51
 
@@ -77,23 +80,3 @@ function getLinkedFeatures(permissionKey: string, userEntitlementsContext: UserE
77
80
  userEntitlementsContext.features[featureKey].linkedPermissions.includes(permissionKey),
78
81
  );
79
82
  }
80
-
81
- /**
82
- * Merges the `custom` and `frontegg` Records into a single Record,
83
- * Alters the `frontegg` Record keys with a prefix
84
- * Example:
85
- * Input: { 'custom': { 'customAttribute': 'someValue' }, 'frontegg': { 'email': 'user@email.com' } }
86
- * Output: { 'customAttribute': 'someValue', 'imported.email': 'user@email.com' }
87
- */
88
- export function prepareAttributes(attributes: Attributes = {}): Record<string, unknown> {
89
- const { custom = {}, frontegg = {} } = attributes;
90
- const importedAttributesPrefix = 'imported.'; // Not Final
91
-
92
- return {
93
- ...custom,
94
- ...Object.keys(frontegg).reduce((modifiedImportedAttributes, key) => {
95
- modifiedImportedAttributes[`${importedAttributesPrefix}${key}`] = frontegg[key];
96
- return modifiedImportedAttributes;
97
- }, {}),
98
- };
99
- }
@@ -0,0 +1,10 @@
1
+ import { Permissions } from './types';
2
+ export function checkPermission(permissions: Permissions, requiredPermission: string): boolean {
3
+ return Object.keys(permissions).some((permissionKey) =>
4
+ createPermissionCheckRegex(permissionKey).test(requiredPermission),
5
+ );
6
+ }
7
+
8
+ export function createPermissionCheckRegex(permissionKey: string): RegExp {
9
+ return new RegExp('^' + permissionKey.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$', 'gs');
10
+ }
@@ -0,0 +1,75 @@
1
+ import * as AttributesUtils from '../attributes.utils';
2
+ import { Attributes, FronteggAttributes, JwtAttributes } from '../types';
3
+ describe('prepareAttributes', () => {
4
+ test('given custom & jwt attributes, expected is merged & flatten attributes record', () => {
5
+ const attributes: Attributes = {
6
+ custom: {
7
+ customAttribute: 'some-value',
8
+ },
9
+ jwt: {
10
+ userId: 'user-1',
11
+ tenantId: 'tenant-1',
12
+ email: 'test@email.com',
13
+ email_verified: true,
14
+ dummyAttribute: 'dummy',
15
+ },
16
+ };
17
+
18
+ const expectedPreparedAttributes = {
19
+ customAttribute: 'some-value',
20
+ 'frontegg.userId': 'user-1',
21
+ 'frontegg.tenantId': 'tenant-1',
22
+ 'frontegg.email': 'test@email.com',
23
+ 'frontegg.emailVerified': true,
24
+ 'jwt.userId': 'user-1',
25
+ 'jwt.tenantId': 'tenant-1',
26
+ 'jwt.email': 'test@email.com',
27
+ 'jwt.email_verified': true,
28
+ 'jwt.dummyAttribute': 'dummy',
29
+ };
30
+
31
+ const preparedAttributes = AttributesUtils.prepareAttributes(attributes);
32
+
33
+ expect(preparedAttributes).toEqual(expectedPreparedAttributes);
34
+ });
35
+ });
36
+
37
+ describe('defaultFronteggAttributesMapper', () => {
38
+ test('given jwt-attributes, expected mapped frontegg-attributes', async () => {
39
+ const jwtAttributes: JwtAttributes = {
40
+ userId: 'user-1',
41
+ tenantId: 'tenant-1',
42
+ email: 'test@email.com',
43
+ email_verified: true,
44
+ dummyAttribute: 'dummy',
45
+ };
46
+
47
+ const expectedFronteggAttributes: FronteggAttributes = {
48
+ userId: 'user-1',
49
+ tenantId: 'tenant-1',
50
+ email: 'test@email.com',
51
+ emailVerified: true,
52
+ };
53
+
54
+ const mappedAttributes = AttributesUtils.defaultFronteggAttributesMapper(jwtAttributes);
55
+
56
+ expect(mappedAttributes).toEqual(expectedFronteggAttributes);
57
+ });
58
+ });
59
+
60
+ describe('modifyObjectKeysWithPrefix', () => {
61
+ test('given object and prefix, object keys should altered with prefix', async () => {
62
+ const prefix = 'test.';
63
+ const obj = {
64
+ property: 'value',
65
+ };
66
+
67
+ const expectedModifiedObject = {
68
+ 'test.property': 'value',
69
+ };
70
+
71
+ const modifiedObject = AttributesUtils.modifyObjectKeysWithPrefix(obj, prefix);
72
+
73
+ expect(modifiedObject).toEqual(expectedModifiedObject);
74
+ });
75
+ });
@@ -1,14 +1,10 @@
1
- import * as FeatureFlags from '../../feature-flags/feature-flag.evaluator';
2
1
  import * as IsEntitledEvaluators from '../is-entitled.evaluator';
2
+ import * as FeatureFlagEvalutor from '../../feature-flags/feature-flag.evaluator';
3
+ import * as AttributesUtils from '../attributes.utils';
3
4
  import { TreatmentEnum } from '../../rules';
4
- import {
5
- EntitlementResult,
6
- NotEntitledJustification,
7
- NO_EXPIRATION_TIME,
8
- UserEntitlementsContext,
9
- Attributes,
10
- } from '../types';
5
+ import { EntitlementResult, NotEntitledJustification, NO_EXPIRATION_TIME, UserEntitlementsContext } from '../types';
11
6
  import { FeatureFlag } from '../../feature-flags/types';
7
+
12
8
  const mockFeatureFlag: FeatureFlag = {
13
9
  on: true,
14
10
  defaultTreatment: TreatmentEnum.True,
@@ -31,10 +27,13 @@ const falsyEntitlementResultMissingPermission: EntitlementResult = {
31
27
  justification: NotEntitledJustification.MISSING_PERMISSION,
32
28
  };
33
29
  describe('evaluateIsEntitledToFeature', () => {
30
+ beforeAll(() => {
31
+ jest.spyOn(AttributesUtils, 'prepareAttributes').mockReturnValue({ testAttribute: 'test-value' });
32
+ });
34
33
  describe('entitled', () => {
35
34
  describe('feature-flag evaluated truthy', () => {
36
35
  beforeAll(async () => {
37
- jest.spyOn(FeatureFlags, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.True });
36
+ jest.spyOn(FeatureFlagEvalutor, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.True });
38
37
  });
39
38
 
40
39
  test('feature granted with valid expiration date', async () => {
@@ -100,7 +99,7 @@ describe('evaluateIsEntitledToFeature', () => {
100
99
  });
101
100
  describe('feature-flag evaluated falsy', () => {
102
101
  beforeAll(async () => {
103
- jest.spyOn(FeatureFlags, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.False });
102
+ jest.spyOn(FeatureFlagEvalutor, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.False });
104
103
  });
105
104
  test('feature granted with no expiration date', async () => {
106
105
  const userEntitlementContext: UserEntitlementsContext = {
@@ -168,7 +167,7 @@ describe('evaluateIsEntitledToFeature', () => {
168
167
  describe('not entitled', () => {
169
168
  describe('feature-flag evaluated falsy', () => {
170
169
  beforeAll(async () => {
171
- jest.spyOn(FeatureFlags, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.False });
170
+ jest.spyOn(FeatureFlagEvalutor, 'evaluateFeatureFlag').mockReturnValue({ treatment: TreatmentEnum.False });
172
171
  });
173
172
  test('feature has not been granted', async () => {
174
173
  const userEntitlementContext: UserEntitlementsContext = {
@@ -176,6 +175,7 @@ describe('evaluateIsEntitledToFeature', () => {
176
175
  'test-feature': {
177
176
  expireTime: null,
178
177
  linkedPermissions: [],
178
+ featureFlag: mockFeatureFlag,
179
179
  },
180
180
  },
181
181
  permissions: {},
@@ -190,6 +190,7 @@ describe('evaluateIsEntitledToFeature', () => {
190
190
  'test-feature': {
191
191
  expireTime: Date.now() - 3600,
192
192
  linkedPermissions: [],
193
+ featureFlag: mockFeatureFlag,
193
194
  },
194
195
  },
195
196
  permissions: {},
@@ -295,21 +296,3 @@ describe('evaluateIsEntitledToPermission', () => {
295
296
  expect(result).toEqual(falsyEntitlementResultBundleExpired);
296
297
  });
297
298
  });
298
-
299
- describe('prepareAttributes', () => {
300
- test('given custom & frontegg attributes, expected is merged Record, frontegg attributes altered with prefix', async () => {
301
- const attributes: Attributes = {
302
- custom: { testAttribute: 'testValue' },
303
- frontegg: { email: 'test@email.com', unknownFronteggAttribute: 'unknownFronteggAttribute' },
304
- };
305
-
306
- const expectedPreparedAttributes: Record<string, unknown> = {
307
- testAttribute: 'testValue',
308
- 'imported.email': 'test@email.com',
309
- 'imported.unknownFronteggAttribute': 'unknownFronteggAttribute',
310
- };
311
- const preparedAttributes = IsEntitledEvaluators.prepareAttributes(attributes);
312
-
313
- expect(preparedAttributes).toEqual(expectedPreparedAttributes);
314
- });
315
- });
@@ -0,0 +1,21 @@
1
+ import { Permissions } from '../types';
2
+ import { checkPermission } from '../permissions.utils';
3
+ describe('checkPermission', () => {
4
+ test.each([[{ 'test.permission': true }], [{ 'test.*': true }], [{ '*': true }]])(
5
+ 'expected truthy result',
6
+ (permissions) => {
7
+ const requiredPermission = 'test.permission';
8
+ const result = checkPermission(permissions as Permissions, requiredPermission);
9
+ expect(result).toBeTruthy();
10
+ },
11
+ );
12
+
13
+ test.each([[{}], [{ 'test.permission.read': true }], [{ 'test.permission.write.*': true }]])(
14
+ 'expected falsy result',
15
+ (permissions) => {
16
+ const requiredPermission = 'test.permission';
17
+ const result = checkPermission(permissions as Permissions, requiredPermission);
18
+ expect(result).toBeFalsy();
19
+ },
20
+ );
21
+ });
@@ -8,7 +8,7 @@ export type UserEntitlementsContext = {
8
8
  featureFlag?: FeatureFlag;
9
9
  }
10
10
  >;
11
- permissions: Record<string, true>;
11
+ permissions: Permissions;
12
12
  };
13
13
 
14
14
  export type EntitlementResult = {
@@ -28,10 +28,9 @@ export type FronteggAttributes = {
28
28
  tenantId?: string;
29
29
  userId?: string;
30
30
  email?: string;
31
- email_verified?: boolean;
32
- [unmappedAttribute: string]: unknown;
31
+ emailVerified?: boolean;
33
32
  };
34
-
35
- export type Attributes = { custom?: CustomAttributes; frontegg?: FronteggAttributes };
36
-
33
+ export type Permissions = Record<string, true>;
34
+ export type Attributes = { custom?: CustomAttributes; jwt?: JwtAttributes };
35
+ export type JwtAttributes = Record<string, unknown>;
37
36
  export const NO_EXPIRATION_TIME = -1;