@contractspec/lib.contracts 1.50.0 → 1.51.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/app-config/contracts.d.ts +51 -51
- package/dist/app-config/events.d.ts +27 -27
- package/dist/app-config/lifecycle-contracts.d.ts +55 -55
- package/dist/app-config/runtime.d.ts +1 -1
- package/dist/app-config/spec.d.ts +1 -1
- package/dist/capabilities/capabilities.d.ts +40 -4
- package/dist/capabilities/capabilities.js +125 -0
- package/dist/capabilities/context.d.ts +88 -0
- package/dist/capabilities/context.js +87 -0
- package/dist/capabilities/docs/capabilities.docblock.js +191 -2
- package/dist/capabilities/guards.d.ts +110 -0
- package/dist/capabilities/guards.js +146 -0
- package/dist/capabilities/index.d.ts +4 -1
- package/dist/capabilities/index.js +4 -1
- package/dist/capabilities/validation.d.ts +76 -0
- package/dist/capabilities/validation.js +141 -0
- package/dist/client/react/feature-render.d.ts +2 -2
- package/dist/contract-registry/schemas.d.ts +2 -2
- package/dist/data-views/runtime.d.ts +1 -1
- package/dist/events.d.ts +6 -0
- package/dist/examples/schema.d.ts +11 -11
- package/dist/experiments/spec.d.ts +1 -1
- package/dist/features/install.d.ts +4 -4
- package/dist/features/types.d.ts +4 -4
- package/dist/index.d.ts +21 -13
- package/dist/index.js +11 -3
- package/dist/install.d.ts +1 -1
- package/dist/integrations/openbanking/contracts/accounts.d.ts +67 -67
- package/dist/integrations/openbanking/contracts/balances.d.ts +35 -35
- package/dist/integrations/openbanking/contracts/transactions.d.ts +49 -49
- package/dist/integrations/openbanking/models.d.ts +55 -55
- package/dist/integrations/operations.d.ts +1 -1
- package/dist/integrations/spec.d.ts +1 -1
- package/dist/knowledge/operations.d.ts +67 -67
- package/dist/llm/exporters.d.ts +2 -2
- package/dist/markdown.d.ts +1 -1
- package/dist/onboarding-base.d.ts +29 -29
- package/dist/operations/operation.d.ts +6 -0
- package/dist/policy/context.d.ts +237 -0
- package/dist/policy/context.js +227 -0
- package/dist/policy/guards.d.ts +145 -0
- package/dist/policy/guards.js +254 -0
- package/dist/policy/index.d.ts +12 -1
- package/dist/policy/index.js +11 -1
- package/dist/policy/spec.d.ts +1 -1
- package/dist/policy/validation.d.ts +67 -0
- package/dist/policy/validation.js +307 -0
- package/dist/presentations/presentations.d.ts +6 -0
- package/dist/tests/spec.d.ts +1 -1
- package/dist/themes.d.ts +1 -1
- package/dist/translations/index.d.ts +6 -0
- package/dist/translations/index.js +5 -0
- package/dist/translations/registry.d.ts +144 -0
- package/dist/translations/registry.js +223 -0
- package/dist/translations/spec.d.ts +126 -0
- package/dist/translations/spec.js +31 -0
- package/dist/translations/validation.d.ts +85 -0
- package/dist/translations/validation.js +328 -0
- package/dist/workflow/context.d.ts +191 -0
- package/dist/workflow/context.js +227 -0
- package/dist/workflow/index.d.ts +4 -2
- package/dist/workflow/index.js +4 -2
- package/dist/workflow/spec.d.ts +1 -1
- package/dist/workflow/validation.d.ts +64 -2
- package/dist/workflow/validation.js +194 -1
- package/package.json +18 -6
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
//#region src/policy/context.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Runtime policy context for opt-in policy enforcement.
|
|
4
|
+
*
|
|
5
|
+
* Provides a context object that can be used to check if a user/tenant
|
|
6
|
+
* has specific roles, permissions, and attributes at runtime.
|
|
7
|
+
*
|
|
8
|
+
* @module policy/context
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { createPolicyContext } from '@contractspec/lib.contracts';
|
|
13
|
+
*
|
|
14
|
+
* // Create context from user's roles and permissions
|
|
15
|
+
* const ctx = createPolicyContext({
|
|
16
|
+
* id: 'user-123',
|
|
17
|
+
* tenantId: 'tenant-456',
|
|
18
|
+
* roles: ['admin', 'editor'],
|
|
19
|
+
* permissions: ['read:articles', 'write:articles'],
|
|
20
|
+
* attributes: { department: 'engineering' },
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Check roles and permissions
|
|
24
|
+
* if (ctx.hasRole('admin')) {
|
|
25
|
+
* // User has admin role
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* // Audit access attempts
|
|
29
|
+
* ctx.auditAccess('article.update', 'allowed');
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Error thrown when a policy violation occurs.
|
|
34
|
+
*/
|
|
35
|
+
declare class PolicyViolationError extends Error {
|
|
36
|
+
readonly violationType: PolicyViolationType;
|
|
37
|
+
readonly details: PolicyViolationDetails;
|
|
38
|
+
constructor(type: PolicyViolationType, details: PolicyViolationDetails);
|
|
39
|
+
}
|
|
40
|
+
type PolicyViolationType = 'missing_role' | 'missing_permission' | 'missing_attribute' | 'rate_limit_exceeded' | 'consent_required' | 'access_denied';
|
|
41
|
+
interface PolicyViolationDetails {
|
|
42
|
+
operation?: string;
|
|
43
|
+
requiredRole?: string;
|
|
44
|
+
requiredPermission?: string;
|
|
45
|
+
requiredAttribute?: {
|
|
46
|
+
key: string;
|
|
47
|
+
expected: unknown;
|
|
48
|
+
};
|
|
49
|
+
rateLimitKey?: string;
|
|
50
|
+
consentIds?: string[];
|
|
51
|
+
reason?: string;
|
|
52
|
+
}
|
|
53
|
+
interface RateLimitResult {
|
|
54
|
+
allowed: boolean;
|
|
55
|
+
remaining: number;
|
|
56
|
+
resetAt?: Date;
|
|
57
|
+
retryAfterMs?: number;
|
|
58
|
+
}
|
|
59
|
+
interface RateLimitState {
|
|
60
|
+
key: string;
|
|
61
|
+
count: number;
|
|
62
|
+
windowStart: number;
|
|
63
|
+
windowMs: number;
|
|
64
|
+
limit: number;
|
|
65
|
+
}
|
|
66
|
+
interface AuditEntry {
|
|
67
|
+
timestamp: Date;
|
|
68
|
+
operation: string;
|
|
69
|
+
result: 'allowed' | 'denied';
|
|
70
|
+
reason?: string;
|
|
71
|
+
userId?: string;
|
|
72
|
+
tenantId?: string;
|
|
73
|
+
attributes?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
type AuditHandler = (entry: AuditEntry) => void | Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* User information for policy evaluation.
|
|
78
|
+
*/
|
|
79
|
+
interface PolicyUser {
|
|
80
|
+
/** Unique user identifier. */
|
|
81
|
+
id: string;
|
|
82
|
+
/** Optional tenant/organization ID for multi-tenant contexts. */
|
|
83
|
+
tenantId?: string;
|
|
84
|
+
/** Roles assigned to the user. */
|
|
85
|
+
roles: string[];
|
|
86
|
+
/** Permissions granted to the user. */
|
|
87
|
+
permissions: string[];
|
|
88
|
+
/** Additional attributes for ABAC evaluation. */
|
|
89
|
+
attributes: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Runtime context for checking policy access.
|
|
93
|
+
*
|
|
94
|
+
* Created from user information (roles, permissions, attributes).
|
|
95
|
+
* Provides methods to check and require policy compliance at runtime.
|
|
96
|
+
*/
|
|
97
|
+
interface PolicyContext {
|
|
98
|
+
/** The user for this context. */
|
|
99
|
+
readonly user: PolicyUser;
|
|
100
|
+
/** Set of user roles. */
|
|
101
|
+
readonly roles: ReadonlySet<string>;
|
|
102
|
+
/** Set of user permissions. */
|
|
103
|
+
readonly permissions: ReadonlySet<string>;
|
|
104
|
+
/**
|
|
105
|
+
* Check if user has a specific role.
|
|
106
|
+
* @param role - Role to check
|
|
107
|
+
* @returns True if user has the role
|
|
108
|
+
*/
|
|
109
|
+
hasRole(role: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Check if user has any of the specified roles.
|
|
112
|
+
* @param roles - Roles to check
|
|
113
|
+
* @returns True if user has at least one role
|
|
114
|
+
*/
|
|
115
|
+
hasAnyRole(roles: string[]): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Check if user has all of the specified roles.
|
|
118
|
+
* @param roles - Roles to check
|
|
119
|
+
* @returns True if user has all roles
|
|
120
|
+
*/
|
|
121
|
+
hasAllRoles(roles: string[]): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Require a role, throwing if not present.
|
|
124
|
+
* @param role - Role to require
|
|
125
|
+
* @throws {PolicyViolationError} If role is missing
|
|
126
|
+
*/
|
|
127
|
+
requireRole(role: string): void;
|
|
128
|
+
/**
|
|
129
|
+
* Check if user has a specific permission.
|
|
130
|
+
* @param permission - Permission to check
|
|
131
|
+
* @returns True if user has the permission
|
|
132
|
+
*/
|
|
133
|
+
hasPermission(permission: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Check if user has any of the specified permissions.
|
|
136
|
+
* @param permissions - Permissions to check
|
|
137
|
+
* @returns True if user has at least one permission
|
|
138
|
+
*/
|
|
139
|
+
hasAnyPermission(permissions: string[]): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Check if user has all of the specified permissions.
|
|
142
|
+
* @param permissions - Permissions to check
|
|
143
|
+
* @returns True if user has all permissions
|
|
144
|
+
*/
|
|
145
|
+
hasAllPermissions(permissions: string[]): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Require a permission, throwing if not present.
|
|
148
|
+
* @param permission - Permission to require
|
|
149
|
+
* @throws {PolicyViolationError} If permission is missing
|
|
150
|
+
*/
|
|
151
|
+
requirePermission(permission: string): void;
|
|
152
|
+
/**
|
|
153
|
+
* Get a user attribute value.
|
|
154
|
+
* @param key - Attribute key
|
|
155
|
+
* @returns Attribute value or undefined
|
|
156
|
+
*/
|
|
157
|
+
getAttribute<T = unknown>(key: string): T | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* Check if an attribute matches an expected value.
|
|
160
|
+
* @param key - Attribute key
|
|
161
|
+
* @param expected - Expected value
|
|
162
|
+
* @returns True if attribute equals expected value
|
|
163
|
+
*/
|
|
164
|
+
checkAttribute(key: string, expected: unknown): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Check if an attribute is one of the allowed values.
|
|
167
|
+
* @param key - Attribute key
|
|
168
|
+
* @param allowedValues - Array of allowed values
|
|
169
|
+
* @returns True if attribute is in allowed values
|
|
170
|
+
*/
|
|
171
|
+
checkAttributeOneOf(key: string, allowedValues: unknown[]): boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Check rate limit for a key.
|
|
174
|
+
* @param key - Rate limit key (e.g., operation name)
|
|
175
|
+
* @returns Rate limit result
|
|
176
|
+
*/
|
|
177
|
+
checkRateLimit(key: string): RateLimitResult;
|
|
178
|
+
/**
|
|
179
|
+
* Consume rate limit for a key (increment counter).
|
|
180
|
+
* @param key - Rate limit key
|
|
181
|
+
* @param cost - Cost of the operation (default: 1)
|
|
182
|
+
* @returns Rate limit result after consumption
|
|
183
|
+
*/
|
|
184
|
+
consumeRateLimit(key: string, cost?: number): RateLimitResult;
|
|
185
|
+
/**
|
|
186
|
+
* Record an access attempt for audit purposes.
|
|
187
|
+
* @param operation - Operation being accessed
|
|
188
|
+
* @param result - Whether access was allowed or denied
|
|
189
|
+
* @param reason - Optional reason for the decision
|
|
190
|
+
*/
|
|
191
|
+
auditAccess(operation: string, result: 'allowed' | 'denied', reason?: string): void;
|
|
192
|
+
}
|
|
193
|
+
interface PolicyContextOptions {
|
|
194
|
+
/** Rate limit configurations by key. */
|
|
195
|
+
rateLimits?: Record<string, {
|
|
196
|
+
limit: number;
|
|
197
|
+
windowMs: number;
|
|
198
|
+
}>;
|
|
199
|
+
/** Handler for audit entries. */
|
|
200
|
+
auditHandler?: AuditHandler;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Creates a policy context from user information.
|
|
204
|
+
*
|
|
205
|
+
* @param user - User information for policy evaluation
|
|
206
|
+
* @param options - Additional context options
|
|
207
|
+
* @returns PolicyContext for checking/requiring policy compliance
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const ctx = createPolicyContext({
|
|
212
|
+
* id: 'user-123',
|
|
213
|
+
* roles: ['editor'],
|
|
214
|
+
* permissions: ['read:articles'],
|
|
215
|
+
* attributes: { department: 'marketing' },
|
|
216
|
+
* });
|
|
217
|
+
*
|
|
218
|
+
* ctx.requireRole('editor');
|
|
219
|
+
* ctx.requirePermission('read:articles');
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function createPolicyContext(user: PolicyUser, options?: PolicyContextOptions): PolicyContext;
|
|
223
|
+
/**
|
|
224
|
+
* Creates an empty policy context (no roles/permissions).
|
|
225
|
+
* Useful for anonymous users or testing.
|
|
226
|
+
*/
|
|
227
|
+
declare function createAnonymousPolicyContext(options?: PolicyContextOptions): PolicyContext;
|
|
228
|
+
/**
|
|
229
|
+
* Creates a bypass policy context with all roles/permissions.
|
|
230
|
+
* Useful for admin users or internal services.
|
|
231
|
+
*
|
|
232
|
+
* @param allRoles - All roles to grant
|
|
233
|
+
* @param allPermissions - All permissions to grant
|
|
234
|
+
*/
|
|
235
|
+
declare function createBypassPolicyContext(allRoles: string[], allPermissions: string[], options?: PolicyContextOptions): PolicyContext;
|
|
236
|
+
//#endregion
|
|
237
|
+
export { AuditEntry, AuditHandler, PolicyContext, PolicyContextOptions, PolicyUser, PolicyViolationDetails, PolicyViolationError, PolicyViolationType, RateLimitResult, RateLimitState, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext };
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
//#region src/policy/context.ts
|
|
2
|
+
/**
|
|
3
|
+
* Runtime policy context for opt-in policy enforcement.
|
|
4
|
+
*
|
|
5
|
+
* Provides a context object that can be used to check if a user/tenant
|
|
6
|
+
* has specific roles, permissions, and attributes at runtime.
|
|
7
|
+
*
|
|
8
|
+
* @module policy/context
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { createPolicyContext } from '@contractspec/lib.contracts';
|
|
13
|
+
*
|
|
14
|
+
* // Create context from user's roles and permissions
|
|
15
|
+
* const ctx = createPolicyContext({
|
|
16
|
+
* id: 'user-123',
|
|
17
|
+
* tenantId: 'tenant-456',
|
|
18
|
+
* roles: ['admin', 'editor'],
|
|
19
|
+
* permissions: ['read:articles', 'write:articles'],
|
|
20
|
+
* attributes: { department: 'engineering' },
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Check roles and permissions
|
|
24
|
+
* if (ctx.hasRole('admin')) {
|
|
25
|
+
* // User has admin role
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* // Audit access attempts
|
|
29
|
+
* ctx.auditAccess('article.update', 'allowed');
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Error thrown when a policy violation occurs.
|
|
34
|
+
*/
|
|
35
|
+
var PolicyViolationError = class extends Error {
|
|
36
|
+
violationType;
|
|
37
|
+
details;
|
|
38
|
+
constructor(type, details) {
|
|
39
|
+
const message = formatPolicyViolationMessage(type, details);
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "PolicyViolationError";
|
|
42
|
+
this.violationType = type;
|
|
43
|
+
this.details = details;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function formatPolicyViolationMessage(type, details) {
|
|
47
|
+
const prefix = details.operation ? `Policy violation for "${details.operation}": ` : "Policy violation: ";
|
|
48
|
+
switch (type) {
|
|
49
|
+
case "missing_role": return `${prefix}Missing required role "${details.requiredRole}"`;
|
|
50
|
+
case "missing_permission": return `${prefix}Missing required permission "${details.requiredPermission}"`;
|
|
51
|
+
case "missing_attribute": return `${prefix}Missing or invalid attribute "${details.requiredAttribute?.key}"`;
|
|
52
|
+
case "rate_limit_exceeded": return `${prefix}Rate limit exceeded for key "${details.rateLimitKey}"`;
|
|
53
|
+
case "consent_required": return `${prefix}Consent required: ${details.consentIds?.join(", ")}`;
|
|
54
|
+
case "access_denied": return `${prefix}${details.reason ?? "Access denied"}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var PolicyContextImpl = class {
|
|
58
|
+
user;
|
|
59
|
+
roles;
|
|
60
|
+
permissions;
|
|
61
|
+
rateLimitStates = /* @__PURE__ */ new Map();
|
|
62
|
+
rateLimitConfigs;
|
|
63
|
+
auditHandler;
|
|
64
|
+
constructor(user, options = {}) {
|
|
65
|
+
this.user = user;
|
|
66
|
+
this.roles = new Set(user.roles);
|
|
67
|
+
this.permissions = new Set(user.permissions);
|
|
68
|
+
this.rateLimitConfigs = options.rateLimits ?? {};
|
|
69
|
+
this.auditHandler = options.auditHandler;
|
|
70
|
+
}
|
|
71
|
+
hasRole(role) {
|
|
72
|
+
return this.roles.has(role);
|
|
73
|
+
}
|
|
74
|
+
hasAnyRole(roles) {
|
|
75
|
+
return roles.some((r) => this.roles.has(r));
|
|
76
|
+
}
|
|
77
|
+
hasAllRoles(roles) {
|
|
78
|
+
return roles.every((r) => this.roles.has(r));
|
|
79
|
+
}
|
|
80
|
+
requireRole(role) {
|
|
81
|
+
if (!this.hasRole(role)) throw new PolicyViolationError("missing_role", { requiredRole: role });
|
|
82
|
+
}
|
|
83
|
+
hasPermission(permission) {
|
|
84
|
+
return this.permissions.has(permission);
|
|
85
|
+
}
|
|
86
|
+
hasAnyPermission(permissions) {
|
|
87
|
+
return permissions.some((p) => this.permissions.has(p));
|
|
88
|
+
}
|
|
89
|
+
hasAllPermissions(permissions) {
|
|
90
|
+
return permissions.every((p) => this.permissions.has(p));
|
|
91
|
+
}
|
|
92
|
+
requirePermission(permission) {
|
|
93
|
+
if (!this.hasPermission(permission)) throw new PolicyViolationError("missing_permission", { requiredPermission: permission });
|
|
94
|
+
}
|
|
95
|
+
getAttribute(key) {
|
|
96
|
+
return this.user.attributes[key];
|
|
97
|
+
}
|
|
98
|
+
checkAttribute(key, expected) {
|
|
99
|
+
return this.user.attributes[key] === expected;
|
|
100
|
+
}
|
|
101
|
+
checkAttributeOneOf(key, allowedValues) {
|
|
102
|
+
return allowedValues.includes(this.user.attributes[key]);
|
|
103
|
+
}
|
|
104
|
+
checkRateLimit(key) {
|
|
105
|
+
const config = this.rateLimitConfigs[key];
|
|
106
|
+
if (!config) return {
|
|
107
|
+
allowed: true,
|
|
108
|
+
remaining: Infinity
|
|
109
|
+
};
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
const state = this.rateLimitStates.get(key);
|
|
112
|
+
if (!state || now - state.windowStart >= config.windowMs) return {
|
|
113
|
+
allowed: true,
|
|
114
|
+
remaining: config.limit
|
|
115
|
+
};
|
|
116
|
+
const remaining = Math.max(0, config.limit - state.count);
|
|
117
|
+
if (remaining <= 0) return {
|
|
118
|
+
allowed: false,
|
|
119
|
+
remaining: 0,
|
|
120
|
+
resetAt: new Date(state.windowStart + config.windowMs),
|
|
121
|
+
retryAfterMs: state.windowStart + config.windowMs - now
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
allowed: true,
|
|
125
|
+
remaining
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
consumeRateLimit(key, cost = 1) {
|
|
129
|
+
const config = this.rateLimitConfigs[key];
|
|
130
|
+
if (!config) return {
|
|
131
|
+
allowed: true,
|
|
132
|
+
remaining: Infinity
|
|
133
|
+
};
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
let state = this.rateLimitStates.get(key);
|
|
136
|
+
if (!state || now - state.windowStart >= config.windowMs) state = {
|
|
137
|
+
key,
|
|
138
|
+
count: 0,
|
|
139
|
+
windowStart: now,
|
|
140
|
+
windowMs: config.windowMs,
|
|
141
|
+
limit: config.limit
|
|
142
|
+
};
|
|
143
|
+
if (state.count + cost > config.limit) {
|
|
144
|
+
const resetAt = new Date(state.windowStart + config.windowMs);
|
|
145
|
+
const retryAfterMs = state.windowStart + config.windowMs - now;
|
|
146
|
+
this.rateLimitStates.set(key, state);
|
|
147
|
+
return {
|
|
148
|
+
allowed: false,
|
|
149
|
+
remaining: Math.max(0, config.limit - state.count),
|
|
150
|
+
resetAt,
|
|
151
|
+
retryAfterMs
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
state.count += cost;
|
|
155
|
+
this.rateLimitStates.set(key, state);
|
|
156
|
+
return {
|
|
157
|
+
allowed: true,
|
|
158
|
+
remaining: Math.max(0, config.limit - state.count)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
auditAccess(operation, result, reason) {
|
|
162
|
+
if (!this.auditHandler) return;
|
|
163
|
+
const entry = {
|
|
164
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
165
|
+
operation,
|
|
166
|
+
result,
|
|
167
|
+
reason,
|
|
168
|
+
userId: this.user.id,
|
|
169
|
+
tenantId: this.user.tenantId,
|
|
170
|
+
attributes: this.user.attributes
|
|
171
|
+
};
|
|
172
|
+
Promise.resolve(this.auditHandler(entry)).catch(() => {});
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Creates a policy context from user information.
|
|
177
|
+
*
|
|
178
|
+
* @param user - User information for policy evaluation
|
|
179
|
+
* @param options - Additional context options
|
|
180
|
+
* @returns PolicyContext for checking/requiring policy compliance
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const ctx = createPolicyContext({
|
|
185
|
+
* id: 'user-123',
|
|
186
|
+
* roles: ['editor'],
|
|
187
|
+
* permissions: ['read:articles'],
|
|
188
|
+
* attributes: { department: 'marketing' },
|
|
189
|
+
* });
|
|
190
|
+
*
|
|
191
|
+
* ctx.requireRole('editor');
|
|
192
|
+
* ctx.requirePermission('read:articles');
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
function createPolicyContext(user, options) {
|
|
196
|
+
return new PolicyContextImpl(user, options);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Creates an empty policy context (no roles/permissions).
|
|
200
|
+
* Useful for anonymous users or testing.
|
|
201
|
+
*/
|
|
202
|
+
function createAnonymousPolicyContext(options) {
|
|
203
|
+
return new PolicyContextImpl({
|
|
204
|
+
id: "anonymous",
|
|
205
|
+
roles: [],
|
|
206
|
+
permissions: [],
|
|
207
|
+
attributes: {}
|
|
208
|
+
}, options);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Creates a bypass policy context with all roles/permissions.
|
|
212
|
+
* Useful for admin users or internal services.
|
|
213
|
+
*
|
|
214
|
+
* @param allRoles - All roles to grant
|
|
215
|
+
* @param allPermissions - All permissions to grant
|
|
216
|
+
*/
|
|
217
|
+
function createBypassPolicyContext(allRoles, allPermissions, options) {
|
|
218
|
+
return new PolicyContextImpl({
|
|
219
|
+
id: "system",
|
|
220
|
+
roles: allRoles,
|
|
221
|
+
permissions: allPermissions,
|
|
222
|
+
attributes: { bypass: true }
|
|
223
|
+
}, options);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
//#endregion
|
|
227
|
+
export { PolicyViolationError, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { AnyOperationSpec } from "../operations/operation.js";
|
|
2
|
+
import { PolicyContext } from "./context.js";
|
|
3
|
+
|
|
4
|
+
//#region src/policy/guards.d.ts
|
|
5
|
+
|
|
6
|
+
/** Result of a policy guard check. */
|
|
7
|
+
interface PolicyGuardResult {
|
|
8
|
+
/** Whether the guard passed. */
|
|
9
|
+
allowed: boolean;
|
|
10
|
+
/** Reason for denial if guard failed. */
|
|
11
|
+
reason?: string;
|
|
12
|
+
/** Missing requirements if guard failed. */
|
|
13
|
+
missing?: {
|
|
14
|
+
roles?: string[];
|
|
15
|
+
permissions?: string[];
|
|
16
|
+
flags?: string[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if an operation's policy constraints are satisfied.
|
|
21
|
+
*
|
|
22
|
+
* @param ctx - Policy context to check against
|
|
23
|
+
* @param operation - Operation spec to check
|
|
24
|
+
* @param flags - Available feature flags
|
|
25
|
+
* @returns Guard result indicating if operation is allowed
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = checkPolicyForOperation(ctx, myOperation, ['feature-x']);
|
|
30
|
+
* if (!result.allowed) {
|
|
31
|
+
* console.log('Denied:', result.reason);
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function checkPolicyForOperation(ctx: PolicyContext, operation: AnyOperationSpec, flags?: string[]): PolicyGuardResult;
|
|
36
|
+
/**
|
|
37
|
+
* Assert that an operation's policy constraints are satisfied.
|
|
38
|
+
*
|
|
39
|
+
* @param ctx - Policy context to check against
|
|
40
|
+
* @param operation - Operation spec to check
|
|
41
|
+
* @param flags - Available feature flags
|
|
42
|
+
* @throws {PolicyViolationError} If policy is not satisfied
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Throws if policy not satisfied
|
|
47
|
+
* assertPolicyForOperation(ctx, myOperation);
|
|
48
|
+
*
|
|
49
|
+
* // Safe to proceed with operation
|
|
50
|
+
* await handler(input);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function assertPolicyForOperation(ctx: PolicyContext, operation: AnyOperationSpec, flags?: string[]): void;
|
|
54
|
+
/**
|
|
55
|
+
* Filter operations to only those with satisfied policy constraints.
|
|
56
|
+
*
|
|
57
|
+
* @param ctx - Policy context to check against
|
|
58
|
+
* @param operations - Operations to filter
|
|
59
|
+
* @param flags - Available feature flags
|
|
60
|
+
* @returns Operations that have their policies satisfied
|
|
61
|
+
*/
|
|
62
|
+
declare function filterOperationsByPolicy(ctx: PolicyContext, operations: AnyOperationSpec[], flags?: string[]): AnyOperationSpec[];
|
|
63
|
+
/**
|
|
64
|
+
* Check if user has the required role for an operation.
|
|
65
|
+
*
|
|
66
|
+
* @param ctx - Policy context to check against
|
|
67
|
+
* @param requiredRole - Role required for the operation
|
|
68
|
+
* @param operation - Optional operation name for error messages
|
|
69
|
+
* @returns Guard result
|
|
70
|
+
*/
|
|
71
|
+
declare function checkRole(ctx: PolicyContext, requiredRole: string, operation?: string): PolicyGuardResult;
|
|
72
|
+
/**
|
|
73
|
+
* Assert user has the required role.
|
|
74
|
+
*
|
|
75
|
+
* @param ctx - Policy context to check against
|
|
76
|
+
* @param requiredRole - Role required
|
|
77
|
+
* @param operation - Optional operation name for error messages
|
|
78
|
+
* @throws {PolicyViolationError} If role is missing
|
|
79
|
+
*/
|
|
80
|
+
declare function assertRole(ctx: PolicyContext, requiredRole: string, operation?: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* Check if user has any of the required roles.
|
|
83
|
+
*
|
|
84
|
+
* @param ctx - Policy context to check against
|
|
85
|
+
* @param requiredRoles - Any of these roles is sufficient
|
|
86
|
+
* @param operation - Optional operation name for error messages
|
|
87
|
+
* @returns Guard result
|
|
88
|
+
*/
|
|
89
|
+
declare function checkAnyRole(ctx: PolicyContext, requiredRoles: string[], operation?: string): PolicyGuardResult;
|
|
90
|
+
/**
|
|
91
|
+
* Check if user has the required permission.
|
|
92
|
+
*
|
|
93
|
+
* @param ctx - Policy context to check against
|
|
94
|
+
* @param requiredPermission - Permission required
|
|
95
|
+
* @param operation - Optional operation name for error messages
|
|
96
|
+
* @returns Guard result
|
|
97
|
+
*/
|
|
98
|
+
declare function checkPermission(ctx: PolicyContext, requiredPermission: string, operation?: string): PolicyGuardResult;
|
|
99
|
+
/**
|
|
100
|
+
* Assert user has the required permission.
|
|
101
|
+
*
|
|
102
|
+
* @param ctx - Policy context to check against
|
|
103
|
+
* @param requiredPermission - Permission required
|
|
104
|
+
* @param operation - Optional operation name for error messages
|
|
105
|
+
* @throws {PolicyViolationError} If permission is missing
|
|
106
|
+
*/
|
|
107
|
+
declare function assertPermission(ctx: PolicyContext, requiredPermission: string, operation?: string): void;
|
|
108
|
+
/**
|
|
109
|
+
* Check if user has all of the required permissions.
|
|
110
|
+
*
|
|
111
|
+
* @param ctx - Policy context to check against
|
|
112
|
+
* @param requiredPermissions - All of these permissions are required
|
|
113
|
+
* @param operation - Optional operation name for error messages
|
|
114
|
+
* @returns Guard result
|
|
115
|
+
*/
|
|
116
|
+
declare function checkAllPermissions(ctx: PolicyContext, requiredPermissions: string[], operation?: string): PolicyGuardResult;
|
|
117
|
+
interface CombinedPolicyRequirements {
|
|
118
|
+
roles?: string[];
|
|
119
|
+
anyRole?: string[];
|
|
120
|
+
permissions?: string[];
|
|
121
|
+
anyPermission?: string[];
|
|
122
|
+
flags?: string[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Check multiple policy requirements at once.
|
|
126
|
+
*
|
|
127
|
+
* @param ctx - Policy context to check against
|
|
128
|
+
* @param requirements - Combined requirements to check
|
|
129
|
+
* @param flags - Available feature flags
|
|
130
|
+
* @param operation - Optional operation name for error messages
|
|
131
|
+
* @returns Guard result
|
|
132
|
+
*/
|
|
133
|
+
declare function checkCombinedPolicy(ctx: PolicyContext, requirements: CombinedPolicyRequirements, flags?: string[], operation?: string): PolicyGuardResult;
|
|
134
|
+
/**
|
|
135
|
+
* Assert multiple policy requirements at once.
|
|
136
|
+
*
|
|
137
|
+
* @param ctx - Policy context to check against
|
|
138
|
+
* @param requirements - Combined requirements to check
|
|
139
|
+
* @param flags - Available feature flags
|
|
140
|
+
* @param operation - Optional operation name for error messages
|
|
141
|
+
* @throws {PolicyViolationError} If any requirement is not met
|
|
142
|
+
*/
|
|
143
|
+
declare function assertCombinedPolicy(ctx: PolicyContext, requirements: CombinedPolicyRequirements, flags?: string[], operation?: string): void;
|
|
144
|
+
//#endregion
|
|
145
|
+
export { CombinedPolicyRequirements, PolicyGuardResult, assertCombinedPolicy, assertPermission, assertPolicyForOperation, assertRole, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, filterOperationsByPolicy };
|