@prosopo/user-access-policy 2.5.3 → 2.6.1

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 (33) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/index.cjs +28 -0
  3. package/dist/cjs/rules/api/apiRulePaths.cjs +7 -0
  4. package/dist/cjs/rules/api/apiRuleRoutesProvider.cjs +23 -0
  5. package/dist/cjs/rules/api/deleteMany/apiDeleteManyRulesArgsSchema.cjs +13 -0
  6. package/dist/cjs/rules/api/deleteMany/apiDeleteManyRulesEndpoint.cjs +19 -0
  7. package/dist/cjs/rules/api/getExpressApiRuleRateLimits.cjs +18 -0
  8. package/dist/cjs/rules/api/insertMany/apiInsertManyRulesArgsSchema.cjs +39 -0
  9. package/dist/cjs/rules/api/insertMany/apiInsertManyRulesEndpoint.cjs +193 -0
  10. package/dist/cjs/rules/blacklistRulesInspector.cjs +44 -0
  11. package/dist/cjs/rules/imageCaptchaConfigRulesResolver.cjs +115 -0
  12. package/dist/cjs/rules/mongoose/indexes/rulePerformanceMongooseIndexes.cjs +75 -0
  13. package/dist/cjs/rules/mongoose/indexes/ruleUniqueMongooseIndexes.cjs +137 -0
  14. package/dist/cjs/rules/mongoose/rulesMongooseStorage.cjs +177 -0
  15. package/dist/cjs/rules/mongoose/schemas/config/configMongooseSchema.cjs +14 -0
  16. package/dist/cjs/rules/mongoose/schemas/config/imageCaptchaConfigMongooseSchema.cjs +17 -0
  17. package/dist/cjs/rules/mongoose/schemas/getRuleMongooseSchema.cjs +19 -0
  18. package/dist/cjs/rules/mongoose/schemas/ip/ipMongooseSchema.cjs +29 -0
  19. package/dist/cjs/rules/mongoose/schemas/ip/v4/ipV4MaskMongooseSchema.cjs +14 -0
  20. package/dist/cjs/rules/mongoose/schemas/ip/v4/ipV4MongooseSchema.cjs +18 -0
  21. package/dist/cjs/rules/mongoose/schemas/ip/v6/ipV6MaskMongooseSchema.cjs +34 -0
  22. package/dist/cjs/rules/mongoose/schemas/ip/v6/ipV6MongooseSchema.cjs +33 -0
  23. package/dist/cjs/rules/mongoose/schemas/ruleMongooseSchema.cjs +43 -0
  24. package/dist/cjs/rules/rule/config/imageCaptcha/imageCaptchaConfigSchema.cjs +8 -0
  25. package/dist/cjs/rules/rule/config/ruleConfigSchema.cjs +8 -0
  26. package/dist/cjs/rules/rule/ip/ruleIpSchema.cjs +10 -0
  27. package/dist/cjs/rules/rule/ip/ruleIpVersion.cjs +8 -0
  28. package/dist/cjs/rules/rule/ip/v4/mask/ruleIpV4MaskSchema.cjs +9 -0
  29. package/dist/cjs/rules/rule/ip/v4/ruleIpV4Schema.cjs +10 -0
  30. package/dist/cjs/rules/rule/ip/v6/mask/ruleIpV6MaskSchema.cjs +9 -0
  31. package/dist/cjs/rules/rule/ip/v6/ruleIpV6NumericMaxLength.cjs +4 -0
  32. package/dist/cjs/rules/rule/ip/v6/ruleIpV6Schema.cjs +10 -0
  33. package/package.json +12 -7
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # @prosopo/user-access-policy
2
+
3
+ ## 2.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [52feffc]
8
+ - @prosopo/types@2.6.1
9
+
10
+ ## 2.6.0
11
+
12
+ ### Minor Changes
13
+
14
+ - a0bfc8a: bump all pkg versions since independent versioning applied
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [a0bfc8a]
19
+ - @prosopo/api-route@2.6.0
20
+ - @prosopo/common@2.6.0
21
+ - @prosopo/types@2.6.0
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRulePaths = require("./rules/api/apiRulePaths.cjs");
4
+ const apiRuleRoutesProvider = require("./rules/api/apiRuleRoutesProvider.cjs");
5
+ const getExpressApiRuleRateLimits = require("./rules/api/getExpressApiRuleRateLimits.cjs");
6
+ const blacklistRulesInspector = require("./rules/blacklistRulesInspector.cjs");
7
+ const imageCaptchaConfigRulesResolver = require("./rules/imageCaptchaConfigRulesResolver.cjs");
8
+ const rulesMongooseStorage = require("./rules/mongoose/rulesMongooseStorage.cjs");
9
+ const getRuleMongooseSchema = require("./rules/mongoose/schemas/getRuleMongooseSchema.cjs");
10
+ const createBlacklistInspector = (rulesStorage, logger) => {
11
+ return new blacklistRulesInspector.BlacklistRulesInspector(rulesStorage, logger);
12
+ };
13
+ const createImageCaptchaConfigResolver = (rulesStorage, logger) => {
14
+ return new imageCaptchaConfigRulesResolver.ImageCaptchaConfigRulesResolver(rulesStorage, logger);
15
+ };
16
+ const createApiRuleRoutesProvider = (rulesStorage) => {
17
+ return new apiRuleRoutesProvider.ApiRuleRoutesProvider(rulesStorage);
18
+ };
19
+ const createMongooseRulesStorage = (logger, readingModel, writingModel = null) => {
20
+ return new rulesMongooseStorage.RulesMongooseStorage(logger, readingModel, writingModel);
21
+ };
22
+ exports.apiRulePaths = apiRulePaths.apiRulePaths;
23
+ exports.getExpressApiRuleRateLimits = getExpressApiRuleRateLimits.getExpressApiRuleRateLimits;
24
+ exports.getRuleMongooseSchema = getRuleMongooseSchema.getRuleMongooseSchema;
25
+ exports.createApiRuleRoutesProvider = createApiRuleRoutesProvider;
26
+ exports.createBlacklistInspector = createBlacklistInspector;
27
+ exports.createImageCaptchaConfigResolver = createImageCaptchaConfigResolver;
28
+ exports.createMongooseRulesStorage = createMongooseRulesStorage;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRulePaths = {
4
+ INSERT_MANY: "/v1/prosopo/user-access-policy/rules/insert-many",
5
+ DELETE_MANY: "/v1/prosopo/user-access-policy/rules/delete-many"
6
+ };
7
+ exports.apiRulePaths = apiRulePaths;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRulePaths = require("./apiRulePaths.cjs");
4
+ const apiDeleteManyRulesEndpoint = require("./deleteMany/apiDeleteManyRulesEndpoint.cjs");
5
+ const apiInsertManyRulesEndpoint = require("./insertMany/apiInsertManyRulesEndpoint.cjs");
6
+ class ApiRuleRoutesProvider {
7
+ constructor(rulesStorage) {
8
+ this.rulesStorage = rulesStorage;
9
+ }
10
+ getRoutes() {
11
+ return [
12
+ {
13
+ path: apiRulePaths.apiRulePaths.INSERT_MANY,
14
+ endpoint: new apiInsertManyRulesEndpoint.ApiInsertManyRulesEndpoint(this.rulesStorage)
15
+ },
16
+ {
17
+ path: apiRulePaths.apiRulePaths.DELETE_MANY,
18
+ endpoint: new apiDeleteManyRulesEndpoint.ApiDeleteManyRulesEndpoint(this.rulesStorage)
19
+ }
20
+ ];
21
+ }
22
+ }
23
+ exports.ApiRuleRoutesProvider = ApiRuleRoutesProvider;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpSchema = require("../../rule/ip/ruleIpSchema.cjs");
5
+ const apiDeleteManyRulesArgsSchema = zod.array(
6
+ zod.object({
7
+ clientId: zod.string().optional(),
8
+ userIp: ruleIpSchema.ruleIpSchema.optional(),
9
+ userId: zod.string().optional(),
10
+ ja4: zod.string().optional()
11
+ })
12
+ );
13
+ exports.apiDeleteManyRulesArgsSchema = apiDeleteManyRulesArgsSchema;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRoute = require("@prosopo/api-route");
4
+ const apiDeleteManyRulesArgsSchema = require("./apiDeleteManyRulesArgsSchema.cjs");
5
+ class ApiDeleteManyRulesEndpoint {
6
+ constructor(rulesStorage) {
7
+ this.rulesStorage = rulesStorage;
8
+ }
9
+ async processRequest(args) {
10
+ await this.rulesStorage.deleteMany(args);
11
+ return {
12
+ status: apiRoute.ApiEndpointResponseStatus.SUCCESS
13
+ };
14
+ }
15
+ getRequestArgsSchema() {
16
+ return apiDeleteManyRulesArgsSchema.apiDeleteManyRulesArgsSchema;
17
+ }
18
+ }
19
+ exports.ApiDeleteManyRulesEndpoint = ApiDeleteManyRulesEndpoint;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRulePaths = require("./apiRulePaths.cjs");
4
+ const getExpressApiRuleRateLimits = () => {
5
+ const defaultWindowsMs = 6e4;
6
+ const defaultLimit = 5;
7
+ return {
8
+ [apiRulePaths.apiRulePaths.INSERT_MANY]: {
9
+ windowMs: process.env.PROSOPO_USER_ACCESS_POLICY_RULE_INSERT_MANY_WINDOW || defaultWindowsMs,
10
+ limit: process.env.PROSOPO_USER_ACCESS_POLICY_RULE_INSERT_MANY_LIMIT || defaultLimit
11
+ },
12
+ [apiRulePaths.apiRulePaths.DELETE_MANY]: {
13
+ windowMs: process.env.PROSOPO_USER_ACCESS_POLICY_RULE_DELETE_MANY_WINDOW || defaultWindowsMs,
14
+ limit: process.env.PROSOPO_USER_ACCESS_POLICY_RULE_DELETE_MANY_LIMIT || defaultLimit
15
+ }
16
+ };
17
+ };
18
+ exports.getExpressApiRuleRateLimits = getExpressApiRuleRateLimits;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleConfigSchema = require("../../rule/config/ruleConfigSchema.cjs");
5
+ const apiInsertManyRulesArgsSchema = zod.object({
6
+ isUserBlocked: zod.boolean(),
7
+ clientId: zod.string().optional(),
8
+ description: zod.string().optional(),
9
+ userIps: zod.object({
10
+ v4: zod.string().array().optional(),
11
+ v6: zod.string().array().optional()
12
+ }),
13
+ // block multiple user ip ranges
14
+ userIpMasks: zod.object({
15
+ v4: zod.object({
16
+ min: zod.string(),
17
+ max: zod.string()
18
+ }).array().optional(),
19
+ v6: zod.object({
20
+ min: zod.string(),
21
+ max: zod.string()
22
+ }).array().optional()
23
+ }),
24
+ // block multiple user ip ranges
25
+ userIds: zod.string().array().optional(),
26
+ // block multiple user ids
27
+ ja4s: zod.string().array().optional(),
28
+ // block multiple ja4s
29
+ // setting individual rule values overrides any array values for the same type
30
+ userIp: zod.object({
31
+ v4: zod.string().optional(),
32
+ v6: zod.string().optional()
33
+ }).optional(),
34
+ userId: zod.string().optional(),
35
+ ja4: zod.string().optional(),
36
+ config: ruleConfigSchema.ruleConfigSchema.optional(),
37
+ score: zod.number().optional()
38
+ });
39
+ exports.apiInsertManyRulesArgsSchema = apiInsertManyRulesArgsSchema;
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const apiRoute = require("@prosopo/api-route");
4
+ const ipAddress = require("ip-address");
5
+ const ruleIpSchema = require("../../rule/ip/ruleIpSchema.cjs");
6
+ const apiInsertManyRulesArgsSchema = require("./apiInsertManyRulesArgsSchema.cjs");
7
+ class ApiInsertManyRulesEndpoint {
8
+ constructor(rulesStorage) {
9
+ this.rulesStorage = rulesStorage;
10
+ }
11
+ async processRequest(args) {
12
+ const singleRule = this.getSingleRule(args);
13
+ const rules = [
14
+ ...this.getUserIpRules(args),
15
+ ...this.getUserIPMaskRules(args),
16
+ ...this.getUserIdRules(args),
17
+ ...this.getJa4Rules(args),
18
+ ...singleRule ? [singleRule] : []
19
+ ];
20
+ return new Promise((resolve) => {
21
+ this.rulesStorage.insertMany(rules).then(() => {
22
+ resolve({
23
+ status: apiRoute.ApiEndpointResponseStatus.SUCCESS
24
+ });
25
+ }).catch((e) => {
26
+ resolve({
27
+ status: apiRoute.ApiEndpointResponseStatus.FAIL
28
+ });
29
+ });
30
+ setTimeout(() => {
31
+ resolve({
32
+ status: apiRoute.ApiEndpointResponseStatus.PROCESSING
33
+ });
34
+ }, 5e3);
35
+ });
36
+ }
37
+ getRequestArgsSchema() {
38
+ return apiInsertManyRulesArgsSchema.apiInsertManyRulesArgsSchema;
39
+ }
40
+ getUserIpRules(args) {
41
+ const rules = [];
42
+ const userIps = args.userIps || [];
43
+ for (const userIp of userIps.v4 || []) {
44
+ const ipAddress$1 = new ipAddress.Address4(userIp);
45
+ rules.push({
46
+ userIp: ruleIpSchema.ruleIpSchema.parse({
47
+ v4: {
48
+ asNumeric: ipAddress$1.bigInt(),
49
+ asString: ipAddress$1.address
50
+ }
51
+ }),
52
+ isUserBlocked: args.isUserBlocked,
53
+ description: args.description,
54
+ clientId: args.clientId,
55
+ config: args.config,
56
+ score: args.score
57
+ });
58
+ }
59
+ for (const userIp of userIps.v6 || []) {
60
+ const ipAddress$1 = new ipAddress.Address6(userIp);
61
+ rules.push({
62
+ userIp: ruleIpSchema.ruleIpSchema.parse({
63
+ v4: {
64
+ asNumeric: ipAddress$1.bigInt(),
65
+ asString: ipAddress$1.address
66
+ }
67
+ }),
68
+ isUserBlocked: args.isUserBlocked,
69
+ description: args.description,
70
+ clientId: args.clientId,
71
+ config: args.config,
72
+ score: args.score
73
+ });
74
+ }
75
+ return rules;
76
+ }
77
+ getUserIPMaskRules(args) {
78
+ const rules = [];
79
+ const userIpMasks = args.userIpMasks || [];
80
+ for (const userMask of userIpMasks.v4 || []) {
81
+ const min = new ipAddress.Address4(userMask.min);
82
+ const max = new ipAddress.Address4(userMask.max);
83
+ rules.push({
84
+ userIp: ruleIpSchema.ruleIpSchema.parse({
85
+ v4: {
86
+ asNumeric: min.bigInt(),
87
+ asString: min.address,
88
+ mask: {
89
+ rangeMinAsNumeric: min.bigInt(),
90
+ rangeMaxAsNumeric: max.bigInt(),
91
+ asNumeric: Number(min.bigInt())
92
+ }
93
+ }
94
+ }),
95
+ isUserBlocked: args.isUserBlocked,
96
+ description: args.description,
97
+ clientId: args.clientId,
98
+ config: args.config,
99
+ score: args.score
100
+ });
101
+ }
102
+ for (const userMask of userIpMasks.v6 || []) {
103
+ const min = new ipAddress.Address6(userMask.min);
104
+ const max = new ipAddress.Address6(userMask.max);
105
+ rules.push({
106
+ userIp: ruleIpSchema.ruleIpSchema.parse({
107
+ v6: {
108
+ asNumeric: min.bigInt(),
109
+ asString: min.address,
110
+ mask: {
111
+ rangeMinAsNumeric: min.bigInt(),
112
+ rangeMaxAsNumeric: max.bigInt(),
113
+ asNumeric: Number(min.bigInt())
114
+ }
115
+ }
116
+ }),
117
+ isUserBlocked: args.isUserBlocked,
118
+ description: args.description,
119
+ clientId: args.clientId,
120
+ config: args.config,
121
+ score: args.score
122
+ });
123
+ }
124
+ return rules;
125
+ }
126
+ getUserIdRules(args) {
127
+ const rules = [];
128
+ const userIds = args.userIds || [];
129
+ for (const userId of userIds) {
130
+ rules.push({
131
+ userId,
132
+ isUserBlocked: args.isUserBlocked,
133
+ description: args.description,
134
+ clientId: args.clientId,
135
+ config: args.config,
136
+ score: args.score
137
+ });
138
+ }
139
+ return rules;
140
+ }
141
+ getJa4Rules(args) {
142
+ const rules = [];
143
+ const ja4s = args.ja4s || [];
144
+ for (const ja4 of ja4s) {
145
+ rules.push({
146
+ ja4,
147
+ isUserBlocked: args.isUserBlocked,
148
+ description: args.description,
149
+ clientId: args.clientId,
150
+ config: args.config,
151
+ score: args.score
152
+ });
153
+ }
154
+ return rules;
155
+ }
156
+ getSingleRule(args) {
157
+ if (!args.userIp && !args.userId && !args.ja4) {
158
+ return void 0;
159
+ }
160
+ const rule = {
161
+ isUserBlocked: args.isUserBlocked,
162
+ ...args.description && { description: args.description },
163
+ ...args.clientId && { clientId: args.clientId },
164
+ ...args.config && { config: args.config },
165
+ ...args.score && { score: args.score },
166
+ ...args.ja4 && { ja4: args.ja4 },
167
+ ...args.userId && { userId: args.userId }
168
+ };
169
+ if (args.userIp) {
170
+ const userIp = args.userIp;
171
+ if (userIp.v4) {
172
+ const ipAddress$1 = new ipAddress.Address4(userIp.v4);
173
+ rule.userIp = ruleIpSchema.ruleIpSchema.parse({
174
+ v4: {
175
+ asNumeric: ipAddress$1.bigInt(),
176
+ asString: ipAddress$1.address
177
+ }
178
+ });
179
+ }
180
+ if (userIp.v6) {
181
+ const ipAddress$1 = new ipAddress.Address6(userIp.v6);
182
+ rule.userIp = ruleIpSchema.ruleIpSchema.parse({
183
+ v6: {
184
+ asNumeric: ipAddress$1.bigInt(),
185
+ asString: ipAddress$1.address
186
+ }
187
+ });
188
+ }
189
+ }
190
+ return rule;
191
+ }
192
+ }
193
+ exports.ApiInsertManyRulesEndpoint = ApiInsertManyRulesEndpoint;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class BlacklistRulesInspector {
4
+ constructor(rulesStorage, logger) {
5
+ this.rulesStorage = rulesStorage;
6
+ this.logger = logger;
7
+ }
8
+ async isUserBlacklisted(clientId, userIpAddress, ja4, userId) {
9
+ this.logger.debug({
10
+ clientId,
11
+ userIpAddress,
12
+ ja4,
13
+ userId
14
+ });
15
+ const accessRules = await this.rulesStorage.find(
16
+ {
17
+ clientId,
18
+ userIpAddress,
19
+ ja4,
20
+ userId
21
+ },
22
+ {
23
+ includeRecordsWithPartialFilterMatches: true,
24
+ includeRecordsWithoutClientId: true
25
+ }
26
+ );
27
+ const blockingRules = accessRules.filter(
28
+ (accessRule) => accessRule.isUserBlocked
29
+ );
30
+ const userBlacklisted = blockingRules.length > 0;
31
+ if (userBlacklisted) {
32
+ this.logger.info({
33
+ userBlacklisted,
34
+ clientId,
35
+ userIpAddress: userIpAddress.address.toString(),
36
+ userId,
37
+ accessRules: accessRules.length,
38
+ blockingRules: blockingRules.length
39
+ });
40
+ }
41
+ return userBlacklisted;
42
+ }
43
+ }
44
+ exports.BlacklistRulesInspector = BlacklistRulesInspector;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class ImageCaptchaConfigRulesResolver {
4
+ constructor(rulesStorage, logger, _accessRule = null) {
5
+ this.rulesStorage = rulesStorage;
6
+ this.logger = logger;
7
+ this._accessRule = _accessRule;
8
+ }
9
+ get accessRule() {
10
+ return this._accessRule;
11
+ }
12
+ async isConfigDefined(clientId, userIpAddress, ja4, userId) {
13
+ const accessRule = await this.fetchUserAccessRule(
14
+ userIpAddress,
15
+ ja4,
16
+ userId,
17
+ clientId
18
+ );
19
+ const imageCaptchaConfig = accessRule?.config?.imageCaptcha || null;
20
+ const configDefined = null !== imageCaptchaConfig;
21
+ if (configDefined) {
22
+ this.logger.info({
23
+ configDefined,
24
+ clientId,
25
+ userIpAddress: userIpAddress.toString(),
26
+ userId,
27
+ imageCaptchaConfig,
28
+ ja4
29
+ });
30
+ }
31
+ return configDefined;
32
+ }
33
+ async resolveConfig(defaults, userIpAddress, ja4, userId, clientId) {
34
+ const logArgs = {
35
+ userIpAddress: userIpAddress.address.toString(),
36
+ userId,
37
+ clientId,
38
+ defaults,
39
+ ja4
40
+ };
41
+ this._accessRule = await this.fetchUserAccessRule(
42
+ userIpAddress,
43
+ ja4,
44
+ userId,
45
+ clientId
46
+ );
47
+ if (null === this.accessRule) {
48
+ this.logger.debug("ImageCaptchaConfigRulesResolver.resolveConfig", {
49
+ configDefined: false,
50
+ ...logArgs
51
+ });
52
+ return defaults;
53
+ }
54
+ const imageCaptchaConfig = this.accessRule.config?.imageCaptcha || {};
55
+ const config = this.getImageCaptchaConfig(defaults, imageCaptchaConfig);
56
+ this.logger.info("ImageCaptchaConfigRulesResolver.resolveConfig", {
57
+ configDefined: true,
58
+ imageCaptchaConfig,
59
+ config,
60
+ ...logArgs
61
+ });
62
+ return config;
63
+ }
64
+ async fetchUserAccessRule(userIpAddress, ja4, userId, clientId) {
65
+ const accessRules = await this.queryUserAccessRules(
66
+ userIpAddress,
67
+ ja4,
68
+ userId,
69
+ clientId
70
+ );
71
+ this.logger.debug("ImageCaptchaConfigRulesResolver.fetchUserAccessRule", {
72
+ accessRules: accessRules.length,
73
+ userIpAddress: userIpAddress.address.toString(),
74
+ userId,
75
+ clientId,
76
+ ja4
77
+ });
78
+ return this.selectPrimaryUserAccessRule(accessRules);
79
+ }
80
+ async queryUserAccessRules(ipAddress, ja4, user, clientId) {
81
+ return await this.rulesStorage.find(
82
+ {
83
+ clientId,
84
+ userId: user,
85
+ userIpAddress: ipAddress,
86
+ ja4
87
+ },
88
+ {
89
+ includeRecordsWithoutClientId: true,
90
+ includeRecordsWithPartialFilterMatches: true
91
+ }
92
+ );
93
+ }
94
+ selectPrimaryUserAccessRule(accessRules) {
95
+ const clientRules = accessRules.filter(
96
+ (accessRule2) => "string" === typeof accessRule2.clientId
97
+ );
98
+ const globalRules = accessRules.filter(
99
+ (accessRule2) => void 0 === accessRule2.clientId
100
+ );
101
+ const accessRule = clientRules.length > 0 ? clientRules.shift() : globalRules.shift();
102
+ return void 0 === accessRule ? null : accessRule;
103
+ }
104
+ getImageCaptchaConfig(defaults, imageCaptchaConfig) {
105
+ return {
106
+ solved: {
107
+ count: imageCaptchaConfig.solvedCount || defaults.solved.count
108
+ },
109
+ unsolved: {
110
+ count: imageCaptchaConfig.unsolvedCount || defaults.unsolved.count
111
+ }
112
+ };
113
+ }
114
+ }
115
+ exports.ImageCaptchaConfigRulesResolver = ImageCaptchaConfigRulesResolver;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const userIpIndexes = [
4
+ {
5
+ definition: {
6
+ "userIp.v4.asNumeric": 1
7
+ },
8
+ options: {
9
+ partialFilterExpression: {
10
+ "userIp.v4.asNumeric": { $exists: true }
11
+ }
12
+ }
13
+ },
14
+ {
15
+ definition: {
16
+ "userIp.v6.asNumericString": 1
17
+ },
18
+ options: {
19
+ partialFilterExpression: {
20
+ "userIp.v6.asNumericString": { $exists: true }
21
+ }
22
+ }
23
+ }
24
+ ];
25
+ const userIpMaskIndexes = [
26
+ {
27
+ definition: {
28
+ "userIp.v4.mask.rangeMinAsNumeric": 1,
29
+ "userIp.v4.mask.rangeMaxAsNumeric": 1,
30
+ "userIp.v4.asNumeric": 1
31
+ },
32
+ options: {
33
+ partialFilterExpression: {
34
+ "userIp.v4.mask.asNumeric": { $exists: true }
35
+ }
36
+ }
37
+ },
38
+ {
39
+ definition: {
40
+ "userIp.v6.mask.rangeMinAsNumericString": 1,
41
+ "userIp.v6.mask.rangeMaxAsNumericString": 1
42
+ },
43
+ options: {
44
+ partialFilterExpression: {
45
+ "userIp.v6.mask.asNumeric": { $exists: true }
46
+ }
47
+ }
48
+ }
49
+ ];
50
+ const otherIndexes = [
51
+ {
52
+ definition: {
53
+ userId: 1
54
+ },
55
+ options: {
56
+ unique: true,
57
+ sparse: true
58
+ }
59
+ },
60
+ {
61
+ definition: {
62
+ ja4: 1
63
+ },
64
+ options: {
65
+ unique: true,
66
+ sparse: true
67
+ }
68
+ }
69
+ ];
70
+ const rulePerformanceMongooseIndexes = [
71
+ ...userIpIndexes,
72
+ ...userIpMaskIndexes,
73
+ ...otherIndexes
74
+ ];
75
+ exports.rulePerformanceMongooseIndexes = rulePerformanceMongooseIndexes;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const globalIpIndexes = [
4
+ {
5
+ definition: {
6
+ "userIp.v4.asNumeric": 1
7
+ },
8
+ options: {
9
+ name: "globalIpV4",
10
+ unique: true,
11
+ partialFilterExpression: {
12
+ clientId: null,
13
+ "userIp.v4.asNumeric": { $exists: true },
14
+ "userIp.v4.mask.asNumeric": null
15
+ }
16
+ }
17
+ },
18
+ {
19
+ definition: {
20
+ "userIp.v6.asNumericString": 1
21
+ },
22
+ options: {
23
+ name: "globalIpV6",
24
+ unique: true,
25
+ partialFilterExpression: {
26
+ clientId: null,
27
+ "userIp.v6.asNumericString": { $exists: true },
28
+ "userIp.v6.mask.asNumeric": null
29
+ }
30
+ }
31
+ }
32
+ ];
33
+ const globalIpMaskIndexes = [
34
+ {
35
+ definition: {
36
+ "userIp.v4.asNumeric": 1,
37
+ "userIp.v4.mask.asNumeric": 1
38
+ },
39
+ options: {
40
+ name: "globalIpMaskV4",
41
+ unique: true,
42
+ partialFilterExpression: {
43
+ clientId: null,
44
+ "userIp.v4.asNumeric": { $exists: true },
45
+ "userIp.v4.mask.asNumeric": { $exists: true }
46
+ }
47
+ }
48
+ },
49
+ {
50
+ definition: {
51
+ "userIp.v6.asNumericString": 1,
52
+ "userIp.v6.mask.asNumeric": 1
53
+ },
54
+ options: {
55
+ name: "globalIpMaskV6",
56
+ unique: true,
57
+ partialFilterExpression: {
58
+ clientId: null,
59
+ "userIp.v6.asNumericString": { $exists: true },
60
+ "userIp.v6.mask.asNumeric": { $exists: true }
61
+ }
62
+ }
63
+ }
64
+ ];
65
+ const ipPerClientIndexes = [
66
+ {
67
+ definition: {
68
+ clientId: 1,
69
+ "userIp.v4.asNumeric": 1
70
+ },
71
+ options: {
72
+ name: "clientIpV4",
73
+ unique: true,
74
+ partialFilterExpression: {
75
+ clientId: { $exists: true },
76
+ "userIp.v4.asNumeric": { $exists: true },
77
+ "userIp.v4.mask.asNumeric": null
78
+ }
79
+ }
80
+ },
81
+ {
82
+ definition: {
83
+ clientId: 1,
84
+ "userIp.v6.asNumericString": 1
85
+ },
86
+ options: {
87
+ name: "clientIpV6",
88
+ unique: true,
89
+ partialFilterExpression: {
90
+ clientId: { $exists: true },
91
+ "userIp.v6.asNumericString": { $exists: true },
92
+ "userIp.v6.mask.asNumeric": null
93
+ }
94
+ }
95
+ }
96
+ ];
97
+ const ipMaskPerClientIndexes = [
98
+ {
99
+ definition: {
100
+ clientId: 1,
101
+ "userIp.v4.asNumeric": 1,
102
+ "userIp.v4.mask.asNumeric": 1
103
+ },
104
+ options: {
105
+ name: "clientIpV4Mask",
106
+ unique: true,
107
+ partialFilterExpression: {
108
+ clientId: { $exists: true },
109
+ "userIp.v4.asNumeric": { $exists: true },
110
+ "userIp.v4.mask.asNumeric": { $exists: true }
111
+ }
112
+ }
113
+ },
114
+ {
115
+ definition: {
116
+ clientId: 1,
117
+ "userIp.v6.asNumericString": 1,
118
+ "userIp.v6.mask.asNumeric": 1
119
+ },
120
+ options: {
121
+ name: "clientIpV6Mask",
122
+ unique: true,
123
+ partialFilterExpression: {
124
+ clientId: { $exists: true },
125
+ "userIp.v6.asNumericString": { $exists: true },
126
+ "userIp.v6.mask.asNumeric": { $exists: true }
127
+ }
128
+ }
129
+ }
130
+ ];
131
+ const ruleUniqueMongooseIndexes = [
132
+ ...globalIpIndexes,
133
+ ...globalIpMaskIndexes,
134
+ ...ipMaskPerClientIndexes,
135
+ ...ipPerClientIndexes
136
+ ];
137
+ exports.ruleUniqueMongooseIndexes = ruleUniqueMongooseIndexes;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const common = require("@prosopo/common");
4
+ const ipAddress = require("ip-address");
5
+ const ruleIpVersion = require("../rule/ip/ruleIpVersion.cjs");
6
+ const ruleIpV6NumericMaxLength = require("../rule/ip/v6/ruleIpV6NumericMaxLength.cjs");
7
+ class RulesMongooseStorage {
8
+ constructor(logger, readingModel, writingModel = null) {
9
+ this.logger = logger;
10
+ this.readingModel = readingModel;
11
+ this.writingModel = writingModel;
12
+ if (null === this.writingModel) {
13
+ this.writingModel = this.readingModel;
14
+ }
15
+ }
16
+ async insert(record) {
17
+ if (!this.writingModel) {
18
+ throw this.modelNotSetProsopoError();
19
+ }
20
+ const filter = {
21
+ ...record.clientId && { clientId: record.clientId },
22
+ ...record.userIp && { userIp: record.userIp },
23
+ ...record.userId && { userId: record.userId },
24
+ ...record.ja4 && { ja4: record.ja4 }
25
+ };
26
+ const validationError = new this.writingModel(record).validateSync();
27
+ if (validationError) {
28
+ throw validationError;
29
+ }
30
+ const document = await this.writingModel.findOneAndUpdate(
31
+ filter,
32
+ record,
33
+ { new: true, upsert: true, runValidators: true }
34
+ // 🔥 Enforce schema validation!
35
+ );
36
+ const ruleRecord = this.convertMongooseRecordToRuleRecord(
37
+ document.toObject()
38
+ );
39
+ return ruleRecord;
40
+ }
41
+ async insertMany(records) {
42
+ if (!this.writingModel) {
43
+ throw this.modelNotSetProsopoError();
44
+ }
45
+ if (!this.readingModel) {
46
+ throw this.modelNotSetProsopoError();
47
+ }
48
+ const beforeDelete = await this.writingModel.find({});
49
+ this.logger.debug("Before deletion, DB records:", beforeDelete.length);
50
+ await this.writingModel.bulkWrite(
51
+ records.map((record) => {
52
+ const filter = {
53
+ ...record.clientId && { clientId: record.clientId },
54
+ ...record.userIp && { userIp: record.userIp },
55
+ ...record.userId && { userId: record.userId },
56
+ ...record.ja4 && { ja4: record.ja4 }
57
+ };
58
+ return {
59
+ deleteOne: {
60
+ filter
61
+ }
62
+ };
63
+ })
64
+ );
65
+ this.logger.debug("After deletion");
66
+ const afterDelete = await this.readingModel.find({});
67
+ this.logger.debug("After deletion, DB records:", afterDelete.length);
68
+ const documents = await this.writingModel.insertMany(records);
69
+ const objectDocuments = documents.map((document) => document.toObject());
70
+ const ruleRecords = this.convertMongooseRecordsToRuleRecords(objectDocuments);
71
+ return ruleRecords;
72
+ }
73
+ async find(filters, filterSettings) {
74
+ if (!this.readingModel) {
75
+ throw this.modelNotSetProsopoError();
76
+ }
77
+ const query = this.createSearchQuery(filters, filterSettings);
78
+ const mongooseRecords = await this.readingModel.find(query).lean().exec();
79
+ const ruleRecords = this.convertMongooseRecordsToRuleRecords(mongooseRecords);
80
+ return ruleRecords;
81
+ }
82
+ async deleteMany(recordFilters) {
83
+ if (!this.writingModel) {
84
+ throw this.modelNotSetProsopoError();
85
+ }
86
+ for (const recordFilter of recordFilters) {
87
+ await this.writingModel.deleteOne(recordFilter).exec();
88
+ }
89
+ }
90
+ async countRecords() {
91
+ if (!this.readingModel) {
92
+ throw this.modelNotSetProsopoError();
93
+ }
94
+ const count = await this.readingModel.countDocuments().exec();
95
+ return count;
96
+ }
97
+ modelNotSetProsopoError() {
98
+ return new common.ProsopoError("USER_ACCESS_POLICY.MONGOOSE_RULE_MODEL_NOT_SET");
99
+ }
100
+ createSearchQuery(filters, filterSettings) {
101
+ const includeRecordsWithoutClientId = filterSettings?.includeRecordsWithoutClientId || false;
102
+ const includeRecordsWithPartialFilterMatches = filterSettings?.includeRecordsWithPartialFilterMatches || false;
103
+ const queryParts = [
104
+ this.getFilterByClientId(includeRecordsWithoutClientId, filters.clientId)
105
+ ];
106
+ const queryFilters = this.getSearchQueryFilters(
107
+ filters,
108
+ includeRecordsWithPartialFilterMatches
109
+ );
110
+ return {
111
+ $and: queryParts.concat(queryFilters)
112
+ };
113
+ }
114
+ getSearchQueryFilters(filters, includeRecordsWithPartialFilterMatches) {
115
+ const queryFilters = [];
116
+ if (filters.userId) {
117
+ queryFilters.push({ userId: filters.userId });
118
+ }
119
+ if (filters.userIpAddress) {
120
+ queryFilters.push(this.getFilterByUserIp(filters.userIpAddress));
121
+ }
122
+ if (filters.ja4) {
123
+ queryFilters.push({ ja4: filters.ja4 });
124
+ }
125
+ return includeRecordsWithPartialFilterMatches && queryFilters.length > 1 ? [{ $or: queryFilters }] : queryFilters;
126
+ }
127
+ getFilterByClientId(includeRecordsWithoutClientId, clientId) {
128
+ const clientIdValue = void 0 === clientId ? { $exists: false } : clientId;
129
+ const clientIdFilter = {
130
+ clientId: clientIdValue
131
+ };
132
+ return includeRecordsWithoutClientId ? {
133
+ $or: [clientIdFilter, { clientId: { $exists: false } }]
134
+ } : clientIdFilter;
135
+ }
136
+ getFilterByUserIp(userIpAddress) {
137
+ return null !== userIpAddress ? this.getFilterByUserIpAddress(userIpAddress) : { userIp: null };
138
+ }
139
+ getFilterByUserIpAddress(userIpAddress) {
140
+ const isIpV4 = userIpAddress instanceof ipAddress.Address4;
141
+ const userIpVersion = isIpV4 ? ruleIpVersion.RuleIpVersion.v4 : ruleIpVersion.RuleIpVersion.v6;
142
+ const userIpAsNumeric = isIpV4 ? userIpAddress.bigInt() : (
143
+ // we must have the exact same string length to guarantee the right comparison.
144
+ userIpAddress.bigInt().toString().padStart(ruleIpV6NumericMaxLength.RULE_IPV6_NUMERIC_MAX_LENGTH, "0")
145
+ );
146
+ const userIpKey = userIpVersion === ruleIpVersion.RuleIpVersion.v4 ? "userIp.v4.asNumeric" : "userIp.v6.asNumericString";
147
+ const rangeMinKey = userIpVersion === ruleIpVersion.RuleIpVersion.v4 ? "userIp.v4.mask.rangeMinAsNumeric" : "userIp.v6.mask.rangeMinAsNumericString";
148
+ const rangeMaxKey = userIpVersion === ruleIpVersion.RuleIpVersion.v4 ? "userIp.v4.mask.rangeMaxAsNumeric" : "userIp.v6.mask.rangeMaxAsNumericString";
149
+ return {
150
+ $or: [
151
+ { [userIpKey]: userIpAsNumeric },
152
+ {
153
+ [rangeMinKey]: {
154
+ $lte: userIpAsNumeric
155
+ },
156
+ [rangeMaxKey]: {
157
+ $gte: userIpAsNumeric
158
+ }
159
+ }
160
+ ]
161
+ };
162
+ }
163
+ convertMongooseRecordsToRuleRecords(mongooseRecords) {
164
+ const ruleRecords = mongooseRecords.map(
165
+ (mongooseRecord) => this.convertMongooseRecordToRuleRecord(mongooseRecord)
166
+ );
167
+ return ruleRecords;
168
+ }
169
+ convertMongooseRecordToRuleRecord(mongooseRecord) {
170
+ const ruleRecord = {
171
+ ...mongooseRecord,
172
+ _id: mongooseRecord._id.toString()
173
+ };
174
+ return ruleRecord;
175
+ }
176
+ }
177
+ exports.RulesMongooseStorage = RulesMongooseStorage;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const imageCaptchaConfigMongooseSchema = require("./imageCaptchaConfigMongooseSchema.cjs");
5
+ const configMongooseSchema = new mongoose.Schema(
6
+ {
7
+ imageCaptcha: {
8
+ type: imageCaptchaConfigMongooseSchema.imageCaptchaConfigMongooseSchema,
9
+ required: false
10
+ }
11
+ },
12
+ { _id: false }
13
+ );
14
+ exports.configMongooseSchema = configMongooseSchema;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const imageCaptchaConfigMongooseSchema = new mongoose.Schema(
5
+ {
6
+ solvedCount: {
7
+ type: Number,
8
+ required: false
9
+ },
10
+ unsolvedCount: {
11
+ type: Number,
12
+ required: false
13
+ }
14
+ },
15
+ { _id: false }
16
+ );
17
+ exports.imageCaptchaConfigMongooseSchema = imageCaptchaConfigMongooseSchema;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const rulePerformanceMongooseIndexes = require("../indexes/rulePerformanceMongooseIndexes.cjs");
4
+ const ruleUniqueMongooseIndexes = require("../indexes/ruleUniqueMongooseIndexes.cjs");
5
+ const ruleMongooseSchema = require("./ruleMongooseSchema.cjs");
6
+ const getRuleMongooseSchema = () => {
7
+ const ruleMongooseIndexes = [
8
+ ...rulePerformanceMongooseIndexes.rulePerformanceMongooseIndexes,
9
+ ...ruleUniqueMongooseIndexes.ruleUniqueMongooseIndexes
10
+ ];
11
+ for (const ruleMongooseIndex of ruleMongooseIndexes) {
12
+ ruleMongooseSchema.ruleMongooseSchema.index(
13
+ ruleMongooseIndex.definition,
14
+ ruleMongooseIndex.options
15
+ );
16
+ }
17
+ return ruleMongooseSchema.ruleMongooseSchema;
18
+ };
19
+ exports.getRuleMongooseSchema = getRuleMongooseSchema;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const ipV4MongooseSchema = require("./v4/ipV4MongooseSchema.cjs");
5
+ const ipV6MongooseSchema = require("./v6/ipV6MongooseSchema.cjs");
6
+ const ipMongooseSchema = new mongoose.Schema(
7
+ {
8
+ v4: {
9
+ type: ipV4MongooseSchema.ipV4MongooseSchema,
10
+ required: [
11
+ function() {
12
+ return !this.v6;
13
+ },
14
+ "v4 is required when v6 is not set"
15
+ ]
16
+ },
17
+ v6: {
18
+ type: ipV6MongooseSchema.ipV6MongooseSchema,
19
+ required: [
20
+ function() {
21
+ return !this.v4;
22
+ },
23
+ "v6 is required when v4 is not set"
24
+ ]
25
+ }
26
+ },
27
+ { _id: false }
28
+ );
29
+ exports.ipMongooseSchema = ipMongooseSchema;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const ipV4MaskMongooseSchema = new mongoose.Schema(
5
+ {
6
+ // Type choice note: Int32 can't store 10 digits of the numeric presentation of ipV4,
7
+ // so we use BigInt, which is supported by Mongoose and turned into Mongo's Long (Int64)
8
+ rangeMinAsNumeric: { type: BigInt, required: true },
9
+ rangeMaxAsNumeric: { type: BigInt, required: true },
10
+ asNumeric: { type: Number, required: true }
11
+ },
12
+ { _id: false }
13
+ );
14
+ exports.ipV4MaskMongooseSchema = ipV4MaskMongooseSchema;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const ipV4MaskMongooseSchema = require("./ipV4MaskMongooseSchema.cjs");
5
+ const ipV4MongooseSchema = new mongoose.Schema(
6
+ {
7
+ // Type choice note: Int32 can't store 10 digits of the numeric presentation of ipV4,
8
+ // so we use BigInt, which is supported by Mongoose and turned into Mongo's Long (Int64)
9
+ asNumeric: { type: BigInt, required: true },
10
+ asString: { type: String, required: true },
11
+ mask: {
12
+ type: ipV4MaskMongooseSchema.ipV4MaskMongooseSchema,
13
+ required: false
14
+ }
15
+ },
16
+ { _id: false }
17
+ );
18
+ exports.ipV4MongooseSchema = ipV4MongooseSchema;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const ruleIpV6NumericMaxLength = require("../../../../rule/ip/v6/ruleIpV6NumericMaxLength.cjs");
5
+ const ipV6MaskMongooseSchema = new mongoose.Schema(
6
+ {
7
+ // 1. Type choice note:
8
+ /**
9
+ * ipV6 takes 128bits (38 digits), so we can't use Mongo's Long (Int64), and can't even Decimal128,
10
+ * cause it supports only 34 digits https://www.mongodb.com/docs/manual/reference/bson-types/
11
+ */
12
+ // 2. String comparison note
13
+ /**
14
+ * Mongo compares strings by unicode codes of each letter, so it works for us,
15
+ * as long we make sure both strings have the exact same length:
16
+ * so '10' and '02', never '10' and '2'.
17
+ */
18
+ rangeMinAsNumericString: {
19
+ type: String,
20
+ required: true,
21
+ // we must have the exact same string length to guarantee the right comparison.
22
+ set: (value) => value.padStart(ruleIpV6NumericMaxLength.RULE_IPV6_NUMERIC_MAX_LENGTH, "0")
23
+ },
24
+ rangeMaxAsNumericString: {
25
+ type: String,
26
+ required: true,
27
+ // we must have the exact same string length to guarantee the right comparison.
28
+ set: (value) => value.padStart(ruleIpV6NumericMaxLength.RULE_IPV6_NUMERIC_MAX_LENGTH, "0")
29
+ },
30
+ asNumeric: { type: Number, required: true }
31
+ },
32
+ { _id: false }
33
+ );
34
+ exports.ipV6MaskMongooseSchema = ipV6MaskMongooseSchema;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const ruleIpV6NumericMaxLength = require("../../../../rule/ip/v6/ruleIpV6NumericMaxLength.cjs");
5
+ const ipV6MaskMongooseSchema = require("./ipV6MaskMongooseSchema.cjs");
6
+ const ipV6MongooseSchema = new mongoose.Schema(
7
+ {
8
+ // 1. Type choice note:
9
+ /**
10
+ * ipV6 takes 128bits (38 digits), so we can't use Mongo's Long (Int64), and can't even Decimal128,
11
+ * cause it supports only 34 digits https://www.mongodb.com/docs/manual/reference/bson-types/
12
+ */
13
+ // 2. String comparison note
14
+ /**
15
+ * Mongo compares strings by unicode codes of each letter, so it works for us,
16
+ * as long we make sure both strings have the exact same length:
17
+ * so '10' and '02', never '10' and '2'.
18
+ */
19
+ asNumericString: {
20
+ type: String,
21
+ required: true,
22
+ // we must have the exact same string length to guarantee the right comparison.
23
+ set: (value) => value.padStart(ruleIpV6NumericMaxLength.RULE_IPV6_NUMERIC_MAX_LENGTH, "0")
24
+ },
25
+ asString: { type: String, required: true },
26
+ mask: {
27
+ type: ipV6MaskMongooseSchema.ipV6MaskMongooseSchema,
28
+ required: false
29
+ }
30
+ },
31
+ { _id: false }
32
+ );
33
+ exports.ipV6MongooseSchema = ipV6MongooseSchema;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const mongoose = require("mongoose");
4
+ const configMongooseSchema = require("./config/configMongooseSchema.cjs");
5
+ const ipMongooseSchema = require("./ip/ipMongooseSchema.cjs");
6
+ const ruleMongooseSchema = new mongoose.Schema({
7
+ isUserBlocked: { type: Boolean, required: true },
8
+ clientId: { type: String, required: false },
9
+ description: { type: String, required: false },
10
+ userIp: {
11
+ type: ipMongooseSchema.ipMongooseSchema,
12
+ required: [
13
+ function() {
14
+ return !this.userId && !this.ja4;
15
+ },
16
+ "userIp is required when userId is not set and ja4 is not set"
17
+ ]
18
+ },
19
+ userId: {
20
+ type: String,
21
+ required: [
22
+ function() {
23
+ return !this.userIp && !this.ja4;
24
+ },
25
+ "userId is required when userIp is not set and ja4 is not set"
26
+ ]
27
+ },
28
+ ja4: {
29
+ type: String,
30
+ required: [
31
+ function() {
32
+ return !this.userIp && !this.userId;
33
+ },
34
+ "ja4 is required when userIp is not set and userId is not set"
35
+ ]
36
+ },
37
+ config: {
38
+ type: configMongooseSchema.configMongooseSchema,
39
+ required: false
40
+ },
41
+ score: { type: Number, required: false }
42
+ });
43
+ exports.ruleMongooseSchema = ruleMongooseSchema;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const imageCaptchaConfigSchema = zod.object({
5
+ solvedCount: zod.number().optional(),
6
+ unsolvedCount: zod.number().optional()
7
+ });
8
+ exports.imageCaptchaConfigSchema = imageCaptchaConfigSchema;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const imageCaptchaConfigSchema = require("./imageCaptcha/imageCaptchaConfigSchema.cjs");
5
+ const ruleConfigSchema = zod.object({
6
+ imageCaptcha: imageCaptchaConfigSchema.imageCaptchaConfigSchema.optional()
7
+ });
8
+ exports.ruleConfigSchema = ruleConfigSchema;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpV4Schema = require("./v4/ruleIpV4Schema.cjs");
5
+ const ruleIpV6Schema = require("./v6/ruleIpV6Schema.cjs");
6
+ const ruleIpSchema = zod.object({
7
+ v4: ruleIpV4Schema.ruleIpV4Schema.optional(),
8
+ v6: ruleIpV6Schema.ruleIpV6Schema.optional()
9
+ });
10
+ exports.ruleIpSchema = ruleIpSchema;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ var RuleIpVersion = /* @__PURE__ */ ((RuleIpVersion2) => {
4
+ RuleIpVersion2["v4"] = "v4";
5
+ RuleIpVersion2["v6"] = "v6";
6
+ return RuleIpVersion2;
7
+ })(RuleIpVersion || {});
8
+ exports.RuleIpVersion = RuleIpVersion;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpV4MaskSchema = zod.object({
5
+ rangeMinAsNumeric: zod.bigint(),
6
+ rangeMaxAsNumeric: zod.bigint(),
7
+ asNumeric: zod.number()
8
+ });
9
+ exports.ruleIpV4MaskSchema = ruleIpV4MaskSchema;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpV4MaskSchema = require("./mask/ruleIpV4MaskSchema.cjs");
5
+ const ruleIpV4Schema = zod.object({
6
+ asNumeric: zod.bigint(),
7
+ asString: zod.string(),
8
+ mask: ruleIpV4MaskSchema.ruleIpV4MaskSchema.optional()
9
+ });
10
+ exports.ruleIpV4Schema = ruleIpV4Schema;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpV6MaskSchema = zod.object({
5
+ rangeMinAsNumericString: zod.string(),
6
+ rangeMaxAsNumericString: zod.string(),
7
+ asNumeric: zod.number()
8
+ });
9
+ exports.ruleIpV6MaskSchema = ruleIpV6MaskSchema;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const RULE_IPV6_NUMERIC_MAX_LENGTH = 38;
4
+ exports.RULE_IPV6_NUMERIC_MAX_LENGTH = RULE_IPV6_NUMERIC_MAX_LENGTH;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const ruleIpV6MaskSchema = require("./mask/ruleIpV6MaskSchema.cjs");
5
+ const ruleIpV6Schema = zod.object({
6
+ asNumericString: zod.string(),
7
+ asString: zod.string(),
8
+ mask: ruleIpV6MaskSchema.ruleIpV6MaskSchema.optional()
9
+ });
10
+ exports.ruleIpV6Schema = ruleIpV6Schema;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/user-access-policy",
3
- "version": "2.5.3",
3
+ "version": "2.6.1",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -23,9 +23,9 @@
23
23
  "storage:benchmark:mongoose:measure-find": "npm run storage:benchmark:mongoose -- measure-find --db-url=mongodb://localhost:27017 --target-ip-v4=0.0.136.184 --target-ip-v6=0000:0000:0000:0000:0000:0000:0000:7530"
