@naylence/runtime 0.3.21 → 0.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/dist/browser/index.cjs +3144 -1307
- package/dist/browser/index.mjs +3116 -1301
- package/dist/cjs/naylence/fame/factory-manifest.js +6 -0
- package/dist/cjs/naylence/fame/node/node-event-listener.js +4 -0
- package/dist/cjs/naylence/fame/security/auth/default-policy-authorizer-factory.js +147 -0
- package/dist/cjs/naylence/fame/security/auth/default-policy-authorizer.js +291 -0
- package/dist/cjs/naylence/fame/security/auth/oauth2-authorizer-factory.js +7 -0
- package/dist/cjs/naylence/fame/security/auth/oauth2-authorizer.js +19 -4
- package/dist/cjs/naylence/fame/security/auth/policy/authorization-policy-definition.js +60 -0
- package/dist/cjs/naylence/fame/security/auth/policy/authorization-policy-factory.js +35 -0
- package/dist/cjs/naylence/fame/security/auth/policy/authorization-policy-source-factory.js +35 -0
- package/dist/cjs/naylence/fame/security/auth/policy/authorization-policy-source.js +2 -0
- package/dist/cjs/naylence/fame/security/auth/policy/authorization-policy.js +2 -0
- package/dist/cjs/naylence/fame/security/auth/policy/basic-authorization-policy-factory.js +99 -0
- package/dist/cjs/naylence/fame/security/auth/policy/basic-authorization-policy.js +449 -0
- package/dist/cjs/naylence/fame/security/auth/policy/index.js +40 -0
- package/dist/cjs/naylence/fame/security/auth/policy/local-file-authorization-policy-source-factory.js +101 -0
- package/dist/cjs/naylence/fame/security/auth/policy/local-file-authorization-policy-source.js +164 -0
- package/dist/cjs/naylence/fame/security/auth/policy/pattern-matcher.js +195 -0
- package/dist/cjs/naylence/fame/security/auth/policy/scope-matcher.js +169 -0
- package/dist/cjs/naylence/fame/security/auth/policy-authorizer.js +2 -0
- package/dist/cjs/naylence/fame/security/default-security-manager.js +94 -0
- package/dist/cjs/naylence/fame/security/index.js +3 -0
- package/dist/cjs/naylence/fame/security/node-security-profile-factory.js +3 -1
- package/dist/cjs/naylence/fame/sentinel/router.js +67 -1
- package/dist/cjs/naylence/fame/sentinel/sentinel.js +46 -2
- package/dist/cjs/naylence/fame/util/register-runtime-factories.js +2 -0
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/factory-manifest.js +6 -0
- package/dist/esm/naylence/fame/node/node-event-listener.js +4 -0
- package/dist/esm/naylence/fame/security/auth/default-policy-authorizer-factory.js +110 -0
- package/dist/esm/naylence/fame/security/auth/default-policy-authorizer.js +287 -0
- package/dist/esm/naylence/fame/security/auth/oauth2-authorizer-factory.js +7 -0
- package/dist/esm/naylence/fame/security/auth/oauth2-authorizer.js +19 -4
- package/dist/esm/naylence/fame/security/auth/policy/authorization-policy-definition.js +57 -0
- package/dist/esm/naylence/fame/security/auth/policy/authorization-policy-factory.js +31 -0
- package/dist/esm/naylence/fame/security/auth/policy/authorization-policy-source-factory.js +31 -0
- package/dist/esm/naylence/fame/security/auth/policy/authorization-policy-source.js +1 -0
- package/dist/esm/naylence/fame/security/auth/policy/authorization-policy.js +1 -0
- package/dist/esm/naylence/fame/security/auth/policy/basic-authorization-policy-factory.js +62 -0
- package/dist/esm/naylence/fame/security/auth/policy/basic-authorization-policy.js +445 -0
- package/dist/esm/naylence/fame/security/auth/policy/index.js +20 -0
- package/dist/esm/naylence/fame/security/auth/policy/local-file-authorization-policy-source-factory.js +64 -0
- package/dist/esm/naylence/fame/security/auth/policy/local-file-authorization-policy-source.js +127 -0
- package/dist/esm/naylence/fame/security/auth/policy/pattern-matcher.js +185 -0
- package/dist/esm/naylence/fame/security/auth/policy/scope-matcher.js +162 -0
- package/dist/esm/naylence/fame/security/auth/policy-authorizer.js +1 -0
- package/dist/esm/naylence/fame/security/default-security-manager.js +94 -0
- package/dist/esm/naylence/fame/security/index.js +3 -0
- package/dist/esm/naylence/fame/security/node-security-profile-factory.js +2 -0
- package/dist/esm/naylence/fame/sentinel/router.js +64 -0
- package/dist/esm/naylence/fame/sentinel/sentinel.js +47 -3
- package/dist/esm/naylence/fame/util/register-runtime-factories.js +2 -0
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +3140 -1303
- package/dist/node/index.mjs +3116 -1301
- package/dist/node/node.cjs +3191 -1338
- package/dist/node/node.mjs +3167 -1336
- package/dist/types/naylence/fame/factory-manifest.d.ts +1 -1
- package/dist/types/naylence/fame/node/node-event-listener.d.ts +31 -0
- package/dist/types/naylence/fame/security/auth/authorizer.d.ts +37 -0
- package/dist/types/naylence/fame/security/auth/default-policy-authorizer-factory.d.ts +55 -0
- package/dist/types/naylence/fame/security/auth/default-policy-authorizer.d.ts +99 -0
- package/dist/types/naylence/fame/security/auth/oauth2-authorizer-factory.d.ts +2 -0
- package/dist/types/naylence/fame/security/auth/oauth2-authorizer.d.ts +2 -0
- package/dist/types/naylence/fame/security/auth/policy/authorization-policy-definition.d.ts +166 -0
- package/dist/types/naylence/fame/security/auth/policy/authorization-policy-factory.d.ts +38 -0
- package/dist/types/naylence/fame/security/auth/policy/authorization-policy-source-factory.d.ts +38 -0
- package/dist/types/naylence/fame/security/auth/policy/authorization-policy-source.d.ts +20 -0
- package/dist/types/naylence/fame/security/auth/policy/authorization-policy.d.ts +55 -0
- package/dist/types/naylence/fame/security/auth/policy/basic-authorization-policy-factory.d.ts +42 -0
- package/dist/types/naylence/fame/security/auth/policy/basic-authorization-policy.d.ts +78 -0
- package/dist/types/naylence/fame/security/auth/policy/index.d.ts +19 -0
- package/dist/types/naylence/fame/security/auth/policy/local-file-authorization-policy-source-factory.d.ts +51 -0
- package/dist/types/naylence/fame/security/auth/policy/local-file-authorization-policy-source.d.ts +67 -0
- package/dist/types/naylence/fame/security/auth/policy/pattern-matcher.d.ts +84 -0
- package/dist/types/naylence/fame/security/auth/policy/scope-matcher.d.ts +61 -0
- package/dist/types/naylence/fame/security/auth/policy-authorizer.d.ts +12 -0
- package/dist/types/naylence/fame/security/default-security-manager.d.ts +22 -0
- package/dist/types/naylence/fame/security/index.d.ts +2 -0
- package/dist/types/naylence/fame/security/node-security-profile-factory.d.ts +1 -0
- package/dist/types/naylence/fame/sentinel/router.d.ts +68 -0
- package/dist/types/naylence/fame/sentinel/sentinel.d.ts +16 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthorizationPolicySourceFactory = exports.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE = void 0;
|
|
4
|
+
const factory_1 = require("@naylence/factory");
|
|
5
|
+
/**
|
|
6
|
+
* Base type identifier for authorization policy source factories.
|
|
7
|
+
*/
|
|
8
|
+
exports.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE = 'AuthorizationPolicySourceFactory';
|
|
9
|
+
/**
|
|
10
|
+
* Abstract factory base class for creating authorization policy sources.
|
|
11
|
+
*
|
|
12
|
+
* Implementations of this factory create specific types of policy sources
|
|
13
|
+
* (e.g., local file, remote store, in-memory, etc.).
|
|
14
|
+
*/
|
|
15
|
+
class AuthorizationPolicySourceFactory extends factory_1.AbstractResourceFactory {
|
|
16
|
+
/**
|
|
17
|
+
* Static helper to create an authorization policy source using the factory registry.
|
|
18
|
+
*
|
|
19
|
+
* @param config - Configuration for the policy source
|
|
20
|
+
* @param options - Resource creation options
|
|
21
|
+
* @returns The created policy source, or undefined if no factory matched
|
|
22
|
+
*/
|
|
23
|
+
static async createAuthorizationPolicySource(config, options = {}) {
|
|
24
|
+
if (config) {
|
|
25
|
+
const source = await (0, factory_1.createResource)(exports.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE, config, options);
|
|
26
|
+
if (!source) {
|
|
27
|
+
throw new Error('Failed to create authorization policy source from configuration');
|
|
28
|
+
}
|
|
29
|
+
return source;
|
|
30
|
+
}
|
|
31
|
+
const source = await (0, factory_1.createDefaultResource)(exports.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE, null, options);
|
|
32
|
+
return source ?? undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.AuthorizationPolicySourceFactory = AuthorizationPolicySourceFactory;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating BasicAuthorizationPolicy instances.
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.BasicAuthorizationPolicyFactory = exports.FACTORY_META = void 0;
|
|
40
|
+
const authorization_policy_factory_js_1 = require("./authorization-policy-factory.js");
|
|
41
|
+
/**
|
|
42
|
+
* Lazy import for tree-shaking.
|
|
43
|
+
*/
|
|
44
|
+
async function safeImportModule() {
|
|
45
|
+
return await Promise.resolve().then(() => __importStar(require('./basic-authorization-policy.js')));
|
|
46
|
+
}
|
|
47
|
+
function normalizeConfig(config) {
|
|
48
|
+
if (!config) {
|
|
49
|
+
throw new Error('BasicAuthorizationPolicyFactory requires a configuration with a policyDefinition');
|
|
50
|
+
}
|
|
51
|
+
const candidate = config;
|
|
52
|
+
// Support both camelCase and snake_case for policyDefinition
|
|
53
|
+
const policyDefinition = (candidate.policyDefinition ??
|
|
54
|
+
candidate.policy_definition);
|
|
55
|
+
if (!policyDefinition || typeof policyDefinition !== 'object') {
|
|
56
|
+
throw new Error('BasicAuthorizationPolicyConfig requires a policyDefinition object');
|
|
57
|
+
}
|
|
58
|
+
// Support both camelCase and snake_case for warnOnUnknownFields
|
|
59
|
+
const warnOnUnknownFields = candidate.warnOnUnknownFields ?? candidate.warn_on_unknown_fields;
|
|
60
|
+
if (warnOnUnknownFields !== undefined && typeof warnOnUnknownFields !== 'boolean') {
|
|
61
|
+
throw new Error('warnOnUnknownFields must be a boolean');
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
policyDefinition,
|
|
65
|
+
warnOnUnknownFields: warnOnUnknownFields ?? true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Factory metadata for registration.
|
|
70
|
+
*/
|
|
71
|
+
exports.FACTORY_META = {
|
|
72
|
+
base: authorization_policy_factory_js_1.AUTHORIZATION_POLICY_FACTORY_BASE_TYPE,
|
|
73
|
+
key: 'BasicAuthorizationPolicy',
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Factory for creating BasicAuthorizationPolicy instances.
|
|
77
|
+
*/
|
|
78
|
+
class BasicAuthorizationPolicyFactory extends authorization_policy_factory_js_1.AuthorizationPolicyFactory {
|
|
79
|
+
constructor() {
|
|
80
|
+
super(...arguments);
|
|
81
|
+
this.type = 'BasicAuthorizationPolicy';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates a BasicAuthorizationPolicy from the given configuration.
|
|
85
|
+
*
|
|
86
|
+
* @param config - Configuration with policyDefinition
|
|
87
|
+
* @returns The created authorization policy
|
|
88
|
+
*/
|
|
89
|
+
async create(config) {
|
|
90
|
+
const normalized = normalizeConfig(config);
|
|
91
|
+
const { BasicAuthorizationPolicy } = await safeImportModule();
|
|
92
|
+
return new BasicAuthorizationPolicy({
|
|
93
|
+
policyDefinition: normalized.policyDefinition,
|
|
94
|
+
warnOnUnknownFields: normalized.warnOnUnknownFields,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.BasicAuthorizationPolicyFactory = BasicAuthorizationPolicyFactory;
|
|
99
|
+
exports.default = BasicAuthorizationPolicyFactory;
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Basic authorization policy implementation.
|
|
4
|
+
*
|
|
5
|
+
* Evaluates authorization rules defined in YAML/JSON policy files.
|
|
6
|
+
* Uses first-match-wins semantics with glob/regex pattern matching.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.BasicAuthorizationPolicy = void 0;
|
|
10
|
+
const logging_js_1 = require("../../../util/logging.js");
|
|
11
|
+
const authorization_policy_definition_js_1 = require("./authorization-policy-definition.js");
|
|
12
|
+
const pattern_matcher_js_1 = require("./pattern-matcher.js");
|
|
13
|
+
const scope_matcher_js_1 = require("./scope-matcher.js");
|
|
14
|
+
const logger = (0, logging_js_1.getLogger)('naylence.fame.security.auth.policy.basic_authorization_policy');
|
|
15
|
+
/**
|
|
16
|
+
* Extracts the target address string from the envelope.
|
|
17
|
+
*/
|
|
18
|
+
function extractAddress(envelope) {
|
|
19
|
+
const to = envelope.to;
|
|
20
|
+
if (!to) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
// FameAddress can be a string or object with toString()
|
|
24
|
+
if (typeof to === 'string') {
|
|
25
|
+
return to;
|
|
26
|
+
}
|
|
27
|
+
if (typeof to === 'object' && 'toString' in to) {
|
|
28
|
+
return to.toString();
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extracts granted scopes from the authorization context.
|
|
34
|
+
*/
|
|
35
|
+
function extractGrantedScopes(context) {
|
|
36
|
+
const authContext = context?.security?.authorization;
|
|
37
|
+
if (!authContext) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
// Check grantedScopes first
|
|
41
|
+
if (Array.isArray(authContext.grantedScopes)) {
|
|
42
|
+
return authContext.grantedScopes;
|
|
43
|
+
}
|
|
44
|
+
// Fall back to claims.scope if available
|
|
45
|
+
const claims = authContext.claims;
|
|
46
|
+
if (claims) {
|
|
47
|
+
const scopeClaim = claims.scope ?? claims.scopes ?? claims.scp;
|
|
48
|
+
if (typeof scopeClaim === 'string') {
|
|
49
|
+
// Space-separated scopes (OAuth2 convention)
|
|
50
|
+
return scopeClaim.split(/\s+/).filter((s) => s.length > 0);
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(scopeClaim)) {
|
|
53
|
+
return scopeClaim.filter((s) => typeof s === 'string');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Basic authorization policy that evaluates rules from a policy definition.
|
|
60
|
+
*
|
|
61
|
+
* Features:
|
|
62
|
+
* - First-match-wins rule evaluation
|
|
63
|
+
* - Glob and regex pattern matching for addresses
|
|
64
|
+
* - Scope matching with any_of/all_of/none_of operators
|
|
65
|
+
* - Action-based filtering (connect, send, receive)
|
|
66
|
+
*/
|
|
67
|
+
class BasicAuthorizationPolicy {
|
|
68
|
+
constructor(options) {
|
|
69
|
+
const { policyDefinition, warnOnUnknownFields = true } = options;
|
|
70
|
+
// Validate and extract default effect
|
|
71
|
+
this.defaultEffect = this.validateDefaultEffect(policyDefinition.default_effect);
|
|
72
|
+
// Warn about unknown policy fields
|
|
73
|
+
if (warnOnUnknownFields) {
|
|
74
|
+
this.warnUnknownPolicyFields(policyDefinition);
|
|
75
|
+
}
|
|
76
|
+
// Compile rules for efficient evaluation
|
|
77
|
+
this.compiledRules = this.compileRules(policyDefinition.rules, warnOnUnknownFields);
|
|
78
|
+
logger.debug('policy_compiled', {
|
|
79
|
+
defaultEffect: this.defaultEffect,
|
|
80
|
+
ruleCount: this.compiledRules.length,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Evaluates the policy against a request with an explicitly provided action.
|
|
85
|
+
*
|
|
86
|
+
* @param _node - The node handling the request (unused in basic policy)
|
|
87
|
+
* @param envelope - The FAME envelope being authorized
|
|
88
|
+
* @param context - Optional delivery context with authorization info
|
|
89
|
+
* @param action - The authorization action token (required, no inference)
|
|
90
|
+
* @returns Authorization decision indicating allow/deny
|
|
91
|
+
*/
|
|
92
|
+
async evaluateRequest(_node, envelope, context, action) {
|
|
93
|
+
// Action must be explicitly provided; default to wildcard if omitted
|
|
94
|
+
// for backward compatibility during transition
|
|
95
|
+
const resolvedAction = action ?? '*';
|
|
96
|
+
const address = extractAddress(envelope);
|
|
97
|
+
const grantedScopes = extractGrantedScopes(context);
|
|
98
|
+
const rawFrameType = envelope.frame
|
|
99
|
+
?.type;
|
|
100
|
+
const frameTypeNormalized = typeof rawFrameType === 'string' && rawFrameType.trim().length > 0
|
|
101
|
+
? rawFrameType.trim().toLowerCase()
|
|
102
|
+
: '';
|
|
103
|
+
// Extract and normalize origin type for rule matching
|
|
104
|
+
const rawOriginType = context?.originType;
|
|
105
|
+
const originTypeNormalized = typeof rawOriginType === 'string' && rawOriginType.trim().length > 0
|
|
106
|
+
? rawOriginType.trim().toLowerCase()
|
|
107
|
+
: undefined;
|
|
108
|
+
const evaluationTrace = [];
|
|
109
|
+
// Evaluate rules in order (first match wins)
|
|
110
|
+
for (const rule of this.compiledRules) {
|
|
111
|
+
const step = {
|
|
112
|
+
ruleId: rule.id,
|
|
113
|
+
result: false,
|
|
114
|
+
};
|
|
115
|
+
// Skip rules with 'when' clause (handled by advanced policy)
|
|
116
|
+
if (rule.hasWhenClause) {
|
|
117
|
+
step.expression = 'when clause (skipped by basic policy)';
|
|
118
|
+
step.result = false;
|
|
119
|
+
evaluationTrace.push(step);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Check frame type match
|
|
123
|
+
if (rule.frameTypes) {
|
|
124
|
+
if (!frameTypeNormalized) {
|
|
125
|
+
step.expression = 'frame_type: missing';
|
|
126
|
+
step.result = false;
|
|
127
|
+
evaluationTrace.push(step);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!rule.frameTypes.has(frameTypeNormalized)) {
|
|
131
|
+
step.expression = `frame_type: ${rawFrameType ?? 'unknown'} not in rule set`;
|
|
132
|
+
step.result = false;
|
|
133
|
+
evaluationTrace.push(step);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Check origin type match (early gate for efficiency)
|
|
138
|
+
if (rule.originTypes) {
|
|
139
|
+
if (originTypeNormalized === undefined) {
|
|
140
|
+
step.expression = 'origin_type: missing (rule requires origin)';
|
|
141
|
+
step.result = false;
|
|
142
|
+
evaluationTrace.push(step);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (!rule.originTypes.has(originTypeNormalized)) {
|
|
146
|
+
step.expression = `origin_type: ${rawOriginType ?? 'unknown'} not in [${Array.from(rule.originTypes).join(', ')}]`;
|
|
147
|
+
step.result = false;
|
|
148
|
+
evaluationTrace.push(step);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Check action match
|
|
153
|
+
if (!rule.actions.has('*') && !rule.actions.has(resolvedAction)) {
|
|
154
|
+
step.expression = `action: ${resolvedAction} not in [${Array.from(rule.actions).join(', ')}]`;
|
|
155
|
+
step.result = false;
|
|
156
|
+
evaluationTrace.push(step);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// Check address match (any pattern in the list matches)
|
|
160
|
+
if (rule.addressPatterns) {
|
|
161
|
+
if (!address) {
|
|
162
|
+
step.expression = `address: pattern requires address, but none provided`;
|
|
163
|
+
step.result = false;
|
|
164
|
+
evaluationTrace.push(step);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const matched = rule.addressPatterns.some((p) => p.match(address));
|
|
168
|
+
if (!matched) {
|
|
169
|
+
const patterns = rule.addressPatterns.map((p) => p.source).join(', ');
|
|
170
|
+
step.expression = `address: none of [${patterns}] matched ${address}`;
|
|
171
|
+
step.result = false;
|
|
172
|
+
evaluationTrace.push(step);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Check scope match
|
|
177
|
+
if (rule.scopeMatcher) {
|
|
178
|
+
if (!rule.scopeMatcher(grantedScopes)) {
|
|
179
|
+
step.expression = `scope: requirement not satisfied`;
|
|
180
|
+
step.boundValues = { grantedScopes: [...grantedScopes] };
|
|
181
|
+
step.result = false;
|
|
182
|
+
evaluationTrace.push(step);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Rule matched
|
|
187
|
+
step.result = true;
|
|
188
|
+
step.expression = 'all conditions matched';
|
|
189
|
+
step.boundValues = {
|
|
190
|
+
action: resolvedAction,
|
|
191
|
+
address,
|
|
192
|
+
grantedScopes: [...grantedScopes],
|
|
193
|
+
};
|
|
194
|
+
evaluationTrace.push(step);
|
|
195
|
+
logger.debug('rule_matched', {
|
|
196
|
+
ruleId: rule.id,
|
|
197
|
+
effect: rule.effect,
|
|
198
|
+
action: resolvedAction,
|
|
199
|
+
address,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
effect: rule.effect,
|
|
203
|
+
reason: rule.description ?? `Matched rule: ${rule.id}`,
|
|
204
|
+
matchedRule: rule.id,
|
|
205
|
+
evaluationTrace,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// No rule matched, apply default effect
|
|
209
|
+
logger.debug('no_rule_matched', {
|
|
210
|
+
defaultEffect: this.defaultEffect,
|
|
211
|
+
action: resolvedAction,
|
|
212
|
+
address,
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
effect: this.defaultEffect,
|
|
216
|
+
reason: `No rule matched, applying default effect: ${this.defaultEffect}`,
|
|
217
|
+
evaluationTrace,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
validateDefaultEffect(effect) {
|
|
221
|
+
if (effect !== 'allow' && effect !== 'deny') {
|
|
222
|
+
throw new Error(`Invalid default_effect: "${String(effect)}". Must be "allow" or "deny"`);
|
|
223
|
+
}
|
|
224
|
+
return effect;
|
|
225
|
+
}
|
|
226
|
+
warnUnknownPolicyFields(definition) {
|
|
227
|
+
for (const key of Object.keys(definition)) {
|
|
228
|
+
if (!authorization_policy_definition_js_1.KNOWN_POLICY_FIELDS.has(key)) {
|
|
229
|
+
logger.warning('unknown_policy_field', { field: key });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
compileRules(rules, warnOnUnknown) {
|
|
234
|
+
return rules.map((rule, index) => this.compileRule(rule, index, warnOnUnknown));
|
|
235
|
+
}
|
|
236
|
+
compileRule(rule, index, warnOnUnknown) {
|
|
237
|
+
// Generate ID if not provided
|
|
238
|
+
const id = rule.id ?? `rule_${index}`;
|
|
239
|
+
// Validate effect
|
|
240
|
+
if (!authorization_policy_definition_js_1.VALID_EFFECTS.includes(rule.effect)) {
|
|
241
|
+
throw new Error(`Invalid effect in rule "${id}": "${String(rule.effect)}". Must be "allow" or "deny"`);
|
|
242
|
+
}
|
|
243
|
+
// Validate and compile action(s)
|
|
244
|
+
const actions = this.compileActions(rule.action, id);
|
|
245
|
+
// Compile address patterns (glob-only, no regex)
|
|
246
|
+
const addressPatterns = this.compileAddress(rule.address, id);
|
|
247
|
+
// Compile frame type gating
|
|
248
|
+
const frameTypes = this.compileFrameTypes(rule.frame_type, id);
|
|
249
|
+
// Compile origin type gating
|
|
250
|
+
const originTypes = this.compileOriginTypes(rule.origin_type, id);
|
|
251
|
+
// Compile scope matcher (glob-only, no regex)
|
|
252
|
+
let scopeMatcher;
|
|
253
|
+
if (rule.scope !== undefined) {
|
|
254
|
+
try {
|
|
255
|
+
const compiled = (0, scope_matcher_js_1.compileGlobOnlyScopeRequirement)(rule.scope, id);
|
|
256
|
+
scopeMatcher = (scopes) => compiled.evaluate(scopes);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
throw new Error(`Invalid scope requirement in rule "${id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Warn about unknown fields
|
|
263
|
+
if (warnOnUnknown) {
|
|
264
|
+
for (const key of Object.keys(rule)) {
|
|
265
|
+
if (!authorization_policy_definition_js_1.KNOWN_RULE_FIELDS.has(key)) {
|
|
266
|
+
logger.warning('unknown_rule_field', { ruleId: id, field: key });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
id,
|
|
272
|
+
description: rule.description,
|
|
273
|
+
effect: rule.effect,
|
|
274
|
+
actions,
|
|
275
|
+
frameTypes,
|
|
276
|
+
originTypes,
|
|
277
|
+
addressPatterns,
|
|
278
|
+
scopeMatcher,
|
|
279
|
+
hasWhenClause: typeof rule.when === 'string' && rule.when.length > 0,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Compiles action field into a Set of valid actions.
|
|
284
|
+
* Supports single RuleAction or array of RuleAction (implicit any-of).
|
|
285
|
+
*/
|
|
286
|
+
compileActions(action, ruleId) {
|
|
287
|
+
// Default to wildcard if not specified
|
|
288
|
+
if (action === undefined) {
|
|
289
|
+
return new Set(['*']);
|
|
290
|
+
}
|
|
291
|
+
// Handle single action
|
|
292
|
+
if (typeof action === 'string') {
|
|
293
|
+
if (!authorization_policy_definition_js_1.VALID_ACTIONS.includes(action)) {
|
|
294
|
+
throw new Error(`Invalid action in rule "${ruleId}": "${action}". Must be one of: ${authorization_policy_definition_js_1.VALID_ACTIONS.join(', ')}`);
|
|
295
|
+
}
|
|
296
|
+
return new Set([action]);
|
|
297
|
+
}
|
|
298
|
+
// Handle array of actions
|
|
299
|
+
if (!Array.isArray(action)) {
|
|
300
|
+
throw new Error(`Invalid action in rule "${ruleId}": must be a string or array of strings`);
|
|
301
|
+
}
|
|
302
|
+
if (action.length === 0) {
|
|
303
|
+
throw new Error(`Invalid action in rule "${ruleId}": array must not be empty`);
|
|
304
|
+
}
|
|
305
|
+
const actions = new Set();
|
|
306
|
+
for (const a of action) {
|
|
307
|
+
if (typeof a !== 'string') {
|
|
308
|
+
throw new Error(`Invalid action in rule "${ruleId}": all values must be strings`);
|
|
309
|
+
}
|
|
310
|
+
if (!authorization_policy_definition_js_1.VALID_ACTIONS.includes(a)) {
|
|
311
|
+
throw new Error(`Invalid action in rule "${ruleId}": "${a}". Must be one of: ${authorization_policy_definition_js_1.VALID_ACTIONS.join(', ')}`);
|
|
312
|
+
}
|
|
313
|
+
actions.add(a);
|
|
314
|
+
}
|
|
315
|
+
return actions;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Compiles address field into an array of glob matchers.
|
|
319
|
+
* Supports single string or array of strings (implicit any-of).
|
|
320
|
+
* Returns undefined if not specified (no address gating).
|
|
321
|
+
*
|
|
322
|
+
* All patterns are treated as globs - `^` prefix is rejected as an error.
|
|
323
|
+
*/
|
|
324
|
+
compileAddress(address, ruleId) {
|
|
325
|
+
if (address === undefined) {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
const context = `address in rule "${ruleId}"`;
|
|
329
|
+
// Handle single address pattern
|
|
330
|
+
if (typeof address === 'string') {
|
|
331
|
+
const trimmed = address.trim();
|
|
332
|
+
if (!trimmed) {
|
|
333
|
+
throw new Error(`Invalid address in rule "${ruleId}": value must not be empty`);
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
return [(0, pattern_matcher_js_1.compileGlobPattern)(trimmed, context)];
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
throw new Error(`Invalid address in rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Handle array of address patterns
|
|
343
|
+
if (!Array.isArray(address)) {
|
|
344
|
+
throw new Error(`Invalid address in rule "${ruleId}": must be a string or array of strings`);
|
|
345
|
+
}
|
|
346
|
+
if (address.length === 0) {
|
|
347
|
+
throw new Error(`Invalid address in rule "${ruleId}": array must not be empty`);
|
|
348
|
+
}
|
|
349
|
+
const patterns = [];
|
|
350
|
+
for (const addr of address) {
|
|
351
|
+
if (typeof addr !== 'string') {
|
|
352
|
+
throw new Error(`Invalid address in rule "${ruleId}": all values must be strings`);
|
|
353
|
+
}
|
|
354
|
+
const trimmed = addr.trim();
|
|
355
|
+
if (!trimmed) {
|
|
356
|
+
throw new Error(`Invalid address in rule "${ruleId}": values must not be empty`);
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
patterns.push((0, pattern_matcher_js_1.compileGlobPattern)(trimmed, context));
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
throw new Error(`Invalid address in rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return patterns;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Compiles frame_type field into a Set of normalized frame types.
|
|
369
|
+
* Supports single string or array of strings (implicit any-of).
|
|
370
|
+
* Returns undefined if not specified (no frame type gating).
|
|
371
|
+
*/
|
|
372
|
+
compileFrameTypes(frameType, ruleId) {
|
|
373
|
+
if (frameType === undefined) {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
// Handle single frame type
|
|
377
|
+
if (typeof frameType === 'string') {
|
|
378
|
+
const normalized = frameType.trim().toLowerCase();
|
|
379
|
+
if (!normalized) {
|
|
380
|
+
throw new Error(`Invalid frame_type in rule "${ruleId}": value must not be empty`);
|
|
381
|
+
}
|
|
382
|
+
return new Set([normalized]);
|
|
383
|
+
}
|
|
384
|
+
// Handle array of frame types
|
|
385
|
+
if (!Array.isArray(frameType)) {
|
|
386
|
+
throw new Error(`Invalid frame_type in rule "${ruleId}": must be a string or array of strings`);
|
|
387
|
+
}
|
|
388
|
+
if (frameType.length === 0) {
|
|
389
|
+
throw new Error(`Invalid frame_type in rule "${ruleId}": array must not be empty`);
|
|
390
|
+
}
|
|
391
|
+
const frameTypes = new Set();
|
|
392
|
+
for (const ft of frameType) {
|
|
393
|
+
if (typeof ft !== 'string') {
|
|
394
|
+
throw new Error(`Invalid frame_type in rule "${ruleId}": all values must be strings`);
|
|
395
|
+
}
|
|
396
|
+
const normalized = ft.trim().toLowerCase();
|
|
397
|
+
if (!normalized) {
|
|
398
|
+
throw new Error(`Invalid frame_type in rule "${ruleId}": values must not be empty`);
|
|
399
|
+
}
|
|
400
|
+
frameTypes.add(normalized);
|
|
401
|
+
}
|
|
402
|
+
return frameTypes;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Compiles origin_type field into a Set of normalized origin types.
|
|
406
|
+
* Supports single string or array of strings (implicit any-of).
|
|
407
|
+
* Returns undefined if not specified (no origin type gating).
|
|
408
|
+
* Valid values: 'downstream', 'upstream', 'peer', 'local' (case-insensitive).
|
|
409
|
+
*/
|
|
410
|
+
compileOriginTypes(originType, ruleId) {
|
|
411
|
+
if (originType === undefined) {
|
|
412
|
+
return undefined;
|
|
413
|
+
}
|
|
414
|
+
// Handle single origin type
|
|
415
|
+
if (typeof originType === 'string') {
|
|
416
|
+
const normalized = originType.trim().toLowerCase();
|
|
417
|
+
if (!normalized) {
|
|
418
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": value must not be empty`);
|
|
419
|
+
}
|
|
420
|
+
if (!authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.includes(normalized)) {
|
|
421
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": "${originType}". Must be one of: ${authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.join(', ')}`);
|
|
422
|
+
}
|
|
423
|
+
return new Set([normalized]);
|
|
424
|
+
}
|
|
425
|
+
// Handle array of origin types
|
|
426
|
+
if (!Array.isArray(originType)) {
|
|
427
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": must be a string or array of strings`);
|
|
428
|
+
}
|
|
429
|
+
if (originType.length === 0) {
|
|
430
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": array must not be empty`);
|
|
431
|
+
}
|
|
432
|
+
const originTypes = new Set();
|
|
433
|
+
for (const ot of originType) {
|
|
434
|
+
if (typeof ot !== 'string') {
|
|
435
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": all values must be strings`);
|
|
436
|
+
}
|
|
437
|
+
const normalized = ot.trim().toLowerCase();
|
|
438
|
+
if (!normalized) {
|
|
439
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": values must not be empty`);
|
|
440
|
+
}
|
|
441
|
+
if (!authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.includes(normalized)) {
|
|
442
|
+
throw new Error(`Invalid origin_type in rule "${ruleId}": "${ot}". Must be one of: ${authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.join(', ')}`);
|
|
443
|
+
}
|
|
444
|
+
originTypes.add(normalized);
|
|
445
|
+
}
|
|
446
|
+
return originTypes;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
exports.BasicAuthorizationPolicy = BasicAuthorizationPolicy;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authorization policy module exports.
|
|
4
|
+
*
|
|
5
|
+
* This module provides interfaces and factories for pluggable authorization policies.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.BasicAuthorizationPolicyFactory = exports.BasicAuthorizationPolicy = exports.AuthorizationPolicySourceFactory = exports.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE = exports.AuthorizationPolicyFactory = exports.AUTHORIZATION_POLICY_FACTORY_BASE_TYPE = exports.compileGlobOnlyScopeRequirement = exports.normalizeScopeRequirement = exports.compileScopeRequirement = exports.evaluateScopeRequirement = exports.assertNotRegexPattern = exports.isRegexPattern = exports.matchPattern = exports.getCompiledGlobPattern = exports.compileGlobPattern = exports.compilePattern = void 0;
|
|
9
|
+
const tslib_1 = require("tslib");
|
|
10
|
+
// Core interfaces and types
|
|
11
|
+
tslib_1.__exportStar(require("./authorization-policy.js"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./authorization-policy-source.js"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./authorization-policy-definition.js"), exports);
|
|
14
|
+
// Pattern and scope matchers
|
|
15
|
+
var pattern_matcher_js_1 = require("./pattern-matcher.js");
|
|
16
|
+
Object.defineProperty(exports, "compilePattern", { enumerable: true, get: function () { return pattern_matcher_js_1.compilePattern; } });
|
|
17
|
+
Object.defineProperty(exports, "compileGlobPattern", { enumerable: true, get: function () { return pattern_matcher_js_1.compileGlobPattern; } });
|
|
18
|
+
Object.defineProperty(exports, "getCompiledGlobPattern", { enumerable: true, get: function () { return pattern_matcher_js_1.getCompiledGlobPattern; } });
|
|
19
|
+
Object.defineProperty(exports, "matchPattern", { enumerable: true, get: function () { return pattern_matcher_js_1.matchPattern; } });
|
|
20
|
+
Object.defineProperty(exports, "isRegexPattern", { enumerable: true, get: function () { return pattern_matcher_js_1.isRegexPattern; } });
|
|
21
|
+
Object.defineProperty(exports, "assertNotRegexPattern", { enumerable: true, get: function () { return pattern_matcher_js_1.assertNotRegexPattern; } });
|
|
22
|
+
var scope_matcher_js_1 = require("./scope-matcher.js");
|
|
23
|
+
Object.defineProperty(exports, "evaluateScopeRequirement", { enumerable: true, get: function () { return scope_matcher_js_1.evaluateScopeRequirement; } });
|
|
24
|
+
Object.defineProperty(exports, "compileScopeRequirement", { enumerable: true, get: function () { return scope_matcher_js_1.compileScopeRequirement; } });
|
|
25
|
+
Object.defineProperty(exports, "normalizeScopeRequirement", { enumerable: true, get: function () { return scope_matcher_js_1.normalizeScopeRequirement; } });
|
|
26
|
+
Object.defineProperty(exports, "compileGlobOnlyScopeRequirement", { enumerable: true, get: function () { return scope_matcher_js_1.compileGlobOnlyScopeRequirement; } });
|
|
27
|
+
// Factory base classes
|
|
28
|
+
var authorization_policy_factory_js_1 = require("./authorization-policy-factory.js");
|
|
29
|
+
Object.defineProperty(exports, "AUTHORIZATION_POLICY_FACTORY_BASE_TYPE", { enumerable: true, get: function () { return authorization_policy_factory_js_1.AUTHORIZATION_POLICY_FACTORY_BASE_TYPE; } });
|
|
30
|
+
Object.defineProperty(exports, "AuthorizationPolicyFactory", { enumerable: true, get: function () { return authorization_policy_factory_js_1.AuthorizationPolicyFactory; } });
|
|
31
|
+
var authorization_policy_source_factory_js_1 = require("./authorization-policy-source-factory.js");
|
|
32
|
+
Object.defineProperty(exports, "AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE", { enumerable: true, get: function () { return authorization_policy_source_factory_js_1.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE; } });
|
|
33
|
+
Object.defineProperty(exports, "AuthorizationPolicySourceFactory", { enumerable: true, get: function () { return authorization_policy_source_factory_js_1.AuthorizationPolicySourceFactory; } });
|
|
34
|
+
// Basic authorization policy (browser and node)
|
|
35
|
+
var basic_authorization_policy_js_1 = require("./basic-authorization-policy.js");
|
|
36
|
+
Object.defineProperty(exports, "BasicAuthorizationPolicy", { enumerable: true, get: function () { return basic_authorization_policy_js_1.BasicAuthorizationPolicy; } });
|
|
37
|
+
var basic_authorization_policy_factory_js_1 = require("./basic-authorization-policy-factory.js");
|
|
38
|
+
Object.defineProperty(exports, "BasicAuthorizationPolicyFactory", { enumerable: true, get: function () { return basic_authorization_policy_factory_js_1.BasicAuthorizationPolicyFactory; } });
|
|
39
|
+
// Note: LocalFileAuthorizationPolicySource and its factory are node-only
|
|
40
|
+
// and are registered via the factory manifest, not exported here directly.
|