@prosopo/user-access-policy 3.4.0 → 3.5.27

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 (292) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +786 -0
  5. package/coverage/coverage-final.json +15 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +146 -0
  8. package/coverage/prettify.css +1 -0
  9. package/coverage/prettify.js +2 -0
  10. package/coverage/sort-arrow-sprite.png +0 -0
  11. package/coverage/sorter.js +210 -0
  12. package/coverage/src/accessPolicy.ts.html +457 -0
  13. package/coverage/src/accessPolicyResolver.ts.html +211 -0
  14. package/coverage/src/accessRules.ts.html +265 -0
  15. package/coverage/src/api/accessRuleApiRoutes.ts.html +379 -0
  16. package/coverage/src/api/accessRulesApiClient.ts.html +274 -0
  17. package/coverage/src/api/deleteAllRulesEndpoint.ts.html +229 -0
  18. package/coverage/src/api/deleteRulesEndpoint.ts.html +301 -0
  19. package/coverage/src/api/index.html +176 -0
  20. package/coverage/src/api/insertRulesEndpoint.ts.html +436 -0
  21. package/coverage/src/index.html +176 -0
  22. package/coverage/src/index.ts.html +277 -0
  23. package/coverage/src/redis/index.html +161 -0
  24. package/coverage/src/redis/redisRulesIndex.ts.html +769 -0
  25. package/coverage/src/redis/redisRulesReader.ts.html +652 -0
  26. package/coverage/src/redis/redisRulesStorage.ts.html +229 -0
  27. package/coverage/src/redis/redisRulesWriter.ts.html +424 -0
  28. package/coverage/src/util.ts.html +136 -0
  29. package/dist/.export.d.ts +6 -0
  30. package/dist/.export.d.ts.map +1 -0
  31. package/dist/.export.js +21 -0
  32. package/dist/.export.js.map +1 -0
  33. package/dist/accessPolicy.d.ts +169 -0
  34. package/dist/accessPolicy.d.ts.map +1 -0
  35. package/dist/accessPolicy.js.map +1 -0
  36. package/dist/accessPolicyResolver.d.ts +110 -0
  37. package/dist/accessPolicyResolver.d.ts.map +1 -0
  38. package/dist/accessPolicyResolver.js.map +1 -0
  39. package/dist/accessRules.d.ts +16 -0
  40. package/dist/accessRules.d.ts.map +1 -0
  41. package/dist/accessRules.js.map +1 -0
  42. package/dist/api/.export.d.ts +7 -0
  43. package/dist/api/.export.d.ts.map +1 -0
  44. package/dist/api/.export.js +11 -0
  45. package/dist/api/.export.js.map +1 -0
  46. package/dist/api/accessRuleApiRoutes.d.ts +27 -0
  47. package/dist/api/accessRuleApiRoutes.d.ts.map +1 -0
  48. package/dist/api/accessRuleApiRoutes.js.map +1 -0
  49. package/dist/api/accessRulesApiClient.d.ts +10 -0
  50. package/dist/api/accessRulesApiClient.d.ts.map +1 -0
  51. package/dist/api/accessRulesApiClient.js +38 -0
  52. package/dist/api/accessRulesApiClient.js.map +1 -0
  53. package/dist/api/delete/.export.d.ts +2 -0
  54. package/dist/api/delete/.export.d.ts.map +1 -0
  55. package/dist/api/delete/.export.js +1 -0
  56. package/dist/api/delete/.export.js.map +1 -0
  57. package/dist/api/delete/deleteAllRules.d.ts +11 -0
  58. package/dist/api/delete/deleteAllRules.d.ts.map +1 -0
  59. package/dist/api/delete/deleteAllRules.js +25 -0
  60. package/dist/api/delete/deleteAllRules.js.map +1 -0
  61. package/dist/api/delete/deleteRuleGroups.d.ts +19 -0
  62. package/dist/api/delete/deleteRuleGroups.d.ts.map +1 -0
  63. package/dist/api/delete/deleteRuleGroups.js +52 -0
  64. package/dist/api/delete/deleteRuleGroups.js.map +1 -0
  65. package/dist/api/delete/deleteRules.d.ts +15 -0
  66. package/dist/api/delete/deleteRules.d.ts.map +1 -0
  67. package/dist/api/delete/deleteRules.js +43 -0
  68. package/dist/api/delete/deleteRules.js.map +1 -0
  69. package/dist/api/deleteAllRulesEndpoint.d.ts +12 -0
  70. package/dist/api/deleteAllRulesEndpoint.d.ts.map +1 -0
  71. package/dist/api/deleteAllRulesEndpoint.js.map +1 -0
  72. package/dist/api/deleteRulesEndpoint.d.ts +116 -0
  73. package/dist/api/deleteRulesEndpoint.d.ts.map +1 -0
  74. package/dist/api/deleteRulesEndpoint.js.map +1 -0
  75. package/dist/api/insertRulesEndpoint.d.ts +22 -0
  76. package/dist/api/insertRulesEndpoint.d.ts.map +1 -0
  77. package/dist/api/insertRulesEndpoint.js.map +1 -0
  78. package/dist/api/read/.export.d.ts +4 -0
  79. package/dist/api/read/.export.d.ts.map +1 -0
  80. package/dist/api/read/.export.js +1 -0
  81. package/dist/api/read/.export.js.map +1 -0
  82. package/dist/api/read/fetchRules.d.ts +53 -0
  83. package/dist/api/read/fetchRules.d.ts.map +1 -0
  84. package/dist/api/read/fetchRules.js +43 -0
  85. package/dist/api/read/fetchRules.js.map +1 -0
  86. package/dist/api/read/findRuleIds.d.ts +28 -0
  87. package/dist/api/read/findRuleIds.d.ts.map +1 -0
  88. package/dist/api/read/findRuleIds.js +50 -0
  89. package/dist/api/read/findRuleIds.js.map +1 -0
  90. package/dist/api/read/getMissingIds.d.ts +28 -0
  91. package/dist/api/read/getMissingIds.d.ts.map +1 -0
  92. package/dist/api/read/getMissingIds.js +41 -0
  93. package/dist/api/read/getMissingIds.js.map +1 -0
  94. package/dist/api/ruleApiRoutes.d.ts +43 -0
  95. package/dist/api/ruleApiRoutes.d.ts.map +1 -0
  96. package/dist/api/ruleApiRoutes.js +131 -0
  97. package/dist/api/ruleApiRoutes.js.map +1 -0
  98. package/dist/api/rulesApiClient.d.ts +20 -0
  99. package/dist/api/rulesApiClient.d.ts.map +1 -0
  100. package/dist/api/rulesApiClient.js +93 -0
  101. package/dist/api/rulesApiClient.js.map +1 -0
  102. package/dist/api/write/.export.d.ts +2 -0
  103. package/dist/api/write/.export.d.ts.map +1 -0
  104. package/dist/api/write/.export.js +1 -0
  105. package/dist/api/write/.export.js.map +1 -0
  106. package/dist/api/write/insertRules.d.ts +29 -0
  107. package/dist/api/write/insertRules.d.ts.map +1 -0
  108. package/dist/api/write/insertRules.js +102 -0
  109. package/dist/api/write/insertRules.js.map +1 -0
  110. package/dist/api/write/rehashRules.d.ts +11 -0
  111. package/dist/api/write/rehashRules.d.ts.map +1 -0
  112. package/dist/api/write/rehashRules.js +57 -0
  113. package/dist/api/write/rehashRules.js.map +1 -0
  114. package/dist/cjs/.export.cjs +21 -0
  115. package/dist/cjs/api/.export.cjs +11 -0
  116. package/dist/cjs/api/delete/.export.cjs +1 -0
  117. package/dist/cjs/api/delete/deleteAllRules.cjs +25 -0
  118. package/dist/cjs/api/delete/deleteRuleGroups.cjs +52 -0
  119. package/dist/cjs/api/delete/deleteRules.cjs +43 -0
  120. package/dist/cjs/api/read/.export.cjs +1 -0
  121. package/dist/cjs/api/read/fetchRules.cjs +43 -0
  122. package/dist/cjs/api/read/findRuleIds.cjs +50 -0
  123. package/dist/cjs/api/read/getMissingIds.cjs +41 -0
  124. package/dist/cjs/api/ruleApiRoutes.cjs +131 -0
  125. package/dist/cjs/api/rulesApiClient.cjs +93 -0
  126. package/dist/cjs/api/write/.export.cjs +1 -0
  127. package/dist/cjs/api/write/insertRules.cjs +102 -0
  128. package/dist/cjs/api/write/rehashRules.cjs +57 -0
  129. package/dist/cjs/index.cjs +2 -0
  130. package/dist/cjs/mongoose/.export.cjs +4 -0
  131. package/dist/cjs/mongoose/mongooseRuleSchema.cjs +36 -0
  132. package/dist/cjs/redis/.export.cjs +6 -0
  133. package/dist/cjs/redis/reader/redisAggregate.cjs +60 -0
  134. package/dist/cjs/redis/reader/redisRulesQuery.cjs +99 -0
  135. package/dist/cjs/redis/reader/redisRulesReader.cjs +230 -0
  136. package/dist/cjs/redis/redisAccessRules.cjs +4 -4
  137. package/dist/cjs/redis/redisAccessRulesIndex.cjs +55 -14
  138. package/dist/cjs/redis/redisClient.cjs +67 -0
  139. package/dist/cjs/redis/redisRuleIndex.cjs +50 -0
  140. package/dist/cjs/redis/redisRulesStorage.cjs +34 -0
  141. package/dist/cjs/redis/redisRulesWriter.cjs +100 -0
  142. package/dist/cjs/rule.cjs +8 -0
  143. package/dist/cjs/ruleInput/.export.cjs +9 -0
  144. package/dist/cjs/ruleInput/policyInput.cjs +25 -0
  145. package/dist/cjs/ruleInput/ruleInput.cjs +50 -0
  146. package/dist/cjs/ruleInput/userScopeInput.cjs +55 -0
  147. package/dist/cjs/ruleRecord.cjs +23 -0
  148. package/dist/cjs/rulesStorage.cjs +8 -0
  149. package/dist/cjs/transformRule.cjs +77 -0
  150. package/dist/index.d.ts +15 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +7 -3
  153. package/dist/index.js.map +1 -0
  154. package/dist/mongoose/.export.d.ts +2 -0
  155. package/dist/mongoose/.export.d.ts.map +1 -0
  156. package/dist/mongoose/.export.js +4 -0
  157. package/dist/mongoose/.export.js.map +1 -0
  158. package/dist/mongoose/mongooseRuleSchema.d.ts +4 -0
  159. package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -0
  160. package/dist/mongoose/mongooseRuleSchema.js +36 -0
  161. package/dist/mongoose/mongooseRuleSchema.js.map +1 -0
  162. package/dist/redis/.export.d.ts +3 -0
  163. package/dist/redis/.export.d.ts.map +1 -0
  164. package/dist/redis/.export.js +6 -0
  165. package/dist/redis/.export.js.map +1 -0
  166. package/dist/redis/reader/redisAggregate.d.ts +4 -0
  167. package/dist/redis/reader/redisAggregate.d.ts.map +1 -0
  168. package/dist/redis/reader/redisAggregate.js +60 -0
  169. package/dist/redis/reader/redisAggregate.js.map +1 -0
  170. package/dist/redis/reader/redisRulesQuery.d.ts +4 -0
  171. package/dist/redis/reader/redisRulesQuery.d.ts.map +1 -0
  172. package/dist/redis/reader/redisRulesQuery.js +99 -0
  173. package/dist/redis/reader/redisRulesQuery.js.map +1 -0
  174. package/dist/redis/reader/redisRulesReader.d.ts +26 -0
  175. package/dist/redis/reader/redisRulesReader.d.ts.map +1 -0
  176. package/dist/redis/reader/redisRulesReader.js +213 -0
  177. package/dist/redis/reader/redisRulesReader.js.map +1 -0
  178. package/dist/redis/redisAccessRules.d.ts +7 -0
  179. package/dist/redis/redisAccessRules.d.ts.map +1 -0
  180. package/dist/redis/redisAccessRules.js +112 -128
  181. package/dist/redis/redisAccessRules.js.map +1 -0
  182. package/dist/redis/redisAccessRulesIndex.d.ts +13 -0
  183. package/dist/redis/redisAccessRulesIndex.d.ts.map +1 -0
  184. package/dist/redis/redisAccessRulesIndex.js +122 -112
  185. package/dist/redis/redisAccessRulesIndex.js.map +1 -0
  186. package/dist/redis/redisClient.d.ts +11 -0
  187. package/dist/redis/redisClient.d.ts.map +1 -0
  188. package/dist/redis/redisClient.js +67 -0
  189. package/dist/redis/redisClient.js.map +1 -0
  190. package/dist/redis/redisIndex.d.ts +9 -0
  191. package/dist/redis/redisIndex.d.ts.map +1 -0
  192. package/dist/redis/redisIndex.js +16 -15
  193. package/dist/redis/redisIndex.js.map +1 -0
  194. package/dist/redis/redisRuleIndex.d.ts +13 -0
  195. package/dist/redis/redisRuleIndex.d.ts.map +1 -0
  196. package/dist/redis/redisRuleIndex.js +50 -0
  197. package/dist/redis/redisRuleIndex.js.map +1 -0
  198. package/dist/redis/redisRulesIndex.d.ts +9 -0
  199. package/dist/redis/redisRulesIndex.d.ts.map +1 -0
  200. package/dist/redis/redisRulesIndex.js +138 -0
  201. package/dist/redis/redisRulesIndex.js.map +1 -0
  202. package/dist/redis/redisRulesReader.d.ts +6 -0
  203. package/dist/redis/redisRulesReader.d.ts.map +1 -0
  204. package/dist/redis/redisRulesReader.js +125 -0
  205. package/dist/redis/redisRulesReader.js.map +1 -0
  206. package/dist/redis/redisRulesStorage.d.ts +5 -0
  207. package/dist/redis/redisRulesStorage.d.ts.map +1 -0
  208. package/dist/redis/redisRulesStorage.js +34 -0
  209. package/dist/redis/redisRulesStorage.js.map +1 -0
  210. package/dist/redis/redisRulesWriter.d.ts +22 -0
  211. package/dist/redis/redisRulesWriter.d.ts.map +1 -0
  212. package/dist/redis/redisRulesWriter.js +100 -0
  213. package/dist/redis/redisRulesWriter.js.map +1 -0
  214. package/dist/rule.d.ts +34 -0
  215. package/dist/rule.d.ts.map +1 -0
  216. package/dist/rule.js +8 -0
  217. package/dist/rule.js.map +1 -0
  218. package/dist/ruleInput/.export.d.ts +4 -0
  219. package/dist/ruleInput/.export.d.ts.map +1 -0
  220. package/dist/ruleInput/.export.js +9 -0
  221. package/dist/ruleInput/.export.js.map +1 -0
  222. package/dist/ruleInput/policyInput.d.ts +38 -0
  223. package/dist/ruleInput/policyInput.d.ts.map +1 -0
  224. package/dist/ruleInput/policyInput.js +25 -0
  225. package/dist/ruleInput/policyInput.js.map +1 -0
  226. package/dist/ruleInput/ruleInput.d.ts +145 -0
  227. package/dist/ruleInput/ruleInput.d.ts.map +1 -0
  228. package/dist/ruleInput/ruleInput.js +50 -0
  229. package/dist/ruleInput/ruleInput.js.map +1 -0
  230. package/dist/ruleInput/userScopeInput.d.ts +93 -0
  231. package/dist/ruleInput/userScopeInput.d.ts.map +1 -0
  232. package/dist/ruleInput/userScopeInput.js +55 -0
  233. package/dist/ruleInput/userScopeInput.js.map +1 -0
  234. package/dist/ruleRecord.d.ts +18 -0
  235. package/dist/ruleRecord.d.ts.map +1 -0
  236. package/dist/ruleRecord.js +23 -0
  237. package/dist/ruleRecord.js.map +1 -0
  238. package/dist/rulesStorage.d.ts +30 -0
  239. package/dist/rulesStorage.d.ts.map +1 -0
  240. package/dist/rulesStorage.js +8 -0
  241. package/dist/rulesStorage.js.map +1 -0
  242. package/dist/tests/accessPolicy.test.d.ts +2 -0
  243. package/dist/tests/accessPolicy.test.d.ts.map +1 -0
  244. package/dist/tests/accessPolicy.test.js +27 -0
  245. package/dist/tests/accessPolicy.test.js.map +1 -0
  246. package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts +2 -0
  247. package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts.map +1 -0
  248. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js +101 -0
  249. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js.map +1 -0
  250. package/dist/tests/redis/redisAccessRules.integration.test.d.ts +2 -0
  251. package/dist/tests/redis/redisAccessRules.integration.test.d.ts.map +1 -0
  252. package/dist/tests/redis/redisAccessRules.integration.test.js +586 -0
  253. package/dist/tests/redis/redisAccessRules.integration.test.js.map +1 -0
  254. package/dist/tests/redis/redisAccessRules.unit.test.d.ts +2 -0
  255. package/dist/tests/redis/redisAccessRules.unit.test.d.ts.map +1 -0
  256. package/dist/tests/redis/redisAccessRules.unit.test.js +198 -0
  257. package/dist/tests/redis/redisAccessRules.unit.test.js.map +1 -0
  258. package/dist/tests/redis/redisIndex.integration.test.d.ts +2 -0
  259. package/dist/tests/redis/redisIndex.integration.test.d.ts.map +1 -0
  260. package/dist/tests/redis/redisIndex.integration.test.js +80 -0
  261. package/dist/tests/redis/redisIndex.integration.test.js.map +1 -0
  262. package/dist/tests/redis/redisRulesIndex.unit.test.d.ts +2 -0
  263. package/dist/tests/redis/redisRulesIndex.unit.test.d.ts.map +1 -0
  264. package/dist/tests/redis/redisRulesIndex.unit.test.js +101 -0
  265. package/dist/tests/redis/redisRulesIndex.unit.test.js.map +1 -0
  266. package/dist/tests/redis/redisRulesStorage.integration.test.d.ts +2 -0
  267. package/dist/tests/redis/redisRulesStorage.integration.test.d.ts.map +1 -0
  268. package/dist/tests/redis/redisRulesStorage.integration.test.js +611 -0
  269. package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -0
  270. package/dist/tests/redis/testRedisClient.d.ts +3 -0
  271. package/dist/tests/redis/testRedisClient.d.ts.map +1 -0
  272. package/dist/tests/redis/testRedisClient.js +8 -0
  273. package/dist/tests/redis/testRedisClient.js.map +1 -0
  274. package/dist/tests/testLogger.d.ts +4 -0
  275. package/dist/tests/testLogger.d.ts.map +1 -0
  276. package/dist/tests/testLogger.js +22 -0
  277. package/dist/tests/testLogger.js.map +1 -0
  278. package/dist/tests/transformRule.unit.test.d.ts +2 -0
  279. package/dist/tests/transformRule.unit.test.d.ts.map +1 -0
  280. package/dist/tests/transformRule.unit.test.js +188 -0
  281. package/dist/tests/transformRule.unit.test.js.map +1 -0
  282. package/dist/transformRule.d.ts +7 -0
  283. package/dist/transformRule.d.ts.map +1 -0
  284. package/dist/transformRule.js +77 -0
  285. package/dist/transformRule.js.map +1 -0
  286. package/dist/util.d.ts +2 -0
  287. package/dist/util.d.ts.map +1 -0
  288. package/dist/util.js.map +1 -0
  289. package/entries.ts +20 -0
  290. package/package.json +42 -21
  291. package/vite.cjs.config.ts +4 -1
  292. package/vite.esm.config.ts +6 -1
