@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.
- package/CHANGELOG.md +309 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +786 -0
- package/coverage/coverage-final.json +15 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +146 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/accessPolicy.ts.html +457 -0
- package/coverage/src/accessPolicyResolver.ts.html +211 -0
- package/coverage/src/accessRules.ts.html +265 -0
- package/coverage/src/api/accessRuleApiRoutes.ts.html +379 -0
- package/coverage/src/api/accessRulesApiClient.ts.html +274 -0
- package/coverage/src/api/deleteAllRulesEndpoint.ts.html +229 -0
- package/coverage/src/api/deleteRulesEndpoint.ts.html +301 -0
- package/coverage/src/api/index.html +176 -0
- package/coverage/src/api/insertRulesEndpoint.ts.html +436 -0
- package/coverage/src/index.html +176 -0
- package/coverage/src/index.ts.html +277 -0
- package/coverage/src/redis/index.html +161 -0
- package/coverage/src/redis/redisRulesIndex.ts.html +769 -0
- package/coverage/src/redis/redisRulesReader.ts.html +652 -0
- package/coverage/src/redis/redisRulesStorage.ts.html +229 -0
- package/coverage/src/redis/redisRulesWriter.ts.html +424 -0
- package/coverage/src/util.ts.html +136 -0
- package/dist/.export.d.ts +6 -0
- package/dist/.export.d.ts.map +1 -0
- package/dist/.export.js +21 -0
- package/dist/.export.js.map +1 -0
- package/dist/accessPolicy.d.ts +169 -0
- package/dist/accessPolicy.d.ts.map +1 -0
- package/dist/accessPolicy.js.map +1 -0
- package/dist/accessPolicyResolver.d.ts +110 -0
- package/dist/accessPolicyResolver.d.ts.map +1 -0
- package/dist/accessPolicyResolver.js.map +1 -0
- package/dist/accessRules.d.ts +16 -0
- package/dist/accessRules.d.ts.map +1 -0
- package/dist/accessRules.js.map +1 -0
- package/dist/api/.export.d.ts +7 -0
- package/dist/api/.export.d.ts.map +1 -0
- package/dist/api/.export.js +11 -0
- package/dist/api/.export.js.map +1 -0
- package/dist/api/accessRuleApiRoutes.d.ts +27 -0
- package/dist/api/accessRuleApiRoutes.d.ts.map +1 -0
- package/dist/api/accessRuleApiRoutes.js.map +1 -0
- package/dist/api/accessRulesApiClient.d.ts +10 -0
- package/dist/api/accessRulesApiClient.d.ts.map +1 -0
- package/dist/api/accessRulesApiClient.js +38 -0
- package/dist/api/accessRulesApiClient.js.map +1 -0
- package/dist/api/delete/.export.d.ts +2 -0
- package/dist/api/delete/.export.d.ts.map +1 -0
- package/dist/api/delete/.export.js +1 -0
- package/dist/api/delete/.export.js.map +1 -0
- package/dist/api/delete/deleteAllRules.d.ts +11 -0
- package/dist/api/delete/deleteAllRules.d.ts.map +1 -0
- package/dist/api/delete/deleteAllRules.js +25 -0
- package/dist/api/delete/deleteAllRules.js.map +1 -0
- package/dist/api/delete/deleteRuleGroups.d.ts +19 -0
- package/dist/api/delete/deleteRuleGroups.d.ts.map +1 -0
- package/dist/api/delete/deleteRuleGroups.js +52 -0
- package/dist/api/delete/deleteRuleGroups.js.map +1 -0
- package/dist/api/delete/deleteRules.d.ts +15 -0
- package/dist/api/delete/deleteRules.d.ts.map +1 -0
- package/dist/api/delete/deleteRules.js +43 -0
- package/dist/api/delete/deleteRules.js.map +1 -0
- package/dist/api/deleteAllRulesEndpoint.d.ts +12 -0
- package/dist/api/deleteAllRulesEndpoint.d.ts.map +1 -0
- package/dist/api/deleteAllRulesEndpoint.js.map +1 -0
- package/dist/api/deleteRulesEndpoint.d.ts +116 -0
- package/dist/api/deleteRulesEndpoint.d.ts.map +1 -0
- package/dist/api/deleteRulesEndpoint.js.map +1 -0
- package/dist/api/insertRulesEndpoint.d.ts +22 -0
- package/dist/api/insertRulesEndpoint.d.ts.map +1 -0
- package/dist/api/insertRulesEndpoint.js.map +1 -0
- package/dist/api/read/.export.d.ts +4 -0
- package/dist/api/read/.export.d.ts.map +1 -0
- package/dist/api/read/.export.js +1 -0
- package/dist/api/read/.export.js.map +1 -0
- package/dist/api/read/fetchRules.d.ts +53 -0
- package/dist/api/read/fetchRules.d.ts.map +1 -0
- package/dist/api/read/fetchRules.js +43 -0
- package/dist/api/read/fetchRules.js.map +1 -0
- package/dist/api/read/findRuleIds.d.ts +28 -0
- package/dist/api/read/findRuleIds.d.ts.map +1 -0
- package/dist/api/read/findRuleIds.js +50 -0
- package/dist/api/read/findRuleIds.js.map +1 -0
- package/dist/api/read/getMissingIds.d.ts +28 -0
- package/dist/api/read/getMissingIds.d.ts.map +1 -0
- package/dist/api/read/getMissingIds.js +41 -0
- package/dist/api/read/getMissingIds.js.map +1 -0
- package/dist/api/ruleApiRoutes.d.ts +43 -0
- package/dist/api/ruleApiRoutes.d.ts.map +1 -0
- package/dist/api/ruleApiRoutes.js +131 -0
- package/dist/api/ruleApiRoutes.js.map +1 -0
- package/dist/api/rulesApiClient.d.ts +20 -0
- package/dist/api/rulesApiClient.d.ts.map +1 -0
- package/dist/api/rulesApiClient.js +93 -0
- package/dist/api/rulesApiClient.js.map +1 -0
- package/dist/api/write/.export.d.ts +2 -0
- package/dist/api/write/.export.d.ts.map +1 -0
- package/dist/api/write/.export.js +1 -0
- package/dist/api/write/.export.js.map +1 -0
- package/dist/api/write/insertRules.d.ts +29 -0
- package/dist/api/write/insertRules.d.ts.map +1 -0
- package/dist/api/write/insertRules.js +102 -0
- package/dist/api/write/insertRules.js.map +1 -0
- package/dist/api/write/rehashRules.d.ts +11 -0
- package/dist/api/write/rehashRules.d.ts.map +1 -0
- package/dist/api/write/rehashRules.js +57 -0
- package/dist/api/write/rehashRules.js.map +1 -0
- package/dist/cjs/.export.cjs +21 -0
- package/dist/cjs/api/.export.cjs +11 -0
- package/dist/cjs/api/delete/.export.cjs +1 -0
- package/dist/cjs/api/delete/deleteAllRules.cjs +25 -0
- package/dist/cjs/api/delete/deleteRuleGroups.cjs +52 -0
- package/dist/cjs/api/delete/deleteRules.cjs +43 -0
- package/dist/cjs/api/read/.export.cjs +1 -0
- package/dist/cjs/api/read/fetchRules.cjs +43 -0
- package/dist/cjs/api/read/findRuleIds.cjs +50 -0
- package/dist/cjs/api/read/getMissingIds.cjs +41 -0
- package/dist/cjs/api/ruleApiRoutes.cjs +131 -0
- package/dist/cjs/api/rulesApiClient.cjs +93 -0
- package/dist/cjs/api/write/.export.cjs +1 -0
- package/dist/cjs/api/write/insertRules.cjs +102 -0
- package/dist/cjs/api/write/rehashRules.cjs +57 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/mongoose/.export.cjs +4 -0
- package/dist/cjs/mongoose/mongooseRuleSchema.cjs +36 -0
- package/dist/cjs/redis/.export.cjs +6 -0
- package/dist/cjs/redis/reader/redisAggregate.cjs +60 -0
- package/dist/cjs/redis/reader/redisRulesQuery.cjs +99 -0
- package/dist/cjs/redis/reader/redisRulesReader.cjs +230 -0
- package/dist/cjs/redis/redisAccessRules.cjs +4 -4
- package/dist/cjs/redis/redisAccessRulesIndex.cjs +55 -14
- package/dist/cjs/redis/redisClient.cjs +67 -0
- package/dist/cjs/redis/redisRuleIndex.cjs +50 -0
- package/dist/cjs/redis/redisRulesStorage.cjs +34 -0
- package/dist/cjs/redis/redisRulesWriter.cjs +100 -0
- package/dist/cjs/rule.cjs +8 -0
- package/dist/cjs/ruleInput/.export.cjs +9 -0
- package/dist/cjs/ruleInput/policyInput.cjs +25 -0
- package/dist/cjs/ruleInput/ruleInput.cjs +50 -0
- package/dist/cjs/ruleInput/userScopeInput.cjs +55 -0
- package/dist/cjs/ruleRecord.cjs +23 -0
- package/dist/cjs/rulesStorage.cjs +8 -0
- package/dist/cjs/transformRule.cjs +77 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -0
- package/dist/mongoose/.export.d.ts +2 -0
- package/dist/mongoose/.export.d.ts.map +1 -0
- package/dist/mongoose/.export.js +4 -0
- package/dist/mongoose/.export.js.map +1 -0
- package/dist/mongoose/mongooseRuleSchema.d.ts +4 -0
- package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -0
- package/dist/mongoose/mongooseRuleSchema.js +36 -0
- package/dist/mongoose/mongooseRuleSchema.js.map +1 -0
- package/dist/redis/.export.d.ts +3 -0
- package/dist/redis/.export.d.ts.map +1 -0
- package/dist/redis/.export.js +6 -0
- package/dist/redis/.export.js.map +1 -0
- package/dist/redis/reader/redisAggregate.d.ts +4 -0
- package/dist/redis/reader/redisAggregate.d.ts.map +1 -0
- package/dist/redis/reader/redisAggregate.js +60 -0
- package/dist/redis/reader/redisAggregate.js.map +1 -0
- package/dist/redis/reader/redisRulesQuery.d.ts +4 -0
- package/dist/redis/reader/redisRulesQuery.d.ts.map +1 -0
- package/dist/redis/reader/redisRulesQuery.js +99 -0
- package/dist/redis/reader/redisRulesQuery.js.map +1 -0
- package/dist/redis/reader/redisRulesReader.d.ts +26 -0
- package/dist/redis/reader/redisRulesReader.d.ts.map +1 -0
- package/dist/redis/reader/redisRulesReader.js +213 -0
- package/dist/redis/reader/redisRulesReader.js.map +1 -0
- package/dist/redis/redisAccessRules.d.ts +7 -0
- package/dist/redis/redisAccessRules.d.ts.map +1 -0
- package/dist/redis/redisAccessRules.js +112 -128
- package/dist/redis/redisAccessRules.js.map +1 -0
- package/dist/redis/redisAccessRulesIndex.d.ts +13 -0
- package/dist/redis/redisAccessRulesIndex.d.ts.map +1 -0
- package/dist/redis/redisAccessRulesIndex.js +122 -112
- package/dist/redis/redisAccessRulesIndex.js.map +1 -0
- package/dist/redis/redisClient.d.ts +11 -0
- package/dist/redis/redisClient.d.ts.map +1 -0
- package/dist/redis/redisClient.js +67 -0
- package/dist/redis/redisClient.js.map +1 -0
- package/dist/redis/redisIndex.d.ts +9 -0
- package/dist/redis/redisIndex.d.ts.map +1 -0
- package/dist/redis/redisIndex.js +16 -15
- package/dist/redis/redisIndex.js.map +1 -0
- package/dist/redis/redisRuleIndex.d.ts +13 -0
- package/dist/redis/redisRuleIndex.d.ts.map +1 -0
- package/dist/redis/redisRuleIndex.js +50 -0
- package/dist/redis/redisRuleIndex.js.map +1 -0
- package/dist/redis/redisRulesIndex.d.ts +9 -0
- package/dist/redis/redisRulesIndex.d.ts.map +1 -0
- package/dist/redis/redisRulesIndex.js +138 -0
- package/dist/redis/redisRulesIndex.js.map +1 -0
- package/dist/redis/redisRulesReader.d.ts +6 -0
- package/dist/redis/redisRulesReader.d.ts.map +1 -0
- package/dist/redis/redisRulesReader.js +125 -0
- package/dist/redis/redisRulesReader.js.map +1 -0
- package/dist/redis/redisRulesStorage.d.ts +5 -0
- package/dist/redis/redisRulesStorage.d.ts.map +1 -0
- package/dist/redis/redisRulesStorage.js +34 -0
- package/dist/redis/redisRulesStorage.js.map +1 -0
- package/dist/redis/redisRulesWriter.d.ts +22 -0
- package/dist/redis/redisRulesWriter.d.ts.map +1 -0
- package/dist/redis/redisRulesWriter.js +100 -0
- package/dist/redis/redisRulesWriter.js.map +1 -0
- package/dist/rule.d.ts +34 -0
- package/dist/rule.d.ts.map +1 -0
- package/dist/rule.js +8 -0
- package/dist/rule.js.map +1 -0
- package/dist/ruleInput/.export.d.ts +4 -0
- package/dist/ruleInput/.export.d.ts.map +1 -0
- package/dist/ruleInput/.export.js +9 -0
- package/dist/ruleInput/.export.js.map +1 -0
- package/dist/ruleInput/policyInput.d.ts +38 -0
- package/dist/ruleInput/policyInput.d.ts.map +1 -0
- package/dist/ruleInput/policyInput.js +25 -0
- package/dist/ruleInput/policyInput.js.map +1 -0
- package/dist/ruleInput/ruleInput.d.ts +145 -0
- package/dist/ruleInput/ruleInput.d.ts.map +1 -0
- package/dist/ruleInput/ruleInput.js +50 -0
- package/dist/ruleInput/ruleInput.js.map +1 -0
- package/dist/ruleInput/userScopeInput.d.ts +93 -0
- package/dist/ruleInput/userScopeInput.d.ts.map +1 -0
- package/dist/ruleInput/userScopeInput.js +55 -0
- package/dist/ruleInput/userScopeInput.js.map +1 -0
- package/dist/ruleRecord.d.ts +18 -0
- package/dist/ruleRecord.d.ts.map +1 -0
- package/dist/ruleRecord.js +23 -0
- package/dist/ruleRecord.js.map +1 -0
- package/dist/rulesStorage.d.ts +30 -0
- package/dist/rulesStorage.d.ts.map +1 -0
- package/dist/rulesStorage.js +8 -0
- package/dist/rulesStorage.js.map +1 -0
- package/dist/tests/accessPolicy.test.d.ts +2 -0
- package/dist/tests/accessPolicy.test.d.ts.map +1 -0
- package/dist/tests/accessPolicy.test.js +27 -0
- package/dist/tests/accessPolicy.test.js.map +1 -0
- package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts +2 -0
- package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts.map +1 -0
- package/dist/tests/redis/reader/redisRulesQuery.unit.test.js +101 -0
- package/dist/tests/redis/reader/redisRulesQuery.unit.test.js.map +1 -0
- package/dist/tests/redis/redisAccessRules.integration.test.d.ts +2 -0
- package/dist/tests/redis/redisAccessRules.integration.test.d.ts.map +1 -0
- package/dist/tests/redis/redisAccessRules.integration.test.js +586 -0
- package/dist/tests/redis/redisAccessRules.integration.test.js.map +1 -0
- package/dist/tests/redis/redisAccessRules.unit.test.d.ts +2 -0
- package/dist/tests/redis/redisAccessRules.unit.test.d.ts.map +1 -0
- package/dist/tests/redis/redisAccessRules.unit.test.js +198 -0
- package/dist/tests/redis/redisAccessRules.unit.test.js.map +1 -0
- package/dist/tests/redis/redisIndex.integration.test.d.ts +2 -0
- package/dist/tests/redis/redisIndex.integration.test.d.ts.map +1 -0
- package/dist/tests/redis/redisIndex.integration.test.js +80 -0
- package/dist/tests/redis/redisIndex.integration.test.js.map +1 -0
- package/dist/tests/redis/redisRulesIndex.unit.test.d.ts +2 -0
- package/dist/tests/redis/redisRulesIndex.unit.test.d.ts.map +1 -0
- package/dist/tests/redis/redisRulesIndex.unit.test.js +101 -0
- package/dist/tests/redis/redisRulesIndex.unit.test.js.map +1 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.d.ts +2 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.d.ts.map +1 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.js +611 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -0
- package/dist/tests/redis/testRedisClient.d.ts +3 -0
- package/dist/tests/redis/testRedisClient.d.ts.map +1 -0
- package/dist/tests/redis/testRedisClient.js +8 -0
- package/dist/tests/redis/testRedisClient.js.map +1 -0
- package/dist/tests/testLogger.d.ts +4 -0
- package/dist/tests/testLogger.d.ts.map +1 -0
- package/dist/tests/testLogger.js +22 -0
- package/dist/tests/testLogger.js.map +1 -0
- package/dist/tests/transformRule.unit.test.d.ts +2 -0
- package/dist/tests/transformRule.unit.test.d.ts.map +1 -0
- package/dist/tests/transformRule.unit.test.js +188 -0
- package/dist/tests/transformRule.unit.test.js.map +1 -0
- package/dist/transformRule.d.ts +7 -0
- package/dist/transformRule.d.ts.map +1 -0
- package/dist/transformRule.js +77 -0
- package/dist/transformRule.js.map +1 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js.map +1 -0
- package/entries.ts +20 -0
- package/package.json +42 -21
- package/vite.cjs.config.ts +4 -1
- package/vite.esm.config.ts +6 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const zod = require("zod");
|
|
4
|
+
const redisRulesQuery = require("./redisRulesQuery.cjs");
|
|
5
|
+
const redisClient = require("../redisClient.cjs");
|
|
6
|
+
const redisRuleIndex = require("../redisRuleIndex.cjs");
|
|
7
|
+
const aggregateRedisKeys = async (client, query, logger, batchHandler) => {
|
|
8
|
+
const keyField = "__key";
|
|
9
|
+
const recordSchema = zod.z.object({
|
|
10
|
+
// it's a reserved name for the record key
|
|
11
|
+
[keyField]: zod.z.string()
|
|
12
|
+
});
|
|
13
|
+
const foundKeys = [];
|
|
14
|
+
const addRecordKeys = async (records) => {
|
|
15
|
+
const parsedRecords = redisClient.parseRedisRecords(records, recordSchema, logger);
|
|
16
|
+
const recordKeys = parsedRecords.map((record) => record[keyField]);
|
|
17
|
+
if (batchHandler) {
|
|
18
|
+
await batchHandler(recordKeys);
|
|
19
|
+
} else {
|
|
20
|
+
foundKeys.push(...recordKeys);
|
|
21
|
+
logger.debug(() => ({
|
|
22
|
+
msg: "Processed aggregation batch",
|
|
23
|
+
data: {
|
|
24
|
+
size: recordKeys.length
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
await executeAggregation(
|
|
30
|
+
client,
|
|
31
|
+
query,
|
|
32
|
+
{
|
|
33
|
+
// #2 is a required option when the 'ismissing()' function is in the query body
|
|
34
|
+
DIALECT: redisRulesQuery.REDIS_QUERY_DIALECT,
|
|
35
|
+
COUNT: redisClient.REDIS_BATCH_SIZE,
|
|
36
|
+
LOAD: `@${keyField}`
|
|
37
|
+
},
|
|
38
|
+
addRecordKeys
|
|
39
|
+
);
|
|
40
|
+
return foundKeys;
|
|
41
|
+
};
|
|
42
|
+
const executeAggregation = async (client, query, aggregateOptions, handleBatch) => {
|
|
43
|
+
const initialReply = await client.ft.aggregateWithCursor(
|
|
44
|
+
redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
|
|
45
|
+
query,
|
|
46
|
+
aggregateOptions
|
|
47
|
+
);
|
|
48
|
+
await handleBatch(initialReply.results);
|
|
49
|
+
let cursor = initialReply.cursor;
|
|
50
|
+
while (0 !== cursor) {
|
|
51
|
+
const batchReply = await client.ft.cursorRead(
|
|
52
|
+
redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
|
|
53
|
+
cursor,
|
|
54
|
+
{ COUNT: aggregateOptions.COUNT }
|
|
55
|
+
);
|
|
56
|
+
await handleBatch(batchReply.results);
|
|
57
|
+
cursor = batchReply.cursor;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
exports.aggregateRedisKeys = aggregateRedisKeys;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const userScopeInput = require("../../ruleInput/userScopeInput.cjs");
|
|
4
|
+
const rulesStorage = require("../../rulesStorage.cjs");
|
|
5
|
+
const REDIS_QUERY_DIALECT = 2;
|
|
6
|
+
const userIpQueries = {
|
|
7
|
+
numericIp: (value, scope) => {
|
|
8
|
+
if (void 0 !== value) {
|
|
9
|
+
return `( @numericIp:[${value} ${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`;
|
|
10
|
+
}
|
|
11
|
+
if (scope.numericIpMaskMin === void 0 && scope.numericIpMaskMax === void 0) {
|
|
12
|
+
return "ismissing(@numericIp) ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)";
|
|
13
|
+
}
|
|
14
|
+
return "";
|
|
15
|
+
},
|
|
16
|
+
numericIpMaskMin: (value, scope) => {
|
|
17
|
+
if (scope.numericIp !== void 0) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
return value !== void 0 ? `@numericIpMaskMin:[-inf ${value}]` : "ismissing(@numericIpMaskMin)";
|
|
21
|
+
},
|
|
22
|
+
numericIpMaskMax: (value, scope) => {
|
|
23
|
+
if (scope.numericIp !== void 0) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
return value !== void 0 ? `@numericIpMaskMax:[${value} +inf]` : "ismissing(@numericIpMaskMax)";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const getUserScopeQuery = (userScope, FilterScopeMatchType, matchingFieldsOnly) => {
|
|
30
|
+
let scopeEntries = Object.entries(userScope);
|
|
31
|
+
let scopeJoinType = " ";
|
|
32
|
+
if (FilterScopeMatchType === rulesStorage.FilterScopeMatch.Greedy) {
|
|
33
|
+
scopeEntries = scopeEntries.filter(
|
|
34
|
+
([_, value]) => value !== void 0
|
|
35
|
+
);
|
|
36
|
+
scopeJoinType = " | ";
|
|
37
|
+
}
|
|
38
|
+
if (matchingFieldsOnly) {
|
|
39
|
+
const scopeMap = new Map(scopeEntries);
|
|
40
|
+
if (scopeMap.has("numericIp") && scopeMap.get("numericIp") === void 0) {
|
|
41
|
+
scopeMap.set("numericIpMaskMin", void 0);
|
|
42
|
+
scopeMap.set("numericIpMaskMax", void 0);
|
|
43
|
+
}
|
|
44
|
+
for (const name of Object.keys(userScopeInput.userScopeSchema.shape)) {
|
|
45
|
+
if (!scopeMap.has(name)) {
|
|
46
|
+
scopeMap.set(name, void 0);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
scopeEntries = [...scopeMap.entries()];
|
|
50
|
+
}
|
|
51
|
+
const scopeObj = Object.fromEntries(scopeEntries);
|
|
52
|
+
return scopeEntries.map(
|
|
53
|
+
([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(
|
|
54
|
+
scopeFieldName,
|
|
55
|
+
scopeFieldValue,
|
|
56
|
+
FilterScopeMatchType,
|
|
57
|
+
scopeObj
|
|
58
|
+
)
|
|
59
|
+
).filter(Boolean).join(scopeJoinType);
|
|
60
|
+
};
|
|
61
|
+
const getUserScopeFieldQuery = (fieldName, fieldValue, scopeMatch, fullScope) => {
|
|
62
|
+
if (fieldName in userIpQueries) {
|
|
63
|
+
const queryBuilder = userIpQueries[fieldName];
|
|
64
|
+
return queryBuilder(fieldValue, fullScope);
|
|
65
|
+
}
|
|
66
|
+
return void 0 === fieldValue ? `ismissing(@${fieldName})` : `@${fieldName}:{${fieldValue}}`;
|
|
67
|
+
};
|
|
68
|
+
const getPolicyScopeQuery = (policyScope, scopeMatch) => {
|
|
69
|
+
const clientId = policyScope?.clientId;
|
|
70
|
+
if ("string" === typeof clientId) {
|
|
71
|
+
return rulesStorage.FilterScopeMatch.Exact === scopeMatch ? `@clientId:{${clientId}}` : `( @clientId:{${clientId}} | ismissing(@clientId) )`;
|
|
72
|
+
}
|
|
73
|
+
return rulesStorage.FilterScopeMatch.Exact === scopeMatch ? "ismissing(@clientId)" : "";
|
|
74
|
+
};
|
|
75
|
+
const getRulesRedisQuery = (filter, matchingFieldsOnly) => {
|
|
76
|
+
const { policyScope, userScope } = filter;
|
|
77
|
+
const queryParts = [];
|
|
78
|
+
if (filter.groupId) {
|
|
79
|
+
queryParts.push(`@groupId:{${filter.groupId}}`);
|
|
80
|
+
}
|
|
81
|
+
const policyScopeQuery = getPolicyScopeQuery(
|
|
82
|
+
policyScope,
|
|
83
|
+
filter.policyScopeMatch
|
|
84
|
+
);
|
|
85
|
+
if (policyScopeQuery) {
|
|
86
|
+
queryParts.push(policyScopeQuery);
|
|
87
|
+
}
|
|
88
|
+
if (userScope && Object.keys(userScope).length > 0) {
|
|
89
|
+
const userScopeFilter = getUserScopeQuery(
|
|
90
|
+
userScope,
|
|
91
|
+
filter.userScopeMatch,
|
|
92
|
+
matchingFieldsOnly
|
|
93
|
+
);
|
|
94
|
+
queryParts.push(`( ${userScopeFilter} )`);
|
|
95
|
+
}
|
|
96
|
+
return queryParts.length > 0 ? queryParts.join(" ") : "*";
|
|
97
|
+
};
|
|
98
|
+
exports.REDIS_QUERY_DIALECT = REDIS_QUERY_DIALECT;
|
|
99
|
+
exports.getRulesRedisQuery = getRulesRedisQuery;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const util = require("node:util");
|
|
4
|
+
const common = require("@prosopo/common");
|
|
5
|
+
const redisRulesQuery = require("./redisRulesQuery.cjs");
|
|
6
|
+
const redisClient = require("../redisClient.cjs");
|
|
7
|
+
const redisRuleIndex = require("../redisRuleIndex.cjs");
|
|
8
|
+
const ruleInput = require("../../ruleInput/ruleInput.cjs");
|
|
9
|
+
const redisAggregate = require("./redisAggregate.cjs");
|
|
10
|
+
function _interopNamespaceDefault(e) {
|
|
11
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
12
|
+
if (e) {
|
|
13
|
+
for (const k in e) {
|
|
14
|
+
if (k !== "default") {
|
|
15
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: () => e[k]
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
|
|
27
|
+
class RedisRulesReader {
|
|
28
|
+
constructor(client, logger) {
|
|
29
|
+
this.client = client;
|
|
30
|
+
this.logger = logger;
|
|
31
|
+
}
|
|
32
|
+
async getMissingRuleIds(ruleIds) {
|
|
33
|
+
const ruleKeys = this.getRuleKeys(ruleIds);
|
|
34
|
+
const keyBatches = common.chunkIntoBatches(ruleKeys, redisClient.REDIS_BATCH_SIZE);
|
|
35
|
+
const missingKeyBatches = await common.executeBatchesSequentially(
|
|
36
|
+
keyBatches,
|
|
37
|
+
async (keysBatch) => redisClient.getMissingRedisKeys(this.client, keysBatch)
|
|
38
|
+
);
|
|
39
|
+
return missingKeyBatches.flat().map((ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length));
|
|
40
|
+
}
|
|
41
|
+
async fetchRules(ruleIds) {
|
|
42
|
+
const ruleKeys = this.getRuleKeys(ruleIds);
|
|
43
|
+
const keyBatches = common.chunkIntoBatches(ruleKeys, redisClient.REDIS_BATCH_SIZE);
|
|
44
|
+
const entryBatches = await common.executeBatchesSequentially(
|
|
45
|
+
keyBatches,
|
|
46
|
+
(keysBatch) => this.fetchRuleEntries(keysBatch)
|
|
47
|
+
);
|
|
48
|
+
return entryBatches.flat();
|
|
49
|
+
}
|
|
50
|
+
async findRules(filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) {
|
|
51
|
+
const query = redisRulesQuery.getRulesRedisQuery(filter, matchingFieldsOnly);
|
|
52
|
+
if (skipEmptyUserScopes && query === "ismissing(@clientId)") {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
let searchReply;
|
|
56
|
+
try {
|
|
57
|
+
searchReply = await this.client.ft.search(
|
|
58
|
+
redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
|
|
59
|
+
query,
|
|
60
|
+
{
|
|
61
|
+
DIALECT: redisRulesQuery.REDIS_QUERY_DIALECT,
|
|
62
|
+
// FT.search doesn't support "unlimited" selects
|
|
63
|
+
LIMIT: {
|
|
64
|
+
from: 0,
|
|
65
|
+
size: redisClient.REDIS_BATCH_SIZE
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
if (searchReply.total > 0) {
|
|
70
|
+
this.logger.debug(() => ({
|
|
71
|
+
msg: "Executed search query",
|
|
72
|
+
data: {
|
|
73
|
+
inspect: util__namespace.inspect(
|
|
74
|
+
{
|
|
75
|
+
filter,
|
|
76
|
+
searchReply,
|
|
77
|
+
query
|
|
78
|
+
},
|
|
79
|
+
{ depth: null }
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
this.logger.error(() => ({
|
|
86
|
+
err: e,
|
|
87
|
+
data: {
|
|
88
|
+
inspect: util__namespace.inspect(
|
|
89
|
+
{
|
|
90
|
+
query,
|
|
91
|
+
filter
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
depth: null
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
msg: "failed to execute search query"
|
|
99
|
+
}));
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const records = searchReply.documents.map(({ value }) => value);
|
|
103
|
+
return redisClient.parseRedisRecords(records, ruleInput.accessRuleInput, this.logger);
|
|
104
|
+
}
|
|
105
|
+
async findRuleIds(filter, matchingFieldsOnly = false) {
|
|
106
|
+
const query = redisRulesQuery.getRulesRedisQuery(filter, matchingFieldsOnly);
|
|
107
|
+
let ruleIds = [];
|
|
108
|
+
try {
|
|
109
|
+
const ruleKeys = await redisAggregate.aggregateRedisKeys(
|
|
110
|
+
this.client,
|
|
111
|
+
query,
|
|
112
|
+
this.logger
|
|
113
|
+
);
|
|
114
|
+
ruleIds = ruleKeys.map(
|
|
115
|
+
(ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length)
|
|
116
|
+
);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
this.logger.error(() => ({
|
|
119
|
+
err: e,
|
|
120
|
+
data: {
|
|
121
|
+
inspect: util__namespace.inspect(
|
|
122
|
+
{
|
|
123
|
+
query,
|
|
124
|
+
filter
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
depth: null
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
},
|
|
131
|
+
msg: "Failed to execute search query for rule IDs"
|
|
132
|
+
}));
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
this.logger.debug(() => ({
|
|
136
|
+
msg: "Executed search query for rule IDs",
|
|
137
|
+
data: {
|
|
138
|
+
query,
|
|
139
|
+
foundCount: ruleIds.length,
|
|
140
|
+
foundIds: ruleIds
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
return ruleIds;
|
|
144
|
+
}
|
|
145
|
+
async fetchAllRuleIds(batchHandler) {
|
|
146
|
+
const keysBatchHandler = async (keys) => {
|
|
147
|
+
const ids = keys.map(
|
|
148
|
+
(ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length)
|
|
149
|
+
);
|
|
150
|
+
await batchHandler(ids);
|
|
151
|
+
};
|
|
152
|
+
await redisAggregate.aggregateRedisKeys(this.client, "*", this.logger, keysBatchHandler);
|
|
153
|
+
}
|
|
154
|
+
async fetchRuleEntries(keys) {
|
|
155
|
+
const { records, expirations } = await redisClient.fetchRedisHashRecords(
|
|
156
|
+
this.client,
|
|
157
|
+
keys,
|
|
158
|
+
this.logger
|
|
159
|
+
);
|
|
160
|
+
const entries = [];
|
|
161
|
+
for (const [index, ruleData] of records.entries()) {
|
|
162
|
+
const isRulePresent = Object.keys(ruleData).length > 0;
|
|
163
|
+
if (isRulePresent) {
|
|
164
|
+
const rule = redisClient.parseRedisRecords(
|
|
165
|
+
[ruleData],
|
|
166
|
+
ruleInput.accessRuleInput,
|
|
167
|
+
this.logger
|
|
168
|
+
)[0];
|
|
169
|
+
if (rule) {
|
|
170
|
+
entries.push({
|
|
171
|
+
rule,
|
|
172
|
+
expiresUnixTimestamp: expirations[index]
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return entries;
|
|
178
|
+
}
|
|
179
|
+
getRuleKeys(ruleIds) {
|
|
180
|
+
return ruleIds.map((id) => `${redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX}${id}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
class DummyRedisRulesReader {
|
|
184
|
+
constructor(logger) {
|
|
185
|
+
this.logger = logger;
|
|
186
|
+
}
|
|
187
|
+
async getMissingRuleIds(ruleIds) {
|
|
188
|
+
this.logger.info(() => ({
|
|
189
|
+
msg: "Dummy getMissingRuleIds() has no effect (redis is not ready)",
|
|
190
|
+
data: {
|
|
191
|
+
ruleIds
|
|
192
|
+
}
|
|
193
|
+
}));
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
async fetchRules(ruleIds) {
|
|
197
|
+
this.logger.info(() => ({
|
|
198
|
+
msg: "Dummy fetchRule() has no effect (redis is not ready)",
|
|
199
|
+
data: {
|
|
200
|
+
ruleIds
|
|
201
|
+
}
|
|
202
|
+
}));
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
async findRules(filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) {
|
|
206
|
+
this.logger.info(() => ({
|
|
207
|
+
msg: "Dummy findRules() has no effect (redis is not ready)",
|
|
208
|
+
data: {
|
|
209
|
+
filter
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
async findRuleIds(filter, matchingFieldsOnly = false) {
|
|
215
|
+
this.logger.info(() => ({
|
|
216
|
+
msg: "Dummy findRuleIds() has no effect (redis is not ready)",
|
|
217
|
+
data: {
|
|
218
|
+
filter
|
|
219
|
+
}
|
|
220
|
+
}));
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
async fetchAllRuleIds(batchHandler) {
|
|
224
|
+
this.logger.info(() => ({
|
|
225
|
+
msg: "Dummy fetchAllRuleIds() has no effect (redis is not ready)"
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.DummyRedisRulesReader = DummyRedisRulesReader;
|
|
230
|
+
exports.RedisRulesReader = RedisRulesReader;
|
|
@@ -22,8 +22,8 @@ function _interopNamespaceDefault(e) {
|
|
|
22
22
|
const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
|
|
23
23
|
const createRedisAccessRulesReader = (client, logger) => {
|
|
24
24
|
return {
|
|
25
|
-
findRules: async (filter, skipEmptyUserScopes = true) => {
|
|
26
|
-
const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter);
|
|
25
|
+
findRules: async (filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) => {
|
|
26
|
+
const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter, matchingFieldsOnly);
|
|
27
27
|
if (skipEmptyUserScopes && query === "ismissing(@clientId)") {
|
|
28
28
|
return [];
|
|
29
29
|
}
|
|
@@ -69,8 +69,8 @@ const createRedisAccessRulesReader = (client, logger) => {
|
|
|
69
69
|
}
|
|
70
70
|
return extractAccessRulesFromSearchReply(searchReply, logger);
|
|
71
71
|
},
|
|
72
|
-
findRuleIds: async (filter) => {
|
|
73
|
-
const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter);
|
|
72
|
+
findRuleIds: async (filter, matchingFieldsOnly = false) => {
|
|
73
|
+
const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter, matchingFieldsOnly);
|
|
74
74
|
let searchReply;
|
|
75
75
|
try {
|
|
76
76
|
searchReply = await client.ft.searchNoContent(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const crypto = require("node:crypto");
|
|
4
4
|
const search = require("@redis/search");
|
|
5
|
+
const accessPolicy = require("../accessPolicy.cjs");
|
|
5
6
|
const accessPolicyResolver = require("../accessPolicyResolver.cjs");
|
|
6
7
|
const redisIndex = require("./redisIndex.cjs");
|
|
7
8
|
const accessRulesRedisIndexName = "index:user-access-rules";
|
|
@@ -24,8 +25,8 @@ const accessRulesIndex = {
|
|
|
24
25
|
// necessary to make possible use of the ismissing() function on this field in the search
|
|
25
26
|
INDEXMISSING: true
|
|
26
27
|
},
|
|
27
|
-
numericIpMaskMin: search.SCHEMA_FIELD_TYPE.NUMERIC,
|
|
28
|
-
numericIpMaskMax: search.SCHEMA_FIELD_TYPE.NUMERIC,
|
|
28
|
+
numericIpMaskMin: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
29
|
+
numericIpMaskMax: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
29
30
|
userId: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
30
31
|
numericIp: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
31
32
|
ja4Hash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
@@ -50,7 +51,27 @@ const numericIndexFields = [
|
|
|
50
51
|
"numericIpMaskMax"
|
|
51
52
|
];
|
|
52
53
|
const greedyFieldComparisons = {
|
|
53
|
-
numericIp: (value) =>
|
|
54
|
+
numericIp: (value, scope) => {
|
|
55
|
+
if (value !== void 0) {
|
|
56
|
+
return `( @numericIp:[${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`;
|
|
57
|
+
}
|
|
58
|
+
if (scope.numericIpMaskMin === void 0 && scope.numericIpMaskMax === void 0) {
|
|
59
|
+
return "ismissing(@numericIp) ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)";
|
|
60
|
+
}
|
|
61
|
+
return "";
|
|
62
|
+
},
|
|
63
|
+
numericIpMaskMin: (value, scope) => {
|
|
64
|
+
if (scope.numericIp !== void 0) {
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
return value !== void 0 ? `@numericIpMaskMin:[-inf ${value}]` : "ismissing(@numericIpMaskMin)";
|
|
68
|
+
},
|
|
69
|
+
numericIpMaskMax: (value, scope) => {
|
|
70
|
+
if (scope.numericIp !== void 0) {
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
return value !== void 0 ? `@numericIpMaskMax:[${value} +inf]` : "ismissing(@numericIpMaskMax)";
|
|
74
|
+
}
|
|
54
75
|
};
|
|
55
76
|
const accessRulesRedisSearchOptions = {
|
|
56
77
|
// #2 is a required option when the 'ismissing()' function is in the query body
|
|
@@ -64,14 +85,18 @@ const accessRulesRedisDeleteOptions = {
|
|
|
64
85
|
size: DEFAULT_SEARCH_LIMIT
|
|
65
86
|
}
|
|
66
87
|
};
|
|
67
|
-
const getRedisAccessRulesQuery = (filter) => {
|
|
88
|
+
const getRedisAccessRulesQuery = (filter, matchingFieldsOnly) => {
|
|
68
89
|
const { policyScope, userScope } = filter;
|
|
69
90
|
const policyScopeFilter = getPolicyScopeQuery(
|
|
70
91
|
policyScope,
|
|
71
92
|
filter.policyScopeMatch
|
|
72
93
|
);
|
|
73
94
|
if (userScope && Object.keys(userScope).length > 0) {
|
|
74
|
-
const userScopeFilter = getUserScopeQuery(
|
|
95
|
+
const userScopeFilter = getUserScopeQuery(
|
|
96
|
+
userScope,
|
|
97
|
+
filter.userScopeMatch,
|
|
98
|
+
matchingFieldsOnly
|
|
99
|
+
);
|
|
75
100
|
return `${policyScopeFilter} ( ${userScopeFilter} )`;
|
|
76
101
|
}
|
|
77
102
|
return policyScopeFilter ? policyScopeFilter : "*";
|
|
@@ -83,7 +108,7 @@ const getPolicyScopeQuery = (policyScope, scopeMatchType) => {
|
|
|
83
108
|
}
|
|
84
109
|
return accessPolicyResolver.ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
|
|
85
110
|
};
|
|
86
|
-
const getUserScopeQuery = (userScope, scopeMatchType) => {
|
|
111
|
+
const getUserScopeQuery = (userScope, scopeMatchType, matchingFieldsOnly) => {
|
|
87
112
|
let scopeEntries = Object.entries(userScope);
|
|
88
113
|
let scopeJoinType = " ";
|
|
89
114
|
if (scopeMatchType === accessPolicyResolver.ScopeMatch.Greedy) {
|
|
@@ -92,16 +117,32 @@ const getUserScopeQuery = (userScope, scopeMatchType) => {
|
|
|
92
117
|
);
|
|
93
118
|
scopeJoinType = " | ";
|
|
94
119
|
}
|
|
120
|
+
if (matchingFieldsOnly) {
|
|
121
|
+
const scopeMap = new Map(scopeEntries);
|
|
122
|
+
if (scopeMap.has("numericIp") && scopeMap.get("numericIp") === void 0) {
|
|
123
|
+
scopeMap.set("numericIpMaskMin", void 0);
|
|
124
|
+
scopeMap.set("numericIpMaskMax", void 0);
|
|
125
|
+
}
|
|
126
|
+
for (const name of Object.keys(accessPolicy.userScopeSchema.shape)) {
|
|
127
|
+
if (!scopeMap.has(name)) {
|
|
128
|
+
scopeMap.set(name, void 0);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
scopeEntries = [...scopeMap.entries()];
|
|
132
|
+
}
|
|
133
|
+
const scopeObj = Object.fromEntries(scopeEntries);
|
|
95
134
|
return scopeEntries.map(
|
|
96
|
-
([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(
|
|
97
|
-
|
|
135
|
+
([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(
|
|
136
|
+
scopeFieldName,
|
|
137
|
+
scopeFieldValue,
|
|
138
|
+
scopeMatchType,
|
|
139
|
+
scopeObj
|
|
140
|
+
)
|
|
141
|
+
).filter(Boolean).join(scopeJoinType);
|
|
98
142
|
};
|
|
99
|
-
const getUserScopeFieldQuery = (fieldName, fieldValue, matchType) => {
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
"function" === typeof greedyFieldComparisons[fieldName]
|
|
103
|
-
) {
|
|
104
|
-
return greedyFieldComparisons[fieldName](fieldValue);
|
|
143
|
+
const getUserScopeFieldQuery = (fieldName, fieldValue, matchType, fullScope) => {
|
|
144
|
+
if ("function" === typeof greedyFieldComparisons[fieldName]) {
|
|
145
|
+
return greedyFieldComparisons[fieldName](fieldValue, fullScope);
|
|
105
146
|
}
|
|
106
147
|
if (fieldValue === void 0) {
|
|
107
148
|
return `ismissing(@${fieldName})`;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const zod = require("zod");
|
|
4
|
+
const REDIS_BATCH_SIZE = 1e3;
|
|
5
|
+
const 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) {
|
|
16
|
+
missingKeys.push(key);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return missingKeys;
|
|
21
|
+
};
|
|
22
|
+
const fetchRedisHashRecords = async (client, keys, logger) => {
|
|
23
|
+
const rulesPipe = client.multi();
|
|
24
|
+
const expirationPipe = client.multi();
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
rulesPipe.hGetAll(key);
|
|
27
|
+
expirationPipe.expireTime(key);
|
|
28
|
+
}
|
|
29
|
+
const records = await rulesPipe.exec();
|
|
30
|
+
const expirationRecords = await expirationPipe.exec();
|
|
31
|
+
return {
|
|
32
|
+
records,
|
|
33
|
+
expirations: parseExpirationRecords(expirationRecords, logger)
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const parseRedisRecords = (records, recordSchema, logger) => records.flatMap((record) => {
|
|
37
|
+
const parseResult = recordSchema.safeParse(record);
|
|
38
|
+
if (parseResult.success) {
|
|
39
|
+
return [parseResult.data];
|
|
40
|
+
}
|
|
41
|
+
logger.error(() => ({
|
|
42
|
+
msg: "Failed to parse Redis record",
|
|
43
|
+
data: { record, error: parseResult.error }
|
|
44
|
+
}));
|
|
45
|
+
return [];
|
|
46
|
+
});
|
|
47
|
+
const expirationRecordSchema = zod.z.coerce.number();
|
|
48
|
+
const UNSET_EXPIRATION_VALUE = -1;
|
|
49
|
+
const parseExpirationRecords = (records, logger) => records.flatMap((record) => {
|
|
50
|
+
const parseResult = expirationRecordSchema.safeParse(record);
|
|
51
|
+
if (parseResult.success) {
|
|
52
|
+
const expiration = UNSET_EXPIRATION_VALUE === parseResult.data ? void 0 : parseResult.data;
|
|
53
|
+
return [expiration];
|
|
54
|
+
}
|
|
55
|
+
logger.error(() => ({
|
|
56
|
+
msg: "Failed to parse Redis expiration record",
|
|
57
|
+
data: {
|
|
58
|
+
record,
|
|
59
|
+
error: parseResult.error
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
return [void 0];
|
|
63
|
+
});
|
|
64
|
+
exports.REDIS_BATCH_SIZE = REDIS_BATCH_SIZE;
|
|
65
|
+
exports.fetchRedisHashRecords = fetchRedisHashRecords;
|
|
66
|
+
exports.getMissingRedisKeys = getMissingRedisKeys;
|
|
67
|
+
exports.parseRedisRecords = parseRedisRecords;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const search = require("@redis/search");
|
|
4
|
+
const transformRule = require("../transformRule.cjs");
|
|
5
|
+
const userIpRedisSchema = {
|
|
6
|
+
numericIpMaskMin: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
7
|
+
numericIpMaskMax: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
8
|
+
numericIp: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true }
|
|
9
|
+
};
|
|
10
|
+
const userAttributesRedisSchema = {
|
|
11
|
+
userId: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
12
|
+
ja4Hash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
13
|
+
headersHash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
14
|
+
userAgentHash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
|
|
15
|
+
};
|
|
16
|
+
const userScopeRedisSchema = {
|
|
17
|
+
...userAttributesRedisSchema,
|
|
18
|
+
...userIpRedisSchema
|
|
19
|
+
};
|
|
20
|
+
const policyScopeRedisSchema = {
|
|
21
|
+
clientId: {
|
|
22
|
+
type: search.SCHEMA_FIELD_TYPE.TAG,
|
|
23
|
+
INDEXMISSING: true
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const accessRuleRedisSchema = {
|
|
27
|
+
...policyScopeRedisSchema,
|
|
28
|
+
...userScopeRedisSchema,
|
|
29
|
+
groupId: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
|
|
30
|
+
};
|
|
31
|
+
const ACCESS_RULES_REDIS_INDEX_NAME = "index:user-access-rules";
|
|
32
|
+
const ACCESS_RULE_REDIS_KEY_PREFIX = "uar:";
|
|
33
|
+
const accessRulesRedisIndex = {
|
|
34
|
+
name: ACCESS_RULES_REDIS_INDEX_NAME,
|
|
35
|
+
schema: accessRuleRedisSchema,
|
|
36
|
+
options: {
|
|
37
|
+
ON: "HASH",
|
|
38
|
+
PREFIX: [ACCESS_RULE_REDIS_KEY_PREFIX]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const getAccessRuleRedisKey = (rule) => ACCESS_RULE_REDIS_KEY_PREFIX + transformRule.makeAccessRuleHash(rule);
|
|
42
|
+
exports.ACCESS_RULES_REDIS_INDEX_NAME = ACCESS_RULES_REDIS_INDEX_NAME;
|
|
43
|
+
exports.ACCESS_RULE_REDIS_KEY_PREFIX = ACCESS_RULE_REDIS_KEY_PREFIX;
|
|
44
|
+
exports.accessRuleRedisSchema = accessRuleRedisSchema;
|
|
45
|
+
exports.accessRulesRedisIndex = accessRulesRedisIndex;
|
|
46
|
+
exports.getAccessRuleRedisKey = getAccessRuleRedisKey;
|
|
47
|
+
exports.policyScopeRedisSchema = policyScopeRedisSchema;
|
|
48
|
+
exports.userAttributesRedisSchema = userAttributesRedisSchema;
|
|
49
|
+
exports.userIpRedisSchema = userIpRedisSchema;
|
|
50
|
+
exports.userScopeRedisSchema = userScopeRedisSchema;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const redisRulesReader = require("./reader/redisRulesReader.cjs");
|
|
4
|
+
const redisRulesWriter = require("./redisRulesWriter.cjs");
|
|
5
|
+
const createRedisAccessRulesStorage = (connection, logger) => {
|
|
6
|
+
const storage = composeStorage(
|
|
7
|
+
new redisRulesReader.DummyRedisRulesReader(logger),
|
|
8
|
+
new redisRulesWriter.DummyRedisRulesWriter(logger)
|
|
9
|
+
);
|
|
10
|
+
connection.getClient().then((client) => {
|
|
11
|
+
const realStorage = composeStorage(
|
|
12
|
+
new redisRulesReader.RedisRulesReader(client, logger),
|
|
13
|
+
new redisRulesWriter.RedisRulesWriter(client, logger)
|
|
14
|
+
);
|
|
15
|
+
Object.assign(storage, realStorage);
|
|
16
|
+
logger.info(() => ({
|
|
17
|
+
msg: "RedisAccessRules storage got a ready Redis client"
|
|
18
|
+
}));
|
|
19
|
+
});
|
|
20
|
+
return storage;
|
|
21
|
+
};
|
|
22
|
+
const composeStorage = (reader, writer) => ({
|
|
23
|
+
// reader
|
|
24
|
+
fetchRules: reader.fetchRules.bind(reader),
|
|
25
|
+
getMissingRuleIds: reader.getMissingRuleIds.bind(reader),
|
|
26
|
+
findRules: reader.findRules.bind(reader),
|
|
27
|
+
findRuleIds: reader.findRuleIds.bind(reader),
|
|
28
|
+
fetchAllRuleIds: reader.fetchAllRuleIds.bind(reader),
|
|
29
|
+
// writer
|
|
30
|
+
insertRules: writer.insertRules.bind(writer),
|
|
31
|
+
deleteRules: writer.deleteRules.bind(writer),
|
|
32
|
+
deleteAllRules: writer.deleteAllRules.bind(writer)
|
|
33
|
+
});
|
|
34
|
+
exports.createRedisAccessRulesStorage = createRedisAccessRulesStorage;
|