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/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-2AWXNP37.js";
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 parsePolicy(raw) {
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 result = v.safeParse(PolicySchema, raw);
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.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": [
File without changes