@prosopo/user-access-policy 3.12.8 → 3.12.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.
Files changed (84) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +45 -41
  2. package/.turbo/turbo-build$colon$tsc.log +14 -14
  3. package/.turbo/turbo-build.log +46 -42
  4. package/CHANGELOG.md +24 -0
  5. package/dist/.export.js +7 -21
  6. package/dist/_virtual/_rolldown/runtime.js +3 -0
  7. package/dist/api/.export.js +2 -9
  8. package/dist/api/accessRulesApiClient.js +1 -1
  9. package/dist/api/delete/.export.js +2 -1
  10. package/dist/api/delete/deleteAllRules.js +22 -24
  11. package/dist/api/delete/deleteRuleGroups.js +39 -51
  12. package/dist/api/delete/deleteRules.js +37 -41
  13. package/dist/api/read/.export.js +2 -1
  14. package/dist/api/read/fetchRules.js +34 -42
  15. package/dist/api/read/findRuleIds.js +35 -48
  16. package/dist/api/read/getMissingIds.js +36 -40
  17. package/dist/api/ruleApiRoutes.js +62 -120
  18. package/dist/api/rulesApiClient.js +51 -87
  19. package/dist/api/write/.export.js +2 -1
  20. package/dist/api/write/insertRules.js +84 -102
  21. package/dist/api/write/rehashRules.js +48 -56
  22. package/dist/cjs/.export.cjs +21 -23
  23. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  24. package/dist/cjs/api/.export.cjs +6 -10
  25. package/dist/cjs/api/accessRulesApiClient.cjs +0 -0
  26. package/dist/cjs/api/delete/.export.cjs +0 -1
  27. package/dist/cjs/api/delete/deleteAllRules.cjs +22 -24
  28. package/dist/cjs/api/delete/deleteRuleGroups.cjs +39 -51
  29. package/dist/cjs/api/delete/deleteRules.cjs +41 -42
  30. package/dist/cjs/api/read/.export.cjs +0 -1
  31. package/dist/cjs/api/read/fetchRules.cjs +34 -41
  32. package/dist/cjs/api/read/findRuleIds.cjs +37 -48
  33. package/dist/cjs/api/read/getMissingIds.cjs +36 -39
  34. package/dist/cjs/api/ruleApiRoutes.cjs +69 -125
  35. package/dist/cjs/api/rulesApiClient.cjs +54 -90
  36. package/dist/cjs/api/write/.export.cjs +0 -1
  37. package/dist/cjs/api/write/insertRules.cjs +85 -103
  38. package/dist/cjs/api/write/rehashRules.cjs +48 -56
  39. package/dist/cjs/classifyOs.cjs +46 -26
  40. package/dist/cjs/mongoose/.export.cjs +2 -3
  41. package/dist/cjs/mongoose/mongooseRuleSchema.cjs +97 -36
  42. package/dist/cjs/redis/.export.cjs +4 -5
  43. package/dist/cjs/redis/reader/redisAggregate.cjs +59 -84
  44. package/dist/cjs/redis/reader/redisRulesQuery.cjs +74 -116
  45. package/dist/cjs/redis/reader/redisRulesReader.cjs +322 -503
  46. package/dist/cjs/redis/reader/redisRulesSplitQuery.cjs +94 -72
  47. package/dist/cjs/redis/redisClient.cjs +53 -58
  48. package/dist/cjs/redis/redisRuleIndex.cjs +93 -49
  49. package/dist/cjs/redis/redisRulesStorage.cjs +21 -30
  50. package/dist/cjs/redis/redisRulesWriter.cjs +86 -112
  51. package/dist/cjs/rule.cjs +8 -7
  52. package/dist/cjs/ruleInput/.export.cjs +7 -8
  53. package/dist/cjs/ruleInput/policyInput.cjs +35 -58
  54. package/dist/cjs/ruleInput/ruleInput.cjs +37 -42
  55. package/dist/cjs/ruleInput/userScopeInput.cjs +47 -55
  56. package/dist/cjs/ruleRecord.cjs +15 -22
  57. package/dist/cjs/rulesStorage.cjs +7 -6
  58. package/dist/cjs/transformRule.cjs +36 -70
  59. package/dist/classifyOs.js +48 -29
  60. package/dist/mongoose/.export.js +2 -3
  61. package/dist/mongoose/mongooseRuleSchema.js +99 -38
  62. package/dist/redis/.export.js +3 -5
  63. package/dist/redis/reader/redisAggregate.js +60 -86
  64. package/dist/redis/reader/redisRulesQuery.js +75 -118
  65. package/dist/redis/reader/redisRulesReader.js +312 -487
  66. package/dist/redis/reader/redisRulesSplitQuery.js +96 -75
  67. package/dist/redis/redisClient.js +54 -62
  68. package/dist/redis/redisRuleIndex.js +93 -58
  69. package/dist/redis/redisRulesStorage.js +21 -30
  70. package/dist/redis/redisRulesWriter.js +82 -113
  71. package/dist/rule.js +10 -10
  72. package/dist/ruleInput/.export.js +3 -7
  73. package/dist/ruleInput/policyInput.js +35 -61
  74. package/dist/ruleInput/ruleInput.js +36 -44
  75. package/dist/ruleInput/userScopeInput.js +44 -55
  76. package/dist/ruleRecord.js +17 -27
  77. package/dist/rulesStorage.js +9 -8
  78. package/dist/tests/redis/redisRulesStorage.integration.test.js +1 -3
  79. package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -1
  80. package/dist/transformRule.js +34 -73
  81. package/entries.ts +25 -6
  82. package/package.json +13 -12
  83. package/src/tests/redis/redisRulesStorage.integration.test.ts +191 -195
  84. package/tsconfig.tsbuildinfo +1 -1