@@ -1,130 +1,140 @@
1
1
  import crypto from "node:crypto";
2
2
  import { SCHEMA_FIELD_TYPE } from "@redis/search";
3
- import { ScopeMatch } from "../accessPolicyResolver.js";
4
- import { createRedisIndex } from "./redisIndex.js";
5
- const accessRulesRedisIndexName = "index:user-access-rules";
6
- const accessRuleRedisKeyPrefix = "uar:";
3
+ import { userScopeSchema, } from "#policy/accessPolicy.js";
4
+ import { ScopeMatch } from "#policy/accessPolicyResolver.js";
5
+ import { createRedisIndex } from "#policy/redis/redisIndex.js";
6
+ export const accessRulesRedisIndexName = "index:user-access-rules";
7
+ export const accessRuleRedisKeyPrefix = "uar:";
7
8
  const accessRuleContentHashAlgorithm = "md5";
8
- const DEFAULT_SEARCH_LIMIT = 1e3;
9
+ const DEFAULT_SEARCH_LIMIT = 1000;
9
10
  const accessRulesIndex = {
10
- name: accessRulesRedisIndexName,
11
- /**
12
- * Note on the field type decision
13
- *
14
- * TAG is designed for the exact value matching
15
- * TEXT is designed for the word-based and pattern matching
16
- *
17
- * For our goal TAG fits perfectly and, more performant
18
- */
19
- schema: {
20
- clientId: {
21
- type: SCHEMA_FIELD_TYPE.TAG,
22
- // necessary to make possible use of the ismissing() function on this field in the search
23
- INDEXMISSING: true
11
+ name: accessRulesRedisIndexName,
12
+ schema: {
13
+ clientId: {
14
+ type: SCHEMA_FIELD_TYPE.TAG,
15
+ INDEXMISSING: true,
16
+ },
17
+ numericIpMaskMin: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
18
+ numericIpMaskMax: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
19
+ userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
20
+ numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
21
+ ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
22
+ headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
23
+ userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
24
+ },
25
+ options: {
26
+ ON: "HASH",
27
+ PREFIX: [accessRuleRedisKeyPrefix],
24
28
  },
25
- numericIpMaskMin: SCHEMA_FIELD_TYPE.NUMERIC,
26
- numericIpMaskMax: SCHEMA_FIELD_TYPE.NUMERIC,
27
- userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
28
- numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
29
- ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
30
- headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
31
- userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
32
- },
33
- // the satisfy statement is to guarantee that the keys are right
34
- options: {
35
- ON: "HASH",
36
- PREFIX: [accessRuleRedisKeyPrefix]
37
- }
38
29
  };
39
- const createRedisAccessRulesIndex = async (client, indexName) => {
40
- if (indexName) {
41
- accessRulesIndex.name = indexName;
42
- }
43
- return createRedisIndex(client, accessRulesIndex);
30
+ export const createRedisAccessRulesIndex = async (client, indexName) => {
31
+ if (indexName) {
32
+ accessRulesIndex.name = indexName;
33
+ }
34
+ return createRedisIndex(client, accessRulesIndex);
44
35
  };
45
36
  const numericIndexFields = [
46
- "numericIp",
47
- "numericIpMaskMin",
48
- "numericIpMaskMax"
37
+ "numericIp",
38
+ "numericIpMaskMin",
39
+ "numericIpMaskMax",
49
40
  ];
50
41
  const greedyFieldComparisons = {
51
- numericIp: (value) => `( @numericIp:[${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`
42
+ numericIp: (value, scope) => {
43
+ if (value !== undefined) {
44
+ return `( @numericIp:[${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`;
45
+ }
46
+ if (scope.numericIpMaskMin === undefined &&
47
+ scope.numericIpMaskMax === undefined) {
48
+ return "ismissing(@numericIp) ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)";
49
+ }
50
+ return "";
51
+ },
52
+ numericIpMaskMin: (value, scope) => {
53
+ if (scope.numericIp !== undefined) {
54
+ return "";
55
+ }
56
+ return value !== undefined
57
+ ? `@numericIpMaskMin:[-inf ${value}]`
58
+ : "ismissing(@numericIpMaskMin)";
59
+ },
60
+ numericIpMaskMax: (value, scope) => {
61
+ if (scope.numericIp !== undefined) {
62
+ return "";
63
+ }
64
+ return value !== undefined
65
+ ? `@numericIpMaskMax:[${value} +inf]`
66
+ : "ismissing(@numericIpMaskMax)";
67
+ },
52
68
  };
53
- const accessRulesRedisSearchOptions = {
54
- // #2 is a required option when the 'ismissing()' function is in the query body
55
- DIALECT: 2
69
+ export const accessRulesRedisSearchOptions = {
70
+ DIALECT: 2,
56
71
  };
57
- const accessRulesRedisDeleteOptions = {
58
- // #2 is a required option when the 'ismissing()' function is in the query body
59
- DIALECT: 2,
60
- LIMIT: {
61
- from: 0,
62
- size: DEFAULT_SEARCH_LIMIT
63
- }
72
+ export const accessRulesRedisDeleteOptions = {
73
+ DIALECT: 2,
74
+ LIMIT: {
75
+ from: 0,
76
+ size: DEFAULT_SEARCH_LIMIT,
77
+ },
64
78
  };
65
- const getRedisAccessRulesQuery = (filter) => {
66
- const { policyScope, userScope } = filter;
67
- const policyScopeFilter = getPolicyScopeQuery(
68
- policyScope,
69
- filter.policyScopeMatch
70
- );
71
- if (userScope && Object.keys(userScope).length > 0) {
72
- const userScopeFilter = getUserScopeQuery(userScope, filter.userScopeMatch);
73
- return `${policyScopeFilter} ( ${userScopeFilter} )`;
74
- }
75
- return policyScopeFilter ? policyScopeFilter : "*";
79
+ export const getRedisAccessRulesQuery = (filter, matchingFieldsOnly) => {
80
+ const { policyScope, userScope } = filter;
81
+ const policyScopeFilter = getPolicyScopeQuery(policyScope, filter.policyScopeMatch);
82
+ if (userScope && Object.keys(userScope).length > 0) {
83
+ const userScopeFilter = getUserScopeQuery(userScope, filter.userScopeMatch, matchingFieldsOnly);
84
+ return `${policyScopeFilter} ( ${userScopeFilter} )`;
85
+ }
86
+ return policyScopeFilter ? policyScopeFilter : "*";
76
87
  };
77
88
  const getPolicyScopeQuery = (policyScope, scopeMatchType) => {
78
- const clientId = policyScope?.clientId;
79
- if ("string" === typeof clientId) {
80
- return ScopeMatch.Exact === scopeMatchType ? `@clientId:{${clientId}}` : `( @clientId:{${clientId}} | ismissing(@clientId) )`;
81
- }
82
- return ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
83
- };
84
- const getUserScopeQuery = (userScope, scopeMatchType) => {
85
- let scopeEntries = Object.entries(userScope);
86
- let scopeJoinType = " ";
87
- if (scopeMatchType === ScopeMatch.Greedy) {
88
- scopeEntries = scopeEntries.filter(
89
- ([_, value]) => value !== void 0
90
- );
91
- scopeJoinType = " | ";
92
- }
93
- return scopeEntries.map(
94
- ([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(scopeFieldName, scopeFieldValue, scopeMatchType)
95
- ).join(scopeJoinType);
89
+ const clientId = policyScope?.clientId;
90
+ if ("string" === typeof clientId) {
91
+ return ScopeMatch.Exact === scopeMatchType
92
+ ? `@clientId:{${clientId}}`
93
+ : `( @clientId:{${clientId}} | ismissing(@clientId) )`;
94
+ }
95
+ return ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
96
96
  };
97
- const getUserScopeFieldQuery = (fieldName, fieldValue, matchType) => {
98
- if (
99
- //ScopeMatch.Greedy === matchType &&
100
- "function" === typeof greedyFieldComparisons[fieldName]
101
- ) {
102
- return greedyFieldComparisons[fieldName](fieldValue);
103
- }
104
- if (fieldValue === void 0) {
105
- return `ismissing(@${fieldName})`;
106
- }
107
- return numericIndexFields.includes(fieldName) ? `@${fieldName}:[${fieldValue}]` : `@${fieldName}:{${fieldValue}}`;
97
+ const getUserScopeQuery = (userScope, scopeMatchType, matchingFieldsOnly) => {
98
+ let scopeEntries = Object.entries(userScope);
99
+ let scopeJoinType = " ";
100
+ if (scopeMatchType === ScopeMatch.Greedy) {
101
+ scopeEntries = scopeEntries.filter(([_, value]) => value !== undefined);
102
+ scopeJoinType = " | ";
103
+ }
104
+ if (matchingFieldsOnly) {
105
+ const scopeMap = new Map(scopeEntries);
106
+ if (scopeMap.has("numericIp") && scopeMap.get("numericIp") === undefined) {
107
+ scopeMap.set("numericIpMaskMin", undefined);
108
+ scopeMap.set("numericIpMaskMax", undefined);
109
+ }
110
+ for (const name of Object.keys(userScopeSchema.shape)) {
111
+ if (!scopeMap.has(name)) {
112
+ scopeMap.set(name, undefined);
113
+ }
114
+ }
115
+ scopeEntries = [...scopeMap.entries()];
116
+ }
117
+ const scopeObj = Object.fromEntries(scopeEntries);
118
+ return scopeEntries
119
+ .map(([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(scopeFieldName, scopeFieldValue, scopeMatchType, scopeObj))
120
+ .filter(Boolean)
121
+ .join(scopeJoinType);
108
122
  };
109
- const getRedisAccessRuleKey = (rule) => accessRuleRedisKeyPrefix + crypto.createHash(accessRuleContentHashAlgorithm).update(
110
- JSON.stringify(
111
- rule,
112
- (key, value) => (
113
- // JSON.stringify can't handle BigInt itself: throws "Do not know how to serialize a BigInt"
114
- "bigint" === typeof value ? value.toString() : value
115
- )
116
- )
117
- ).digest("hex");
118
- const getRedisAccessRuleValue = (rule) => Object.fromEntries(
119
- Object.entries(rule).map(([key, value]) => [key, String(value)])
120
- );
121
- export {
122
- accessRuleRedisKeyPrefix,
123
- accessRulesRedisDeleteOptions,
124
- accessRulesRedisIndexName,
125
- accessRulesRedisSearchOptions,
126
- createRedisAccessRulesIndex,
127
- getRedisAccessRuleKey,
128
- getRedisAccessRuleValue,
129
- getRedisAccessRulesQuery
123
+ const getUserScopeFieldQuery = (fieldName, fieldValue, matchType, fullScope) => {
124
+ if ("function" === typeof greedyFieldComparisons[fieldName]) {
125
+ return greedyFieldComparisons[fieldName](fieldValue, fullScope);
126
+ }
127
+ if (fieldValue === undefined) {
128
+ return `ismissing(@${fieldName})`;
129
+ }
130
+ return numericIndexFields.includes(fieldName)
131
+ ? `@${fieldName}:[${fieldValue}]`
132
+ : `@${fieldName}:{${fieldValue}}`;
130
133
  };
134
+ export const getRedisAccessRuleKey = (rule) => accessRuleRedisKeyPrefix +
135
+ crypto
136
+ .createHash(accessRuleContentHashAlgorithm)
137
+ .update(JSON.stringify(rule, (key, value) => "bigint" === typeof value ? value.toString() : value))
138
+ .digest("hex");
139
+ export const getRedisAccessRuleValue = (rule) => Object.fromEntries(Object.entries(rule).map(([key, value]) => [key, String(value)]));
140
+ //# sourceMappingURL=redisAccessRulesIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisAccessRulesIndex.js","sourceRoot":"","sources":["../../src/redis/redisAccessRulesIndex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Logger } from "@prosopo/common";
2
+ import type { RedisClientType } from "redis";
3
+ import { type ZodType } from "zod";
4
+ export declare const REDIS_BATCH_SIZE = 1000;
5
+ export declare const getMissingRedisKeys: (client: RedisClientType, keys: string[]) => Promise<string[]>;
6
+ export declare const fetchRedisHashRecords: (client: RedisClientType, keys: string[], logger: Logger) => Promise<{
7
+ records: object[];
8
+ expirations: (number | undefined)[];
9
+ }>;
10
+ export declare const parseRedisRecords: <T>(records: unknown[], recordSchema: ZodType<T>, logger: Logger) => T[];
11
+ //# sourceMappingURL=redisClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisClient.d.ts","sourceRoot":"","sources":["../../src/redis/redisClient.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAK,MAAM,KAAK,CAAC;AAEtC,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AAEtC,eAAO,MAAM,mBAAmB,WACvB,eAAe,QACjB,MAAM,EAAE,KACZ,OAAO,CAAC,MAAM,EAAE,CAsBlB,CAAC;AAEF,eAAO,MAAM,qBAAqB,WACzB,eAAe,QACjB,MAAM,EAAE,UACN,MAAM,KACZ,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;CAAE,CAgBpE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,WACzB,OAAO,EAAE,gBACJ,OAAO,CAAC,CAAC,CAAC,UAChB,MAAM,KACZ,CAAC,EAcD,CAAC"}
@@ -0,0 +1,67 @@
1
+ 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;
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
+ };
33
+ };
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 [];
44
+ });
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];
61
+ });
62
+ export {
63
+ REDIS_BATCH_SIZE,
64
+ fetchRedisHashRecords,
65
+ getMissingRedisKeys,
66
+ parseRedisRecords
67
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisClient.js","sourceRoot":"","sources":["../../src/redis/redisClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { RediSearchSchema } from "@redis/search";
2
+ import type { RedisClientType } from "redis";
3
+ export type RedisIndex = {
4
+ name: string;
5
+ schema: RediSearchSchema;
6
+ options: object;
7
+ };
8
+ export declare const createRedisIndex: (client: RedisClientType, index: RedisIndex) => Promise<void>;
9
+ //# sourceMappingURL=redisIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisIndex.d.ts","sourceRoot":"","sources":["../../src/redis/redisIndex.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAc7C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AASF,eAAO,MAAM,gBAAgB,WACpB,eAAe,SAChB,UAAU,KACf,OAAO,CAAC,IAAI,CAkBd,CAAC"}
@@ -1,22 +1,23 @@
1
1
  import crypto from "node:crypto";
2
2
  const redisIndexHashesRecordKey = "_index_hashes";
3
3
  const redisIndexHashAlgorithm = "sha256";
4
- const createRedisIndex = async (client, index) => {
5
- const indexHash = createIndexHash(index);
6
- const existingIndexes = await client.ft._LIST();
7
- if (existingIndexes.includes(index.name)) {
8
- const existingIndexHash = await fetchIndexHash(client, index.name);
9
- if (indexHash === existingIndexHash) {
10
- return;
4
+ export const createRedisIndex = async (client, index) => {
5
+ const indexHash = createIndexHash(index);
6
+ const existingIndexes = await client.ft._LIST();
7
+ if (existingIndexes.includes(index.name)) {
8
+ const existingIndexHash = await fetchIndexHash(client, index.name);
9
+ if (indexHash === existingIndexHash) {
10
+ return;
11
+ }
12
+ await client.ft.dropIndex(index.name);
11
13
  }
12
- await client.ft.dropIndex(index.name);
13
- }
14
- await client.ft.create(index.name, index.schema, index.options);
15
- await saveIndexHash(client, index.name, indexHash);
14
+ await client.ft.create(index.name, index.schema, index.options);
15
+ await saveIndexHash(client, index.name, indexHash);
16
16
  };
17
- const createIndexHash = (index) => crypto.createHash(redisIndexHashAlgorithm).update(JSON.stringify(index)).digest("hex");
17
+ const createIndexHash = (index) => crypto
18
+ .createHash(redisIndexHashAlgorithm)
19
+ .update(JSON.stringify(index))
20
+ .digest("hex");
18
21
  const fetchIndexHash = async (client, indexName) => client.hGet(redisIndexHashesRecordKey, indexName);
19
22
  const saveIndexHash = async (client, indexName, indexHash) => client.hSet(redisIndexHashesRecordKey, indexName, indexHash);
20
- export {
21
- createRedisIndex
22
- };
23
+ //# sourceMappingURL=redisIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisIndex.js","sourceRoot":"","sources":["../../src/redis/redisIndex.ts"],"names":[],"mappings":"AAcA,OAAO,MAAM,MAAM,aAAa,CAAC;AA0BjC,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAClD,MAAM,uBAAuB,GAAG,QAAQ,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACpC,MAAuB,EACvB,KAAiB,EACD,EAAE;IAClB,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAEzC,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAEhD,IAAI,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,iBAAiB,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAEnE,IAAI,SAAS,KAAK,iBAAiB,EAAE,CAAC;YACrC,OAAO;QACR,CAAC;QAED,MAAM,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAEhE,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAiB,EAAU,EAAE,CACrD,MAAM;KACJ,UAAU,CAAC,uBAAuB,CAAC;KACnC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;KAC7B,MAAM,CAAC,KAAK,CAAC,CAAC;AAEjB,MAAM,cAAc,GAAG,KAAK,EAC3B,MAAuB,EACvB,SAAiB,EACQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC,CAAC;AAE/E,MAAM,aAAa,GAAG,KAAK,EAC1B,MAAuB,EACvB,SAAiB,EACjB,SAAiB,EACC,EAAE,CACpB,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { RedisIndex } from "@prosopo/redis-client";
2
+ import { type RediSearchSchema } from "@redis/search";
3
+ import type { AccessRule } from "#policy/rule.js";
4
+ export declare const userIpRedisSchema: RediSearchSchema;
5
+ export declare const userAttributesRedisSchema: RediSearchSchema;
6
+ export declare const userScopeRedisSchema: RediSearchSchema;
7
+ export declare const policyScopeRedisSchema: RediSearchSchema;
8
+ export declare const accessRuleRedisSchema: RediSearchSchema;
9
+ export declare const ACCESS_RULES_REDIS_INDEX_NAME = "index:user-access-rules";
10
+ export declare const ACCESS_RULE_REDIS_KEY_PREFIX = "uar:";
11
+ export declare const accessRulesRedisIndex: RedisIndex;
12
+ export declare const getAccessRuleRedisKey: (rule: AccessRule) => string;
13
+ //# sourceMappingURL=redisRuleIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisRuleIndex.d.ts","sourceRoot":"","sources":["../../src/redis/redisRuleIndex.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EAKV,MAAM,iBAAiB,CAAC;AAGzB,eAAO,MAAM,iBAAiB,EAAE,gBAIL,CAAC;AAE5B,eAAO,MAAM,yBAAyB,EAAE,gBAKL,CAAC;AAEpC,eAAO,MAAM,oBAAoB,EAAE,gBAGR,CAAC;AAE5B,eAAO,MAAM,sBAAsB,EAAE,gBAKL,CAAC;AAUjC,eAAO,MAAM,qBAAqB,EAAE,gBAIR,CAAC;AAE7B,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAGvE,eAAO,MAAM,4BAA4B,SAAS,CAAC;AAEnD,eAAO,MAAM,qBAAqB,EAAE,UAOnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,SAAU,UAAU,KAAG,MACD,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { SCHEMA_FIELD_TYPE } from "@redis/search";
2
+ import { makeAccessRuleHash } from "../transformRule.js";
3
+ const userIpRedisSchema = {
4
+ numericIpMaskMin: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
5
+ numericIpMaskMax: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
6
+ numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true }
7
+ };
8
+ const userAttributesRedisSchema = {
9
+ userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
10
+ ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
11
+ headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
12
+ userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
13
+ };
14
+ const userScopeRedisSchema = {
15
+ ...userAttributesRedisSchema,
16
+ ...userIpRedisSchema
17
+ };
18
+ const policyScopeRedisSchema = {
19
+ clientId: {
20
+ type: SCHEMA_FIELD_TYPE.TAG,
21
+ INDEXMISSING: true
22
+ }
23
+ };
24
+ const accessRuleRedisSchema = {
25
+ ...policyScopeRedisSchema,
26
+ ...userScopeRedisSchema,
27
+ groupId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
28
+ };
29
+ const ACCESS_RULES_REDIS_INDEX_NAME = "index:user-access-rules";
30
+ const ACCESS_RULE_REDIS_KEY_PREFIX = "uar:";
31
+ const accessRulesRedisIndex = {
32
+ name: ACCESS_RULES_REDIS_INDEX_NAME,
33
+ schema: accessRuleRedisSchema,
34
+ options: {
35
+ ON: "HASH",
36
+ PREFIX: [ACCESS_RULE_REDIS_KEY_PREFIX]
37
+ }
38
+ };
39
+ const getAccessRuleRedisKey = (rule) => ACCESS_RULE_REDIS_KEY_PREFIX + makeAccessRuleHash(rule);
40
+ export {
41
+ ACCESS_RULES_REDIS_INDEX_NAME,
42
+ ACCESS_RULE_REDIS_KEY_PREFIX,
43
+ accessRuleRedisSchema,
44
+ accessRulesRedisIndex,
45
+ getAccessRuleRedisKey,
46
+ policyScopeRedisSchema,
47
+ userAttributesRedisSchema,
48
+ userIpRedisSchema,
49
+ userScopeRedisSchema
50
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisRuleIndex.js","sourceRoot":"","sources":["../../src/redis/redisRuleIndex.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAyB,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,CAAC,MAAM,iBAAiB,GAAqB;IAClD,gBAAgB,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE;IACzE,gBAAgB,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE;IACzE,SAAS,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE;CACxC,CAAC;AAE5B,MAAM,CAAC,MAAM,yBAAyB,GAAqB;IAC1D,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE;IAC3D,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE;IAC5D,WAAW,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE;IAChE,aAAa,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE;CAChC,CAAC;AAEpC,MAAM,CAAC,MAAM,oBAAoB,GAAqB;IACrD,GAAG,yBAAyB;IAC5B,GAAG,iBAAiB;CACM,CAAC;AAE5B,MAAM,CAAC,MAAM,sBAAsB,GAAqB;IACvD,QAAQ,EAAE;QACT,IAAI,EAAE,iBAAiB,CAAC,GAAG;QAC3B,YAAY,EAAE,IAAI;KAClB;CAC8B,CAAC;AAUjC,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACtD,GAAG,sBAAsB;IACzB,GAAG,oBAAoB;IACvB,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE;CACjC,CAAC;AAE7B,MAAM,CAAC,MAAM,6BAA6B,GAAG,yBAAyB,CAAC;AAGvE,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAAe;IAChD,IAAI,EAAE,6BAA6B;IACnC,MAAM,EAAE,qBAAqB;IAC7B,OAAO,EAAE;QACR,EAAE,EAAE,MAAe;QACnB,MAAM,EAAE,CAAC,4BAA4B,CAAC;KACtC;CACD,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAgB,EAAU,EAAE,CACjE,4BAA4B,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RedisIndex } from "@prosopo/redis-client";
2
+ import { type FtSearchOptions } from "@redis/search";
3
+ import { type PolicyFilter } from "#policy/accessPolicyResolver.js";
4
+ export declare const redisRulesIndexName = "index:user-access-rules";
5
+ export declare const redisRuleKeyPrefix = "uar:";
6
+ export declare const redisAccessRulesIndex: RedisIndex;
7
+ export declare const redisRulesSearchOptions: FtSearchOptions;
8
+ export declare const getRedisRulesQuery: (filter: PolicyFilter, matchingFieldsOnly: boolean) => string;
9
+ //# sourceMappingURL=redisRulesIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisRulesIndex.d.ts","sourceRoot":"","sources":["../../src/redis/redisRulesIndex.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,KAAK,eAAe,EAAqB,MAAM,eAAe,CAAC;AAMxE,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,iCAAiC,CAAC;AAGhF,eAAO,MAAM,mBAAmB,4BAA4B,CAAC;AAE7D,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,eAAO,MAAM,qBAAqB,EAAE,UA6BnC,CAAC;AA8CF,eAAO,MAAM,uBAAuB,EAAE,eAGrC,CAAC;AAYF,eAAO,MAAM,kBAAkB,WACtB,YAAY,sBACA,OAAO,KACzB,MAkBF,CAAC"}
@@ -0,0 +1,138 @@
1
+ import { SCHEMA_FIELD_TYPE } from "@redis/search";
2
+ import { userScopeSchema } from "../accessPolicy.js";
3
+ import { ScopeMatch } from "../accessPolicyResolver.js";
4
+ const redisRulesIndexName = "index:user-access-rules";
5
+ const redisRuleKeyPrefix = "uar:";
6
+ const redisAccessRulesIndex = {
7
+ name: redisRulesIndexName,
8
+ /**
9
+ * Note on the field type decision
10
+ *
11
+ * TAG is designed for the exact value matching
12
+ * TEXT is designed for the word-based and pattern matching
13
+ *
14
+ * For our goal TAG fits perfectly and, more performant
15
+ */
16
+ schema: {
17
+ clientId: {
18
+ type: SCHEMA_FIELD_TYPE.TAG,
19
+ // necessary to make possible use of the ismissing() function on this field in the search
20
+ INDEXMISSING: true
21
+ },
22
+ numericIpMaskMin: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
23
+ numericIpMaskMax: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
24
+ userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
25
+ numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
26
+ ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
27
+ headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
28
+ userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
29
+ },
30
+ // the satisfy statement is to guarantee that the keys are right
31
+ options: {
32
+ ON: "HASH",
33
+ PREFIX: [redisRuleKeyPrefix]
34
+ }
35
+ };
36
+ const numericIndexFields = [
37
+ "numericIp",
38
+ "numericIpMaskMin",
39
+ "numericIpMaskMax"
40
+ ];
41
+ const greedyFieldComparisons = {
42
+ numericIp: (value, scope) => {
43
+ if (value !== void 0) {
44
+ return `( @numericIp:[${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`;
45
+ }
46
+ if (scope.numericIpMaskMin === void 0 && scope.numericIpMaskMax === void 0) {
47
+ return "ismissing(@numericIp) ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)";
48
+ }
49
+ return "";
50
+ },
51
+ numericIpMaskMin: (value, scope) => {
52
+ if (scope.numericIp !== void 0) {
53
+ return "";
54
+ }
55
+ return value !== void 0 ? `@numericIpMaskMin:[-inf ${value}]` : "ismissing(@numericIpMaskMin)";
56
+ },
57
+ numericIpMaskMax: (value, scope) => {
58
+ if (scope.numericIp !== void 0) {
59
+ return "";
60
+ }
61
+ return value !== void 0 ? `@numericIpMaskMax:[${value} +inf]` : "ismissing(@numericIpMaskMax)";
62
+ }
63
+ };
64
+ const redisRulesSearchOptions = {
65
+ // #2 is a required option when the 'ismissing()' function is in the query body
66
+ DIALECT: 2
67
+ };
68
+ const getRedisRulesQuery = (filter, matchingFieldsOnly) => {
69
+ const { policyScope, userScope } = filter;
70
+ const policyScopeFilter = getPolicyScopeQuery(
71
+ policyScope,
72
+ filter.policyScopeMatch
73
+ );
74
+ if (userScope && Object.keys(userScope).length > 0) {
75
+ const userScopeFilter = getUserScopeQuery(
76
+ userScope,
77
+ filter.userScopeMatch,
78
+ matchingFieldsOnly
79
+ );
80
+ return `${policyScopeFilter} ( ${userScopeFilter} )`;
81
+ }
82
+ return policyScopeFilter ? policyScopeFilter : "*";
83
+ };
84
+ const getPolicyScopeQuery = (policyScope, scopeMatchType) => {
85
+ const clientId = policyScope?.clientId;
86
+ if ("string" === typeof clientId) {
87
+ return ScopeMatch.Exact === scopeMatchType ? `@clientId:{${clientId}}` : `( @clientId:{${clientId}} | ismissing(@clientId) )`;
88
+ }
89
+ return ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
90
+ };
91
+ const getUserScopeQuery = (userScope, scopeMatchType, matchingFieldsOnly) => {
92
+ let scopeEntries = Object.entries(userScope);
93
+ let scopeJoinType = " ";
94
+ if (scopeMatchType === ScopeMatch.Greedy) {
95
+ scopeEntries = scopeEntries.filter(
96
+ ([_, value]) => value !== void 0
97
+ );
98
+ scopeJoinType = " | ";
99
+ }
100
+ if (matchingFieldsOnly) {
101
+ const scopeMap = new Map(scopeEntries);
102
+ if (scopeMap.has("numericIp") && scopeMap.get("numericIp") === void 0) {
103
+ scopeMap.set("numericIpMaskMin", void 0);
104
+ scopeMap.set("numericIpMaskMax", void 0);
105
+ }
106
+ for (const name of Object.keys(userScopeSchema.shape)) {
107
+ if (!scopeMap.has(name)) {
108
+ scopeMap.set(name, void 0);
109
+ }
110
+ }
111
+ scopeEntries = [...scopeMap.entries()];
112
+ }
113
+ const scopeObj = Object.fromEntries(scopeEntries);
114
+ return scopeEntries.map(
115
+ ([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(
116
+ scopeFieldName,
117
+ scopeFieldValue,
118
+ scopeMatchType,
119
+ scopeObj
120
+ )
121
+ ).filter(Boolean).join(scopeJoinType);
122
+ };
123
+ const getUserScopeFieldQuery = (fieldName, fieldValue, matchType, fullScope) => {
124
+ if ("function" === typeof greedyFieldComparisons[fieldName]) {
125
+ return greedyFieldComparisons[fieldName](fieldValue, fullScope);
126
+ }
127
+ if (fieldValue === void 0) {
128
+ return `ismissing(@${fieldName})`;
129
+ }
130
+ return numericIndexFields.includes(fieldName) ? `@${fieldName}:[${fieldValue}]` : `@${fieldName}:{${fieldValue}}`;
131
+ };
132
+ export {
133
+ getRedisRulesQuery,
134
+ redisAccessRulesIndex,
135
+ redisRuleKeyPrefix,
136
+ redisRulesIndexName,
137
+ redisRulesSearchOptions
138
+ };