@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,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LocalFileAuthorizationPolicySourceFactory = exports.FACTORY_META = void 0;
|
|
37
|
+
const lazy_import_js_1 = require("../../../util/lazy-import.js");
|
|
38
|
+
const authorization_policy_source_factory_js_1 = require("./authorization-policy-source-factory.js");
|
|
39
|
+
let localFileModulePromise = null;
|
|
40
|
+
async function getLocalFileModule() {
|
|
41
|
+
if (!localFileModulePromise) {
|
|
42
|
+
localFileModulePromise = (0, lazy_import_js_1.safeImport)(() => Promise.resolve().then(() => __importStar(require('./local-file-authorization-policy-source.js'))), 'local-file-authorization-policy-source');
|
|
43
|
+
}
|
|
44
|
+
return localFileModulePromise;
|
|
45
|
+
}
|
|
46
|
+
function normalizeConfig(config) {
|
|
47
|
+
if (!config) {
|
|
48
|
+
throw new Error('LocalFileAuthorizationPolicySourceFactory requires a configuration with a path');
|
|
49
|
+
}
|
|
50
|
+
const candidate = config;
|
|
51
|
+
const path = candidate.path;
|
|
52
|
+
if (typeof path !== 'string' || path.trim().length === 0) {
|
|
53
|
+
throw new Error('LocalFileAuthorizationPolicySourceConfig requires a non-empty path');
|
|
54
|
+
}
|
|
55
|
+
const format = candidate.format;
|
|
56
|
+
if (format !== undefined && !['yaml', 'json', 'auto'].includes(format)) {
|
|
57
|
+
throw new Error(`Invalid format "${String(format)}". Must be "yaml", "json", or "auto"`);
|
|
58
|
+
}
|
|
59
|
+
const policyFactory = candidate.policyFactory;
|
|
60
|
+
return {
|
|
61
|
+
path: path.trim(),
|
|
62
|
+
format: format ?? 'auto',
|
|
63
|
+
policyFactory,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Factory metadata for registration.
|
|
68
|
+
*/
|
|
69
|
+
exports.FACTORY_META = {
|
|
70
|
+
base: authorization_policy_source_factory_js_1.AUTHORIZATION_POLICY_SOURCE_FACTORY_BASE_TYPE,
|
|
71
|
+
key: 'LocalFileAuthorizationPolicySource',
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Factory for creating LocalFileAuthorizationPolicySource instances.
|
|
75
|
+
*
|
|
76
|
+
* This factory uses lazy loading to avoid pulling in Node.js-specific
|
|
77
|
+
* code (filesystem operations) in browser environments.
|
|
78
|
+
*/
|
|
79
|
+
class LocalFileAuthorizationPolicySourceFactory extends authorization_policy_source_factory_js_1.AuthorizationPolicySourceFactory {
|
|
80
|
+
constructor() {
|
|
81
|
+
super(...arguments);
|
|
82
|
+
this.type = 'LocalFileAuthorizationPolicySource';
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a LocalFileAuthorizationPolicySource from the given configuration.
|
|
86
|
+
*
|
|
87
|
+
* @param config - Configuration specifying the policy file path and options
|
|
88
|
+
* @returns The created policy source
|
|
89
|
+
*/
|
|
90
|
+
async create(config) {
|
|
91
|
+
const normalized = normalizeConfig(config);
|
|
92
|
+
const { LocalFileAuthorizationPolicySource } = await getLocalFileModule();
|
|
93
|
+
return new LocalFileAuthorizationPolicySource({
|
|
94
|
+
path: normalized.path,
|
|
95
|
+
format: normalized.format,
|
|
96
|
+
policyFactory: normalized.policyFactory,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.LocalFileAuthorizationPolicySourceFactory = LocalFileAuthorizationPolicySourceFactory;
|
|
101
|
+
exports.default = LocalFileAuthorizationPolicySourceFactory;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LocalFileAuthorizationPolicySource = void 0;
|
|
37
|
+
const yaml_1 = require("yaml");
|
|
38
|
+
const logging_js_1 = require("../../../util/logging.js");
|
|
39
|
+
const authorization_policy_factory_js_1 = require("./authorization-policy-factory.js");
|
|
40
|
+
const logger = (0, logging_js_1.getLogger)('naylence.fame.security.auth.policy.local_file_authorization_policy_source');
|
|
41
|
+
function isPlainObject(value) {
|
|
42
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function parseJson(content) {
|
|
45
|
+
const parsed = JSON.parse(content);
|
|
46
|
+
if (!isPlainObject(parsed)) {
|
|
47
|
+
throw new Error('Parsed JSON policy must be an object');
|
|
48
|
+
}
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
function parseYamlContent(content) {
|
|
52
|
+
const parsed = (0, yaml_1.parse)(content ?? '');
|
|
53
|
+
if (parsed == null) {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
if (!isPlainObject(parsed)) {
|
|
57
|
+
throw new Error('Parsed YAML policy must be an object');
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
function detectFormat(filePath) {
|
|
62
|
+
const lower = filePath.toLowerCase();
|
|
63
|
+
if (lower.endsWith('.yaml') || lower.endsWith('.yml')) {
|
|
64
|
+
return 'yaml';
|
|
65
|
+
}
|
|
66
|
+
if (lower.endsWith('.json')) {
|
|
67
|
+
return 'json';
|
|
68
|
+
}
|
|
69
|
+
// Default to YAML for unknown extensions
|
|
70
|
+
return 'yaml';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* An authorization policy source that loads policy definitions from a local file.
|
|
74
|
+
*
|
|
75
|
+
* Supports YAML and JSON formats. The file must contain a valid policy
|
|
76
|
+
* configuration object that can be used to create an AuthorizationPolicy
|
|
77
|
+
* via the factory system.
|
|
78
|
+
*
|
|
79
|
+
* This is a Node.js-only implementation that uses the filesystem.
|
|
80
|
+
*/
|
|
81
|
+
class LocalFileAuthorizationPolicySource {
|
|
82
|
+
constructor(options) {
|
|
83
|
+
this.cachedPolicy = null;
|
|
84
|
+
this.path = options.path;
|
|
85
|
+
this.format = options.format ?? 'auto';
|
|
86
|
+
this.policyFactoryConfig = options.policyFactory;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Loads the authorization policy from the configured file.
|
|
90
|
+
*
|
|
91
|
+
* The file is read and parsed according to the configured format.
|
|
92
|
+
* The parsed content is then used to create an AuthorizationPolicy
|
|
93
|
+
* via the factory system.
|
|
94
|
+
*
|
|
95
|
+
* @returns The loaded authorization policy
|
|
96
|
+
*/
|
|
97
|
+
async loadPolicy() {
|
|
98
|
+
// Return cached policy if available
|
|
99
|
+
if (this.cachedPolicy) {
|
|
100
|
+
return this.cachedPolicy;
|
|
101
|
+
}
|
|
102
|
+
logger.debug('loading_policy_from_file', { path: this.path });
|
|
103
|
+
// Dynamic import of fs for Node.js
|
|
104
|
+
const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
105
|
+
// Read the file
|
|
106
|
+
const content = await fs.readFile(this.path, 'utf-8');
|
|
107
|
+
// Determine format
|
|
108
|
+
const effectiveFormat = this.format === 'auto' ? detectFormat(this.path) : this.format;
|
|
109
|
+
// Parse the content
|
|
110
|
+
let policyDefinition;
|
|
111
|
+
if (effectiveFormat === 'json') {
|
|
112
|
+
policyDefinition = parseJson(content);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
policyDefinition = parseYamlContent(content);
|
|
116
|
+
}
|
|
117
|
+
logger.debug('parsed_policy_definition', {
|
|
118
|
+
path: this.path,
|
|
119
|
+
format: effectiveFormat,
|
|
120
|
+
hasType: 'type' in policyDefinition,
|
|
121
|
+
});
|
|
122
|
+
// Determine the factory configuration to use
|
|
123
|
+
const factoryConfig = this.policyFactoryConfig ?? policyDefinition;
|
|
124
|
+
// Ensure we have a type field for the factory
|
|
125
|
+
if (!('type' in factoryConfig) || typeof factoryConfig.type !== 'string') {
|
|
126
|
+
throw new Error(`Policy definition at ${this.path} must have a 'type' field, ` +
|
|
127
|
+
`or policyFactory config must be provided`);
|
|
128
|
+
}
|
|
129
|
+
// Build the factory config with the policy definition
|
|
130
|
+
// The file content IS the policy definition, so we extract the type
|
|
131
|
+
// and wrap the remaining content as the policyDefinition
|
|
132
|
+
const { type, ...restOfFile } = policyDefinition;
|
|
133
|
+
const mergedConfig = this.policyFactoryConfig != null
|
|
134
|
+
? { ...this.policyFactoryConfig, policyDefinition }
|
|
135
|
+
: { type: factoryConfig.type, policyDefinition: restOfFile };
|
|
136
|
+
// Create the policy using the factory system
|
|
137
|
+
const policy = await authorization_policy_factory_js_1.AuthorizationPolicyFactory.createAuthorizationPolicy(mergedConfig);
|
|
138
|
+
if (!policy) {
|
|
139
|
+
throw new Error(`Failed to create authorization policy from ${this.path}`);
|
|
140
|
+
}
|
|
141
|
+
this.cachedPolicy = policy;
|
|
142
|
+
logger.info('loaded_policy_from_file', {
|
|
143
|
+
path: this.path,
|
|
144
|
+
policyType: factoryConfig.type,
|
|
145
|
+
});
|
|
146
|
+
return policy;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Clears the cached policy, forcing a reload on the next loadPolicy() call.
|
|
150
|
+
*/
|
|
151
|
+
clearCache() {
|
|
152
|
+
this.cachedPolicy = null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Reloads the policy from the file, clearing any cached version.
|
|
156
|
+
*
|
|
157
|
+
* @returns The reloaded authorization policy
|
|
158
|
+
*/
|
|
159
|
+
async reloadPolicy() {
|
|
160
|
+
this.clearCache();
|
|
161
|
+
return this.loadPolicy();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.LocalFileAuthorizationPolicySource = LocalFileAuthorizationPolicySource;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern matching utilities for authorization policies.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - Glob patterns: `*` (single segment), `**` (any depth), `?` (single char)
|
|
7
|
+
* - Regex patterns: patterns starting with `^` (for advanced/BSL use only)
|
|
8
|
+
*
|
|
9
|
+
* The OSS/basic policy uses glob-only matching via `compileGlobPattern()`.
|
|
10
|
+
* The advanced/BSL policy may use `compilePattern()` which interprets `^` as regex.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isRegexPattern = isRegexPattern;
|
|
14
|
+
exports.assertNotRegexPattern = assertNotRegexPattern;
|
|
15
|
+
exports.compilePattern = compilePattern;
|
|
16
|
+
exports.compileGlobPattern = compileGlobPattern;
|
|
17
|
+
exports.getCompiledPattern = getCompiledPattern;
|
|
18
|
+
exports.getCompiledGlobPattern = getCompiledGlobPattern;
|
|
19
|
+
exports.matchPattern = matchPattern;
|
|
20
|
+
exports.clearPatternCache = clearPatternCache;
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a pattern string is a regex pattern.
|
|
23
|
+
* Regex patterns start with `^`.
|
|
24
|
+
*/
|
|
25
|
+
function isRegexPattern(pattern) {
|
|
26
|
+
return pattern.startsWith('^');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Asserts that a pattern is not a regex pattern.
|
|
30
|
+
* Throws an error if the pattern starts with `^`.
|
|
31
|
+
*
|
|
32
|
+
* Use this in OSS/basic policy to reject regex patterns.
|
|
33
|
+
*
|
|
34
|
+
* @param pattern - The pattern to check
|
|
35
|
+
* @param context - Optional context for the error message (e.g., "address", "scope")
|
|
36
|
+
* @throws Error if the pattern is a regex pattern
|
|
37
|
+
*/
|
|
38
|
+
function assertNotRegexPattern(pattern, context) {
|
|
39
|
+
if (pattern.startsWith('^')) {
|
|
40
|
+
const contextStr = context ? ` in ${context}` : '';
|
|
41
|
+
throw new Error(`Regex patterns are not supported${contextStr} in OSS/basic policy. ` +
|
|
42
|
+
`Pattern "${pattern}" starts with '^'. Use glob patterns instead.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Escapes special regex characters in a string.
|
|
47
|
+
*/
|
|
48
|
+
function escapeRegex(str) {
|
|
49
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Converts a glob pattern to a regex pattern.
|
|
53
|
+
*
|
|
54
|
+
* Glob syntax:
|
|
55
|
+
* - `*` matches a single segment (no dots)
|
|
56
|
+
* - `**` matches any number of segments (including zero)
|
|
57
|
+
* - Other characters are matched literally
|
|
58
|
+
*
|
|
59
|
+
* @param glob - The glob pattern to convert
|
|
60
|
+
* @returns A regex pattern string (without anchors)
|
|
61
|
+
*/
|
|
62
|
+
function globToRegex(glob) {
|
|
63
|
+
const parts = [];
|
|
64
|
+
let i = 0;
|
|
65
|
+
while (i < glob.length) {
|
|
66
|
+
if (glob[i] === '*') {
|
|
67
|
+
if (glob[i + 1] === '*') {
|
|
68
|
+
// `**` matches any characters (including dots)
|
|
69
|
+
parts.push('.*');
|
|
70
|
+
i += 2;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// `*` matches any characters except dots (single segment)
|
|
74
|
+
parts.push('[^.]*');
|
|
75
|
+
i += 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (glob[i] === '?') {
|
|
79
|
+
// `?` matches a single character (not a dot)
|
|
80
|
+
parts.push('[^.]');
|
|
81
|
+
i += 1;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Escape and add literal character
|
|
85
|
+
parts.push(escapeRegex(glob[i]));
|
|
86
|
+
i += 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return parts.join('');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Compiles a pattern string into an efficient matcher.
|
|
93
|
+
*
|
|
94
|
+
* @param pattern - Glob pattern or regex (starting with `^`)
|
|
95
|
+
* @returns A compiled pattern object
|
|
96
|
+
* @throws Error if the regex pattern is invalid
|
|
97
|
+
*/
|
|
98
|
+
function compilePattern(pattern) {
|
|
99
|
+
if (isRegexPattern(pattern)) {
|
|
100
|
+
// Regex pattern - compile directly
|
|
101
|
+
const regex = new RegExp(pattern);
|
|
102
|
+
return {
|
|
103
|
+
source: pattern,
|
|
104
|
+
isRegex: true,
|
|
105
|
+
match: (value) => regex.test(value),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Glob pattern - convert to regex with anchors
|
|
109
|
+
const regexStr = `^${globToRegex(pattern)}$`;
|
|
110
|
+
const regex = new RegExp(regexStr);
|
|
111
|
+
return {
|
|
112
|
+
source: pattern,
|
|
113
|
+
isRegex: false,
|
|
114
|
+
match: (value) => regex.test(value),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compiles a pattern string as a glob pattern only (no regex interpretation).
|
|
119
|
+
*
|
|
120
|
+
* This is the preferred method for OSS/basic policy evaluation.
|
|
121
|
+
* Patterns starting with `^` are rejected with an error.
|
|
122
|
+
*
|
|
123
|
+
* @param pattern - Glob pattern (regex patterns rejected)
|
|
124
|
+
* @param context - Optional context for error messages
|
|
125
|
+
* @returns A compiled pattern object
|
|
126
|
+
* @throws Error if pattern starts with `^` (regex attempt)
|
|
127
|
+
*/
|
|
128
|
+
function compileGlobPattern(pattern, context) {
|
|
129
|
+
// Reject regex patterns in OSS/basic policy
|
|
130
|
+
assertNotRegexPattern(pattern, context);
|
|
131
|
+
// Convert glob to regex with anchors
|
|
132
|
+
const regexStr = `^${globToRegex(pattern)}$`;
|
|
133
|
+
const regex = new RegExp(regexStr);
|
|
134
|
+
return {
|
|
135
|
+
source: pattern,
|
|
136
|
+
isRegex: false,
|
|
137
|
+
match: (value) => regex.test(value),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Cache for compiled patterns to avoid recompilation.
|
|
142
|
+
*/
|
|
143
|
+
const patternCache = new Map();
|
|
144
|
+
/**
|
|
145
|
+
* Cache for glob-only compiled patterns.
|
|
146
|
+
*/
|
|
147
|
+
const globPatternCache = new Map();
|
|
148
|
+
/**
|
|
149
|
+
* Gets or compiles a pattern, with caching.
|
|
150
|
+
*
|
|
151
|
+
* @param pattern - Glob pattern or regex
|
|
152
|
+
* @returns A compiled pattern object
|
|
153
|
+
*/
|
|
154
|
+
function getCompiledPattern(pattern) {
|
|
155
|
+
let compiled = patternCache.get(pattern);
|
|
156
|
+
if (!compiled) {
|
|
157
|
+
compiled = compilePattern(pattern);
|
|
158
|
+
patternCache.set(pattern, compiled);
|
|
159
|
+
}
|
|
160
|
+
return compiled;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Gets or compiles a glob-only pattern, with caching.
|
|
164
|
+
*
|
|
165
|
+
* This is the preferred method for OSS/basic policy evaluation.
|
|
166
|
+
* Patterns are always treated as globs, never regex.
|
|
167
|
+
*
|
|
168
|
+
* @param pattern - Glob pattern (never interpreted as regex)
|
|
169
|
+
* @returns A compiled pattern object
|
|
170
|
+
*/
|
|
171
|
+
function getCompiledGlobPattern(pattern) {
|
|
172
|
+
let compiled = globPatternCache.get(pattern);
|
|
173
|
+
if (!compiled) {
|
|
174
|
+
compiled = compileGlobPattern(pattern);
|
|
175
|
+
globPatternCache.set(pattern, compiled);
|
|
176
|
+
}
|
|
177
|
+
return compiled;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Matches a value against a pattern string.
|
|
181
|
+
*
|
|
182
|
+
* @param pattern - Glob pattern or regex (starting with `^`)
|
|
183
|
+
* @param value - The value to match
|
|
184
|
+
* @returns True if the value matches the pattern
|
|
185
|
+
*/
|
|
186
|
+
function matchPattern(pattern, value) {
|
|
187
|
+
return getCompiledPattern(pattern).match(value);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Clears the pattern cache.
|
|
191
|
+
* Useful for testing or when memory is a concern.
|
|
192
|
+
*/
|
|
193
|
+
function clearPatternCache() {
|
|
194
|
+
patternCache.clear();
|
|
195
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scope matching utilities for authorization policies.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - Simple string patterns (glob only in OSS/basic policy)
|
|
7
|
+
* - Logical operators: any_of, all_of, none_of
|
|
8
|
+
* - Recursive nesting with depth limits
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.normalizeScopeRequirement = normalizeScopeRequirement;
|
|
12
|
+
exports.evaluateNormalizedScopeRequirement = evaluateNormalizedScopeRequirement;
|
|
13
|
+
exports.evaluateScopeRequirement = evaluateScopeRequirement;
|
|
14
|
+
exports.compileScopeRequirement = compileScopeRequirement;
|
|
15
|
+
exports.compileGlobOnlyScopeRequirement = compileGlobOnlyScopeRequirement;
|
|
16
|
+
const authorization_policy_definition_js_1 = require("./authorization-policy-definition.js");
|
|
17
|
+
const pattern_matcher_js_1 = require("./pattern-matcher.js");
|
|
18
|
+
/**
|
|
19
|
+
* Checks if any of the granted scopes match the given pattern.
|
|
20
|
+
*/
|
|
21
|
+
function anyScopeMatchesPattern(grantedScopes, pattern) {
|
|
22
|
+
return grantedScopes.some((scope) => (0, pattern_matcher_js_1.matchPattern)(pattern, scope));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes a scope requirement into a typed structure.
|
|
26
|
+
*
|
|
27
|
+
* @param requirement - The scope requirement to normalize
|
|
28
|
+
* @param depth - Current nesting depth (for recursion limit)
|
|
29
|
+
* @returns Normalized scope requirement
|
|
30
|
+
* @throws Error if nesting exceeds maximum depth
|
|
31
|
+
*/
|
|
32
|
+
function normalizeScopeRequirement(requirement, depth = 0) {
|
|
33
|
+
if (depth > authorization_policy_definition_js_1.MAX_SCOPE_NESTING_DEPTH) {
|
|
34
|
+
throw new Error(`Scope requirement nesting exceeds maximum depth of ${authorization_policy_definition_js_1.MAX_SCOPE_NESTING_DEPTH}`);
|
|
35
|
+
}
|
|
36
|
+
// Simple string pattern
|
|
37
|
+
if (typeof requirement === 'string') {
|
|
38
|
+
return { type: 'pattern', pattern: requirement };
|
|
39
|
+
}
|
|
40
|
+
// Object with logical operator
|
|
41
|
+
if (typeof requirement !== 'object' || requirement === null) {
|
|
42
|
+
throw new Error(`Invalid scope requirement: ${String(requirement)}`);
|
|
43
|
+
}
|
|
44
|
+
const keys = Object.keys(requirement);
|
|
45
|
+
if (keys.length !== 1) {
|
|
46
|
+
throw new Error(`Scope requirement object must have exactly one key (any_of, all_of, or none_of), got: ${keys.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
const key = keys[0];
|
|
49
|
+
const value = requirement[key];
|
|
50
|
+
if (!Array.isArray(value)) {
|
|
51
|
+
throw new Error(`Scope requirement "${key}" must have an array value, got: ${typeof value}`);
|
|
52
|
+
}
|
|
53
|
+
const nested = value.map((item) => normalizeScopeRequirement(item, depth + 1));
|
|
54
|
+
switch (key) {
|
|
55
|
+
case 'any_of':
|
|
56
|
+
return { type: 'any_of', requirements: nested };
|
|
57
|
+
case 'all_of':
|
|
58
|
+
return { type: 'all_of', requirements: nested };
|
|
59
|
+
case 'none_of':
|
|
60
|
+
return { type: 'none_of', requirements: nested };
|
|
61
|
+
default:
|
|
62
|
+
throw new Error(`Unknown scope requirement operator: "${key}". Expected any_of, all_of, or none_of`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Evaluates a normalized scope requirement against granted scopes.
|
|
67
|
+
*
|
|
68
|
+
* @param requirement - The normalized scope requirement
|
|
69
|
+
* @param grantedScopes - The scopes granted to the principal
|
|
70
|
+
* @returns True if the requirement is satisfied
|
|
71
|
+
*/
|
|
72
|
+
function evaluateNormalizedScopeRequirement(requirement, grantedScopes) {
|
|
73
|
+
switch (requirement.type) {
|
|
74
|
+
case 'pattern':
|
|
75
|
+
return anyScopeMatchesPattern(grantedScopes, requirement.pattern);
|
|
76
|
+
case 'any_of':
|
|
77
|
+
return requirement.requirements.some((req) => evaluateNormalizedScopeRequirement(req, grantedScopes));
|
|
78
|
+
case 'all_of':
|
|
79
|
+
return requirement.requirements.every((req) => evaluateNormalizedScopeRequirement(req, grantedScopes));
|
|
80
|
+
case 'none_of':
|
|
81
|
+
return !requirement.requirements.some((req) => evaluateNormalizedScopeRequirement(req, grantedScopes));
|
|
82
|
+
default:
|
|
83
|
+
// Exhaustive check
|
|
84
|
+
throw new Error(`Unknown scope requirement type: ${requirement.type}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Evaluates a scope requirement against granted scopes.
|
|
89
|
+
*
|
|
90
|
+
* This is the main entry point for scope matching.
|
|
91
|
+
*
|
|
92
|
+
* @param requirement - The scope requirement (string or object)
|
|
93
|
+
* @param grantedScopes - The scopes granted to the principal
|
|
94
|
+
* @returns True if the requirement is satisfied
|
|
95
|
+
*/
|
|
96
|
+
function evaluateScopeRequirement(requirement, grantedScopes) {
|
|
97
|
+
const normalized = normalizeScopeRequirement(requirement);
|
|
98
|
+
return evaluateNormalizedScopeRequirement(normalized, grantedScopes);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Pre-compiles a scope requirement for efficient repeated evaluation.
|
|
102
|
+
*
|
|
103
|
+
* @param requirement - The scope requirement to compile
|
|
104
|
+
* @returns A function that evaluates the requirement against granted scopes
|
|
105
|
+
*/
|
|
106
|
+
function compileScopeRequirement(requirement) {
|
|
107
|
+
const normalized = normalizeScopeRequirement(requirement);
|
|
108
|
+
return (grantedScopes) => evaluateNormalizedScopeRequirement(normalized, grantedScopes);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Pre-compiles a scope requirement for OSS/basic policy (glob-only, no regex).
|
|
112
|
+
*
|
|
113
|
+
* This version rejects patterns starting with `^` at compile time.
|
|
114
|
+
*
|
|
115
|
+
* @param requirement - The scope requirement to compile
|
|
116
|
+
* @param ruleId - Rule ID for error messages
|
|
117
|
+
* @returns A compiled scope requirement
|
|
118
|
+
* @throws Error if any pattern starts with `^` (regex attempt)
|
|
119
|
+
*/
|
|
120
|
+
function compileGlobOnlyScopeRequirement(requirement, ruleId) {
|
|
121
|
+
const context = `scope in rule "${ruleId}"`;
|
|
122
|
+
// Compile the requirement, pre-compiling all patterns as globs
|
|
123
|
+
const compiled = compileGlobOnlyNormalized(normalizeScopeRequirement(requirement), context);
|
|
124
|
+
return {
|
|
125
|
+
evaluate: (grantedScopes) => evaluateCompiledScope(compiled, grantedScopes),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Compiles a normalized scope requirement into efficient matchers (glob-only).
|
|
130
|
+
*/
|
|
131
|
+
function compileGlobOnlyNormalized(requirement, context) {
|
|
132
|
+
switch (requirement.type) {
|
|
133
|
+
case 'pattern':
|
|
134
|
+
return {
|
|
135
|
+
type: 'pattern',
|
|
136
|
+
matcher: (0, pattern_matcher_js_1.compileGlobPattern)(requirement.pattern, context),
|
|
137
|
+
};
|
|
138
|
+
case 'any_of':
|
|
139
|
+
return {
|
|
140
|
+
type: 'any_of',
|
|
141
|
+
requirements: requirement.requirements.map((r) => compileGlobOnlyNormalized(r, context)),
|
|
142
|
+
};
|
|
143
|
+
case 'all_of':
|
|
144
|
+
return {
|
|
145
|
+
type: 'all_of',
|
|
146
|
+
requirements: requirement.requirements.map((r) => compileGlobOnlyNormalized(r, context)),
|
|
147
|
+
};
|
|
148
|
+
case 'none_of':
|
|
149
|
+
return {
|
|
150
|
+
type: 'none_of',
|
|
151
|
+
requirements: requirement.requirements.map((r) => compileGlobOnlyNormalized(r, context)),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Evaluates a compiled scope node against granted scopes.
|
|
157
|
+
*/
|
|
158
|
+
function evaluateCompiledScope(node, grantedScopes) {
|
|
159
|
+
switch (node.type) {
|
|
160
|
+
case 'pattern':
|
|
161
|
+
return grantedScopes.some((scope) => node.matcher.match(scope));
|
|
162
|
+
case 'any_of':
|
|
163
|
+
return node.requirements.some((r) => evaluateCompiledScope(r, grantedScopes));
|
|
164
|
+
case 'all_of':
|
|
165
|
+
return node.requirements.every((r) => evaluateCompiledScope(r, grantedScopes));
|
|
166
|
+
case 'none_of':
|
|
167
|
+
return !node.requirements.some((r) => evaluateCompiledScope(r, grantedScopes));
|
|
168
|
+
}
|
|
169
|
+
}
|