@@ -1,80 +1,101 @@
1
- import { GLOBAL_CLIENT_SCOPE_SENTINEL, AccessPolicyType } from "../../rule.js";
2
- const escapeTagValue = (value) => value.replace(/([,.<>{}\[\]"':;!@#$%^&*()\-+=~|/\\])/g, "\\$1");
3
- const FIELDS_REQUIRING_ESCAPE = /* @__PURE__ */ new Set([
4
- "coords"
5
- ]);
6
- const NUMERIC_FIELDS = /* @__PURE__ */ new Set(["asn"]);
7
- const SCALAR_USER_SCOPE_FIELDS = [
8
- "userId",
9
- "ja4Hash",
10
- "headersHash",
11
- "userAgentHash",
12
- "headHash",
13
- "coords",
14
- "countryCode",
15
- "asn"
1
+ import "../../_virtual/_rolldown/runtime.js";
2
+ import { AccessPolicyType, GLOBAL_CLIENT_SCOPE_SENTINEL } from "../../rule.js";
3
+ //#region src/redis/reader/redisRulesSplitQuery.ts
4
+ var escapeTagValue = (value) => value.replace(/([,.<>{}\[\]"':;!@#$%^&*()\-+=~|/\\])/g, "\\$1");
5
+ var FIELDS_REQUIRING_ESCAPE = /* @__PURE__ */ new Set(["coords"]);
6
+ var NUMERIC_FIELDS = /* @__PURE__ */ new Set(["asn"]);
7
+ var SCALAR_USER_SCOPE_FIELDS = [
8
+ "userId",
9
+ "ja4Hash",
10
+ "headersHash",
11
+ "userAgentHash",
12
+ "headHash",
13
+ "coords",
14
+ "countryCode",
15
+ "asn"
16
16
  ];
17
- const ALL_USER_SCOPE_FIELDS = [
18
- ...SCALAR_USER_SCOPE_FIELDS,
19
- "numericIp",
20
- "numericIpMaskMin",
21
- "numericIpMaskMax"
17
+ var ALL_USER_SCOPE_FIELDS = [
18
+ ...SCALAR_USER_SCOPE_FIELDS,
19
+ "numericIp",
20
+ "numericIpMaskMin",
21
+ "numericIpMaskMax"
22
22
  ];
23
- const GLOBAL_SCOPE_INNER = `@clientId:{${GLOBAL_CLIENT_SCOPE_SENTINEL}} | ismissing(@clientId)`;
24
- const buildScopeClause = (clientId) => {
25
- if (clientId === void 0) {
26
- return `( ${GLOBAL_SCOPE_INNER} )`;
27
- }
28
- return `( @clientId:{${clientId}} | ${GLOBAL_SCOPE_INNER} )`;
23
+ var GLOBAL_SCOPE_INNER = `@clientId:{${GLOBAL_CLIENT_SCOPE_SENTINEL}} | ismissing(@clientId)`;
24
+ var buildScopeClause = (clientId) => {
25
+ if (clientId === void 0) return `( ${GLOBAL_SCOPE_INNER} )`;
26
+ return `( @clientId:{${clientId}} | ${GLOBAL_SCOPE_INNER} )`;
29
27
  };
30
- const buildFieldClause = (field, value) => {
31
- if (value === void 0) {
32
- return null;
33
- }
34
- const stringValue = String(value);
35
- if (NUMERIC_FIELDS.has(field)) {
36
- return `@${field}:[${stringValue} ${stringValue}]`;
37
- }
38
- const queryValue = FIELDS_REQUIRING_ESCAPE.has(field) ? escapeTagValue(stringValue) : stringValue;
39
- return `@${field}:{${queryValue}}`;
28
+ var buildFieldClause = (field, value) => {
29
+ if (value === void 0) return null;
30
+ const stringValue = String(value);
31
+ if (NUMERIC_FIELDS.has(field)) return `@${field}:[${stringValue} ${stringValue}]`;
32
+ return `@${field}:{${FIELDS_REQUIRING_ESCAPE.has(field) ? escapeTagValue(stringValue) : stringValue}}`;
40
33
  };
41
- const buildScopedRulesSubQueries = (userScope, clientId, options = {}) => {
42
- const typeClause = options.blockOnly ? `@type:{${AccessPolicyType.Block}} ` : "";
43
- const scopeClause = buildScopeClause(clientId);
44
- const prefix = `${typeClause}${scopeClause}`;
45
- const subQueries = [];
46
- for (const field of SCALAR_USER_SCOPE_FIELDS) {
47
- const clause = buildFieldClause(field, userScope[field]);
48
- if (clause === null) {
49
- continue;
50
- }
51
- subQueries.push({
52
- kind: `field:${field}`,
53
- query: `${prefix} ${clause}`
54
- });
55
- }
56
- const requestIp = userScope.numericIp;
57
- if (requestIp !== void 0) {
58
- subQueries.push({
59
- kind: "ip:exact",
60
- query: `${prefix} @numericIp:[${requestIp} ${requestIp}]`
61
- });
62
- subQueries.push({
63
- kind: "ip:mask",
64
- query: `${prefix} @numericIpMaskMin:[-inf ${requestIp}] @numericIpMaskMax:[${requestIp} +inf]`
65
- });
66
- }
67
- const noScopeIsmissing = ALL_USER_SCOPE_FIELDS.map(
68
- (field) => `ismissing(@${field})`
69
- ).join(" ");
70
- subQueries.push({
71
- kind: "no-user-scope",
72
- query: `${prefix} ${noScopeIsmissing}`
73
- });
74
- return subQueries;
75
- };
76
- const buildScopedBlockSubQueries = (userScope, clientId) => buildScopedRulesSubQueries(userScope, clientId, { blockOnly: true });
77
- export {
78
- buildScopedBlockSubQueries,
79
- buildScopedRulesSubQueries
34
+ /**
35
+ * Build the set of FT sub-queries that together cover every rule that
36
+ * could apply to a request.
37
+ *
38
+ * The single-query design this replaces produced one FT.AGGREGATE whose
39
+ * candidate set was the entire scope's rule pool — the APPLY / SORTBY
40
+ * pipeline then walked every rule per request, which degrades sharply
41
+ * once rule populations reach the 10k+ range. The split approach fires
42
+ * one probe per populated request field. Each probe uses the field's
43
+ * own index posting list, so a 17k IP-ban population collapses to 1
44
+ * result (exact IP) plus a handful (CIDR ranges containing the IP)
45
+ * instead of forcing a full-set scan.
46
+ *
47
+ * A single fall-through probe covers rules with no user-scope constraint
48
+ * (client-wide restriction); those are rare so the ismissing()-heavy
49
+ * query is cheap in practice.
50
+ *
51
+ * Rank + strict-match filtering happens in JS via `rankCandidateRules`
52
+ * after the union — the FT layer is used purely as a candidate fetcher.
53
+ *
54
+ * `blockOnly` narrows the candidate set to rules with `type:{block}` —
55
+ * used by the hard-block middleware which never needs Restrict rules.
56
+ * Every other caller passes false so both Block and Restrict rules are
57
+ * fetched; the JS-side ranker picks the right one via specificity +
58
+ * severity. Merging the block and restrict paths behind one split-query
59
+ * fixed the Twickets IP-rule regression: the previous FT.AGGREGATE ranker
60
+ * used for non-block lookups was truncating specific-IP restrict rules
61
+ * out of the top-N when many higher-specificity irrelevant rules matched
62
+ * the greedy `ismissing()` disjunction.
63
+ */
64
+ var buildScopedRulesSubQueries = (userScope, clientId, options = {}) => {
65
+ const prefix = `${options.blockOnly ? `@type:{${AccessPolicyType.Block}} ` : ""}${buildScopeClause(clientId)}`;
66
+ const subQueries = [];
67
+ for (const field of SCALAR_USER_SCOPE_FIELDS) {
68
+ const clause = buildFieldClause(field, userScope[field]);
69
+ if (clause === null) continue;
70
+ subQueries.push({
71
+ kind: `field:${field}`,
72
+ query: `${prefix} ${clause}`
73
+ });
74
+ }
75
+ const requestIp = userScope.numericIp;
76
+ if (requestIp !== void 0) {
77
+ subQueries.push({
78
+ kind: "ip:exact",
79
+ query: `${prefix} @numericIp:[${requestIp} ${requestIp}]`
80
+ });
81
+ subQueries.push({
82
+ kind: "ip:mask",
83
+ query: `${prefix} @numericIpMaskMin:[-inf ${requestIp}] @numericIpMaskMax:[${requestIp} +inf]`
84
+ });
85
+ }
86
+ const noScopeIsmissing = ALL_USER_SCOPE_FIELDS.map((field) => `ismissing(@${field})`).join(" ");
87
+ subQueries.push({
88
+ kind: "no-user-scope",
89
+ query: `${prefix} ${noScopeIsmissing}`
90
+ });
91
+ return subQueries;
80
92
  };
93
+ /**
94
+ * Backwards-compatible alias for the previous block-only builder. New
95
+ * callers should use `buildScopedRulesSubQueries` directly.
96
+ *
97
+ * @deprecated use `buildScopedRulesSubQueries(..., { blockOnly: true })`
98
+ */
99
+ var buildScopedBlockSubQueries = (userScope, clientId) => buildScopedRulesSubQueries(userScope, clientId, { blockOnly: true });
100
+ //#endregion
101
+ export { buildScopedBlockSubQueries, buildScopedRulesSubQueries };
@@ -1,67 +1,59 @@
1
+ import "../_virtual/_rolldown/runtime.js";
1
2
  import { z } from "zod";
2
- const REDIS_BATCH_SIZE = 1e3;
3
- const getMissingRedisKeys = async (client, keys) => {
4
- const queries = client.multi();
5
- keys.map((key) => {
6
- queries.exists(key);
7
- });
8
- const records = await queries.exec();
9
- const missingKeys = [];
10
- records.map((exists, recordIndex) => {
11
- if ("0" === String(exists)) {
12
- const key = keys[recordIndex];
13
- if (key) {
14
- missingKeys.push(key);
15
- }
16
- }
17
- });
18
- return missingKeys;
3
+ //#region src/redis/redisClient.ts
4
+ var REDIS_BATCH_SIZE = 1e3;
5
+ var getMissingRedisKeys = async (client, keys) => {
6
+ const queries = client.multi();
7
+ keys.map((key) => {
8
+ queries.exists(key);
9
+ });
10
+ const records = await queries.exec();
11
+ const missingKeys = [];
12
+ records.map((exists, recordIndex) => {
13
+ if ("0" === String(exists)) {
14
+ const key = keys[recordIndex];
15
+ if (key) missingKeys.push(key);
16
+ }
17
+ });
18
+ return missingKeys;
19
19
  };
20
- const fetchRedisHashRecords = async (client, keys, logger) => {
21
- const rulesPipe = client.multi();
22
- const expirationPipe = client.multi();
23
- for (const key of keys) {
24
- rulesPipe.hGetAll(key);
25
- expirationPipe.expireTime(key);
26
- }
27
- const records = await rulesPipe.exec();
28
- const expirationRecords = await expirationPipe.exec();
29
- return {
30
- records,
31
- expirations: parseExpirationRecords(expirationRecords, logger)
32
- };
20
+ var fetchRedisHashRecords = async (client, keys, logger) => {
21
+ const rulesPipe = client.multi();
22
+ const expirationPipe = client.multi();
23
+ for (const key of keys) {
24
+ rulesPipe.hGetAll(key);
25
+ expirationPipe.expireTime(key);
26
+ }
27
+ return {
28
+ records: await rulesPipe.exec(),
29
+ expirations: parseExpirationRecords(await expirationPipe.exec(), logger)
30
+ };
33
31
  };
34
- const parseRedisRecords = (records, recordSchema, logger) => records.flatMap((record) => {
35
- const parseResult = recordSchema.safeParse(record);
36
- if (parseResult.success) {
37
- return [parseResult.data];
38
- }
39
- logger.error(() => ({
40
- msg: "Failed to parse Redis record",
41
- data: { record, error: parseResult.error }
42
- }));
43
- return [];
32
+ var parseRedisRecords = (records, recordSchema, logger) => records.flatMap((record) => {
33
+ const parseResult = recordSchema.safeParse(record);
34
+ if (parseResult.success) return [parseResult.data];
35
+ logger.error(() => ({
36
+ msg: "Failed to parse Redis record",
37
+ data: {
38
+ record,
39
+ error: parseResult.error
40
+ }
41
+ }));
42
+ return [];
44
43
  });
45
- const expirationRecordSchema = z.coerce.number();
46
- const UNSET_EXPIRATION_VALUE = -1;
47
- const parseExpirationRecords = (records, logger) => records.flatMap((record) => {
48
- const parseResult = expirationRecordSchema.safeParse(record);
49
- if (parseResult.success) {
50
- const expiration = UNSET_EXPIRATION_VALUE === parseResult.data ? void 0 : parseResult.data;
51
- return [expiration];
52
- }
53
- logger.error(() => ({
54
- msg: "Failed to parse Redis expiration record",
55
- data: {
56
- record,
57
- error: parseResult.error
58
- }
59
- }));
60
- return [void 0];
44
+ var expirationRecordSchema = z.coerce.number();
45
+ var UNSET_EXPIRATION_VALUE = -1;
46
+ var parseExpirationRecords = (records, logger) => records.flatMap((record) => {
47
+ const parseResult = expirationRecordSchema.safeParse(record);
48
+ if (parseResult.success) return [UNSET_EXPIRATION_VALUE === parseResult.data ? void 0 : parseResult.data];
49
+ logger.error(() => ({
50
+ msg: "Failed to parse Redis expiration record",
51
+ data: {
52
+ record,
53
+ error: parseResult.error
54
+ }
55
+ }));
56
+ return [void 0];
61
57
  });
62
- export {
63
- REDIS_BATCH_SIZE,
64
- fetchRedisHashRecords,
65
- getMissingRedisKeys,
66
- parseRedisRecords
67
- };
58
+ //#endregion
59
+ export { REDIS_BATCH_SIZE, fetchRedisHashRecords, getMissingRedisKeys, parseRedisRecords };
@@ -1,64 +1,99 @@
1
- import { SCHEMA_FIELD_TYPE } from "@redis/search";
2
- import { makeAccessRuleHash } from "../transformRule.js";
1
+ import "../_virtual/_rolldown/runtime.js";
3
2
  import { GLOBAL_CLIENT_SCOPE_SENTINEL } from "../rule.js";
4
- const userIpRedisSchema = {
5
- numericIpMaskMin: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
6
- numericIpMaskMax: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
7
- numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true }
8
- };
9
- const userAttributesRedisSchema = {
10
- userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
11
- ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
12
- headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
13
- userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
14
- headHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
15
- // Use pipe separator for coords since JSON strings contain commas
16
- coords: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true, SEPARATOR: "|" },
17
- countryCode: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
18
- asn: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
19
- os: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
20
- };
21
- const userScopeRedisSchema = {
22
- ...userAttributesRedisSchema,
23
- ...userIpRedisSchema
3
+ import { makeAccessRuleHash } from "../transformRule.js";
4
+ import { SCHEMA_FIELD_TYPE } from "@redis/search";
5
+ //#region src/redis/redisRuleIndex.ts
6
+ var userIpRedisSchema = {
7
+ numericIpMaskMin: {
8
+ type: SCHEMA_FIELD_TYPE.NUMERIC,
9
+ INDEXMISSING: true
10
+ },
11
+ numericIpMaskMax: {
12
+ type: SCHEMA_FIELD_TYPE.NUMERIC,
13
+ INDEXMISSING: true
14
+ },
15
+ numericIp: {
16
+ type: SCHEMA_FIELD_TYPE.NUMERIC,
17
+ INDEXMISSING: true
18
+ }
24
19
  };
25
- const policyScopeRedisSchema = {
26
- clientId: {
27
- type: SCHEMA_FIELD_TYPE.TAG,
28
- INDEXMISSING: true
29
- }
20
+ var userAttributesRedisSchema = {
21
+ userId: {
22
+ type: SCHEMA_FIELD_TYPE.TAG,
23
+ INDEXMISSING: true
24
+ },
25
+ ja4Hash: {
26
+ type: SCHEMA_FIELD_TYPE.TAG,
27
+ INDEXMISSING: true
28
+ },
29
+ headersHash: {
30
+ type: SCHEMA_FIELD_TYPE.TAG,
31
+ INDEXMISSING: true
32
+ },
33
+ userAgentHash: {
34
+ type: SCHEMA_FIELD_TYPE.TAG,
35
+ INDEXMISSING: true
36
+ },
37
+ headHash: {
38
+ type: SCHEMA_FIELD_TYPE.TAG,
39
+ INDEXMISSING: true
40
+ },
41
+ coords: {
42
+ type: SCHEMA_FIELD_TYPE.TAG,
43
+ INDEXMISSING: true,
44
+ SEPARATOR: "|"
45
+ },
46
+ countryCode: {
47
+ type: SCHEMA_FIELD_TYPE.TAG,
48
+ INDEXMISSING: true
49
+ },
50
+ asn: {
51
+ type: SCHEMA_FIELD_TYPE.NUMERIC,
52
+ INDEXMISSING: true
53
+ },
54
+ os: {
55
+ type: SCHEMA_FIELD_TYPE.TAG,
56
+ INDEXMISSING: true
57
+ }
30
58
  };
31
- const accessRuleRedisSchema = {
32
- ...policyScopeRedisSchema,
33
- ...userScopeRedisSchema,
34
- groupId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
35
- // Indexed so request-time callers that only care about Block policies
36
- // (checkForHardBlock, blockMiddleware) can pre-filter via @type:{block}
37
- // before the SERVER_SIDE_RANK_TOP_N cap kicks in. Without this, dense
38
- // Restrict / routing-Block populations push the hard-block rules out
39
- // of the top-N candidate set and the lookup silently misses them.
40
- type: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
59
+ var userScopeRedisSchema = {
60
+ ...userAttributesRedisSchema,
61
+ ...userIpRedisSchema
41
62
  };
42
- const ACCESS_RULES_REDIS_INDEX_NAME = "index:user-access-rules";
43
- const ACCESS_RULE_REDIS_KEY_PREFIX = "uar:";
44
- const accessRulesRedisIndex = {
45
- name: ACCESS_RULES_REDIS_INDEX_NAME,
46
- schema: accessRuleRedisSchema,
47
- options: {
48
- ON: "HASH",
49
- PREFIX: [ACCESS_RULE_REDIS_KEY_PREFIX]
50
- }
63
+ var policyScopeRedisSchema = { clientId: {
64
+ type: SCHEMA_FIELD_TYPE.TAG,
65
+ INDEXMISSING: true
66
+ } };
67
+ /**
68
+ * Note on the field type decision
69
+ *
70
+ * TAG is designed for the exact value matching
71
+ * TEXT is designed for the word-based and pattern matching
72
+ *
73
+ * For our goal TAG fits perfectly and, more performant
74
+ */
75
+ var accessRuleRedisSchema = {
76
+ ...policyScopeRedisSchema,
77
+ ...userScopeRedisSchema,
78
+ groupId: {
79
+ type: SCHEMA_FIELD_TYPE.TAG,
80
+ INDEXMISSING: true
81
+ },
82
+ type: {
83
+ type: SCHEMA_FIELD_TYPE.TAG,
84
+ INDEXMISSING: true
85
+ }
51
86
  };
52
- const getAccessRuleRedisKey = (rule) => ACCESS_RULE_REDIS_KEY_PREFIX + makeAccessRuleHash(rule);
53
- export {
54
- ACCESS_RULES_REDIS_INDEX_NAME,
55
- ACCESS_RULE_REDIS_KEY_PREFIX,
56
- GLOBAL_CLIENT_SCOPE_SENTINEL,
57
- accessRuleRedisSchema,
58
- accessRulesRedisIndex,
59
- getAccessRuleRedisKey,
60
- policyScopeRedisSchema,
61
- userAttributesRedisSchema,
62
- userIpRedisSchema,
63
- userScopeRedisSchema
87
+ var ACCESS_RULES_REDIS_INDEX_NAME = "index:user-access-rules";
88
+ var ACCESS_RULE_REDIS_KEY_PREFIX = "uar:";
89
+ var accessRulesRedisIndex = {
90
+ name: ACCESS_RULES_REDIS_INDEX_NAME,
91
+ schema: accessRuleRedisSchema,
92
+ options: {
93
+ ON: "HASH",
94
+ PREFIX: [ACCESS_RULE_REDIS_KEY_PREFIX]
95
+ }
64
96
  };
97
+ var getAccessRuleRedisKey = (rule) => ACCESS_RULE_REDIS_KEY_PREFIX + makeAccessRuleHash(rule);
98
+ //#endregion
99
+ export { ACCESS_RULES_REDIS_INDEX_NAME, ACCESS_RULE_REDIS_KEY_PREFIX, GLOBAL_CLIENT_SCOPE_SENTINEL, accessRuleRedisSchema, accessRulesRedisIndex, getAccessRuleRedisKey, policyScopeRedisSchema, userAttributesRedisSchema, userIpRedisSchema, userScopeRedisSchema };
@@ -1,34 +1,25 @@
1
+ import "../_virtual/_rolldown/runtime.js";
1
2
  import { DummyRedisRulesReader, RedisRulesReader } from "./reader/redisRulesReader.js";
2
3
  import { DummyRedisRulesWriter, RedisRulesWriter } from "./redisRulesWriter.js";
3
- const createRedisAccessRulesStorage = (connection, logger) => {
4
- const storage = composeStorage(
5
- new DummyRedisRulesReader(logger),
6
- new DummyRedisRulesWriter(logger)
7
- );
8
- connection.getClient().then((client) => {
9
- const realStorage = composeStorage(
10
- new RedisRulesReader(client, logger),
11
- new RedisRulesWriter(client, logger)
12
- );
13
- Object.assign(storage, realStorage);
14
- logger.info(() => ({
15
- msg: "RedisAccessRules storage got a ready Redis client"
16
- }));
17
- });
18
- return storage;
4
+ //#region src/redis/redisRulesStorage.ts
5
+ var createRedisAccessRulesStorage = (connection, logger) => {
6
+ const storage = composeStorage(new DummyRedisRulesReader(logger), new DummyRedisRulesWriter(logger));
7
+ connection.getClient().then((client) => {
8
+ const realStorage = composeStorage(new RedisRulesReader(client, logger), new RedisRulesWriter(client, logger));
9
+ Object.assign(storage, realStorage);
10
+ logger.info(() => ({ msg: "RedisAccessRules storage got a ready Redis client" }));
11
+ });
12
+ return storage;
19
13
  };
20
- const composeStorage = (reader, writer) => ({
21
- // reader
22
- fetchRules: reader.fetchRules.bind(reader),
23
- getMissingRuleIds: reader.getMissingRuleIds.bind(reader),
24
- findRules: reader.findRules.bind(reader),
25
- findRuleIds: reader.findRuleIds.bind(reader),
26
- fetchAllRuleIds: reader.fetchAllRuleIds.bind(reader),
27
- // writer
28
- insertRules: writer.insertRules.bind(writer),
29
- deleteRules: writer.deleteRules.bind(writer),
30
- deleteAllRules: writer.deleteAllRules.bind(writer)
14
+ var composeStorage = (reader, writer) => ({
15
+ fetchRules: reader.fetchRules.bind(reader),
16
+ getMissingRuleIds: reader.getMissingRuleIds.bind(reader),
17
+ findRules: reader.findRules.bind(reader),
18
+ findRuleIds: reader.findRuleIds.bind(reader),
19
+ fetchAllRuleIds: reader.fetchAllRuleIds.bind(reader),
20
+ insertRules: writer.insertRules.bind(writer),
21
+ deleteRules: writer.deleteRules.bind(writer),
22
+ deleteAllRules: writer.deleteAllRules.bind(writer)
31
23
  });
32
- export {
33
- createRedisAccessRulesStorage
34
- };
24
+ //#endregion
25
+ export { createRedisAccessRulesStorage };