@kysera/rls 0.8.1 → 0.8.3
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/README.md +819 -3
- package/dist/index.d.ts +2841 -11
- package/dist/index.js +2451 -1
- package/dist/index.js.map +1 -1
- package/dist/native/index.d.ts +1 -1
- package/dist/{types-Dowjd6zG.d.ts → types-CyqksFKU.d.ts} +72 -1
- package/package.json +11 -6
- package/src/audit/index.ts +25 -0
- package/src/audit/logger.ts +465 -0
- package/src/audit/types.ts +625 -0
- package/src/composition/builder.ts +556 -0
- package/src/composition/index.ts +43 -0
- package/src/composition/types.ts +415 -0
- package/src/field-access/index.ts +38 -0
- package/src/field-access/processor.ts +442 -0
- package/src/field-access/registry.ts +259 -0
- package/src/field-access/types.ts +453 -0
- package/src/index.ts +180 -2
- package/src/policy/builder.ts +187 -10
- package/src/policy/types.ts +84 -0
- package/src/rebac/index.ts +30 -0
- package/src/rebac/registry.ts +303 -0
- package/src/rebac/transformer.ts +391 -0
- package/src/rebac/types.ts +412 -0
- package/src/resolvers/index.ts +30 -0
- package/src/resolvers/manager.ts +507 -0
- package/src/resolvers/types.ts +447 -0
- package/src/testing/index.ts +554 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Composition Types
|
|
3
|
+
*
|
|
4
|
+
* Provides types for creating reusable, composable RLS policies.
|
|
5
|
+
*
|
|
6
|
+
* @module @kysera/rls/composition/types
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
PolicyDefinition,
|
|
11
|
+
PolicyCondition,
|
|
12
|
+
FilterCondition,
|
|
13
|
+
PolicyEvaluationContext,
|
|
14
|
+
Operation,
|
|
15
|
+
PolicyHints
|
|
16
|
+
} from '../policy/types.js'
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Reusable Policy Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A named, reusable policy template
|
|
24
|
+
*
|
|
25
|
+
* Can be composed with other policies and applied to multiple tables.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const tenantIsolation = definePolicy({
|
|
30
|
+
* name: 'tenantIsolation',
|
|
31
|
+
* type: 'filter',
|
|
32
|
+
* operation: 'read',
|
|
33
|
+
* filter: ctx => ({ tenant_id: ctx.auth.tenantId }),
|
|
34
|
+
* priority: 1000
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface ReusablePolicy {
|
|
39
|
+
/**
|
|
40
|
+
* Unique name for this policy
|
|
41
|
+
*/
|
|
42
|
+
name: string
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Description for documentation
|
|
46
|
+
*/
|
|
47
|
+
description?: string
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Policy definitions (can include multiple policies)
|
|
51
|
+
*/
|
|
52
|
+
policies: PolicyDefinition[]
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Tags for categorization
|
|
56
|
+
*/
|
|
57
|
+
tags?: string[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Configuration for a reusable policy template
|
|
62
|
+
*/
|
|
63
|
+
export interface ReusablePolicyConfig {
|
|
64
|
+
/**
|
|
65
|
+
* Policy name
|
|
66
|
+
*/
|
|
67
|
+
name: string
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Description
|
|
71
|
+
*/
|
|
72
|
+
description?: string
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Tags for categorization
|
|
76
|
+
*/
|
|
77
|
+
tags?: string[]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Policy Builder Configuration
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Configuration for creating a filter policy
|
|
86
|
+
*/
|
|
87
|
+
export interface FilterPolicyConfig<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> {
|
|
88
|
+
/**
|
|
89
|
+
* Filter condition (returns WHERE clause conditions)
|
|
90
|
+
*/
|
|
91
|
+
filter: FilterCondition<TCtx>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Priority for policy evaluation
|
|
95
|
+
* @default 0
|
|
96
|
+
*/
|
|
97
|
+
priority?: number
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Performance hints
|
|
101
|
+
*/
|
|
102
|
+
hints?: PolicyHints
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Configuration for creating an allow policy
|
|
107
|
+
*/
|
|
108
|
+
export interface AllowPolicyConfig<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> {
|
|
109
|
+
/**
|
|
110
|
+
* Operations this policy applies to
|
|
111
|
+
*/
|
|
112
|
+
operation: Operation | Operation[]
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Allow condition
|
|
116
|
+
*/
|
|
117
|
+
allow: PolicyCondition<TCtx>
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Priority for policy evaluation
|
|
121
|
+
* @default 0
|
|
122
|
+
*/
|
|
123
|
+
priority?: number
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Performance hints
|
|
127
|
+
*/
|
|
128
|
+
hints?: PolicyHints
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Configuration for creating a deny policy
|
|
133
|
+
*/
|
|
134
|
+
export interface DenyPolicyConfig<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> {
|
|
135
|
+
/**
|
|
136
|
+
* Operations this policy applies to
|
|
137
|
+
*/
|
|
138
|
+
operation: Operation | Operation[]
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Deny condition (optional - if not provided, always denies)
|
|
142
|
+
*/
|
|
143
|
+
deny?: PolicyCondition<TCtx>
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Priority for policy evaluation
|
|
147
|
+
* @default 100
|
|
148
|
+
*/
|
|
149
|
+
priority?: number
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Performance hints
|
|
153
|
+
*/
|
|
154
|
+
hints?: PolicyHints
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Configuration for creating a validate policy
|
|
159
|
+
*/
|
|
160
|
+
export interface ValidatePolicyConfig<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> {
|
|
161
|
+
/**
|
|
162
|
+
* Operations this policy applies to
|
|
163
|
+
*/
|
|
164
|
+
operation: 'create' | 'update' | 'all'
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Validation condition
|
|
168
|
+
*/
|
|
169
|
+
validate: PolicyCondition<TCtx>
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Priority for policy evaluation
|
|
173
|
+
* @default 0
|
|
174
|
+
*/
|
|
175
|
+
priority?: number
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Performance hints
|
|
179
|
+
*/
|
|
180
|
+
hints?: PolicyHints
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Combined policy configuration
|
|
185
|
+
*/
|
|
186
|
+
export interface CombinedPolicyConfig<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> {
|
|
187
|
+
/**
|
|
188
|
+
* Filter policy (for read operations)
|
|
189
|
+
*/
|
|
190
|
+
filter?: FilterCondition<TCtx>
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Allow policies by operation
|
|
194
|
+
*/
|
|
195
|
+
allow?: {
|
|
196
|
+
[K in Operation]?: PolicyCondition<TCtx>
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Deny policies by operation
|
|
201
|
+
*/
|
|
202
|
+
deny?: {
|
|
203
|
+
[K in Operation]?: PolicyCondition<TCtx>
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Validate policies by operation
|
|
208
|
+
*/
|
|
209
|
+
validate?: {
|
|
210
|
+
create?: PolicyCondition<TCtx>
|
|
211
|
+
update?: PolicyCondition<TCtx>
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// Table Configuration with Composition
|
|
217
|
+
// ============================================================================
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Extended table RLS configuration with policy composition support
|
|
221
|
+
*/
|
|
222
|
+
export interface ComposableTableConfig {
|
|
223
|
+
/**
|
|
224
|
+
* Reusable policies to extend from
|
|
225
|
+
* Policies are applied in order (first = lowest priority)
|
|
226
|
+
*/
|
|
227
|
+
extends?: ReusablePolicy[]
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Additional table-specific policies
|
|
231
|
+
*/
|
|
232
|
+
policies?: PolicyDefinition[]
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Whether to allow access by default when no policies match
|
|
236
|
+
* @default true
|
|
237
|
+
*/
|
|
238
|
+
defaultDeny?: boolean
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Roles that bypass RLS
|
|
242
|
+
*/
|
|
243
|
+
skipFor?: string[]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Complete schema with composition support
|
|
248
|
+
*
|
|
249
|
+
* @typeParam DB - Database schema type
|
|
250
|
+
*/
|
|
251
|
+
export type ComposableRLSSchema<DB> = {
|
|
252
|
+
[K in keyof DB]?: ComposableTableConfig
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Policy Inheritance Types
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Base policy that can be extended
|
|
261
|
+
*/
|
|
262
|
+
export interface BasePolicyDefinition {
|
|
263
|
+
/**
|
|
264
|
+
* Unique identifier for this base policy
|
|
265
|
+
*/
|
|
266
|
+
id: string
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Human-readable name
|
|
270
|
+
*/
|
|
271
|
+
name: string
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Description
|
|
275
|
+
*/
|
|
276
|
+
description?: string
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Policies included in this base
|
|
280
|
+
*/
|
|
281
|
+
policies: PolicyDefinition[]
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Other base policies this extends
|
|
285
|
+
*/
|
|
286
|
+
extends?: string[]
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Priority offset applied to all policies
|
|
290
|
+
* @default 0
|
|
291
|
+
*/
|
|
292
|
+
priorityOffset?: number
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Policy inheritance chain resolution
|
|
297
|
+
*/
|
|
298
|
+
export interface ResolvedInheritance {
|
|
299
|
+
/**
|
|
300
|
+
* Final merged policies
|
|
301
|
+
*/
|
|
302
|
+
policies: PolicyDefinition[]
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Chain of base policies used
|
|
306
|
+
*/
|
|
307
|
+
inheritanceChain: string[]
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Any conflicts detected
|
|
311
|
+
*/
|
|
312
|
+
conflicts: {
|
|
313
|
+
policy: string
|
|
314
|
+
reason: string
|
|
315
|
+
}[]
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// Common Policy Patterns
|
|
320
|
+
// ============================================================================
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Multi-tenancy policy configuration
|
|
324
|
+
*/
|
|
325
|
+
export interface TenantIsolationConfig {
|
|
326
|
+
/**
|
|
327
|
+
* Column name for tenant ID
|
|
328
|
+
* @default 'tenant_id'
|
|
329
|
+
*/
|
|
330
|
+
tenantColumn?: string
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Operations to apply tenant isolation to
|
|
334
|
+
* @default ['read', 'create', 'update', 'delete']
|
|
335
|
+
*/
|
|
336
|
+
operations?: Operation[]
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Whether to validate tenant on create/update
|
|
340
|
+
* @default true
|
|
341
|
+
*/
|
|
342
|
+
validateOnMutation?: boolean
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Ownership policy configuration
|
|
347
|
+
*/
|
|
348
|
+
export interface OwnershipConfig {
|
|
349
|
+
/**
|
|
350
|
+
* Column name for owner ID
|
|
351
|
+
* @default 'owner_id' or 'user_id'
|
|
352
|
+
*/
|
|
353
|
+
ownerColumn?: string
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Operations owners can perform
|
|
357
|
+
* @default ['read', 'update', 'delete']
|
|
358
|
+
*/
|
|
359
|
+
ownerOperations?: Operation[]
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Whether owners can delete
|
|
363
|
+
* @default true
|
|
364
|
+
*/
|
|
365
|
+
canDelete?: boolean
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Soft delete policy configuration
|
|
370
|
+
*/
|
|
371
|
+
export interface SoftDeleteConfig {
|
|
372
|
+
/**
|
|
373
|
+
* Column name for soft delete flag
|
|
374
|
+
* @default 'deleted_at'
|
|
375
|
+
*/
|
|
376
|
+
deletedColumn?: string
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Whether to filter soft-deleted rows on read
|
|
380
|
+
* @default true
|
|
381
|
+
*/
|
|
382
|
+
filterOnRead?: boolean
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Whether to prevent hard deletes
|
|
386
|
+
* @default true
|
|
387
|
+
*/
|
|
388
|
+
preventHardDelete?: boolean
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Status-based access configuration
|
|
393
|
+
*/
|
|
394
|
+
export interface StatusAccessConfig {
|
|
395
|
+
/**
|
|
396
|
+
* Column name for status
|
|
397
|
+
* @default 'status'
|
|
398
|
+
*/
|
|
399
|
+
statusColumn?: string
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Statuses that are publicly readable
|
|
403
|
+
*/
|
|
404
|
+
publicStatuses?: string[]
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Statuses that can be updated
|
|
408
|
+
*/
|
|
409
|
+
editableStatuses?: string[]
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Statuses that can be deleted
|
|
413
|
+
*/
|
|
414
|
+
deletableStatuses?: string[]
|
|
415
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-Level Access Control Module
|
|
3
|
+
*
|
|
4
|
+
* Provides field-level security for controlling access to individual columns.
|
|
5
|
+
*
|
|
6
|
+
* @module @kysera/rls/field-access
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Types
|
|
10
|
+
export type {
|
|
11
|
+
FieldOperation,
|
|
12
|
+
FieldAccessCondition,
|
|
13
|
+
FieldAccessConfig,
|
|
14
|
+
TableFieldAccessConfig,
|
|
15
|
+
FieldAccessSchema,
|
|
16
|
+
CompiledFieldAccess,
|
|
17
|
+
CompiledTableFieldAccess,
|
|
18
|
+
FieldAccessResult,
|
|
19
|
+
MaskedRow,
|
|
20
|
+
FieldAccessOptions
|
|
21
|
+
} from './types.js'
|
|
22
|
+
|
|
23
|
+
// Predefined patterns
|
|
24
|
+
export {
|
|
25
|
+
neverAccessible,
|
|
26
|
+
ownerOnly,
|
|
27
|
+
ownerOrRoles,
|
|
28
|
+
rolesOnly,
|
|
29
|
+
readOnly,
|
|
30
|
+
publicReadRestrictedWrite,
|
|
31
|
+
maskedField
|
|
32
|
+
} from './types.js'
|
|
33
|
+
|
|
34
|
+
// Registry
|
|
35
|
+
export { FieldAccessRegistry, createFieldAccessRegistry } from './registry.js'
|
|
36
|
+
|
|
37
|
+
// Processor
|
|
38
|
+
export { FieldAccessProcessor, createFieldAccessProcessor } from './processor.js'
|