@naylence/runtime 0.4.2 → 0.4.4

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.
@@ -93,6 +93,7 @@ class BasicAuthorizationPolicy {
93
93
  // Action must be explicitly provided; default to wildcard if omitted
94
94
  // for backward compatibility during transition
95
95
  const resolvedAction = action ?? '*';
96
+ const resolvedActionNormalized = this.normalizeActionToken(resolvedAction) ?? resolvedAction;
96
97
  const address = extractAddress(envelope);
97
98
  const grantedScopes = extractGrantedScopes(context);
98
99
  const rawFrameType = envelope.frame
@@ -102,8 +103,8 @@ class BasicAuthorizationPolicy {
102
103
  : '';
103
104
  // Extract and normalize origin type for rule matching
104
105
  const rawOriginType = context?.originType;
105
- const originTypeNormalized = typeof rawOriginType === 'string' && rawOriginType.trim().length > 0
106
- ? rawOriginType.trim().toLowerCase()
106
+ const originTypeNormalized = typeof rawOriginType === 'string'
107
+ ? this.normalizeOriginTypeToken(rawOriginType) ?? undefined
107
108
  : undefined;
108
109
  const evaluationTrace = [];
109
110
  // Evaluate rules in order (first match wins)
@@ -150,8 +151,8 @@ class BasicAuthorizationPolicy {
150
151
  }
151
152
  }
152
153
  // Check action match
153
- if (!rule.actions.has('*') && !rule.actions.has(resolvedAction)) {
154
- step.expression = `action: ${resolvedAction} not in [${Array.from(rule.actions).join(', ')}]`;
154
+ if (!rule.actions.has('*') && !rule.actions.has(resolvedActionNormalized)) {
155
+ step.expression = `action: ${resolvedActionNormalized} not in [${Array.from(rule.actions).join(', ')}]`;
155
156
  step.result = false;
156
157
  evaluationTrace.push(step);
157
158
  continue;
@@ -218,6 +219,9 @@ class BasicAuthorizationPolicy {
218
219
  };
219
220
  }
220
221
  validateDefaultEffect(effect) {
222
+ if (effect === undefined || effect === null) {
223
+ return 'deny';
224
+ }
221
225
  if (effect !== 'allow' && effect !== 'deny') {
222
226
  throw new Error(`Invalid default_effect: "${String(effect)}". Must be "allow" or "deny"`);
223
227
  }
@@ -290,10 +294,11 @@ class BasicAuthorizationPolicy {
290
294
  }
291
295
  // Handle single action
292
296
  if (typeof action === 'string') {
293
- if (!authorization_policy_definition_js_1.VALID_ACTIONS.includes(action)) {
297
+ const normalized = this.normalizeActionToken(action);
298
+ if (!normalized) {
294
299
  throw new Error(`Invalid action in rule "${ruleId}": "${action}". Must be one of: ${authorization_policy_definition_js_1.VALID_ACTIONS.join(', ')}`);
295
300
  }
296
- return new Set([action]);
301
+ return new Set([normalized]);
297
302
  }
298
303
  // Handle array of actions
299
304
  if (!Array.isArray(action)) {
@@ -307,10 +312,11 @@ class BasicAuthorizationPolicy {
307
312
  if (typeof a !== 'string') {
308
313
  throw new Error(`Invalid action in rule "${ruleId}": all values must be strings`);
309
314
  }
310
- if (!authorization_policy_definition_js_1.VALID_ACTIONS.includes(a)) {
315
+ const normalized = this.normalizeActionToken(a);
316
+ if (!normalized) {
311
317
  throw new Error(`Invalid action in rule "${ruleId}": "${a}". Must be one of: ${authorization_policy_definition_js_1.VALID_ACTIONS.join(', ')}`);
312
318
  }
313
- actions.add(a);
319
+ actions.add(normalized);
314
320
  }
315
321
  return actions;
316
322
  }
@@ -413,11 +419,12 @@ class BasicAuthorizationPolicy {
413
419
  }
414
420
  // Handle single origin type
415
421
  if (typeof originType === 'string') {
416
- const normalized = originType.trim().toLowerCase();
417
- if (!normalized) {
422
+ const trimmed = originType.trim();
423
+ if (!trimmed) {
418
424
  throw new Error(`Invalid origin_type in rule "${ruleId}": value must not be empty`);
419
425
  }
420
- if (!authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.includes(normalized)) {
426
+ const normalized = this.normalizeOriginTypeToken(trimmed);
427
+ if (!normalized) {
421
428
  throw new Error(`Invalid origin_type in rule "${ruleId}": "${originType}". Must be one of: ${authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.join(', ')}`);
422
429
  }
423
430
  return new Set([normalized]);
@@ -434,16 +441,49 @@ class BasicAuthorizationPolicy {
434
441
  if (typeof ot !== 'string') {
435
442
  throw new Error(`Invalid origin_type in rule "${ruleId}": all values must be strings`);
436
443
  }
437
- const normalized = ot.trim().toLowerCase();
438
- if (!normalized) {
444
+ const trimmed = ot.trim();
445
+ if (!trimmed) {
439
446
  throw new Error(`Invalid origin_type in rule "${ruleId}": values must not be empty`);
440
447
  }
441
- if (!authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.includes(normalized)) {
448
+ const normalized = this.normalizeOriginTypeToken(trimmed);
449
+ if (!normalized) {
442
450
  throw new Error(`Invalid origin_type in rule "${ruleId}": "${ot}". Must be one of: ${authorization_policy_definition_js_1.VALID_ORIGIN_TYPES.join(', ')}`);
443
451
  }
444
452
  originTypes.add(normalized);
445
453
  }
446
454
  return originTypes;
447
455
  }
456
+ normalizeActionToken(value) {
457
+ const trimmed = value.trim();
458
+ if (!trimmed) {
459
+ return null;
460
+ }
461
+ if (trimmed === '*') {
462
+ return '*';
463
+ }
464
+ const normalized = trimmed.replace(/[\s_-]+/g, '').toLowerCase();
465
+ const map = {
466
+ connect: 'Connect',
467
+ forwardupstream: 'ForwardUpstream',
468
+ forwarddownstream: 'ForwardDownstream',
469
+ forwardpeer: 'ForwardPeer',
470
+ deliverlocal: 'DeliverLocal',
471
+ };
472
+ return map[normalized] ?? null;
473
+ }
474
+ normalizeOriginTypeToken(value) {
475
+ const trimmed = value.trim();
476
+ if (!trimmed) {
477
+ return null;
478
+ }
479
+ const normalized = trimmed.replace(/[\s_-]+/g, '').toLowerCase();
480
+ const map = {
481
+ downstream: 'downstream',
482
+ upstream: 'upstream',
483
+ peer: 'peer',
484
+ local: 'local',
485
+ };
486
+ return map[normalized] ?? null;
487
+ }
448
488
  }
449
489
  exports.BasicAuthorizationPolicy = BasicAuthorizationPolicy;
@@ -123,16 +123,22 @@ class LocalFileAuthorizationPolicySource {
123
123
  const factoryConfig = this.policyFactoryConfig ?? policyDefinition;
124
124
  // Ensure we have a type field for the factory
125
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`);
126
+ logger.warning('policy_type_missing_defaulting_to_basic', {
127
+ path: this.path,
128
+ });
129
+ factoryConfig.type =
130
+ 'BasicAuthorizationPolicy';
128
131
  }
129
132
  // Build the factory config with the policy definition
130
133
  // The file content IS the policy definition, so we extract the type
131
134
  // and wrap the remaining content as the policyDefinition
132
- const { type, ...restOfFile } = policyDefinition;
135
+ const { type: fileType, ...restOfFile } = policyDefinition;
136
+ const resolvedType = typeof fileType === 'string' && fileType.trim().length > 0
137
+ ? fileType
138
+ : factoryConfig.type;
133
139
  const mergedConfig = this.policyFactoryConfig != null
134
140
  ? { ...this.policyFactoryConfig, policyDefinition }
135
- : { type: factoryConfig.type, policyDefinition: restOfFile };
141
+ : { type: resolvedType, policyDefinition: restOfFile };
136
142
  // Create the policy using the factory system
137
143
  const policy = await authorization_policy_factory_js_1.AuthorizationPolicyFactory.createAuthorizationPolicy(mergedConfig);
138
144
  if (!policy) {
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  // This file is auto-generated during build - do not edit manually
3
- // Generated from package.json version: 0.4.2
3
+ // Generated from package.json version: 0.4.4
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.VERSION = void 0;
6
6
  /**
7
7
  * The package version, injected at build time.
8
8
  * @internal
9
9
  */
10
- exports.VERSION = '0.4.2';
10
+ exports.VERSION = '0.4.4';
@@ -6,6 +6,7 @@ export const PROFILE_NAME_DEFAULT = 'jwt';
6
6
  export const PROFILE_NAME_OAUTH2 = 'oauth2';
7
7
  export const PROFILE_NAME_OAUTH2_GATED = 'oauth2-gated';
8
8
  export const PROFILE_NAME_OAUTH2_CALLBACK = 'oauth2-callback';
9
+ export const PROFILE_NAME_POLICY_LOCALFILE = 'policy-localfile';
9
10
  export const PROFILE_NAME_NOOP = 'noop';
10
11
  export const ENV_VAR_JWT_TRUSTED_ISSUER = 'FAME_JWT_TRUSTED_ISSUER';
11
12
  export const ENV_VAR_JWT_ALGORITHM = 'FAME_JWT_ALGORITHM';
@@ -13,18 +14,21 @@ export const ENV_VAR_JWT_AUDIENCE = 'FAME_JWT_AUDIENCE';
13
14
  export const ENV_VAR_JWKS_URL = 'FAME_JWKS_URL';
14
15
  export const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
15
16
  export const ENV_VAR_TRUSTED_CLIENT_SCOPE = 'FAME_TRUSTED_CLIENT_SCOPE';
17
+ export const ENV_VAR_AUTH_POLICY_PATH = 'FAME_AUTH_POLICY_PATH';
18
+ export const ENV_VAR_AUTH_POLICY_FORMAT = 'FAME_AUTH_POLICY_FORMAT';
16
19
  export const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
17
20
  export const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
18
21
  export const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
19
22
  const DEFAULT_REVERSE_AUTH_ISSUER = 'reverse-auth.naylence.ai';
20
23
  const DEFAULT_REVERSE_AUTH_AUDIENCE = 'dev.naylence.ai';
24
+ const DEFAULT_VERIFIER_CONFIG = {
25
+ type: 'JWKSJWTTokenVerifier',
26
+ jwks_url: Expressions.env(ENV_VAR_JWKS_URL),
27
+ issuer: Expressions.env(ENV_VAR_JWT_TRUSTED_ISSUER),
28
+ };
21
29
  const DEFAULT_PROFILE = {
22
30
  type: 'DefaultAuthorizer',
23
- verifier: {
24
- type: 'JWKSJWTTokenVerifier',
25
- jwks_url: Expressions.env(ENV_VAR_JWKS_URL),
26
- issuer: Expressions.env(ENV_VAR_JWT_TRUSTED_ISSUER),
27
- },
31
+ verifier: DEFAULT_VERIFIER_CONFIG,
28
32
  };
29
33
  const OAUTH2_PROFILE = {
30
34
  type: 'OAuth2Authorizer',
@@ -69,11 +73,22 @@ const OAUTH2_CALLBACK_PROFILE = {
69
73
  const NOOP_PROFILE = {
70
74
  type: 'NoopAuthorizer',
71
75
  };
76
+ const DEFAULT_POLICY_SOURCE = {
77
+ type: 'LocalFileAuthorizationPolicySource',
78
+ path: Expressions.env(ENV_VAR_AUTH_POLICY_PATH, './auth-policy.yaml'),
79
+ format: Expressions.env(ENV_VAR_AUTH_POLICY_FORMAT, 'auto'),
80
+ };
81
+ const POLICY_LOCALFILE_PROFILE = {
82
+ type: 'PolicyAuthorizer',
83
+ verifier: DEFAULT_VERIFIER_CONFIG,
84
+ policySource: DEFAULT_POLICY_SOURCE,
85
+ };
72
86
  const PROFILE_MAP = {
73
87
  [PROFILE_NAME_DEFAULT]: DEFAULT_PROFILE,
74
88
  [PROFILE_NAME_OAUTH2]: OAUTH2_PROFILE,
75
89
  [PROFILE_NAME_OAUTH2_GATED]: OAUTH2_GATED_PROFILE,
76
90
  [PROFILE_NAME_OAUTH2_CALLBACK]: OAUTH2_CALLBACK_PROFILE,
91
+ [PROFILE_NAME_POLICY_LOCALFILE]: POLICY_LOCALFILE_PROFILE,
77
92
  [PROFILE_NAME_NOOP]: NOOP_PROFILE,
78
93
  };
79
94
  const PROFILE_ALIASES = {
@@ -87,6 +102,9 @@ const PROFILE_ALIASES = {
87
102
  'oauth2-callback': PROFILE_NAME_OAUTH2_CALLBACK,
88
103
  oauth2_callback: PROFILE_NAME_OAUTH2_CALLBACK,
89
104
  'reverse-auth': PROFILE_NAME_OAUTH2_CALLBACK,
105
+ policy: PROFILE_NAME_POLICY_LOCALFILE,
106
+ 'policy-localfile': PROFILE_NAME_POLICY_LOCALFILE,
107
+ policy_localfile: PROFILE_NAME_POLICY_LOCALFILE,
90
108
  noop: PROFILE_NAME_NOOP,
91
109
  'no-op': PROFILE_NAME_NOOP,
92
110
  no_op: PROFILE_NAME_NOOP,
@@ -90,6 +90,7 @@ export class BasicAuthorizationPolicy {
90
90
  // Action must be explicitly provided; default to wildcard if omitted
91
91
  // for backward compatibility during transition
92
92
  const resolvedAction = action ?? '*';
93
+ const resolvedActionNormalized = this.normalizeActionToken(resolvedAction) ?? resolvedAction;
93
94
  const address = extractAddress(envelope);
94
95
  const grantedScopes = extractGrantedScopes(context);
95
96
  const rawFrameType = envelope.frame
@@ -99,8 +100,8 @@ export class BasicAuthorizationPolicy {
99
100
  : '';
100
101
  // Extract and normalize origin type for rule matching
101
102
  const rawOriginType = context?.originType;
102
- const originTypeNormalized = typeof rawOriginType === 'string' && rawOriginType.trim().length > 0
103
- ? rawOriginType.trim().toLowerCase()
103
+ const originTypeNormalized = typeof rawOriginType === 'string'
104
+ ? this.normalizeOriginTypeToken(rawOriginType) ?? undefined
104
105
  : undefined;
105
106
  const evaluationTrace = [];
106
107
  // Evaluate rules in order (first match wins)
@@ -147,8 +148,8 @@ export class BasicAuthorizationPolicy {
147
148
  }
148
149
  }
149
150
  // Check action match
150
- if (!rule.actions.has('*') && !rule.actions.has(resolvedAction)) {
151
- step.expression = `action: ${resolvedAction} not in [${Array.from(rule.actions).join(', ')}]`;
151
+ if (!rule.actions.has('*') && !rule.actions.has(resolvedActionNormalized)) {
152
+ step.expression = `action: ${resolvedActionNormalized} not in [${Array.from(rule.actions).join(', ')}]`;
152
153
  step.result = false;
153
154
  evaluationTrace.push(step);
154
155
  continue;
@@ -215,6 +216,9 @@ export class BasicAuthorizationPolicy {
215
216
  };
216
217
  }
217
218
  validateDefaultEffect(effect) {
219
+ if (effect === undefined || effect === null) {
220
+ return 'deny';
221
+ }
218
222
  if (effect !== 'allow' && effect !== 'deny') {
219
223
  throw new Error(`Invalid default_effect: "${String(effect)}". Must be "allow" or "deny"`);
220
224
  }
@@ -287,10 +291,11 @@ export class BasicAuthorizationPolicy {
287
291
  }
288
292
  // Handle single action
289
293
  if (typeof action === 'string') {
290
- if (!VALID_ACTIONS.includes(action)) {
294
+ const normalized = this.normalizeActionToken(action);
295
+ if (!normalized) {
291
296
  throw new Error(`Invalid action in rule "${ruleId}": "${action}". Must be one of: ${VALID_ACTIONS.join(', ')}`);
292
297
  }
293
- return new Set([action]);
298
+ return new Set([normalized]);
294
299
  }
295
300
  // Handle array of actions
296
301
  if (!Array.isArray(action)) {
@@ -304,10 +309,11 @@ export class BasicAuthorizationPolicy {
304
309
  if (typeof a !== 'string') {
305
310
  throw new Error(`Invalid action in rule "${ruleId}": all values must be strings`);
306
311
  }
307
- if (!VALID_ACTIONS.includes(a)) {
312
+ const normalized = this.normalizeActionToken(a);
313
+ if (!normalized) {
308
314
  throw new Error(`Invalid action in rule "${ruleId}": "${a}". Must be one of: ${VALID_ACTIONS.join(', ')}`);
309
315
  }
310
- actions.add(a);
316
+ actions.add(normalized);
311
317
  }
312
318
  return actions;
313
319
  }
@@ -410,11 +416,12 @@ export class BasicAuthorizationPolicy {
410
416
  }
411
417
  // Handle single origin type
412
418
  if (typeof originType === 'string') {
413
- const normalized = originType.trim().toLowerCase();
414
- if (!normalized) {
419
+ const trimmed = originType.trim();
420
+ if (!trimmed) {
415
421
  throw new Error(`Invalid origin_type in rule "${ruleId}": value must not be empty`);
416
422
  }
417
- if (!VALID_ORIGIN_TYPES.includes(normalized)) {
423
+ const normalized = this.normalizeOriginTypeToken(trimmed);
424
+ if (!normalized) {
418
425
  throw new Error(`Invalid origin_type in rule "${ruleId}": "${originType}". Must be one of: ${VALID_ORIGIN_TYPES.join(', ')}`);
419
426
  }
420
427
  return new Set([normalized]);
@@ -431,15 +438,48 @@ export class BasicAuthorizationPolicy {
431
438
  if (typeof ot !== 'string') {
432
439
  throw new Error(`Invalid origin_type in rule "${ruleId}": all values must be strings`);
433
440
  }
434
- const normalized = ot.trim().toLowerCase();
435
- if (!normalized) {
441
+ const trimmed = ot.trim();
442
+ if (!trimmed) {
436
443
  throw new Error(`Invalid origin_type in rule "${ruleId}": values must not be empty`);
437
444
  }
438
- if (!VALID_ORIGIN_TYPES.includes(normalized)) {
445
+ const normalized = this.normalizeOriginTypeToken(trimmed);
446
+ if (!normalized) {
439
447
  throw new Error(`Invalid origin_type in rule "${ruleId}": "${ot}". Must be one of: ${VALID_ORIGIN_TYPES.join(', ')}`);
440
448
  }
441
449
  originTypes.add(normalized);
442
450
  }
443
451
  return originTypes;
444
452
  }
453
+ normalizeActionToken(value) {
454
+ const trimmed = value.trim();
455
+ if (!trimmed) {
456
+ return null;
457
+ }
458
+ if (trimmed === '*') {
459
+ return '*';
460
+ }
461
+ const normalized = trimmed.replace(/[\s_-]+/g, '').toLowerCase();
462
+ const map = {
463
+ connect: 'Connect',
464
+ forwardupstream: 'ForwardUpstream',
465
+ forwarddownstream: 'ForwardDownstream',
466
+ forwardpeer: 'ForwardPeer',
467
+ deliverlocal: 'DeliverLocal',
468
+ };
469
+ return map[normalized] ?? null;
470
+ }
471
+ normalizeOriginTypeToken(value) {
472
+ const trimmed = value.trim();
473
+ if (!trimmed) {
474
+ return null;
475
+ }
476
+ const normalized = trimmed.replace(/[\s_-]+/g, '').toLowerCase();
477
+ const map = {
478
+ downstream: 'downstream',
479
+ upstream: 'upstream',
480
+ peer: 'peer',
481
+ local: 'local',
482
+ };
483
+ return map[normalized] ?? null;
484
+ }
445
485
  }
@@ -87,16 +87,22 @@ export class LocalFileAuthorizationPolicySource {
87
87
  const factoryConfig = this.policyFactoryConfig ?? policyDefinition;
88
88
  // Ensure we have a type field for the factory
89
89
  if (!('type' in factoryConfig) || typeof factoryConfig.type !== 'string') {
90
- throw new Error(`Policy definition at ${this.path} must have a 'type' field, ` +
91
- `or policyFactory config must be provided`);
90
+ logger.warning('policy_type_missing_defaulting_to_basic', {
91
+ path: this.path,
92
+ });
93
+ factoryConfig.type =
94
+ 'BasicAuthorizationPolicy';
92
95
  }
93
96
  // Build the factory config with the policy definition
94
97
  // The file content IS the policy definition, so we extract the type
95
98
  // and wrap the remaining content as the policyDefinition
96
- const { type, ...restOfFile } = policyDefinition;
99
+ const { type: fileType, ...restOfFile } = policyDefinition;
100
+ const resolvedType = typeof fileType === 'string' && fileType.trim().length > 0
101
+ ? fileType
102
+ : factoryConfig.type;
97
103
  const mergedConfig = this.policyFactoryConfig != null
98
104
  ? { ...this.policyFactoryConfig, policyDefinition }
99
- : { type: factoryConfig.type, policyDefinition: restOfFile };
105
+ : { type: resolvedType, policyDefinition: restOfFile };
100
106
  // Create the policy using the factory system
101
107
  const policy = await AuthorizationPolicyFactory.createAuthorizationPolicy(mergedConfig);
102
108
  if (!policy) {
@@ -1,7 +1,7 @@
1
1
  // This file is auto-generated during build - do not edit manually
2
- // Generated from package.json version: 0.4.2
2
+ // Generated from package.json version: 0.4.4
3
3
  /**
4
4
  * The package version, injected at build time.
5
5
  * @internal
6
6
  */
7
- export const VERSION = '0.4.2';
7
+ export const VERSION = '0.4.4';