@prosopo/user-access-policy 3.3.1 → 3.4.0
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 +37 -0
- package/dist/accessPolicyResolver.js +1 -40
- package/dist/cjs/accessPolicyResolver.cjs +0 -56
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/redis/redisAccessRulesIndex.cjs +25 -8
- package/dist/index.js +3 -3
- package/dist/redis/redisAccessRulesIndex.js +25 -8
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @prosopo/user-access-policy
|
|
2
2
|
|
|
3
|
+
## 3.4.0
|
|
4
|
+
### Minor Changes
|
|
5
|
+
|
|
6
|
+
- df4e030: Revising UAP rule getters
|
|
7
|
+
|
|
8
|
+
### Patch Changes
|
|
9
|
+
|
|
10
|
+
- 91bbe87: configure typecheck before bundle for vue packages
|
|
11
|
+
- 91bbe87: make typecheck script always recompile
|
|
12
|
+
- 346e092: NODE_ENV default to "development"
|
|
13
|
+
- 5d36e05: remove tsc --force
|
|
14
|
+
- Updated dependencies [828066d]
|
|
15
|
+
- Updated dependencies [df4e030]
|
|
16
|
+
- Updated dependencies [91bbe87]
|
|
17
|
+
- Updated dependencies [3ef4fd2]
|
|
18
|
+
- Updated dependencies [91bbe87]
|
|
19
|
+
- Updated dependencies [346e092]
|
|
20
|
+
- Updated dependencies [5d36e05]
|
|
21
|
+
- @prosopo/api-route@2.6.10
|
|
22
|
+
- @prosopo/common@3.1.2
|
|
23
|
+
- @prosopo/types@3.0.6
|
|
24
|
+
- @prosopo/config@3.1.3
|
|
25
|
+
- @prosopo/util@3.0.5
|
|
26
|
+
|
|
27
|
+
## 3.3.2
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- eb71691: configure typecheck before bundle for vue packages
|
|
31
|
+
- eb71691: make typecheck script always recompile
|
|
32
|
+
- Updated dependencies [eb71691]
|
|
33
|
+
- Updated dependencies [eb71691]
|
|
34
|
+
- @prosopo/api-route@2.6.9
|
|
35
|
+
- @prosopo/common@3.1.1
|
|
36
|
+
- @prosopo/types@3.0.5
|
|
37
|
+
- @prosopo/util@3.0.4
|
|
38
|
+
- @prosopo/config@3.1.2
|
|
39
|
+
|
|
3
40
|
## 3.3.1
|
|
4
41
|
### Patch Changes
|
|
5
42
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as util from "node:util";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { userScopeInputSchema, policyScopeSchema
|
|
2
|
+
import { userScopeInputSchema, policyScopeSchema } from "./accessPolicy.js";
|
|
4
3
|
var ScopeMatch = /* @__PURE__ */ ((ScopeMatch2) => {
|
|
5
4
|
ScopeMatch2["Exact"] = "exact";
|
|
6
5
|
ScopeMatch2["Greedy"] = "greedy";
|
|
@@ -26,45 +25,7 @@ const policyFilterSchema = z.object({
|
|
|
26
25
|
/* Exact */
|
|
27
26
|
)
|
|
28
27
|
});
|
|
29
|
-
const createAccessPolicyResolver = (accessRulesReader, logger) => {
|
|
30
|
-
return async (filter) => {
|
|
31
|
-
const accessRules = await accessRulesReader.findRules(filter);
|
|
32
|
-
const primaryAccessRule = resolvePrimaryRule(accessRules);
|
|
33
|
-
logger.debug(() => ({
|
|
34
|
-
msg: "Resolved access policy",
|
|
35
|
-
// filter contains BigInt, which can't be handled directly via logger.
|
|
36
|
-
data: {
|
|
37
|
-
inspect: util.inspect(
|
|
38
|
-
{
|
|
39
|
-
filter,
|
|
40
|
-
accessRules,
|
|
41
|
-
primaryAccessRule
|
|
42
|
-
},
|
|
43
|
-
{ depth: null }
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
}));
|
|
47
|
-
return primaryAccessRule;
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
const resolvePrimaryRule = (rules) => {
|
|
51
|
-
const blockingRules = rules.filter(
|
|
52
|
-
(accessRule) => AccessPolicyType.Block === accessRule.type
|
|
53
|
-
);
|
|
54
|
-
const rulesToEvaluate = blockingRules.length > 0 ? blockingRules : rules;
|
|
55
|
-
return resolveMostLocalRule(rulesToEvaluate);
|
|
56
|
-
};
|
|
57
|
-
const resolveMostLocalRule = (rules) => {
|
|
58
|
-
const clientRules = rules.filter(
|
|
59
|
-
(accessRule) => "string" === typeof accessRule.clientId
|
|
60
|
-
);
|
|
61
|
-
if (clientRules.length > 0) {
|
|
62
|
-
return clientRules.shift();
|
|
63
|
-
}
|
|
64
|
-
return rules.shift();
|
|
65
|
-
};
|
|
66
28
|
export {
|
|
67
29
|
ScopeMatch,
|
|
68
|
-
createAccessPolicyResolver,
|
|
69
30
|
policyFilterSchema
|
|
70
31
|
};
|
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const util = require("node:util");
|
|
4
3
|
const zod = require("zod");
|
|
5
4
|
const accessPolicy = require("./accessPolicy.cjs");
|
|
6
|
-
function _interopNamespaceDefault(e) {
|
|
7
|
-
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
8
|
-
if (e) {
|
|
9
|
-
for (const k in e) {
|
|
10
|
-
if (k !== "default") {
|
|
11
|
-
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: () => e[k]
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
n.default = e;
|
|
20
|
-
return Object.freeze(n);
|
|
21
|
-
}
|
|
22
|
-
const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
|
|
23
5
|
var ScopeMatch = /* @__PURE__ */ ((ScopeMatch2) => {
|
|
24
6
|
ScopeMatch2["Exact"] = "exact";
|
|
25
7
|
ScopeMatch2["Greedy"] = "greedy";
|
|
@@ -45,43 +27,5 @@ const policyFilterSchema = zod.z.object({
|
|
|
45
27
|
/* Exact */
|
|
46
28
|
)
|
|
47
29
|
});
|
|
48
|
-
const createAccessPolicyResolver = (accessRulesReader, logger) => {
|
|
49
|
-
return async (filter) => {
|
|
50
|
-
const accessRules = await accessRulesReader.findRules(filter);
|
|
51
|
-
const primaryAccessRule = resolvePrimaryRule(accessRules);
|
|
52
|
-
logger.debug(() => ({
|
|
53
|
-
msg: "Resolved access policy",
|
|
54
|
-
// filter contains BigInt, which can't be handled directly via logger.
|
|
55
|
-
data: {
|
|
56
|
-
inspect: util__namespace.inspect(
|
|
57
|
-
{
|
|
58
|
-
filter,
|
|
59
|
-
accessRules,
|
|
60
|
-
primaryAccessRule
|
|
61
|
-
},
|
|
62
|
-
{ depth: null }
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
}));
|
|
66
|
-
return primaryAccessRule;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
const resolvePrimaryRule = (rules) => {
|
|
70
|
-
const blockingRules = rules.filter(
|
|
71
|
-
(accessRule) => accessPolicy.AccessPolicyType.Block === accessRule.type
|
|
72
|
-
);
|
|
73
|
-
const rulesToEvaluate = blockingRules.length > 0 ? blockingRules : rules;
|
|
74
|
-
return resolveMostLocalRule(rulesToEvaluate);
|
|
75
|
-
};
|
|
76
|
-
const resolveMostLocalRule = (rules) => {
|
|
77
|
-
const clientRules = rules.filter(
|
|
78
|
-
(accessRule) => "string" === typeof accessRule.clientId
|
|
79
|
-
);
|
|
80
|
-
if (clientRules.length > 0) {
|
|
81
|
-
return clientRules.shift();
|
|
82
|
-
}
|
|
83
|
-
return rules.shift();
|
|
84
|
-
};
|
|
85
30
|
exports.ScopeMatch = ScopeMatch;
|
|
86
|
-
exports.createAccessPolicyResolver = createAccessPolicyResolver;
|
|
87
31
|
exports.policyFilterSchema = policyFilterSchema;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -13,10 +13,10 @@ const createApiRuleRoutesProvider = (rulesStorage) => {
|
|
|
13
13
|
};
|
|
14
14
|
exports.AccessPolicyType = accessPolicy.AccessPolicyType;
|
|
15
15
|
exports.accessPolicySchema = accessPolicy.accessPolicySchema;
|
|
16
|
+
exports.accessRuleSchemaExtended = accessPolicy.accessRuleSchemaExtended;
|
|
16
17
|
exports.policyScopeSchema = accessPolicy.policyScopeSchema;
|
|
17
18
|
exports.userScopeInputSchema = accessPolicy.userScopeInputSchema;
|
|
18
19
|
exports.ScopeMatch = accessPolicyResolver.ScopeMatch;
|
|
19
|
-
exports.createAccessPolicyResolver = accessPolicyResolver.createAccessPolicyResolver;
|
|
20
20
|
exports.accessRuleApiPaths = accessRuleApiRoutes.accessRuleApiPaths;
|
|
21
21
|
exports.getExpressApiRuleRateLimits = accessRuleApiRoutes.getExpressApiRuleRateLimits;
|
|
22
22
|
exports.deleteAllRulesEndpointSchema = deleteAllRulesEndpoint.deleteAllRulesEndpointSchema;
|
|
@@ -26,11 +26,11 @@ const accessRulesIndex = {
|
|
|
26
26
|
},
|
|
27
27
|
numericIpMaskMin: search.SCHEMA_FIELD_TYPE.NUMERIC,
|
|
28
28
|
numericIpMaskMax: search.SCHEMA_FIELD_TYPE.NUMERIC,
|
|
29
|
-
userId: search.SCHEMA_FIELD_TYPE.TAG,
|
|
29
|
+
userId: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
30
30
|
numericIp: { type: search.SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
31
|
-
ja4Hash: search.SCHEMA_FIELD_TYPE.TAG,
|
|
32
|
-
headersHash: search.SCHEMA_FIELD_TYPE.TAG,
|
|
33
|
-
userAgentHash: search.SCHEMA_FIELD_TYPE.TAG
|
|
31
|
+
ja4Hash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
32
|
+
headersHash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
33
|
+
userAgentHash: { type: search.SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
|
|
34
34
|
},
|
|
35
35
|
// the satisfy statement is to guarantee that the keys are right
|
|
36
36
|
options: {
|
|
@@ -38,7 +38,12 @@ const accessRulesIndex = {
|
|
|
38
38
|
PREFIX: [accessRuleRedisKeyPrefix]
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
-
const createRedisAccessRulesIndex = async (client) =>
|
|
41
|
+
const createRedisAccessRulesIndex = async (client, indexName) => {
|
|
42
|
+
if (indexName) {
|
|
43
|
+
accessRulesIndex.name = indexName;
|
|
44
|
+
}
|
|
45
|
+
return redisIndex.createRedisIndex(client, accessRulesIndex);
|
|
46
|
+
};
|
|
42
47
|
const numericIndexFields = [
|
|
43
48
|
"numericIp",
|
|
44
49
|
"numericIpMaskMin",
|
|
@@ -79,16 +84,28 @@ const getPolicyScopeQuery = (policyScope, scopeMatchType) => {
|
|
|
79
84
|
return accessPolicyResolver.ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
|
|
80
85
|
};
|
|
81
86
|
const getUserScopeQuery = (userScope, scopeMatchType) => {
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
let scopeEntries = Object.entries(userScope);
|
|
88
|
+
let scopeJoinType = " ";
|
|
89
|
+
if (scopeMatchType === accessPolicyResolver.ScopeMatch.Greedy) {
|
|
90
|
+
scopeEntries = scopeEntries.filter(
|
|
91
|
+
([_, value]) => value !== void 0
|
|
92
|
+
);
|
|
93
|
+
scopeJoinType = " | ";
|
|
94
|
+
}
|
|
84
95
|
return scopeEntries.map(
|
|
85
96
|
([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(scopeFieldName, scopeFieldValue, scopeMatchType)
|
|
86
97
|
).join(scopeJoinType);
|
|
87
98
|
};
|
|
88
99
|
const getUserScopeFieldQuery = (fieldName, fieldValue, matchType) => {
|
|
89
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
//ScopeMatch.Greedy === matchType &&
|
|
102
|
+
"function" === typeof greedyFieldComparisons[fieldName]
|
|
103
|
+
) {
|
|
90
104
|
return greedyFieldComparisons[fieldName](fieldValue);
|
|
91
105
|
}
|
|
106
|
+
if (fieldValue === void 0) {
|
|
107
|
+
return `ismissing(@${fieldName})`;
|
|
108
|
+
}
|
|
92
109
|
return numericIndexFields.includes(fieldName) ? `@${fieldName}:[${fieldValue}]` : `@${fieldName}:{${fieldValue}}`;
|
|
93
110
|
};
|
|
94
111
|
const getRedisAccessRuleKey = (rule) => accessRuleRedisKeyPrefix + crypto.createHash(accessRuleContentHashAlgorithm).update(
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AccessPolicyType, accessPolicySchema, policyScopeSchema, userScopeInputSchema } from "./accessPolicy.js";
|
|
2
|
-
import { ScopeMatch
|
|
1
|
+
import { AccessPolicyType, accessPolicySchema, accessRuleSchemaExtended, policyScopeSchema, userScopeInputSchema } from "./accessPolicy.js";
|
|
2
|
+
import { ScopeMatch } from "./accessPolicyResolver.js";
|
|
3
3
|
import { AccessRuleApiRoutes } from "./api/accessRuleApiRoutes.js";
|
|
4
4
|
import { accessRuleApiPaths, getExpressApiRuleRateLimits } from "./api/accessRuleApiRoutes.js";
|
|
5
5
|
import { deleteAllRulesEndpointSchema } from "./api/deleteAllRulesEndpoint.js";
|
|
@@ -15,7 +15,7 @@ export {
|
|
|
15
15
|
ScopeMatch,
|
|
16
16
|
accessPolicySchema,
|
|
17
17
|
accessRuleApiPaths,
|
|
18
|
-
|
|
18
|
+
accessRuleSchemaExtended,
|
|
19
19
|
createApiRuleRoutesProvider,
|
|
20
20
|
createRedisAccessRulesIndex,
|
|
21
21
|
createRedisAccessRulesStorage,
|
|
@@ -24,11 +24,11 @@ const accessRulesIndex = {
|
|
|
24
24
|
},
|
|
25
25
|
numericIpMaskMin: SCHEMA_FIELD_TYPE.NUMERIC,
|
|
26
26
|
numericIpMaskMax: SCHEMA_FIELD_TYPE.NUMERIC,
|
|
27
|
-
userId: SCHEMA_FIELD_TYPE.TAG,
|
|
27
|
+
userId: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
28
28
|
numericIp: { type: SCHEMA_FIELD_TYPE.NUMERIC, INDEXMISSING: true },
|
|
29
|
-
ja4Hash: SCHEMA_FIELD_TYPE.TAG,
|
|
30
|
-
headersHash: SCHEMA_FIELD_TYPE.TAG,
|
|
31
|
-
userAgentHash: SCHEMA_FIELD_TYPE.TAG
|
|
29
|
+
ja4Hash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
30
|
+
headersHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true },
|
|
31
|
+
userAgentHash: { type: SCHEMA_FIELD_TYPE.TAG, INDEXMISSING: true }
|
|
32
32
|
},
|
|
33
33
|
// the satisfy statement is to guarantee that the keys are right
|
|
34
34
|
options: {
|
|
@@ -36,7 +36,12 @@ const accessRulesIndex = {
|
|
|
36
36
|
PREFIX: [accessRuleRedisKeyPrefix]
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
-
const createRedisAccessRulesIndex = async (client) =>
|
|
39
|
+
const createRedisAccessRulesIndex = async (client, indexName) => {
|
|
40
|
+
if (indexName) {
|
|
41
|
+
accessRulesIndex.name = indexName;
|
|
42
|
+
}
|
|
43
|
+
return createRedisIndex(client, accessRulesIndex);
|
|
44
|
+
};
|
|
40
45
|
const numericIndexFields = [
|
|
41
46
|
"numericIp",
|
|
42
47
|
"numericIpMaskMin",
|
|
@@ -77,16 +82,28 @@ const getPolicyScopeQuery = (policyScope, scopeMatchType) => {
|
|
|
77
82
|
return ScopeMatch.Exact === scopeMatchType ? "ismissing(@clientId)" : "";
|
|
78
83
|
};
|
|
79
84
|
const getUserScopeQuery = (userScope, scopeMatchType) => {
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
let scopeEntries = Object.entries(userScope);
|
|
86
|
+
let scopeJoinType = " ";
|
|
87
|
+
if (scopeMatchType === ScopeMatch.Greedy) {
|
|
88
|
+
scopeEntries = scopeEntries.filter(
|
|
89
|
+
([_, value]) => value !== void 0
|
|
90
|
+
);
|
|
91
|
+
scopeJoinType = " | ";
|
|
92
|
+
}
|
|
82
93
|
return scopeEntries.map(
|
|
83
94
|
([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(scopeFieldName, scopeFieldValue, scopeMatchType)
|
|
84
95
|
).join(scopeJoinType);
|
|
85
96
|
};
|
|
86
97
|
const getUserScopeFieldQuery = (fieldName, fieldValue, matchType) => {
|
|
87
|
-
if (
|
|
98
|
+
if (
|
|
99
|
+
//ScopeMatch.Greedy === matchType &&
|
|
100
|
+
"function" === typeof greedyFieldComparisons[fieldName]
|
|
101
|
+
) {
|
|
88
102
|
return greedyFieldComparisons[fieldName](fieldValue);
|
|
89
103
|
}
|
|
104
|
+
if (fieldValue === void 0) {
|
|
105
|
+
return `ismissing(@${fieldName})`;
|
|
106
|
+
}
|
|
90
107
|
return numericIndexFields.includes(fieldName) ? `@${fieldName}:[${fieldValue}]` : `@${fieldName}:{${fieldValue}}`;
|
|
91
108
|
};
|
|
92
109
|
const getRedisAccessRuleKey = (rule) => accessRuleRedisKeyPrefix + crypto.createHash(accessRuleContentHashAlgorithm).update(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/user-access-policy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -20,23 +20,23 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"clean": "del-cli --verbose dist tsconfig.tsbuildinfo",
|
|
23
|
-
"build": "NODE_ENV=${NODE_ENV:-
|
|
23
|
+
"build": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.esm.config.ts --mode $NODE_ENV",
|
|
24
24
|
"build:tsc": "tsc --build --verbose",
|
|
25
|
-
"build:cjs": "NODE_ENV=${NODE_ENV:-
|
|
25
|
+
"build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
|
|
26
26
|
"typecheck": "tsc --build --declaration --emitDeclarationOnly",
|
|
27
27
|
"test": "NODE_ENV=${NODE_ENV:-test}; npx vitest run --config ./vite.test.config.ts"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@prosopo/api-route": "2.6.
|
|
31
|
-
"@prosopo/common": "3.1.
|
|
32
|
-
"@prosopo/types": "3.0.
|
|
33
|
-
"@prosopo/util": "3.0.
|
|
30
|
+
"@prosopo/api-route": "2.6.10",
|
|
31
|
+
"@prosopo/common": "3.1.2",
|
|
32
|
+
"@prosopo/types": "3.0.6",
|
|
33
|
+
"@prosopo/util": "3.0.5",
|
|
34
34
|
"axios": "1.10.0",
|
|
35
35
|
"esbuild": "0.25.6",
|
|
36
36
|
"ip-address": "10.0.1",
|
|
37
37
|
"redis": "5.0.0",
|
|
38
38
|
"zod": "3.23.8",
|
|
39
|
-
"@prosopo/config": "3.1.
|
|
39
|
+
"@prosopo/config": "3.1.3",
|
|
40
40
|
"webpack-dev-server": "5.2.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|