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
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,7 +14,7 @@ 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
20
|
} from "./chunk-S6DKDABO.js";
|
|
@@ -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
|