toride 0.2.0 → 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/{chunk-24PMDTLE.js → chunk-GRFSE3QO.js} +90 -21
- package/dist/{chunk-475CNU63.js → chunk-S6DKDABO.js} +2 -1
- package/dist/cli.js +86 -2
- package/dist/{client-RqwW0K_-.d.ts → client-CBp3admQ.d.ts} +92 -11
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +94 -24
- package/dist/index.js +125 -7
- package/package.json +3 -2
- package/schema/.gitkeep +0 -0
- package/schema/policy.schema.json +1308 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { P as Policy, T as TorideSchema, D as DefaultSchema, a as TorideOptions, A as ActorRef, R as ResourceRef, C as CheckOptions, E as ExplainResult, b as PermissionSnapshot, B as BatchCheckItem, c as TestCase, d as Resolvers } from './client-CBp3admQ.js';
|
|
3
|
+
export { e as ActorDeclaration, f as ArrayAttributeSchema, g as AttributeSchema, h as AttributeType, i as ClientResourceRef, j as ConditionExpression, k as ConditionOperator, l as ConditionValue, m as CycleError, n as DecisionEvent, o as DepthLimitError, p as DerivedRoleEntry, q as DerivedRoleTrace, r as EvaluatorFn, F as FieldAccessDef, s as ForbiddenError, G as GlobalRole, M as MatchedRule, O as ObjectAttributeSchema, t as PayloadRelations, u as PrimitiveAttributeSchema, Q as QueryEvent, v as ResolvedRolesDetail, w as ResourceBlock, x as ResourceResolver, y as Rule, S as SimpleConditions, z as TorideClient, V as ValidationError, H as VirtualFieldMapping, I as VirtualFieldMappingFor, J as VirtualFieldsConfig } from './client-CBp3admQ.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ConditionExpression: recursive type using lazy().
|
|
7
|
+
* Either simple conditions, { any: [...] }, or { all: [...] }.
|
|
8
|
+
*/
|
|
9
|
+
declare const ConditionExpressionSchema: v.GenericSchema<unknown>;
|
|
10
|
+
declare const PolicySchema: v.ObjectSchema<{
|
|
11
|
+
readonly version: v.LiteralSchema<"1", undefined>;
|
|
12
|
+
readonly actors: v.RecordSchema<v.StringSchema<undefined>, v.ObjectSchema<{
|
|
13
|
+
readonly attributes: v.RecordSchema<v.StringSchema<undefined>, any, undefined>;
|
|
14
|
+
}, undefined>, undefined>;
|
|
15
|
+
readonly global_roles: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.ObjectSchema<{
|
|
16
|
+
readonly actor_type: v.StringSchema<undefined>;
|
|
17
|
+
readonly when: v.GenericSchema<unknown>;
|
|
18
|
+
}, undefined>, undefined>, undefined>;
|
|
19
|
+
readonly resources: v.RecordSchema<v.StringSchema<undefined>, v.ObjectSchema<{
|
|
20
|
+
readonly roles: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
21
|
+
readonly permissions: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
22
|
+
readonly attributes: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, any, undefined>, undefined>;
|
|
23
|
+
readonly relations: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.StringSchema<undefined>, undefined>, undefined>;
|
|
24
|
+
readonly grants: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>, undefined>;
|
|
25
|
+
readonly derived_roles: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
26
|
+
readonly role: v.StringSchema<undefined>;
|
|
27
|
+
readonly from_global_role: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
28
|
+
readonly from_role: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
29
|
+
readonly on_relation: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
30
|
+
readonly from_relation: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
31
|
+
readonly actor_type: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
32
|
+
readonly when: v.OptionalSchema<v.GenericSchema<unknown>, undefined>;
|
|
33
|
+
}, undefined>, undefined>, undefined>;
|
|
34
|
+
readonly rules: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
35
|
+
readonly effect: v.PicklistSchema<["permit", "forbid"], undefined>;
|
|
36
|
+
readonly roles: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
37
|
+
readonly permissions: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
38
|
+
readonly when: v.GenericSchema<unknown>;
|
|
39
|
+
}, undefined>, undefined>, undefined>;
|
|
40
|
+
readonly field_access: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.ObjectSchema<{
|
|
41
|
+
readonly read: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
42
|
+
readonly update: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
43
|
+
}, undefined>, undefined>, undefined>;
|
|
44
|
+
}, undefined>, undefined>;
|
|
45
|
+
readonly tests: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
46
|
+
readonly name: v.StringSchema<undefined>;
|
|
47
|
+
readonly actor: v.ObjectSchema<{
|
|
48
|
+
readonly type: v.StringSchema<undefined>;
|
|
49
|
+
readonly id: v.StringSchema<undefined>;
|
|
50
|
+
readonly attributes: v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>;
|
|
51
|
+
}, undefined>;
|
|
52
|
+
readonly resolvers: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>, undefined>;
|
|
53
|
+
readonly action: v.StringSchema<undefined>;
|
|
54
|
+
readonly resource: v.ObjectSchema<{
|
|
55
|
+
readonly type: v.StringSchema<undefined>;
|
|
56
|
+
readonly id: v.StringSchema<undefined>;
|
|
57
|
+
readonly attributes: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
|
|
58
|
+
}, undefined>;
|
|
59
|
+
readonly expected: v.PicklistSchema<["allow", "deny"], undefined>;
|
|
60
|
+
}, undefined>, undefined>, undefined>;
|
|
61
|
+
}, undefined>;
|
|
3
62
|
|
|
4
63
|
/**
|
|
5
64
|
* Parse and validate a YAML string into a typed Policy object.
|
|
@@ -149,23 +208,30 @@ interface NeverConstraint {
|
|
|
149
208
|
type Constraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | FieldGteConstraint | FieldLtConstraint | FieldLteConstraint | FieldInConstraint | FieldNinConstraint | FieldExistsConstraint | FieldIncludesConstraint | FieldContainsConstraint | RelationConstraint | HasRoleConstraint | UnknownConstraint | AndConstraint | OrConstraint | NotConstraint | AlwaysConstraint | NeverConstraint;
|
|
150
209
|
/** Leaf constraint subset for ConstraintAdapter.translate(). */
|
|
151
210
|
type LeafConstraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | FieldGteConstraint | FieldLtConstraint | FieldLteConstraint | FieldInConstraint | FieldNinConstraint | FieldExistsConstraint | FieldIncludesConstraint | FieldContainsConstraint;
|
|
152
|
-
/** Result of partial evaluation. */
|
|
153
|
-
type ConstraintResult = {
|
|
154
|
-
readonly
|
|
155
|
-
|
|
156
|
-
readonly
|
|
211
|
+
/** Result of partial evaluation, tagged with resource type R (phantom). */
|
|
212
|
+
type ConstraintResult<R extends string = string> = {
|
|
213
|
+
readonly ok: true;
|
|
214
|
+
readonly constraint: Constraint | null;
|
|
215
|
+
readonly __resource?: R;
|
|
157
216
|
} | {
|
|
158
|
-
readonly
|
|
217
|
+
readonly ok: false;
|
|
218
|
+
readonly __resource?: R;
|
|
159
219
|
};
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
220
|
+
/**
|
|
221
|
+
* User-provided adapter for translating constraint ASTs to queries.
|
|
222
|
+
* TQueryMap maps resource type names to their query output types.
|
|
223
|
+
*
|
|
224
|
+
* BREAKING CHANGE: Previously ConstraintAdapter<TQuery> with a single query type.
|
|
225
|
+
* Now uses a resource-to-query-type map for per-resource output typing.
|
|
226
|
+
*/
|
|
227
|
+
interface ConstraintAdapter<TQueryMap extends Record<string, unknown> = Record<string, unknown>> {
|
|
228
|
+
translate(constraint: LeafConstraint): TQueryMap[string];
|
|
229
|
+
relation(field: string, resourceType: string, childQuery: TQueryMap[string]): TQueryMap[string];
|
|
230
|
+
hasRole(actorId: string, actorType: string, role: string): TQueryMap[string];
|
|
231
|
+
unknown(name: string): TQueryMap[string];
|
|
232
|
+
and(queries: TQueryMap[string][]): TQueryMap[string];
|
|
233
|
+
or(queries: TQueryMap[string][]): TQueryMap[string];
|
|
234
|
+
not(query: TQueryMap[string]): TQueryMap[string];
|
|
169
235
|
}
|
|
170
236
|
|
|
171
237
|
/**
|
|
@@ -205,22 +271,22 @@ declare class Toride<S extends TorideSchema = DefaultSchema> {
|
|
|
205
271
|
* keyed by "Type:id" with arrays of permitted action strings.
|
|
206
272
|
* Suitable for serializing to the client via TorideClient.
|
|
207
273
|
*/
|
|
208
|
-
snapshot(actor: ActorRef<S>, resources: ResourceRef<S>[], options?: CheckOptions): Promise<PermissionSnapshot
|
|
274
|
+
snapshot(actor: ActorRef<S>, resources: ResourceRef<S>[], options?: CheckOptions): Promise<PermissionSnapshot<S>>;
|
|
209
275
|
/**
|
|
210
276
|
* T095: Check if an actor can perform a field-level operation on a specific field.
|
|
211
277
|
* Restricted fields require the actor to have a role listed in field_access.
|
|
212
278
|
* Unlisted fields are unrestricted: any actor with the resource-level permission can access them.
|
|
213
279
|
*/
|
|
214
|
-
canField<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, field: string, options?: CheckOptions): Promise<boolean>;
|
|
280
|
+
canField<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, field: keyof S["resourceAttributeMap"][R] & string, options?: CheckOptions): Promise<boolean>;
|
|
215
281
|
/**
|
|
216
282
|
* T095: Return the list of declared field_access field names the actor can access
|
|
217
283
|
* for the given operation. Only returns explicitly declared fields.
|
|
218
284
|
*/
|
|
219
|
-
permittedFields<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, options?: CheckOptions): Promise<string[]>;
|
|
285
|
+
permittedFields<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, options?: CheckOptions): Promise<(keyof S["resourceAttributeMap"][R] & string)[]>;
|
|
220
286
|
/**
|
|
221
287
|
* T070: Return flat deduplicated list of all resolved roles (direct + derived).
|
|
222
288
|
*/
|
|
223
|
-
resolvedRoles<R extends S["resources"]>(actor: ActorRef<S>, resource: ResourceRef<S, R>, options?: CheckOptions): Promise<
|
|
289
|
+
resolvedRoles<R extends S["resources"]>(actor: ActorRef<S>, resource: ResourceRef<S, R>, options?: CheckOptions): Promise<S["roleMap"][R][]>;
|
|
224
290
|
/**
|
|
225
291
|
* T071: Evaluate multiple checks for the same actor with a shared resolver cache.
|
|
226
292
|
* Returns boolean[] in the same order as the input checks.
|
|
@@ -230,12 +296,16 @@ declare class Toride<S extends TorideSchema = DefaultSchema> {
|
|
|
230
296
|
* T064: Build constraint AST for partial evaluation / data filtering.
|
|
231
297
|
* Returns ConstraintResult with unrestricted/forbidden sentinels or constraint AST.
|
|
232
298
|
*/
|
|
233
|
-
buildConstraints<R extends S["resources"]>(actor: ActorRef<S>, action: S["permissionMap"][R], resourceType: R, options?: CheckOptions): Promise<ConstraintResult
|
|
299
|
+
buildConstraints<R extends S["resources"]>(actor: ActorRef<S>, action: S["permissionMap"][R], resourceType: R, options?: CheckOptions): Promise<ConstraintResult<R>>;
|
|
234
300
|
/**
|
|
235
301
|
* T064: Translate constraint AST using an adapter.
|
|
236
302
|
* Dispatches each constraint node to the adapter's methods.
|
|
303
|
+
*
|
|
304
|
+
* Accepts Constraint (the AST) from buildConstraints() and returns
|
|
305
|
+
* TQueryMap[R] — the adapter's mapped output type for resource R.
|
|
306
|
+
* Callers must check result.ok and result.constraint before calling this method.
|
|
237
307
|
*/
|
|
238
|
-
translateConstraints<
|
|
308
|
+
translateConstraints<R extends string, TQueryMap extends Record<string, unknown>>(constraint: Constraint, adapter: ConstraintAdapter<TQueryMap>): TQueryMap[R];
|
|
239
309
|
/**
|
|
240
310
|
* T072: Fire onDecision audit callback via microtask (non-blocking).
|
|
241
311
|
* Errors are silently swallowed to prevent audit failures from affecting authorization.
|
|
@@ -306,4 +376,4 @@ declare function runTestCases(policy: Policy, tests: TestCase[]): Promise<TestRe
|
|
|
306
376
|
|
|
307
377
|
declare const VERSION = "0.0.1";
|
|
308
378
|
|
|
309
|
-
export { ActorRef, BatchCheckItem, CheckOptions, type Constraint, type ConstraintAdapter, type ConstraintResult, DefaultSchema, ExplainResult, type LeafConstraint, PermissionSnapshot, Policy, Resolvers, ResourceRef, type StrictValidationResult, TestCase, type TestFileResult, type TestResult, Toride, TorideOptions, TorideSchema, VERSION, type ValidationDiagnostic, type ValidationResult, createMockResolver, createToride, loadJson, loadYaml, mergePolicies, parseInlineTests, parseTestFile, runTestCases, validatePolicy, validatePolicyResult, validatePolicyStrict };
|
|
379
|
+
export { ActorRef, BatchCheckItem, CheckOptions, ConditionExpressionSchema, type Constraint, type ConstraintAdapter, type ConstraintResult, DefaultSchema, ExplainResult, type LeafConstraint, PermissionSnapshot, Policy, PolicySchema, Resolvers, ResourceRef, type StrictValidationResult, TestCase, type TestFileResult, type TestResult, Toride, TorideOptions, TorideSchema, VERSION, type ValidationDiagnostic, type ValidationResult, createMockResolver, createToride, loadJson, loadYaml, mergePolicies, parseInlineTests, parseTestFile, runTestCases, validatePolicy, validatePolicyResult, validatePolicyStrict };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ConditionExpressionSchema,
|
|
2
3
|
CycleError,
|
|
3
4
|
DepthLimitError,
|
|
5
|
+
ForbiddenError,
|
|
4
6
|
PolicySchema,
|
|
5
7
|
Toride,
|
|
6
8
|
ValidationError,
|
|
@@ -12,10 +14,10 @@ import {
|
|
|
12
14
|
validatePolicy,
|
|
13
15
|
validatePolicyResult,
|
|
14
16
|
validatePolicyStrict
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-GRFSE3QO.js";
|
|
16
18
|
import {
|
|
17
19
|
TorideClient
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-S6DKDABO.js";
|
|
19
21
|
|
|
20
22
|
// src/policy/parser.ts
|
|
21
23
|
import * as YAML from "yaml";
|
|
@@ -47,9 +49,114 @@ function detectOldRelationSyntax(raw) {
|
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
function normalizeAttributeValue(raw, depth = 0, format = "yaml") {
|
|
53
|
+
if (raw === null || raw === void 0) return raw;
|
|
54
|
+
if (typeof raw === "string") {
|
|
55
|
+
if (["string", "number", "boolean"].includes(raw)) {
|
|
56
|
+
return { kind: "primitive", type: raw };
|
|
57
|
+
}
|
|
58
|
+
if (format === "yaml" && raw.endsWith("[]")) {
|
|
59
|
+
const baseType = raw.slice(0, -2);
|
|
60
|
+
if (["string", "number", "boolean"].includes(baseType)) {
|
|
61
|
+
return {
|
|
62
|
+
kind: "array",
|
|
63
|
+
items: { kind: "primitive", type: baseType }
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
69
|
+
const obj = raw;
|
|
70
|
+
const isCanonical = obj.kind === "primitive" && "type" in obj || obj.kind === "object" && "fields" in obj || obj.kind === "array" && "items" in obj;
|
|
71
|
+
if (isCanonical) {
|
|
72
|
+
if (obj.kind === "object" && typeof obj.fields === "object" && obj.fields !== null) {
|
|
73
|
+
const normalizedFields = {};
|
|
74
|
+
for (const [key, value] of Object.entries(obj.fields)) {
|
|
75
|
+
normalizedFields[key] = normalizeAttributeValue(value, depth, format);
|
|
76
|
+
}
|
|
77
|
+
return { ...obj, fields: normalizedFields };
|
|
78
|
+
}
|
|
79
|
+
if (obj.kind === "array" && "items" in obj) {
|
|
80
|
+
return { ...obj, items: normalizeAttributeValue(obj.items, depth, format) };
|
|
81
|
+
}
|
|
82
|
+
return obj;
|
|
83
|
+
}
|
|
84
|
+
if (obj.type === "array" && "items" in obj) {
|
|
85
|
+
return {
|
|
86
|
+
kind: "array",
|
|
87
|
+
items: normalizeAttributeValue(obj.items, depth, format)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (depth > 3) {
|
|
91
|
+
throw new ValidationError(
|
|
92
|
+
"Attribute nesting depth exceeds maximum of 3",
|
|
93
|
+
"attributes"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const normalized = {};
|
|
97
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
98
|
+
normalized[key] = normalizeAttributeValue(value, depth + 1, format);
|
|
99
|
+
}
|
|
100
|
+
return { kind: "object", fields: normalized };
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(raw)) {
|
|
103
|
+
return raw.map((item) => normalizeAttributeValue(item, depth, format));
|
|
104
|
+
}
|
|
105
|
+
return raw;
|
|
106
|
+
}
|
|
107
|
+
function normalizeAttributeRecord(attrs, format) {
|
|
108
|
+
const normalizedAttrs = {};
|
|
109
|
+
for (const [attrKey, attrValue] of Object.entries(attrs)) {
|
|
110
|
+
normalizedAttrs[attrKey] = normalizeAttributeValue(attrValue, 0, format);
|
|
111
|
+
}
|
|
112
|
+
return normalizedAttrs;
|
|
113
|
+
}
|
|
114
|
+
function normalizeAttributes(raw, format) {
|
|
115
|
+
if (raw === null || raw === void 0 || typeof raw !== "object" || Array.isArray(raw)) {
|
|
116
|
+
return raw;
|
|
117
|
+
}
|
|
118
|
+
const obj = raw;
|
|
119
|
+
const normalized = { ...obj };
|
|
120
|
+
if ("actors" in normalized && typeof normalized.actors === "object" && normalized.actors !== null) {
|
|
121
|
+
const actors = normalized.actors;
|
|
122
|
+
const normalizedActors = {};
|
|
123
|
+
for (const [actorName, actorDef] of Object.entries(actors)) {
|
|
124
|
+
if (typeof actorDef === "object" && actorDef !== null) {
|
|
125
|
+
const actor = actorDef;
|
|
126
|
+
const normalizedActor = { ...actor };
|
|
127
|
+
if ("attributes" in normalizedActor && typeof normalizedActor.attributes === "object" && normalizedActor.attributes !== null) {
|
|
128
|
+
normalizedActor.attributes = normalizeAttributeRecord(normalizedActor.attributes, format);
|
|
129
|
+
}
|
|
130
|
+
normalizedActors[actorName] = normalizedActor;
|
|
131
|
+
} else {
|
|
132
|
+
normalizedActors[actorName] = actorDef;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
normalized.actors = normalizedActors;
|
|
136
|
+
}
|
|
137
|
+
if ("resources" in normalized && typeof normalized.resources === "object" && normalized.resources !== null) {
|
|
138
|
+
const resources = normalized.resources;
|
|
139
|
+
const normalizedResources = {};
|
|
140
|
+
for (const [resName, resDef] of Object.entries(resources)) {
|
|
141
|
+
if (typeof resDef === "object" && resDef !== null) {
|
|
142
|
+
const resource = resDef;
|
|
143
|
+
const normalizedResource = { ...resource };
|
|
144
|
+
if ("attributes" in normalizedResource && typeof normalizedResource.attributes === "object" && normalizedResource.attributes !== null) {
|
|
145
|
+
normalizedResource.attributes = normalizeAttributeRecord(normalizedResource.attributes, format);
|
|
146
|
+
}
|
|
147
|
+
normalizedResources[resName] = normalizedResource;
|
|
148
|
+
} else {
|
|
149
|
+
normalizedResources[resName] = resDef;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
normalized.resources = normalizedResources;
|
|
153
|
+
}
|
|
154
|
+
return normalized;
|
|
155
|
+
}
|
|
156
|
+
function parsePolicy(raw, format) {
|
|
51
157
|
detectOldRelationSyntax(raw);
|
|
52
|
-
const
|
|
158
|
+
const normalizedRaw = normalizeAttributes(raw, format);
|
|
159
|
+
const result = v.safeParse(PolicySchema, normalizedRaw);
|
|
53
160
|
if (!result.success) {
|
|
54
161
|
const issue = result.issues[0];
|
|
55
162
|
const path = issue?.path?.map((p) => String(p.key)).join(".") ?? "";
|
|
@@ -72,7 +179,7 @@ async function loadYaml(input) {
|
|
|
72
179
|
""
|
|
73
180
|
);
|
|
74
181
|
}
|
|
75
|
-
return parsePolicy(raw);
|
|
182
|
+
return parsePolicy(raw, "yaml");
|
|
76
183
|
}
|
|
77
184
|
async function loadJson(input) {
|
|
78
185
|
let raw;
|
|
@@ -84,7 +191,7 @@ async function loadJson(input) {
|
|
|
84
191
|
""
|
|
85
192
|
);
|
|
86
193
|
}
|
|
87
|
-
return parsePolicy(raw);
|
|
194
|
+
return parsePolicy(raw, "json");
|
|
88
195
|
}
|
|
89
196
|
|
|
90
197
|
// src/policy/merger.ts
|
|
@@ -155,6 +262,7 @@ function mergeResourceBlock(resourceName, base, overlay) {
|
|
|
155
262
|
const derived_roles = appendArrays(base.derived_roles, overlay.derived_roles);
|
|
156
263
|
const rules = appendArrays(base.rules, overlay.rules);
|
|
157
264
|
const field_access = mergeFieldAccess(base.field_access, overlay.field_access);
|
|
265
|
+
const attributes = mergeAttributes(base.attributes, overlay.attributes);
|
|
158
266
|
const result = {
|
|
159
267
|
roles,
|
|
160
268
|
permissions,
|
|
@@ -162,10 +270,17 @@ function mergeResourceBlock(resourceName, base, overlay) {
|
|
|
162
270
|
...relations && Object.keys(relations).length > 0 ? { relations } : {},
|
|
163
271
|
...derived_roles && derived_roles.length > 0 ? { derived_roles } : {},
|
|
164
272
|
...rules && rules.length > 0 ? { rules } : {},
|
|
165
|
-
...field_access && Object.keys(field_access).length > 0 ? { field_access } : {}
|
|
273
|
+
...field_access && Object.keys(field_access).length > 0 ? { field_access } : {},
|
|
274
|
+
...attributes && Object.keys(attributes).length > 0 ? { attributes } : {}
|
|
166
275
|
};
|
|
167
276
|
return result;
|
|
168
277
|
}
|
|
278
|
+
function mergeAttributes(base, overlay) {
|
|
279
|
+
if (!base && !overlay) return void 0;
|
|
280
|
+
if (base) assertNoDangerousKeys(base, "base attributes");
|
|
281
|
+
if (overlay) assertNoDangerousKeys(overlay, "overlay attributes");
|
|
282
|
+
return { ...base ?? {}, ...overlay ?? {} };
|
|
283
|
+
}
|
|
169
284
|
function unionArrays(a, b) {
|
|
170
285
|
return [.../* @__PURE__ */ new Set([...a, ...b])];
|
|
171
286
|
}
|
|
@@ -226,8 +341,11 @@ function mergeFieldAccess(base, overlay) {
|
|
|
226
341
|
// src/index.ts
|
|
227
342
|
var VERSION = "0.0.1";
|
|
228
343
|
export {
|
|
344
|
+
ConditionExpressionSchema,
|
|
229
345
|
CycleError,
|
|
230
346
|
DepthLimitError,
|
|
347
|
+
ForbiddenError,
|
|
348
|
+
PolicySchema,
|
|
231
349
|
Toride,
|
|
232
350
|
TorideClient,
|
|
233
351
|
VERSION,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toride",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Relation-aware authorization engine for TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"toride": "./dist/cli.js"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"dist"
|
|
22
|
+
"dist",
|
|
23
|
+
"schema"
|
|
23
24
|
],
|
|
24
25
|
"nx": {
|
|
25
26
|
"tags": [
|
package/schema/.gitkeep
ADDED
|
File without changes
|