@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.
- package/.turbo/turbo-build$colon$cjs.log +45 -41
- package/.turbo/turbo-build$colon$tsc.log +14 -14
- package/.turbo/turbo-build.log +46 -42
- package/CHANGELOG.md +24 -0
- package/dist/.export.js +7 -21
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/api/.export.js +2 -9
- package/dist/api/accessRulesApiClient.js +1 -1
- package/dist/api/delete/.export.js +2 -1
- package/dist/api/delete/deleteAllRules.js +22 -24
- package/dist/api/delete/deleteRuleGroups.js +39 -51
- package/dist/api/delete/deleteRules.js +37 -41
- package/dist/api/read/.export.js +2 -1
- package/dist/api/read/fetchRules.js +34 -42
- package/dist/api/read/findRuleIds.js +35 -48
- package/dist/api/read/getMissingIds.js +36 -40
- package/dist/api/ruleApiRoutes.js +62 -120
- package/dist/api/rulesApiClient.js +51 -87
- package/dist/api/write/.export.js +2 -1
- package/dist/api/write/insertRules.js +84 -102
- package/dist/api/write/rehashRules.js +48 -56
- package/dist/cjs/.export.cjs +21 -23
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/api/.export.cjs +6 -10
- package/dist/cjs/api/accessRulesApiClient.cjs +0 -0
- package/dist/cjs/api/delete/.export.cjs +0 -1
- package/dist/cjs/api/delete/deleteAllRules.cjs +22 -24
- package/dist/cjs/api/delete/deleteRuleGroups.cjs +39 -51
- package/dist/cjs/api/delete/deleteRules.cjs +41 -42
- package/dist/cjs/api/read/.export.cjs +0 -1
- package/dist/cjs/api/read/fetchRules.cjs +34 -41
- package/dist/cjs/api/read/findRuleIds.cjs +37 -48
- package/dist/cjs/api/read/getMissingIds.cjs +36 -39
- package/dist/cjs/api/ruleApiRoutes.cjs +69 -125
- package/dist/cjs/api/rulesApiClient.cjs +54 -90
- package/dist/cjs/api/write/.export.cjs +0 -1
- package/dist/cjs/api/write/insertRules.cjs +85 -103
- package/dist/cjs/api/write/rehashRules.cjs +48 -56
- package/dist/cjs/classifyOs.cjs +46 -26
- package/dist/cjs/mongoose/.export.cjs +2 -3
- package/dist/cjs/mongoose/mongooseRuleSchema.cjs +97 -36
- package/dist/cjs/redis/.export.cjs +4 -5
- package/dist/cjs/redis/reader/redisAggregate.cjs +59 -84
- package/dist/cjs/redis/reader/redisRulesQuery.cjs +74 -116
- package/dist/cjs/redis/reader/redisRulesReader.cjs +322 -503
- package/dist/cjs/redis/reader/redisRulesSplitQuery.cjs +94 -72
- package/dist/cjs/redis/redisClient.cjs +53 -58
- package/dist/cjs/redis/redisRuleIndex.cjs +93 -49
- package/dist/cjs/redis/redisRulesStorage.cjs +21 -30
- package/dist/cjs/redis/redisRulesWriter.cjs +86 -112
- package/dist/cjs/rule.cjs +8 -7
- package/dist/cjs/ruleInput/.export.cjs +7 -8
- package/dist/cjs/ruleInput/policyInput.cjs +35 -58
- package/dist/cjs/ruleInput/ruleInput.cjs +37 -42
- package/dist/cjs/ruleInput/userScopeInput.cjs +47 -55
- package/dist/cjs/ruleRecord.cjs +15 -22
- package/dist/cjs/rulesStorage.cjs +7 -6
- package/dist/cjs/transformRule.cjs +36 -70
- package/dist/classifyOs.js +48 -29
- package/dist/mongoose/.export.js +2 -3
- package/dist/mongoose/mongooseRuleSchema.js +99 -38
- package/dist/redis/.export.js +3 -5
- package/dist/redis/reader/redisAggregate.js +60 -86
- package/dist/redis/reader/redisRulesQuery.js +75 -118
- package/dist/redis/reader/redisRulesReader.js +312 -487
- package/dist/redis/reader/redisRulesSplitQuery.js +96 -75
- package/dist/redis/redisClient.js +54 -62
- package/dist/redis/redisRuleIndex.js +93 -58
- package/dist/redis/redisRulesStorage.js +21 -30
- package/dist/redis/redisRulesWriter.js +82 -113
- package/dist/rule.js +10 -10
- package/dist/ruleInput/.export.js +3 -7
- package/dist/ruleInput/policyInput.js +35 -61
- package/dist/ruleInput/ruleInput.js +36 -44
- package/dist/ruleInput/userScopeInput.js +44 -55
- package/dist/ruleRecord.js +17 -27
- package/dist/rulesStorage.js +9 -8
- package/dist/tests/redis/redisRulesStorage.integration.test.js +1 -3
- package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -1
- package/dist/transformRule.js +34 -73
- package/entries.ts +25 -6
- package/package.json +13 -12
- package/src/tests/redis/redisRulesStorage.integration.test.ts +191 -195
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,117 +1,86 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { REDIS_BATCH_SIZE } from "./redisClient.js";
|
|
1
|
+
import "../_virtual/_rolldown/runtime.js";
|
|
3
2
|
import { GLOBAL_CLIENT_SCOPE_SENTINEL } from "../rule.js";
|
|
3
|
+
import { REDIS_BATCH_SIZE } from "./redisClient.js";
|
|
4
4
|
import { ACCESS_RULE_REDIS_KEY_PREFIX, getAccessRuleRedisKey } from "./redisRuleIndex.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
queries.expireAt(ruleKey, expiresUnixTimestamp);
|
|
65
|
-
}
|
|
66
|
-
return ruleKey;
|
|
67
|
-
});
|
|
68
|
-
await queries.exec();
|
|
69
|
-
return ruleKeys;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const getRedisRuleValue = (rule) => {
|
|
73
|
-
const record = {};
|
|
74
|
-
for (const [key, value] of Object.entries(rule)) {
|
|
75
|
-
if (value === void 0) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
record[key] = String(value);
|
|
79
|
-
}
|
|
80
|
-
if (record.clientId === void 0) {
|
|
81
|
-
record.clientId = GLOBAL_CLIENT_SCOPE_SENTINEL;
|
|
82
|
-
}
|
|
83
|
-
return record;
|
|
5
|
+
import { chunkIntoBatches, executeBatchesSequentially } from "@prosopo/common";
|
|
6
|
+
//#region src/redis/redisRulesWriter.ts
|
|
7
|
+
var RedisRulesWriter = class {
|
|
8
|
+
constructor(client, logger) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
this.logger = logger;
|
|
11
|
+
}
|
|
12
|
+
async insertRules(ruleEntries) {
|
|
13
|
+
return (await executeBatchesSequentially(chunkIntoBatches(ruleEntries, REDIS_BATCH_SIZE), async (entriesBatch) => this.insertRuleEntries(entriesBatch))).flatMap((ruleKey) => ruleKey.slice(ACCESS_RULE_REDIS_KEY_PREFIX.length));
|
|
14
|
+
}
|
|
15
|
+
async deleteRules(ruleIds) {
|
|
16
|
+
await executeBatchesSequentially(chunkIntoBatches(ruleIds.map((ruleId) => ACCESS_RULE_REDIS_KEY_PREFIX + ruleId), REDIS_BATCH_SIZE), async (keysBatch) => {
|
|
17
|
+
const queries = this.client.multi();
|
|
18
|
+
for (const ruleKey of keysBatch) queries.del(ruleKey);
|
|
19
|
+
await queries.exec();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async deleteAllRules() {
|
|
23
|
+
let cursor = "0";
|
|
24
|
+
let total = 0;
|
|
25
|
+
do {
|
|
26
|
+
const reply = await this.client.scan(cursor, {
|
|
27
|
+
MATCH: `${ACCESS_RULE_REDIS_KEY_PREFIX}*`,
|
|
28
|
+
COUNT: REDIS_BATCH_SIZE
|
|
29
|
+
});
|
|
30
|
+
const ids = reply.keys.map((key) => key.slice(ACCESS_RULE_REDIS_KEY_PREFIX.length));
|
|
31
|
+
await this.deleteRules(ids);
|
|
32
|
+
total += ids.length;
|
|
33
|
+
cursor = reply.cursor;
|
|
34
|
+
} while ("0" !== cursor);
|
|
35
|
+
return total;
|
|
36
|
+
}
|
|
37
|
+
async insertRuleEntries(ruleEntries) {
|
|
38
|
+
const queries = this.client.multi();
|
|
39
|
+
const ruleKeys = ruleEntries.map((ruleEntry) => {
|
|
40
|
+
const { rule, expiresUnixTimestamp } = ruleEntry;
|
|
41
|
+
const ruleKey = getAccessRuleRedisKey(rule);
|
|
42
|
+
const ruleValue = getRedisRuleValue(rule);
|
|
43
|
+
queries.hSet(ruleKey, ruleValue);
|
|
44
|
+
if (expiresUnixTimestamp) {
|
|
45
|
+
if (expiresUnixTimestamp > 1e10) throw new Error(`Invalid expiry timestamp: ${expiresUnixTimestamp}. Timestamp must be in seconds, not milliseconds.`);
|
|
46
|
+
queries.expireAt(ruleKey, expiresUnixTimestamp);
|
|
47
|
+
}
|
|
48
|
+
return ruleKey;
|
|
49
|
+
});
|
|
50
|
+
await queries.exec();
|
|
51
|
+
return ruleKeys;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var getRedisRuleValue = (rule) => {
|
|
55
|
+
const record = {};
|
|
56
|
+
for (const [key, value] of Object.entries(rule)) {
|
|
57
|
+
if (value === void 0) continue;
|
|
58
|
+
record[key] = String(value);
|
|
59
|
+
}
|
|
60
|
+
if (record.clientId === void 0) record.clientId = GLOBAL_CLIENT_SCOPE_SENTINEL;
|
|
61
|
+
return record;
|
|
84
62
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
async deleteAllRules() {
|
|
107
|
-
this.logger.info(() => ({
|
|
108
|
-
msg: "Dummy deleteAllRules() has no effect (redis is not ready)"
|
|
109
|
-
}));
|
|
110
|
-
return 0;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
export {
|
|
114
|
-
DummyRedisRulesWriter,
|
|
115
|
-
RedisRulesWriter,
|
|
116
|
-
getRedisRuleValue
|
|
63
|
+
var DummyRedisRulesWriter = class {
|
|
64
|
+
constructor(logger) {
|
|
65
|
+
this.logger = logger;
|
|
66
|
+
}
|
|
67
|
+
async insertRules(ruleEntries) {
|
|
68
|
+
this.logger.info(() => ({
|
|
69
|
+
msg: "Dummy insertRules() has no effect (redis is not ready)",
|
|
70
|
+
data: { ruleEntries }
|
|
71
|
+
}));
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
async deleteRules(ruleIds) {
|
|
75
|
+
this.logger.info(() => ({
|
|
76
|
+
msg: "Dummy deleteRules() has no effect (redis is not ready)",
|
|
77
|
+
data: { ruleIds }
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
async deleteAllRules() {
|
|
81
|
+
this.logger.info(() => ({ msg: "Dummy deleteAllRules() has no effect (redis is not ready)" }));
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
117
84
|
};
|
|
85
|
+
//#endregion
|
|
86
|
+
export { DummyRedisRulesWriter, RedisRulesWriter, getRedisRuleValue };
|
package/dist/rule.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
1
|
+
import "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/rule.ts
|
|
3
|
+
var AccessPolicyType = /* @__PURE__ */ function(AccessPolicyType) {
|
|
4
|
+
AccessPolicyType["Block"] = "block";
|
|
5
|
+
AccessPolicyType["Restrict"] = "restrict";
|
|
6
|
+
return AccessPolicyType;
|
|
7
|
+
}({});
|
|
8
|
+
var GLOBAL_CLIENT_SCOPE_SENTINEL = "global";
|
|
9
|
+
//#endregion
|
|
10
|
+
export { AccessPolicyType, GLOBAL_CLIENT_SCOPE_SENTINEL };
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { accessPolicyInput, policyScopeInput } from "./policyInput.js";
|
|
3
3
|
import { userScopeInput } from "./userScopeInput.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
accessRuleInput,
|
|
7
|
-
policyScopeInput,
|
|
8
|
-
userScopeInput
|
|
9
|
-
};
|
|
4
|
+
import { accessRuleInput } from "./ruleInput.js";
|
|
5
|
+
export { accessPolicyInput, accessRuleInput, policyScopeInput, userScopeInput };
|
|
@@ -1,65 +1,39 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { AccessPolicyType } from "../rule.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
unsolvedImagesCount: z.coerce.number().optional(),
|
|
16
|
-
// used to increase the user's score
|
|
17
|
-
frictionlessScore: z.coerce.number().optional(),
|
|
18
|
-
// Skip the request-time block middleware and only fire at verify.
|
|
19
|
-
// Redis stores booleans as strings — preprocess so "true"/"false"
|
|
20
|
-
// round-trip to the JS boolean the matcher expects.
|
|
21
|
-
deferToVerify: z.preprocess((v) => typeof v === "string" ? v === "true" : v, z.boolean()).optional()
|
|
4
|
+
import { CaptchaTypeSchema } from "@prosopo/types";
|
|
5
|
+
//#region src/ruleInput/policyInput.ts
|
|
6
|
+
var accessPolicyInputShape = z.object({
|
|
7
|
+
type: z.nativeEnum(AccessPolicyType),
|
|
8
|
+
captchaType: CaptchaTypeSchema.optional(),
|
|
9
|
+
description: z.coerce.string().optional(),
|
|
10
|
+
solvedImagesCount: z.coerce.number().optional(),
|
|
11
|
+
imageThreshold: z.coerce.number().optional(),
|
|
12
|
+
powDifficulty: z.coerce.number().optional(),
|
|
13
|
+
unsolvedImagesCount: z.coerce.number().optional(),
|
|
14
|
+
frictionlessScore: z.coerce.number().optional(),
|
|
15
|
+
deferToVerify: z.preprocess((v) => typeof v === "string" ? v === "true" : v, z.boolean()).optional()
|
|
22
16
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
code: z.ZodIssueCode.custom,
|
|
36
|
-
path: ["solvedImagesCount"],
|
|
37
|
-
message: "Block policies cannot set solvedImagesCount — the field only applies to Restrict policies that adjust image-captcha rounds."
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
);
|
|
42
|
-
const sanitizeAccessPolicy = (policy) => {
|
|
43
|
-
if (policy.type === AccessPolicyType.Block) {
|
|
44
|
-
const { captchaType, solvedImagesCount, ...blockPolicy } = policy;
|
|
45
|
-
return blockPolicy;
|
|
46
|
-
}
|
|
47
|
-
return policy;
|
|
48
|
-
};
|
|
49
|
-
const policyScopeInput = z.object({
|
|
50
|
-
// `getRedisRuleValue` stamps a sentinel string on global rules so the
|
|
51
|
-
// read-time query can probe `@clientId:{global}` cheaply. Undo the
|
|
52
|
-
// stamp here so consumers (rankCandidateRules, response payloads,
|
|
53
|
-
// tests) continue to see `undefined` for global rules — the mongoose
|
|
54
|
-
// side and the API input side never emit the sentinel.
|
|
55
|
-
clientId: z.preprocess(
|
|
56
|
-
(v) => v === GLOBAL_CLIENT_SCOPE_SENTINEL ? void 0 : v,
|
|
57
|
-
z.coerce.string().optional()
|
|
58
|
-
)
|
|
17
|
+
var accessPolicyInput = accessPolicyInputShape.superRefine((policy, ctx) => {
|
|
18
|
+
if (policy.type !== AccessPolicyType.Block) return;
|
|
19
|
+
if (policy.captchaType !== void 0) ctx.addIssue({
|
|
20
|
+
code: z.ZodIssueCode.custom,
|
|
21
|
+
path: ["captchaType"],
|
|
22
|
+
message: "Block policies cannot pin a captchaType — Block always applies to every captcha type. Use a Restrict policy if you want to narrow the effect to one captcha type."
|
|
23
|
+
});
|
|
24
|
+
if (policy.solvedImagesCount !== void 0) ctx.addIssue({
|
|
25
|
+
code: z.ZodIssueCode.custom,
|
|
26
|
+
path: ["solvedImagesCount"],
|
|
27
|
+
message: "Block policies cannot set solvedImagesCount — the field only applies to Restrict policies that adjust image-captcha rounds."
|
|
28
|
+
});
|
|
59
29
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
30
|
+
var sanitizeAccessPolicy = (policy) => {
|
|
31
|
+
if (policy.type === AccessPolicyType.Block) {
|
|
32
|
+
const { captchaType, solvedImagesCount, ...blockPolicy } = policy;
|
|
33
|
+
return blockPolicy;
|
|
34
|
+
}
|
|
35
|
+
return policy;
|
|
65
36
|
};
|
|
37
|
+
var policyScopeInput = z.object({ clientId: z.preprocess((v) => v === "global" ? void 0 : v, z.coerce.string().optional()) });
|
|
38
|
+
//#endregion
|
|
39
|
+
export { accessPolicyInput, accessPolicyInputShape, policyScopeInput, sanitizeAccessPolicy };
|
|
@@ -1,51 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { FilterScopeMatch } from "../rulesStorage.js";
|
|
3
|
-
import {
|
|
3
|
+
import { accessPolicyInputShape, policyScopeInput } from "./policyInput.js";
|
|
4
4
|
import { userScopeInput } from "./userScopeInput.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
//#region src/ruleInput/ruleInput.ts
|
|
7
|
+
var ruleGroupInput = z.object({
|
|
8
|
+
groupId: z.coerce.string().optional(),
|
|
9
|
+
ruleGroupId: z.coerce.string().optional()
|
|
10
|
+
}).transform((ruleGroupInput) => {
|
|
11
|
+
const { ruleGroupId, ...ruleGroup } = ruleGroupInput;
|
|
12
|
+
if ("string" === typeof ruleGroupId) ruleGroup.groupId = ruleGroupId;
|
|
13
|
+
return ruleGroup;
|
|
14
14
|
});
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
var accessRuleInput = z.object({
|
|
16
|
+
...accessPolicyInputShape.shape,
|
|
17
|
+
...policyScopeInput.shape
|
|
18
18
|
}).and(userScopeInput).and(ruleGroupInput).transform((ruleInput) => ruleInput);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
var ruleEntryInput = z.object({
|
|
20
|
+
rule: accessRuleInput,
|
|
21
|
+
expiresUnixTimestamp: z.coerce.number().optional()
|
|
22
22
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
var accessRulesFilterInput = z.object({
|
|
24
|
+
policyScope: policyScopeInput.optional(),
|
|
25
|
+
policyScopes: z.array(policyScopeInput).optional(),
|
|
26
|
+
policyScopeMatch: z.nativeEnum(FilterScopeMatch).default(FilterScopeMatch.Exact),
|
|
27
|
+
userScope: userScopeInput.optional(),
|
|
28
|
+
userScopeMatch: z.nativeEnum(FilterScopeMatch).default(FilterScopeMatch.Exact),
|
|
29
|
+
groupId: z.string().optional(),
|
|
30
|
+
blockOnly: z.boolean().optional()
|
|
31
31
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
policyScope: policyScope2
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
return [filterBase];
|
|
45
|
-
};
|
|
46
|
-
export {
|
|
47
|
-
accessRuleInput,
|
|
48
|
-
accessRulesFilterInput,
|
|
49
|
-
getAccessRuleFiltersFromInput,
|
|
50
|
-
ruleEntryInput
|
|
32
|
+
var getAccessRuleFiltersFromInput = (filterInput) => {
|
|
33
|
+
const { policyScopes, policyScope, ...filterBase } = filterInput;
|
|
34
|
+
const allPolicyScopes = policyScopes || [];
|
|
35
|
+
if (policyScope) allPolicyScopes.push(policyScope);
|
|
36
|
+
if (allPolicyScopes.length > 0) return allPolicyScopes.map((policyScope) => ({
|
|
37
|
+
...filterBase,
|
|
38
|
+
policyScope
|
|
39
|
+
}));
|
|
40
|
+
return [filterBase];
|
|
51
41
|
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { accessRuleInput, accessRulesFilterInput, getAccessRuleFiltersFromInput, ruleEntryInput };
|
|
@@ -1,63 +1,52 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { z } from "zod";
|
|
1
3
|
import crypto from "node:crypto";
|
|
2
4
|
import { getIPAddress } from "@prosopo/util";
|
|
3
5
|
import { Address4, Address6 } from "ip-address";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Plain lowercase OS tag (see `classifyOs`). Loose `string` like
|
|
16
|
-
// countryCode: a stale/unknown value just never matches a request rather
|
|
17
|
-
// than failing the whole rule parse.
|
|
18
|
-
os: z.coerce.string().optional()
|
|
6
|
+
//#region src/ruleInput/userScopeInput.ts
|
|
7
|
+
var userAttributesSchema = z.object({
|
|
8
|
+
userId: z.coerce.string().optional(),
|
|
9
|
+
ja4Hash: z.coerce.string().optional(),
|
|
10
|
+
headersHash: z.coerce.string().optional(),
|
|
11
|
+
userAgentHash: z.coerce.string().optional(),
|
|
12
|
+
headHash: z.coerce.string().optional(),
|
|
13
|
+
coords: z.coerce.string().optional(),
|
|
14
|
+
countryCode: z.coerce.string().optional(),
|
|
15
|
+
asn: z.coerce.number().int().nonnegative().optional(),
|
|
16
|
+
os: z.coerce.string().optional()
|
|
19
17
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}).transform((
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
return userScope;
|
|
18
|
+
var userAttributesInput = z.object({
|
|
19
|
+
...userAttributesSchema.shape,
|
|
20
|
+
userAgent: z.coerce.string().optional()
|
|
21
|
+
}).transform((userAttributesInput) => {
|
|
22
|
+
const { userAgent, ...userScope } = userAttributesInput;
|
|
23
|
+
if ("string" === typeof userAgent) userScope.userAgentHash = hashUserAgent(userAgent);
|
|
24
|
+
return userScope;
|
|
29
25
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
var hashUserAgent = (userAgent) => crypto.createHash("sha256").update(userAgent).digest("hex");
|
|
27
|
+
var userIpSchema = z.object({
|
|
28
|
+
numericIp: z.coerce.bigint().optional(),
|
|
29
|
+
numericIpMaskMin: z.coerce.bigint().optional(),
|
|
30
|
+
numericIpMaskMax: z.coerce.bigint().optional()
|
|
35
31
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}).transform((
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
return numericUserIp;
|
|
32
|
+
var userIpInput = z.object({
|
|
33
|
+
...userIpSchema.shape,
|
|
34
|
+
ip: z.string().optional(),
|
|
35
|
+
ipMask: z.string().optional()
|
|
36
|
+
}).transform((userIpInput) => {
|
|
37
|
+
const { ip, ipMask, ...numericUserIp } = userIpInput;
|
|
38
|
+
if ("string" === typeof ip) numericUserIp.numericIp = getIPAddress(ip).bigInt();
|
|
39
|
+
if ("string" === typeof ipMask) {
|
|
40
|
+
const ipObject = Address4.isValid(ipMask) ? new Address4(ipMask) : new Address6(ipMask);
|
|
41
|
+
numericUserIp.numericIpMaskMin = ipObject.startAddress().bigInt();
|
|
42
|
+
numericUserIp.numericIpMaskMax = ipObject.endAddress().bigInt();
|
|
43
|
+
}
|
|
44
|
+
return numericUserIp;
|
|
51
45
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
var userScopeSchema = z.object({
|
|
47
|
+
...userIpSchema.shape,
|
|
48
|
+
...userAttributesSchema.shape
|
|
55
49
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
);
|
|
60
|
-
export {
|
|
61
|
-
userScopeInput,
|
|
62
|
-
userScopeSchema
|
|
63
|
-
};
|
|
50
|
+
var userScopeInput = z.object({}).and(userIpInput).and(userAttributesInput).transform((userScopeInput) => userScopeInput);
|
|
51
|
+
//#endregion
|
|
52
|
+
export { userScopeInput, userScopeSchema };
|
package/dist/ruleRecord.js
CHANGED
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/ruleRecord.ts
|
|
3
|
+
var userAttributesRecordFields = [
|
|
4
|
+
"userId",
|
|
5
|
+
"ja4Hash",
|
|
6
|
+
"headersHash",
|
|
7
|
+
"userAgent",
|
|
8
|
+
"headHash",
|
|
9
|
+
"coords",
|
|
10
|
+
"countryCode",
|
|
11
|
+
"asn",
|
|
12
|
+
"os"
|
|
11
13
|
];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
...userAttributesRecordFields,
|
|
18
|
-
...userIpRecordFields
|
|
19
|
-
];
|
|
20
|
-
const getUserScopeRecordFromAccessRuleRecord = (ruleRecord) => Object.fromEntries(
|
|
21
|
-
userScopeRecordFields.map((field) => [field, ruleRecord[field]]).filter(([, value]) => value !== void 0)
|
|
22
|
-
);
|
|
23
|
-
export {
|
|
24
|
-
getUserScopeRecordFromAccessRuleRecord,
|
|
25
|
-
userAttributesRecordFields,
|
|
26
|
-
userIpRecordFields,
|
|
27
|
-
userScopeRecordFields
|
|
28
|
-
};
|
|
14
|
+
var userIpRecordFields = ["ip", "ipMask"];
|
|
15
|
+
var userScopeRecordFields = [...userAttributesRecordFields, ...userIpRecordFields];
|
|
16
|
+
var getUserScopeRecordFromAccessRuleRecord = (ruleRecord) => Object.fromEntries(userScopeRecordFields.map((field) => [field, ruleRecord[field]]).filter(([, value]) => value !== void 0));
|
|
17
|
+
//#endregion
|
|
18
|
+
export { getUserScopeRecordFromAccessRuleRecord, userAttributesRecordFields, userIpRecordFields, userScopeRecordFields };
|
package/dist/rulesStorage.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/rulesStorage.ts
|
|
3
|
+
var FilterScopeMatch = /* @__PURE__ */ function(FilterScopeMatch) {
|
|
4
|
+
FilterScopeMatch["Exact"] = "exact";
|
|
5
|
+
FilterScopeMatch["Greedy"] = "greedy";
|
|
6
|
+
return FilterScopeMatch;
|
|
7
|
+
}({});
|
|
8
|
+
//#endregion
|
|
9
|
+
export { FilterScopeMatch };
|
|
@@ -168,9 +168,7 @@ describe("redisAccessRulesStorage", () => {
|
|
|
168
168
|
const afterDeleteIndexRecordsCount = await getIndexRecordsCount(indexName);
|
|
169
169
|
expect(afterDeleteIndexRecordsCount).toBe(0);
|
|
170
170
|
});
|
|
171
|
-
},
|
|
172
|
-
timeout: 240_000,
|
|
173
|
-
});
|
|
171
|
+
}, 240_000);
|
|
174
172
|
describe("reader", () => {
|
|
175
173
|
let accessRulesReader;
|
|
176
174
|
beforeAll(async () => {
|