24
24
  },
25
25
  "dependencies": {
26
- "@prosopo/api-route": "2.5.3",
27
- "@prosopo/common": "2.5.3",
28
- "@prosopo/types": "2.5.3",
26
+ "@prosopo/api-route": "2.6.0",
27
+ "@prosopo/common": "2.6.0",
28
+ "@prosopo/types": "2.6.1",
29
29
  "ip-address": "10.0.1",
30
30
  "mongoose": "8.6.2",
31
31
  "zod": "3.23.8"
@@ -33,8 +33,8 @@
33
33
  "devDependencies": {
34
34
  "mongodb": "6.9.0",
35
35
  "mongodb-memory-server": "10.0.0",
36
- "vite": "5.4.6",
37
- "vitest": "2.1.1",
36
+ "vite": "6.2.3",
37
+ "vitest": "3.0.9",
38
38
  "yargs": "17.7.2"
39
39
  },
40
40
  "author": "PROSOPO LIMITED <info@prosopo.io>",
@@ -43,5 +43,10 @@
43
43
  "url": "https://github.com/prosopo/captcha/issues"
44
44
  },
45
45
  "homepage": "https://github.com/prosopo/captcha#readme",
46
- "sideEffects": false
46
+ "sideEffects": false,
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/prosopo/captcha.git",
50
+ "directory": "packages/user-access-policy"
51
+ }
47
52
  }