toride 0.3.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-2AWXNP37.js → chunk-GRFSE3QO.js} +85 -26
- package/dist/cli.js +86 -2
- package/dist/{client-aExMng5T.d.ts → client-CBp3admQ.d.ts} +80 -4
- package/dist/client.d.ts +1 -1
- package/dist/index.d.ts +68 -11
- package/dist/index.js +124 -6
- package/package.json +3 -2
- package/schema/.gitkeep +0 -0
- package/schema/policy.schema.json +1308 -0
|
@@ -1,8 +1,56 @@
|
|
|
1
1
|
// src/policy/schema.ts
|
|
2
2
|
import * as v from "valibot";
|
|
3
|
-
var AttributeTypeSchema = v.picklist(["string", "number", "boolean"]);
|
|
3
|
+
var AttributeTypeSchema = v.picklist(["string", "number", "boolean", "string[]", "number[]", "boolean[]"]);
|
|
4
|
+
var MAX_ATTRIBUTE_DEPTH = 3;
|
|
5
|
+
function getAttributeDepth(schema, currentDepth = 0) {
|
|
6
|
+
if (typeof schema !== "object" || schema === null) {
|
|
7
|
+
return currentDepth;
|
|
8
|
+
}
|
|
9
|
+
const obj = schema;
|
|
10
|
+
if (obj.kind === "primitive") {
|
|
11
|
+
return currentDepth;
|
|
12
|
+
}
|
|
13
|
+
if (obj.kind === "object" && typeof obj.fields === "object" && obj.fields !== null) {
|
|
14
|
+
const fields = obj.fields;
|
|
15
|
+
let maxDepth = currentDepth;
|
|
16
|
+
for (const field of Object.values(fields)) {
|
|
17
|
+
const fieldDepth = getAttributeDepth(field, currentDepth + 1);
|
|
18
|
+
if (fieldDepth > maxDepth) {
|
|
19
|
+
maxDepth = fieldDepth;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return maxDepth;
|
|
23
|
+
}
|
|
24
|
+
if (obj.kind === "array" && typeof obj.items === "object" && obj.items !== null) {
|
|
25
|
+
return getAttributeDepth(obj.items, currentDepth);
|
|
26
|
+
}
|
|
27
|
+
return currentDepth;
|
|
28
|
+
}
|
|
29
|
+
function checkAttributeDepth(input) {
|
|
30
|
+
return getAttributeDepth(input) <= MAX_ATTRIBUTE_DEPTH;
|
|
31
|
+
}
|
|
32
|
+
var PrimitiveAttributeSchemaNodeSchema = v.object({
|
|
33
|
+
kind: v.literal("primitive"),
|
|
34
|
+
type: AttributeTypeSchema
|
|
35
|
+
});
|
|
36
|
+
var ObjectAttributeSchemaNodeSchema = v.object({
|
|
37
|
+
kind: v.literal("object"),
|
|
38
|
+
fields: v.record(v.string(), v.lazy(() => AttributeSchemaNodeSchema))
|
|
39
|
+
});
|
|
40
|
+
var ArrayAttributeSchemaNodeSchema = v.object({
|
|
41
|
+
kind: v.literal("array"),
|
|
42
|
+
items: v.lazy(() => AttributeSchemaNodeSchema)
|
|
43
|
+
});
|
|
44
|
+
var AttributeSchemaNodeSchema = v.pipe(
|
|
45
|
+
v.union([
|
|
46
|
+
PrimitiveAttributeSchemaNodeSchema,
|
|
47
|
+
ObjectAttributeSchemaNodeSchema,
|
|
48
|
+
ArrayAttributeSchemaNodeSchema
|
|
49
|
+
]),
|
|
50
|
+
v.check(checkAttributeDepth, "Attribute schema exceeds maximum depth of 3")
|
|
51
|
+
);
|
|
4
52
|
var ActorDeclarationSchema = v.object({
|
|
5
|
-
attributes: v.record(v.string(),
|
|
53
|
+
attributes: v.record(v.string(), AttributeSchemaNodeSchema)
|
|
6
54
|
});
|
|
7
55
|
var ConditionOperatorSchema = v.union([
|
|
8
56
|
v.object({ eq: v.unknown() }),
|
|
@@ -54,7 +102,7 @@ var FieldAccessDefSchema = v.object({
|
|
|
54
102
|
var ResourceBlockSchema = v.object({
|
|
55
103
|
roles: v.array(v.string()),
|
|
56
104
|
permissions: v.array(v.string()),
|
|
57
|
-
attributes: v.optional(v.record(v.string(),
|
|
105
|
+
attributes: v.optional(v.record(v.string(), AttributeSchemaNodeSchema)),
|
|
58
106
|
relations: v.optional(v.record(v.string(), v.string())),
|
|
59
107
|
grants: v.optional(v.record(v.string(), v.array(v.string()))),
|
|
60
108
|
derived_roles: v.optional(v.array(DerivedRoleEntrySchema)),
|
|
@@ -122,6 +170,20 @@ var DepthLimitError = class extends Error {
|
|
|
122
170
|
this.limitType = limitType;
|
|
123
171
|
}
|
|
124
172
|
};
|
|
173
|
+
var ForbiddenError = class extends Error {
|
|
174
|
+
actor;
|
|
175
|
+
action;
|
|
176
|
+
resourceType;
|
|
177
|
+
constructor(actor, action, resourceType) {
|
|
178
|
+
super(
|
|
179
|
+
`Actor "${actor.type}:${actor.id}" is forbidden from performing "${action}" on resource type "${resourceType}"`
|
|
180
|
+
);
|
|
181
|
+
this.name = "ForbiddenError";
|
|
182
|
+
this.actor = actor;
|
|
183
|
+
this.action = action;
|
|
184
|
+
this.resourceType = resourceType;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
125
187
|
|
|
126
188
|
// src/policy/validator.ts
|
|
127
189
|
function extractActorAttributes(condition) {
|
|
@@ -1434,7 +1496,7 @@ var OPERATOR_KEYS2 = /* @__PURE__ */ new Set([
|
|
|
1434
1496
|
async function buildConstraints(actor, action, resourceType, cache, policy, options) {
|
|
1435
1497
|
const resourceBlock = policy.resources[resourceType];
|
|
1436
1498
|
if (!resourceBlock) {
|
|
1437
|
-
return {
|
|
1499
|
+
return { ok: false };
|
|
1438
1500
|
}
|
|
1439
1501
|
const env = options?.env ?? {};
|
|
1440
1502
|
const rolesGrantingAction = findRolesGrantingAction(action, resourceBlock);
|
|
@@ -1443,7 +1505,7 @@ async function buildConstraints(actor, action, resourceType, cache, policy, opti
|
|
|
1443
1505
|
(r) => r.effect === "permit" && r.permissions.includes(action)
|
|
1444
1506
|
);
|
|
1445
1507
|
if (permitRules2.length === 0) {
|
|
1446
|
-
return {
|
|
1508
|
+
return { ok: false };
|
|
1447
1509
|
}
|
|
1448
1510
|
}
|
|
1449
1511
|
const roleConstraintCache = /* @__PURE__ */ new Map();
|
|
@@ -1504,7 +1566,7 @@ async function buildConstraints(actor, action, resourceType, cache, policy, opti
|
|
|
1504
1566
|
}
|
|
1505
1567
|
}
|
|
1506
1568
|
if (pathConstraints.length === 0) {
|
|
1507
|
-
return {
|
|
1569
|
+
return { ok: false };
|
|
1508
1570
|
}
|
|
1509
1571
|
let combined;
|
|
1510
1572
|
if (pathConstraints.length === 1) {
|
|
@@ -1526,12 +1588,12 @@ async function buildConstraints(actor, action, resourceType, cache, policy, opti
|
|
|
1526
1588
|
}
|
|
1527
1589
|
combined = simplify(combined);
|
|
1528
1590
|
if (combined.type === "always") {
|
|
1529
|
-
return {
|
|
1591
|
+
return { ok: true, constraint: null };
|
|
1530
1592
|
}
|
|
1531
1593
|
if (combined.type === "never") {
|
|
1532
|
-
return {
|
|
1594
|
+
return { ok: false };
|
|
1533
1595
|
}
|
|
1534
|
-
return {
|
|
1596
|
+
return { ok: true, constraint: combined };
|
|
1535
1597
|
}
|
|
1536
1598
|
function findRolesGrantingAction(action, resourceBlock) {
|
|
1537
1599
|
const grants = resourceBlock.grants ?? {};
|
|
@@ -1949,11 +2011,11 @@ function translateConstraints(constraint, adapter, _depth = 0) {
|
|
|
1949
2011
|
// Terminal nodes - should not reach the translator
|
|
1950
2012
|
case "always":
|
|
1951
2013
|
throw new Error(
|
|
1952
|
-
'Constraint node "always" should be simplified out before translation.
|
|
2014
|
+
'Constraint node "always" should be simplified out before translation. Check result.ok and result.constraint before calling translateConstraints().'
|
|
1953
2015
|
);
|
|
1954
2016
|
case "never":
|
|
1955
2017
|
throw new Error(
|
|
1956
|
-
'Constraint node "never" should be simplified out before translation.
|
|
2018
|
+
'Constraint node "never" should be simplified out before translation. Check result.ok and result.constraint before calling translateConstraints().'
|
|
1957
2019
|
);
|
|
1958
2020
|
default: {
|
|
1959
2021
|
const _exhaustive = constraint;
|
|
@@ -2168,9 +2230,9 @@ var Toride = class {
|
|
|
2168
2230
|
const a = actor;
|
|
2169
2231
|
const sharedCache = new AttributeCache(this.resolvers);
|
|
2170
2232
|
const results = [];
|
|
2171
|
-
for (const
|
|
2172
|
-
const r =
|
|
2173
|
-
const act =
|
|
2233
|
+
for (const check2 of checks) {
|
|
2234
|
+
const r = check2.resource;
|
|
2235
|
+
const act = check2.action;
|
|
2174
2236
|
const result = await this.evaluateInternal(
|
|
2175
2237
|
a,
|
|
2176
2238
|
act,
|
|
@@ -2211,17 +2273,12 @@ var Toride = class {
|
|
|
2211
2273
|
* T064: Translate constraint AST using an adapter.
|
|
2212
2274
|
* Dispatches each constraint node to the adapter's methods.
|
|
2213
2275
|
*
|
|
2214
|
-
* Accepts
|
|
2276
|
+
* Accepts Constraint (the AST) from buildConstraints() and returns
|
|
2215
2277
|
* TQueryMap[R] — the adapter's mapped output type for resource R.
|
|
2216
|
-
*
|
|
2278
|
+
* Callers must check result.ok and result.constraint before calling this method.
|
|
2217
2279
|
*/
|
|
2218
|
-
translateConstraints(
|
|
2219
|
-
|
|
2220
|
-
throw new Error(
|
|
2221
|
-
"Cannot translate unrestricted or forbidden ConstraintResult. Check for 'constraints' property before calling translateConstraints()."
|
|
2222
|
-
);
|
|
2223
|
-
}
|
|
2224
|
-
return translateConstraints(constraints.constraints, adapter);
|
|
2280
|
+
translateConstraints(constraint, adapter) {
|
|
2281
|
+
return translateConstraints(constraint, adapter);
|
|
2225
2282
|
}
|
|
2226
2283
|
/**
|
|
2227
2284
|
* T072: Fire onDecision audit callback via microtask (non-blocking).
|
|
@@ -2260,10 +2317,10 @@ var Toride = class {
|
|
|
2260
2317
|
const callback = this.options.onQuery;
|
|
2261
2318
|
if (!callback) return;
|
|
2262
2319
|
let resultType;
|
|
2263
|
-
if (
|
|
2264
|
-
resultType = "unrestricted";
|
|
2265
|
-
} else if ("forbidden" in constraintResult && constraintResult.forbidden) {
|
|
2320
|
+
if (!constraintResult.ok) {
|
|
2266
2321
|
resultType = "forbidden";
|
|
2322
|
+
} else if (constraintResult.constraint === null) {
|
|
2323
|
+
resultType = "unrestricted";
|
|
2267
2324
|
} else {
|
|
2268
2325
|
resultType = "constrained";
|
|
2269
2326
|
}
|
|
@@ -2360,10 +2417,12 @@ async function runTestCases(policy, tests) {
|
|
|
2360
2417
|
}
|
|
2361
2418
|
|
|
2362
2419
|
export {
|
|
2420
|
+
ConditionExpressionSchema,
|
|
2363
2421
|
PolicySchema,
|
|
2364
2422
|
ValidationError,
|
|
2365
2423
|
CycleError,
|
|
2366
2424
|
DepthLimitError,
|
|
2425
|
+
ForbiddenError,
|
|
2367
2426
|
validatePolicy,
|
|
2368
2427
|
validatePolicyResult,
|
|
2369
2428
|
validatePolicyStrict,
|
package/dist/cli.js
CHANGED
|
@@ -7,13 +7,96 @@ import {
|
|
|
7
7
|
runTestCases,
|
|
8
8
|
validatePolicyResult,
|
|
9
9
|
validatePolicyStrict
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GRFSE3QO.js";
|
|
11
11
|
|
|
12
12
|
// src/cli.ts
|
|
13
13
|
import { readFileSync, readdirSync, statSync } from "fs";
|
|
14
14
|
import { resolve, dirname, join } from "path";
|
|
15
15
|
import * as YAML from "yaml";
|
|
16
16
|
import * as v from "valibot";
|
|
17
|
+
function normalizeAttributeValue(raw) {
|
|
18
|
+
if (raw === null || raw === void 0) return raw;
|
|
19
|
+
if (typeof raw === "string" && ["string", "number", "boolean"].includes(raw)) {
|
|
20
|
+
return { kind: "primitive", type: raw };
|
|
21
|
+
}
|
|
22
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
23
|
+
const obj = raw;
|
|
24
|
+
if ("kind" in obj && obj.kind !== void 0) {
|
|
25
|
+
if (obj.kind === "object" && typeof obj.fields === "object" && obj.fields !== null) {
|
|
26
|
+
const normalizedFields = {};
|
|
27
|
+
for (const [key, value] of Object.entries(obj.fields)) {
|
|
28
|
+
normalizedFields[key] = normalizeAttributeValue(value);
|
|
29
|
+
}
|
|
30
|
+
return { ...obj, fields: normalizedFields };
|
|
31
|
+
}
|
|
32
|
+
if (obj.kind === "array" && "items" in obj) {
|
|
33
|
+
return { ...obj, items: normalizeAttributeValue(obj.items) };
|
|
34
|
+
}
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
39
|
+
const obj = raw;
|
|
40
|
+
const normalized = {};
|
|
41
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
42
|
+
normalized[key] = normalizeAttributeValue(value);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(raw)) {
|
|
47
|
+
return raw.map((item) => normalizeAttributeValue(item));
|
|
48
|
+
}
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
function normalizeAttributeRecord(attrs) {
|
|
52
|
+
const normalizedAttrs = {};
|
|
53
|
+
for (const [attrKey, attrValue] of Object.entries(attrs)) {
|
|
54
|
+
normalizedAttrs[attrKey] = normalizeAttributeValue(attrValue);
|
|
55
|
+
}
|
|
56
|
+
return normalizedAttrs;
|
|
57
|
+
}
|
|
58
|
+
function normalizeAttributes(raw) {
|
|
59
|
+
if (raw === null || raw === void 0 || typeof raw !== "object" || Array.isArray(raw)) {
|
|
60
|
+
return raw;
|
|
61
|
+
}
|
|
62
|
+
const obj = raw;
|
|
63
|
+
const normalized = { ...obj };
|
|
64
|
+
if ("actors" in normalized && typeof normalized.actors === "object" && normalized.actors !== null) {
|
|
65
|
+
const actors = normalized.actors;
|
|
66
|
+
const normalizedActors = {};
|
|
67
|
+
for (const [actorName, actorDef] of Object.entries(actors)) {
|
|
68
|
+
if (typeof actorDef === "object" && actorDef !== null) {
|
|
69
|
+
const actor = actorDef;
|
|
70
|
+
const normalizedActor = { ...actor };
|
|
71
|
+
if ("attributes" in normalizedActor && typeof normalizedActor.attributes === "object" && normalizedActor.attributes !== null) {
|
|
72
|
+
normalizedActor.attributes = normalizeAttributeRecord(normalizedActor.attributes);
|
|
73
|
+
}
|
|
74
|
+
normalizedActors[actorName] = normalizedActor;
|
|
75
|
+
} else {
|
|
76
|
+
normalizedActors[actorName] = actorDef;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
normalized.actors = normalizedActors;
|
|
80
|
+
}
|
|
81
|
+
if ("resources" in normalized && typeof normalized.resources === "object" && normalized.resources !== null) {
|
|
82
|
+
const resources = normalized.resources;
|
|
83
|
+
const normalizedResources = {};
|
|
84
|
+
for (const [resName, resDef] of Object.entries(resources)) {
|
|
85
|
+
if (typeof resDef === "object" && resDef !== null) {
|
|
86
|
+
const resource = resDef;
|
|
87
|
+
const normalizedResource = { ...resource };
|
|
88
|
+
if ("attributes" in normalizedResource && typeof normalizedResource.attributes === "object" && normalizedResource.attributes !== null) {
|
|
89
|
+
normalizedResource.attributes = normalizeAttributeRecord(normalizedResource.attributes);
|
|
90
|
+
}
|
|
91
|
+
normalizedResources[resName] = normalizedResource;
|
|
92
|
+
} else {
|
|
93
|
+
normalizedResources[resName] = resDef;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
normalized.resources = normalizedResources;
|
|
97
|
+
}
|
|
98
|
+
return normalized;
|
|
99
|
+
}
|
|
17
100
|
function loadPolicyFile(filePath) {
|
|
18
101
|
const absPath = resolve(filePath);
|
|
19
102
|
let content;
|
|
@@ -42,7 +125,8 @@ function loadPolicyFile(filePath) {
|
|
|
42
125
|
);
|
|
43
126
|
}
|
|
44
127
|
}
|
|
45
|
-
const
|
|
128
|
+
const normalizedRaw = normalizeAttributes(raw);
|
|
129
|
+
const result = v.safeParse(PolicySchema, normalizedRaw);
|
|
46
130
|
if (!result.success) {
|
|
47
131
|
const issue = result.issues[0];
|
|
48
132
|
const path = issue?.path?.map((p) => String(p.key)).join(".") ?? "";
|
|
@@ -45,6 +45,58 @@ interface DefaultSchema extends TorideSchema {
|
|
|
45
45
|
actorAttributeMap: Record<string, Record<string, unknown>>;
|
|
46
46
|
relationMap: Record<string, Record<string, string>>;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Virtual field mapping configuration.
|
|
50
|
+
* Describes how a virtual field maps to a related resource query.
|
|
51
|
+
*/
|
|
52
|
+
interface VirtualFieldMapping {
|
|
53
|
+
/** The relation name to query */
|
|
54
|
+
readonly relation: string;
|
|
55
|
+
/** The field in the related resource to match against */
|
|
56
|
+
readonly matchField: string;
|
|
57
|
+
/** Optional filter to apply to the relation query */
|
|
58
|
+
readonly filter?: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extracts keys from T whose values are arrays.
|
|
62
|
+
*/
|
|
63
|
+
type ArrayKeys<T> = {
|
|
64
|
+
[K in keyof T]: T[K] extends unknown[] ? K : never;
|
|
65
|
+
}[keyof T];
|
|
66
|
+
type ModelScalars<T> = T extends {
|
|
67
|
+
scalars: infer S extends Record<string, unknown>;
|
|
68
|
+
} ? S : T extends Record<string, unknown> ? T : never;
|
|
69
|
+
type UnwrapRelation<T> = T extends readonly (infer U)[] ? U : NonNullable<T>;
|
|
70
|
+
type PayloadRelations<T> = T extends {
|
|
71
|
+
objects: infer O extends Record<string, unknown>;
|
|
72
|
+
} ? {
|
|
73
|
+
[K in keyof O & string]: ModelScalars<UnwrapRelation<O[K]>>;
|
|
74
|
+
} : Record<string, never>;
|
|
75
|
+
type VirtualFieldMappingFor<TRelations extends Record<string, Record<string, unknown>>> = {
|
|
76
|
+
[R in keyof TRelations & string]: {
|
|
77
|
+
readonly relation: R;
|
|
78
|
+
readonly matchField: keyof TRelations[R] & string;
|
|
79
|
+
readonly filter?: {
|
|
80
|
+
readonly [F in keyof TRelations[R]]?: TRelations[R][F];
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}[keyof TRelations & string];
|
|
84
|
+
/**
|
|
85
|
+
* Conditional type that uses model-aware detection when TModelMap is provided,
|
|
86
|
+
* falls back to ArrayKeys otherwise.
|
|
87
|
+
*/
|
|
88
|
+
type VirtualFieldKeysFor<S extends TorideSchema, R extends string, TModelMap> = [TModelMap] extends [never] ? S["resourceAttributeMap"][R] extends Record<string, unknown> ? Record<string, unknown> extends S["resourceAttributeMap"][R] ? string : ArrayKeys<S["resourceAttributeMap"][R]> : never : R extends keyof TModelMap ? Exclude<keyof S["resourceAttributeMap"][R] & string, keyof ModelScalars<TModelMap[R]> & string> : string;
|
|
89
|
+
/**
|
|
90
|
+
* Per-resource mapping of virtual field keys to VirtualFieldMapping.
|
|
91
|
+
* When TModelMap is provided, uses model-aware detection for virtual field keys.
|
|
92
|
+
*/
|
|
93
|
+
type VirtualFieldsConfig<S extends TorideSchema, TModelMap = never> = {
|
|
94
|
+
[R in S["resources"]]?: {
|
|
95
|
+
[K in VirtualFieldKeysFor<S, R & string, TModelMap>]?: R extends keyof TModelMap ? TModelMap[R] extends {
|
|
96
|
+
objects: Record<string, unknown>;
|
|
97
|
+
} ? VirtualFieldMappingFor<PayloadRelations<TModelMap[R]>> : VirtualFieldMapping : VirtualFieldMapping;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
48
100
|
/**
|
|
49
101
|
* Represents an entity performing actions.
|
|
50
102
|
* Generic discriminated union over actor types in S.
|
|
@@ -145,10 +197,27 @@ interface TorideOptions<S extends TorideSchema = DefaultSchema> {
|
|
|
145
197
|
readonly onQuery?: (event: QueryEvent) => void;
|
|
146
198
|
}
|
|
147
199
|
/** Attribute type for actor declarations. */
|
|
148
|
-
type AttributeType = "string" | "number" | "boolean";
|
|
200
|
+
type AttributeType = "string" | "number" | "boolean" | "string[]" | "number[]" | "boolean[]";
|
|
201
|
+
/** Schema for a primitive attribute. */
|
|
202
|
+
interface PrimitiveAttributeSchema {
|
|
203
|
+
readonly kind: "primitive";
|
|
204
|
+
readonly type: AttributeType;
|
|
205
|
+
}
|
|
206
|
+
/** Schema for a nested object attribute. */
|
|
207
|
+
interface ObjectAttributeSchema {
|
|
208
|
+
readonly kind: "object";
|
|
209
|
+
readonly fields: Record<string, AttributeSchema>;
|
|
210
|
+
}
|
|
211
|
+
/** Schema for an array attribute. */
|
|
212
|
+
interface ArrayAttributeSchema {
|
|
213
|
+
readonly kind: "array";
|
|
214
|
+
readonly items: AttributeSchema;
|
|
215
|
+
}
|
|
216
|
+
/** Discriminated union of all attribute schema types. */
|
|
217
|
+
type AttributeSchema = PrimitiveAttributeSchema | ObjectAttributeSchema | ArrayAttributeSchema;
|
|
149
218
|
/** Actor type declaration with attribute schema. */
|
|
150
219
|
interface ActorDeclaration {
|
|
151
|
-
readonly attributes: Record<string,
|
|
220
|
+
readonly attributes: Record<string, AttributeSchema>;
|
|
152
221
|
}
|
|
153
222
|
/** Global role definition derived from actor attributes. */
|
|
154
223
|
interface GlobalRole {
|
|
@@ -190,7 +259,7 @@ interface ResourceBlock {
|
|
|
190
259
|
readonly roles: string[];
|
|
191
260
|
readonly permissions: string[];
|
|
192
261
|
/** Optional typed attribute declarations for this resource type. */
|
|
193
|
-
readonly attributes?: Record<string,
|
|
262
|
+
readonly attributes?: Record<string, AttributeSchema>;
|
|
194
263
|
/** Relations map field names to target resource type names (simplified). */
|
|
195
264
|
readonly relations?: Record<string, string>;
|
|
196
265
|
readonly grants?: Record<string, string[]>;
|
|
@@ -324,6 +393,13 @@ declare class DepthLimitError extends Error {
|
|
|
324
393
|
readonly limitType: "condition" | "derivation";
|
|
325
394
|
constructor(message: string, limit: number, limitType: "condition" | "derivation");
|
|
326
395
|
}
|
|
396
|
+
/** Thrown when an actor is forbidden from performing an action. */
|
|
397
|
+
declare class ForbiddenError extends Error {
|
|
398
|
+
readonly actor: ActorRef;
|
|
399
|
+
readonly action: string;
|
|
400
|
+
readonly resourceType: string;
|
|
401
|
+
constructor(actor: ActorRef, action: string, resourceType: string);
|
|
402
|
+
}
|
|
327
403
|
|
|
328
404
|
/**
|
|
329
405
|
* A serializable map of permissions keyed by "Type:id".
|
|
@@ -370,4 +446,4 @@ declare class TorideClient<S extends TorideSchema = DefaultSchema> {
|
|
|
370
446
|
permittedActions<R extends S["resources"]>(resource: ClientResourceRef<S, R>): S["permissionMap"][R][];
|
|
371
447
|
}
|
|
372
448
|
|
|
373
|
-
export { type ActorRef as A, type BatchCheckItem as B, type CheckOptions as C, type DefaultSchema as D, type ExplainResult as E, type FieldAccessDef as F, type GlobalRole as G, type MatchedRule as M, type Policy as P, type QueryEvent as Q, type ResourceRef as R, type SimpleConditions as S, type TorideSchema as T, ValidationError as V, type TorideOptions as a, type PermissionSnapshot as b, type TestCase as c, type Resolvers as d, type ActorDeclaration as e, type
|
|
449
|
+
export { type ActorRef as A, type BatchCheckItem as B, type CheckOptions as C, type DefaultSchema as D, type ExplainResult as E, type FieldAccessDef as F, type GlobalRole as G, type VirtualFieldMapping as H, type VirtualFieldMappingFor as I, type VirtualFieldsConfig as J, CLIENT_VERSION as K, type MatchedRule as M, type ObjectAttributeSchema as O, type Policy as P, type QueryEvent as Q, type ResourceRef as R, type SimpleConditions as S, type TorideSchema as T, ValidationError as V, type TorideOptions as a, type PermissionSnapshot as b, type TestCase as c, type Resolvers as d, type ActorDeclaration as e, type ArrayAttributeSchema as f, type AttributeSchema as g, type AttributeType as h, type ClientResourceRef as i, type ConditionExpression as j, type ConditionOperator as k, type ConditionValue as l, CycleError as m, type DecisionEvent as n, DepthLimitError as o, type DerivedRoleEntry as p, type DerivedRoleTrace as q, type EvaluatorFn as r, ForbiddenError as s, type PayloadRelations as t, type PrimitiveAttributeSchema as u, type ResolvedRolesDetail as v, type ResourceBlock as w, type ResourceResolver as x, type Rule as y, TorideClient as z };
|
package/dist/client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { K as CLIENT_VERSION, i as ClientResourceRef, b as PermissionSnapshot, z as TorideClient } from './client-CBp3admQ.js';
|
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.
|
|
@@ -151,13 +210,11 @@ type Constraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | F
|
|
|
151
210
|
type LeafConstraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | FieldGteConstraint | FieldLtConstraint | FieldLteConstraint | FieldInConstraint | FieldNinConstraint | FieldExistsConstraint | FieldIncludesConstraint | FieldContainsConstraint;
|
|
152
211
|
/** Result of partial evaluation, tagged with resource type R (phantom). */
|
|
153
212
|
type ConstraintResult<R extends string = string> = {
|
|
154
|
-
readonly
|
|
155
|
-
readonly
|
|
156
|
-
} | {
|
|
157
|
-
readonly forbidden: true;
|
|
213
|
+
readonly ok: true;
|
|
214
|
+
readonly constraint: Constraint | null;
|
|
158
215
|
readonly __resource?: R;
|
|
159
216
|
} | {
|
|
160
|
-
readonly
|
|
217
|
+
readonly ok: false;
|
|
161
218
|
readonly __resource?: R;
|
|
162
219
|
};
|
|
163
220
|
/**
|
|
@@ -244,11 +301,11 @@ declare class Toride<S extends TorideSchema = DefaultSchema> {
|
|
|
244
301
|
* T064: Translate constraint AST using an adapter.
|
|
245
302
|
* Dispatches each constraint node to the adapter's methods.
|
|
246
303
|
*
|
|
247
|
-
* Accepts
|
|
304
|
+
* Accepts Constraint (the AST) from buildConstraints() and returns
|
|
248
305
|
* TQueryMap[R] — the adapter's mapped output type for resource R.
|
|
249
|
-
*
|
|
306
|
+
* Callers must check result.ok and result.constraint before calling this method.
|
|
250
307
|
*/
|
|
251
|
-
translateConstraints<R extends string, TQueryMap extends Record<string, unknown>>(
|
|
308
|
+
translateConstraints<R extends string, TQueryMap extends Record<string, unknown>>(constraint: Constraint, adapter: ConstraintAdapter<TQueryMap>): TQueryMap[R];
|
|
252
309
|
/**
|
|
253
310
|
* T072: Fire onDecision audit callback via microtask (non-blocking).
|
|
254
311
|
* Errors are silently swallowed to prevent audit failures from affecting authorization.
|
|
@@ -319,4 +376,4 @@ declare function runTestCases(policy: Policy, tests: TestCase[]): Promise<TestRe
|
|
|
319
376
|
|
|
320
377
|
declare const VERSION = "0.0.1";
|
|
321
378
|
|
|
322
|
-
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 };
|