cedar-mcp-server 1.0.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/.editorconfig +12 -0
- package/.github/workflows/ci.yml +31 -0
- package/.github/workflows/release.yml +42 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +241 -0
- package/CONTRIBUTING.md +83 -0
- package/LICENSE +182 -0
- package/README.md +1635 -0
- package/SECURITY.md +37 -0
- package/dist/http-server.d.ts +61 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +194 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +270 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/policy-ast.d.ts +49 -0
- package/dist/parser/policy-ast.d.ts.map +1 -0
- package/dist/parser/policy-ast.js +311 -0
- package/dist/parser/policy-ast.js.map +1 -0
- package/dist/prompts/index.d.ts +38 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +172 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/ref-resolver.d.ts +23 -0
- package/dist/resources/ref-resolver.d.ts.map +1 -0
- package/dist/resources/ref-resolver.js +128 -0
- package/dist/resources/ref-resolver.js.map +1 -0
- package/dist/resources/store-manager.d.ts +64 -0
- package/dist/resources/store-manager.d.ts.map +1 -0
- package/dist/resources/store-manager.js +221 -0
- package/dist/resources/store-manager.js.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +539 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/advise/avp-rules.d.ts +49 -0
- package/dist/tools/advise/avp-rules.d.ts.map +1 -0
- package/dist/tools/advise/avp-rules.js +59 -0
- package/dist/tools/advise/avp-rules.js.map +1 -0
- package/dist/tools/advise/cedar-patterns.d.ts +24 -0
- package/dist/tools/advise/cedar-patterns.d.ts.map +1 -0
- package/dist/tools/advise/cedar-patterns.js +57 -0
- package/dist/tools/advise/cedar-patterns.js.map +1 -0
- package/dist/tools/advise/context-builder.d.ts +28 -0
- package/dist/tools/advise/context-builder.d.ts.map +1 -0
- package/dist/tools/advise/context-builder.js +89 -0
- package/dist/tools/advise/context-builder.js.map +1 -0
- package/dist/tools/advise/gotchas.d.ts +15 -0
- package/dist/tools/advise/gotchas.d.ts.map +1 -0
- package/dist/tools/advise/gotchas.js +83 -0
- package/dist/tools/advise/gotchas.js.map +1 -0
- package/dist/tools/advise.d.ts +96 -0
- package/dist/tools/advise.d.ts.map +1 -0
- package/dist/tools/advise.js +258 -0
- package/dist/tools/advise.js.map +1 -0
- package/dist/tools/authorize-batch.d.ts +35 -0
- package/dist/tools/authorize-batch.d.ts.map +1 -0
- package/dist/tools/authorize-batch.js +262 -0
- package/dist/tools/authorize-batch.js.map +1 -0
- package/dist/tools/authorize.d.ts +115 -0
- package/dist/tools/authorize.d.ts.map +1 -0
- package/dist/tools/authorize.js +373 -0
- package/dist/tools/authorize.js.map +1 -0
- package/dist/tools/check-change.d.ts +19 -0
- package/dist/tools/check-change.d.ts.map +1 -0
- package/dist/tools/check-change.js +91 -0
- package/dist/tools/check-change.js.map +1 -0
- package/dist/tools/diff-schema.d.ts +103 -0
- package/dist/tools/diff-schema.d.ts.map +1 -0
- package/dist/tools/diff-schema.js +379 -0
- package/dist/tools/diff-schema.js.map +1 -0
- package/dist/tools/diff-stores.d.ts +45 -0
- package/dist/tools/diff-stores.d.ts.map +1 -0
- package/dist/tools/diff-stores.js +222 -0
- package/dist/tools/diff-stores.js.map +1 -0
- package/dist/tools/explain.d.ts +80 -0
- package/dist/tools/explain.d.ts.map +1 -0
- package/dist/tools/explain.js +187 -0
- package/dist/tools/explain.js.map +1 -0
- package/dist/tools/format.d.ts +11 -0
- package/dist/tools/format.d.ts.map +1 -0
- package/dist/tools/format.js +20 -0
- package/dist/tools/format.js.map +1 -0
- package/dist/tools/generate-sample.d.ts +28 -0
- package/dist/tools/generate-sample.d.ts.map +1 -0
- package/dist/tools/generate-sample.js +568 -0
- package/dist/tools/generate-sample.js.map +1 -0
- package/dist/tools/link-template.d.ts +17 -0
- package/dist/tools/link-template.d.ts.map +1 -0
- package/dist/tools/link-template.js +78 -0
- package/dist/tools/link-template.js.map +1 -0
- package/dist/tools/list-template-links.d.ts +16 -0
- package/dist/tools/list-template-links.d.ts.map +1 -0
- package/dist/tools/list-template-links.js +22 -0
- package/dist/tools/list-template-links.js.map +1 -0
- package/dist/tools/list-templates.d.ts +16 -0
- package/dist/tools/list-templates.d.ts.map +1 -0
- package/dist/tools/list-templates.js +36 -0
- package/dist/tools/list-templates.js.map +1 -0
- package/dist/tools/translate.d.ts +11 -0
- package/dist/tools/translate.d.ts.map +1 -0
- package/dist/tools/translate.js +53 -0
- package/dist/tools/translate.js.map +1 -0
- package/dist/tools/validate-entities.d.ts +19 -0
- package/dist/tools/validate-entities.d.ts.map +1 -0
- package/dist/tools/validate-entities.js +88 -0
- package/dist/tools/validate-entities.js.map +1 -0
- package/dist/tools/validate-schema.d.ts +22 -0
- package/dist/tools/validate-schema.d.ts.map +1 -0
- package/dist/tools/validate-schema.js +89 -0
- package/dist/tools/validate-schema.js.map +1 -0
- package/dist/tools/validate-template.d.ts +18 -0
- package/dist/tools/validate-template.d.ts.map +1 -0
- package/dist/tools/validate-template.js +59 -0
- package/dist/tools/validate-template.js.map +1 -0
- package/dist/tools/validate.d.ts +90 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +351 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/utils/format-detector.d.ts +49 -0
- package/dist/utils/format-detector.d.ts.map +1 -0
- package/dist/utils/format-detector.js +298 -0
- package/dist/utils/format-detector.js.map +1 -0
- package/examples/README.md +36 -0
- package/examples/abac-multi-tenant/README.md +150 -0
- package/examples/abac-multi-tenant/entities/users-and-docs.json +33 -0
- package/examples/abac-multi-tenant/policies/member-read-internal.cedar +9 -0
- package/examples/abac-multi-tenant/policies/owner-full-access.cedar +9 -0
- package/examples/abac-multi-tenant/policies/premium-share-guard.cedar +9 -0
- package/examples/abac-multi-tenant/policies/private-doc-guard.cedar +13 -0
- package/examples/abac-multi-tenant/run.ts +92 -0
- package/examples/abac-multi-tenant/schema.json +60 -0
- package/examples/api-gateway-path-routing/README.md +154 -0
- package/examples/api-gateway-path-routing/entities/users-and-roles.json +20 -0
- package/examples/api-gateway-path-routing/policies/admin-full-access.cedar +6 -0
- package/examples/api-gateway-path-routing/policies/developer-projects.cedar +14 -0
- package/examples/api-gateway-path-routing/policies/viewer-readonly.cedar +10 -0
- package/examples/api-gateway-path-routing/run.ts +108 -0
- package/examples/api-gateway-path-routing/schema.json +54 -0
- package/examples/rbac-document-management/README.md +167 -0
- package/examples/rbac-document-management/entities/users-and-docs.json +43 -0
- package/examples/rbac-document-management/policies/admin.cedar +6 -0
- package/examples/rbac-document-management/policies/editor.cedar +6 -0
- package/examples/rbac-document-management/policies/top-secret-forbid.cedar +13 -0
- package/examples/rbac-document-management/policies/viewer.cedar +6 -0
- package/examples/rbac-document-management/run.ts +87 -0
- package/examples/rbac-document-management/schema.json +57 -0
- package/package.json +50 -0
- package/src/http-server.ts +239 -0
- package/src/index.ts +294 -0
- package/src/parser/policy-ast.ts +345 -0
- package/src/prompts/README.md +3 -0
- package/src/prompts/index.ts +217 -0
- package/src/resources/ref-resolver.ts +134 -0
- package/src/resources/store-manager.ts +248 -0
- package/src/server.ts +711 -0
- package/src/tools/advise/avp-rules.ts +70 -0
- package/src/tools/advise/cedar-patterns.ts +73 -0
- package/src/tools/advise/context-builder.ts +109 -0
- package/src/tools/advise/gotchas.ts +92 -0
- package/src/tools/advise.ts +366 -0
- package/src/tools/authorize-batch.ts +345 -0
- package/src/tools/authorize.ts +464 -0
- package/src/tools/check-change.ts +119 -0
- package/src/tools/diff-schema.ts +510 -0
- package/src/tools/diff-stores.ts +298 -0
- package/src/tools/explain.ts +278 -0
- package/src/tools/format.ts +33 -0
- package/src/tools/generate-sample.ts +665 -0
- package/src/tools/link-template.ts +109 -0
- package/src/tools/list-template-links.ts +41 -0
- package/src/tools/list-templates.ts +55 -0
- package/src/tools/translate.ts +66 -0
- package/src/tools/validate-entities.ts +125 -0
- package/src/tools/validate-schema.ts +128 -0
- package/src/tools/validate-template.ts +72 -0
- package/src/tools/validate.ts +459 -0
- package/src/utils/format-detector.ts +356 -0
- package/test/fixtures/docmgmt.ts +121 -0
- package/test/fixtures/multitenant.ts +163 -0
- package/test/index.test.ts +96 -0
- package/test/integration/e2e/behavior.test.ts +359 -0
- package/test/integration/e2e/edge-cases.test.ts +365 -0
- package/test/integration/e2e/failure-modes.test.ts +266 -0
- package/test/integration/e2e/protocol.test.ts +252 -0
- package/test/integration/http-smoke.test.ts +588 -0
- package/test/integration/smoke.test.ts +475 -0
- package/test/prompts/prompts.test.ts +173 -0
- package/test/property/properties.test.ts +234 -0
- package/test/resources/ref-resolver.test.ts +186 -0
- package/test/resources/store-manager.test.ts +344 -0
- package/test/setup.test.ts +7 -0
- package/test/tools/advise/avp-rules.test.ts +76 -0
- package/test/tools/advise.test.ts +339 -0
- package/test/tools/authorize-batch.test.ts +459 -0
- package/test/tools/authorize.test.ts +682 -0
- package/test/tools/check-change.test.ts +104 -0
- package/test/tools/cross-fixture.test.ts +170 -0
- package/test/tools/diff-schema.test.ts +355 -0
- package/test/tools/diff-stores.test.ts +291 -0
- package/test/tools/explain.test.ts +221 -0
- package/test/tools/format.test.ts +33 -0
- package/test/tools/generate-sample.test.ts +480 -0
- package/test/tools/link-template.test.ts +90 -0
- package/test/tools/list-templates.test.ts +151 -0
- package/test/tools/translate.test.ts +89 -0
- package/test/tools/validate-entities.test.ts +178 -0
- package/test/tools/validate-schema.test.ts +86 -0
- package/test/tools/validate-template.test.ts +89 -0
- package/test/tools/validate.test.ts +331 -0
- package/test/utils/format-detector.test.ts +518 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cedar input format detection and normalization.
|
|
3
|
+
*
|
|
4
|
+
* Three AVP SDK variants exist in the wild — all need conversion to Cedar WASM format:
|
|
5
|
+
*
|
|
6
|
+
* Ruby SDK (snake_case): identifier.entity_type / entity_id, string/long/boolean
|
|
7
|
+
* Python/JS SDK (camelCase): identifier.entityType / entityId, string/long/boolean
|
|
8
|
+
* Official API/Console (PascalCase): Identifier.EntityType / EntityId, String/Long/Boolean
|
|
9
|
+
*
|
|
10
|
+
* Cedar WASM format:
|
|
11
|
+
* uid: { type, id }, attrs: { key: rawValue }, parents: [{ type, id }]
|
|
12
|
+
* Entity refs in attrs: { __entity: { type, id } }
|
|
13
|
+
* Extension types: { __extn: { fn, arg } }
|
|
14
|
+
*
|
|
15
|
+
* Detection strategy: case-insensitive key lookup handles all three casing variants
|
|
16
|
+
* in a single code path. One normalizer to rule them all.
|
|
17
|
+
*
|
|
18
|
+
* Cedar CLI format (uid.__entity wrapper): WASM accepts natively — no conversion needed.
|
|
19
|
+
*
|
|
20
|
+
* Attribute value wrapper detection rule:
|
|
21
|
+
* Single-key object whose key (lowercased) is a known AVP type name AND whose value
|
|
22
|
+
* is the matching primitive. Multi-key objects are Cedar Records — not touched.
|
|
23
|
+
*
|
|
24
|
+
* Limitation: a Cedar Record with exactly one field named "string"/"long"/"boolean"
|
|
25
|
+
* would be misidentified. Adding a second field removes the ambiguity.
|
|
26
|
+
*
|
|
27
|
+
* Sources confirmed by SDK docs (2026-05-20):
|
|
28
|
+
* Ruby: entity_type/entity_id/entity_identifier (snake_case)
|
|
29
|
+
* Python/JS: entityType/entityId/entityIdentifier (camelCase)
|
|
30
|
+
* Official API: EntityType/EntityId/EntityIdentifier (PascalCase)
|
|
31
|
+
*/
|
|
32
|
+
// ─── Case-insensitive key access ──────────────────────────────────────────────
|
|
33
|
+
/** Find the value of a key case-insensitively. First match wins. */
|
|
34
|
+
function getCI(obj, key) {
|
|
35
|
+
const lower = key.toLowerCase();
|
|
36
|
+
const found = Object.keys(obj).find((k) => k.toLowerCase() === lower);
|
|
37
|
+
return found !== undefined ? obj[found] : undefined;
|
|
38
|
+
}
|
|
39
|
+
function hasKeyCI(obj, key) {
|
|
40
|
+
const lower = key.toLowerCase();
|
|
41
|
+
return Object.keys(obj).some((k) => k.toLowerCase() === lower);
|
|
42
|
+
}
|
|
43
|
+
// ─── Detection ────────────────────────────────────────────────────────────────
|
|
44
|
+
export function detectFormat(entities, principal, action, resource) {
|
|
45
|
+
// AVP principal/action/resource — any casing of entity_type/entityType/EntityType
|
|
46
|
+
if (isAvpRef(principal) || isAvpActionRef(action) || isAvpRef(resource)) {
|
|
47
|
+
return {
|
|
48
|
+
format: "avp",
|
|
49
|
+
confidence: "high",
|
|
50
|
+
note: "Principal, action, or resource is in AVP format (entity_type/entityType/EntityType keys). Automatically converted to Cedar format.",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// AVP entity list: `identifier` key (case-insensitive) is the clearest signal
|
|
54
|
+
if (entities.some(hasAvpIdentifierKey)) {
|
|
55
|
+
return {
|
|
56
|
+
format: "avp",
|
|
57
|
+
confidence: "high",
|
|
58
|
+
note: "Entities are in AVP format (identifier/Identifier key, typed attribute wrappers). Automatically converted to Cedar format.",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// AVP-typed attribute values without identifier key (partial AVP)
|
|
62
|
+
if (entities.some(hasAvpTypedAttributeValues)) {
|
|
63
|
+
return {
|
|
64
|
+
format: "avp",
|
|
65
|
+
confidence: "medium",
|
|
66
|
+
note: "Entity attributes appear to use AVP typed wrappers (string/long/boolean/set/record). Automatically unwrapped to raw Cedar values.",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Cedar CLI uid.__entity wrapper — WASM accepts natively, no conversion needed
|
|
70
|
+
if (entities.some(hasCedarCliEntityWrapper)) {
|
|
71
|
+
return {
|
|
72
|
+
format: "cedar_cli",
|
|
73
|
+
confidence: "high",
|
|
74
|
+
note: "Entity UIDs use the __entity wrapper (Cedar CLI format). Compatible with Cedar WASM — no conversion needed.",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
format: "cedar",
|
|
79
|
+
confidence: "high",
|
|
80
|
+
note: "Input is in Cedar/WASM format.",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// ─── Normalization ────────────────────────────────────────────────────────────
|
|
84
|
+
export function normalizeEntities(entities, format) {
|
|
85
|
+
if (format === "cedar" || format === "cedar_cli")
|
|
86
|
+
return entities;
|
|
87
|
+
return entities.map(normalizeAvpEntity);
|
|
88
|
+
}
|
|
89
|
+
export function normalizePrincipalRef(ref) {
|
|
90
|
+
// Cedar string literal: 'Ns::Type::"id"'
|
|
91
|
+
if (typeof ref === "string") {
|
|
92
|
+
const match = ref.match(/^(.+)::"(.+)"$/);
|
|
93
|
+
if (!match)
|
|
94
|
+
return { error: `Invalid Cedar entity reference: "${ref}". Expected: Namespace::Type::"id"` };
|
|
95
|
+
return { type: match[1], id: match[2] };
|
|
96
|
+
}
|
|
97
|
+
if (typeof ref !== "object" || ref === null) {
|
|
98
|
+
return { error: `Unrecognized entity reference format: ${JSON.stringify(ref)}` };
|
|
99
|
+
}
|
|
100
|
+
const obj = ref;
|
|
101
|
+
// WASM native: { type, id }
|
|
102
|
+
if (typeof obj["type"] === "string" && typeof obj["id"] === "string") {
|
|
103
|
+
return { type: obj["type"], id: obj["id"] };
|
|
104
|
+
}
|
|
105
|
+
// WASM Cedar CLI: { __entity: { type, id } }
|
|
106
|
+
if (obj["__entity"] && typeof obj["__entity"] === "object") {
|
|
107
|
+
const inner = obj["__entity"];
|
|
108
|
+
if (typeof inner["type"] === "string" && typeof inner["id"] === "string") {
|
|
109
|
+
return { type: inner["type"], id: inner["id"] };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// AVP entity ref — all three casings (entity_type / entityType / EntityType)
|
|
113
|
+
const entityType = getCI(obj, "entity_type") ?? getCI(obj, "entityType") ?? getCI(obj, "EntityType");
|
|
114
|
+
const entityId = getCI(obj, "entity_id") ?? getCI(obj, "entityId") ?? getCI(obj, "EntityId");
|
|
115
|
+
if (typeof entityType === "string" && typeof entityId === "string") {
|
|
116
|
+
return { type: entityType, id: entityId };
|
|
117
|
+
}
|
|
118
|
+
// AVP action ref — all three casings (action_type / actionType / ActionType)
|
|
119
|
+
const actionType = getCI(obj, "action_type") ?? getCI(obj, "actionType") ?? getCI(obj, "ActionType");
|
|
120
|
+
const actionId = getCI(obj, "action_id") ?? getCI(obj, "actionId") ?? getCI(obj, "ActionId");
|
|
121
|
+
if (typeof actionType === "string" && typeof actionId === "string") {
|
|
122
|
+
return { type: actionType, id: actionId };
|
|
123
|
+
}
|
|
124
|
+
return { error: `Unrecognized entity reference format: ${JSON.stringify(ref)}` };
|
|
125
|
+
}
|
|
126
|
+
export function unwrapAvpAttributes(attrs) {
|
|
127
|
+
const result = {};
|
|
128
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
129
|
+
result[key] = unwrapAvpValue(value);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
// ─── Private: detection helpers ───────────────────────────────────────────────
|
|
134
|
+
function isAvpRef(ref) {
|
|
135
|
+
if (typeof ref !== "object" || ref === null)
|
|
136
|
+
return false;
|
|
137
|
+
const obj = ref;
|
|
138
|
+
const hasType = typeof getCI(obj, "entity_type") === "string" ||
|
|
139
|
+
typeof getCI(obj, "entityType") === "string" ||
|
|
140
|
+
typeof getCI(obj, "EntityType") === "string";
|
|
141
|
+
const hasId = typeof getCI(obj, "entity_id") === "string" ||
|
|
142
|
+
typeof getCI(obj, "entityId") === "string" ||
|
|
143
|
+
typeof getCI(obj, "EntityId") === "string";
|
|
144
|
+
return hasType && hasId;
|
|
145
|
+
}
|
|
146
|
+
function isAvpActionRef(ref) {
|
|
147
|
+
if (typeof ref !== "object" || ref === null)
|
|
148
|
+
return false;
|
|
149
|
+
const obj = ref;
|
|
150
|
+
const hasType = typeof getCI(obj, "action_type") === "string" ||
|
|
151
|
+
typeof getCI(obj, "actionType") === "string" ||
|
|
152
|
+
typeof getCI(obj, "ActionType") === "string";
|
|
153
|
+
const hasId = typeof getCI(obj, "action_id") === "string" ||
|
|
154
|
+
typeof getCI(obj, "actionId") === "string" ||
|
|
155
|
+
typeof getCI(obj, "ActionId") === "string";
|
|
156
|
+
return hasType && hasId;
|
|
157
|
+
}
|
|
158
|
+
function hasAvpIdentifierKey(entity) {
|
|
159
|
+
if (typeof entity !== "object" || entity === null)
|
|
160
|
+
return false;
|
|
161
|
+
return hasKeyCI(entity, "identifier");
|
|
162
|
+
}
|
|
163
|
+
function hasAvpTypedAttributeValues(entity) {
|
|
164
|
+
if (typeof entity !== "object" || entity === null)
|
|
165
|
+
return false;
|
|
166
|
+
const e = entity;
|
|
167
|
+
const rawAttrs = getCI(e, "attributes") ??
|
|
168
|
+
getCI(e, "Attributes") ??
|
|
169
|
+
e["attrs"];
|
|
170
|
+
if (!rawAttrs || typeof rawAttrs !== "object")
|
|
171
|
+
return false;
|
|
172
|
+
return Object.values(rawAttrs).some(isAvpTypedValue);
|
|
173
|
+
}
|
|
174
|
+
function hasCedarCliEntityWrapper(entity) {
|
|
175
|
+
if (typeof entity !== "object" || entity === null)
|
|
176
|
+
return false;
|
|
177
|
+
const e = entity;
|
|
178
|
+
const uid = e["uid"];
|
|
179
|
+
if (typeof uid !== "object" || uid === null)
|
|
180
|
+
return false;
|
|
181
|
+
return "__entity" in uid;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Detects AVP typed value wrappers (case-insensitive type key).
|
|
185
|
+
* Rule: single-key object whose key lowercased is a known AVP type name with matching value type.
|
|
186
|
+
* Multi-key objects are Cedar Records.
|
|
187
|
+
*/
|
|
188
|
+
function isAvpTypedValue(v) {
|
|
189
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
190
|
+
return false;
|
|
191
|
+
const keys = Object.keys(v);
|
|
192
|
+
if (keys.length !== 1)
|
|
193
|
+
return false;
|
|
194
|
+
const key = keys[0].toLowerCase();
|
|
195
|
+
const val = v[keys[0]];
|
|
196
|
+
return ((key === "string" && typeof val === "string") ||
|
|
197
|
+
(key === "long" && typeof val === "number") ||
|
|
198
|
+
(key === "boolean" && typeof val === "boolean") ||
|
|
199
|
+
key === "entityidentifier" ||
|
|
200
|
+
key === "entity_identifier" ||
|
|
201
|
+
key === "set" ||
|
|
202
|
+
key === "record" ||
|
|
203
|
+
key === "ipaddr" ||
|
|
204
|
+
key === "ipaddress" ||
|
|
205
|
+
key === "decimal" ||
|
|
206
|
+
key === "datetime" ||
|
|
207
|
+
key === "duration");
|
|
208
|
+
}
|
|
209
|
+
// ─── Private: value unwrapping ────────────────────────────────────────────────
|
|
210
|
+
function unwrapAvpValue(v) {
|
|
211
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
212
|
+
return v;
|
|
213
|
+
const keys = Object.keys(v);
|
|
214
|
+
if (keys.length !== 1)
|
|
215
|
+
return v; // Multi-key object = Cedar Record, not AVP wrapper
|
|
216
|
+
const key = keys[0];
|
|
217
|
+
const lowerKey = key.toLowerCase();
|
|
218
|
+
const val = v[key];
|
|
219
|
+
switch (lowerKey) {
|
|
220
|
+
case "string":
|
|
221
|
+
return typeof val === "string" ? val : v;
|
|
222
|
+
case "long":
|
|
223
|
+
return typeof val === "number" ? val : v;
|
|
224
|
+
case "boolean":
|
|
225
|
+
return typeof val === "boolean" ? val : v;
|
|
226
|
+
// Entity reference → WASM __entity
|
|
227
|
+
case "entityidentifier":
|
|
228
|
+
case "entity_identifier": {
|
|
229
|
+
const ref = resolveAvpEntityRef(val);
|
|
230
|
+
return ref ? { __entity: ref } : v;
|
|
231
|
+
}
|
|
232
|
+
// Set → array (recursively normalize values)
|
|
233
|
+
case "set":
|
|
234
|
+
return Array.isArray(val) ? val.map(unwrapAvpValue) : v;
|
|
235
|
+
// Record → object (recursively normalize values)
|
|
236
|
+
case "record":
|
|
237
|
+
if (typeof val === "object" && val !== null && !Array.isArray(val)) {
|
|
238
|
+
return unwrapAvpAttributes(val);
|
|
239
|
+
}
|
|
240
|
+
return v;
|
|
241
|
+
// Cedar extension types → WASM __extn format
|
|
242
|
+
case "ipaddr":
|
|
243
|
+
case "ipaddress":
|
|
244
|
+
return typeof val === "string" ? { __extn: { fn: "ip", arg: val } } : v;
|
|
245
|
+
case "decimal":
|
|
246
|
+
return typeof val === "string" ? { __extn: { fn: "decimal", arg: val } } : v;
|
|
247
|
+
case "datetime":
|
|
248
|
+
return typeof val === "string" ? { __extn: { fn: "datetime", arg: val } } : v;
|
|
249
|
+
case "duration":
|
|
250
|
+
return typeof val === "string" ? { __extn: { fn: "duration", arg: val } } : v;
|
|
251
|
+
default:
|
|
252
|
+
return v;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** Resolves an AVP entity reference object (any casing) to { type, id }. */
|
|
256
|
+
function resolveAvpEntityRef(ref) {
|
|
257
|
+
if (typeof ref !== "object" || ref === null)
|
|
258
|
+
return null;
|
|
259
|
+
const obj = ref;
|
|
260
|
+
const type = (getCI(obj, "entity_type") ?? getCI(obj, "entityType") ?? getCI(obj, "EntityType"));
|
|
261
|
+
const id = (getCI(obj, "entity_id") ?? getCI(obj, "entityId") ?? getCI(obj, "EntityId"));
|
|
262
|
+
if (type && id)
|
|
263
|
+
return { type, id };
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
// ─── Private: entity normalization ───────────────────────────────────────────
|
|
267
|
+
function normalizeAvpEntity(entity) {
|
|
268
|
+
if (typeof entity !== "object" || entity === null)
|
|
269
|
+
return entity;
|
|
270
|
+
const e = entity;
|
|
271
|
+
// UID: find identifier/Identifier key (any casing), convert to { type, id }
|
|
272
|
+
const identifierKey = Object.keys(e).find((k) => k.toLowerCase() === "identifier");
|
|
273
|
+
let uid;
|
|
274
|
+
if (identifierKey) {
|
|
275
|
+
const idObj = e[identifierKey];
|
|
276
|
+
const type = getCI(idObj, "entity_type") ?? getCI(idObj, "entityType") ?? getCI(idObj, "EntityType");
|
|
277
|
+
const id = getCI(idObj, "entity_id") ?? getCI(idObj, "entityId") ?? getCI(idObj, "EntityId");
|
|
278
|
+
uid = { type, id };
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
uid = e["uid"];
|
|
282
|
+
}
|
|
283
|
+
// Attrs: find attributes/Attributes key (any casing), fall back to attrs
|
|
284
|
+
const attrsKey = Object.keys(e).find((k) => k.toLowerCase() === "attributes");
|
|
285
|
+
const rawAttrs = (attrsKey ? e[attrsKey] : e["attrs"]) ?? {};
|
|
286
|
+
const attrs = unwrapAvpAttributes(rawAttrs);
|
|
287
|
+
// Parents: find parents/Parents key (any casing), convert entity_type/entityType/EntityType → type/id
|
|
288
|
+
const parentsKey = Object.keys(e).find((k) => k.toLowerCase() === "parents");
|
|
289
|
+
const rawParents = ((parentsKey ? e[parentsKey] : e["parents"]) ?? []);
|
|
290
|
+
const parents = rawParents.map((p) => {
|
|
291
|
+
if (typeof p !== "object" || p === null)
|
|
292
|
+
return p;
|
|
293
|
+
const ref = resolveAvpEntityRef(p);
|
|
294
|
+
return ref ?? p;
|
|
295
|
+
});
|
|
296
|
+
return { uid, attrs, parents };
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=format-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-detector.js","sourceRoot":"","sources":["../../src/utils/format-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Three self-contained Cedar authorization scenarios. Each one has a schema, policy files, an entity store, a runnable script, and a README with copy-paste prompts for Claude Code.
|
|
4
|
+
|
|
5
|
+
## Pick your pattern
|
|
6
|
+
|
|
7
|
+
| Example | Cedar patterns | Start here if... |
|
|
8
|
+
|---------|---------------|-----------------|
|
|
9
|
+
| [rbac-document-management](./rbac-document-management/) | Role membership, `forbid` + `unless`, default deny | You're new to Cedar or building a simple role-based system |
|
|
10
|
+
| [abac-multi-tenant](./abac-multi-tenant/) | Attribute conditions, `contains()`, optional attribute guards, plan-tier gating | You need decisions based on resource or user attributes, not just roles |
|
|
11
|
+
| [api-gateway-path-routing](./api-gateway-path-routing/) | Path matching with `like`, depth limiting via negation, method restriction | You're authorizing HTTP requests at a gateway or proxy |
|
|
12
|
+
|
|
13
|
+
## Run all examples offline
|
|
14
|
+
|
|
15
|
+
Each example includes a `run.ts` that exercises all tools against the example files. No MCP client or AI assistant required.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx tsx examples/rbac-document-management/run.ts
|
|
19
|
+
npx tsx examples/abac-multi-tenant/run.ts
|
|
20
|
+
npx tsx examples/api-gateway-path-routing/run.ts
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Use with Claude Code
|
|
24
|
+
|
|
25
|
+
Add `cedar-mcp-server` to your MCP configuration, then paste the prompts from any example README directly into a conversation. The examples are designed so the tool inputs fit in a single message.
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"cedar": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "cedar-mcp-server"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# ABAC Multi-Tenant (SaaS document collaboration)
|
|
2
|
+
|
|
3
|
+
Attribute-based access control for a multi-tenant SaaS app where decisions depend on document visibility, ownership, and user plan tier.
|
|
4
|
+
|
|
5
|
+
## What this example covers
|
|
6
|
+
|
|
7
|
+
Four Cedar patterns that appear together in almost every real SaaS authorization model:
|
|
8
|
+
|
|
9
|
+
- **Name-based identity**: `principal.name == resource.owner_id` — matching a user to their own resources
|
|
10
|
+
- **Array containment**: `["internal", "public"].contains(resource.visibility)` — allowlist of values
|
|
11
|
+
- **Plan-tier gating**: `principal.plan == "pro" || principal.plan == "enterprise"` — feature flagging in Cedar
|
|
12
|
+
- **`forbid` with `unless`**: blocks private document access except for the owner
|
|
13
|
+
|
|
14
|
+
Demonstrates all five tools, with `cedar_check_policy_change` showing the principal-change trap.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
Configure the MCP server in Claude Code (`.claude/settings.json`):
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"cedar": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "cedar-mcp-server"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or run offline:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx tsx examples/abac-multi-tenant/run.ts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Files
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
schema.json SaaS namespace: User (name, plan), Document (visibility, owner_id)
|
|
41
|
+
policies/
|
|
42
|
+
owner-full-access.cedar Owners have full access to their documents
|
|
43
|
+
member-read-internal.cedar Authenticated users can read internal/public docs
|
|
44
|
+
premium-share-guard.cedar Only pro/enterprise plan can share
|
|
45
|
+
private-doc-guard.cedar Private docs blocked for non-owners (forbid + unless)
|
|
46
|
+
entities/
|
|
47
|
+
users-and-docs.json Alice (enterprise), Bob (pro), Charlie (free) + documents
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Tool examples — copy and paste to Claude Code
|
|
53
|
+
|
|
54
|
+
### cedar_authorize
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Is charlie allowed to read the salary-review document?
|
|
58
|
+
|
|
59
|
+
Policies: [paste all .cedar files]
|
|
60
|
+
Principal: SaaS::User::"charlie"
|
|
61
|
+
Action: SaaS::Action::"READ"
|
|
62
|
+
Resource: SaaS::Document::"salary-review"
|
|
63
|
+
Entities: [paste entities/users-and-docs.json]
|
|
64
|
+
Schema: [paste schema.json]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Expected: **Deny** — `salary-review` has `visibility: "private"` and `owner_id: "alice"`. Charlie is not the owner, so the `private-doc-guard` forbid fires.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Can charlie share the public-changelog?
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Expected: **Deny** — `premium-share-guard` requires `plan == "pro"` or `"enterprise"`. Charlie is on the free plan.
|
|
74
|
+
|
|
75
|
+
### cedar_explain
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Explain this Cedar policy:
|
|
79
|
+
|
|
80
|
+
permit (
|
|
81
|
+
principal,
|
|
82
|
+
action == SaaS::Action::"READ",
|
|
83
|
+
resource
|
|
84
|
+
)
|
|
85
|
+
when {
|
|
86
|
+
["internal", "public"].contains(resource.visibility)
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Expected: plain-English breakdown including the `contains()` containment check and the pattern detection `attribute_containment_check`.
|
|
91
|
+
|
|
92
|
+
### cedar_generate_sample_request
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Generate a sample request that would be ALLOWED by this policy:
|
|
96
|
+
|
|
97
|
+
permit (
|
|
98
|
+
principal,
|
|
99
|
+
action == SaaS::Action::"SHARE",
|
|
100
|
+
resource
|
|
101
|
+
)
|
|
102
|
+
when {
|
|
103
|
+
principal.plan == "pro" || principal.plan == "enterprise"
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
Schema: [paste schema.json]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Expected: a principal with `plan: "pro"` or `"enterprise"`, with an explanation of which condition was satisfied.
|
|
110
|
+
|
|
111
|
+
### cedar_check_policy_change
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Can this change be applied in-place in Amazon Verified Permissions?
|
|
115
|
+
|
|
116
|
+
Old policy:
|
|
117
|
+
permit(principal, action, resource) when { principal.name == resource.owner_id };
|
|
118
|
+
|
|
119
|
+
New policy:
|
|
120
|
+
permit(principal in SaaS::Role::"editor", action, resource);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Expected: **cannot update in-place** — switching from a condition-based identity check to a role-based principal scope changes the principal clause, which AVP treats as immutable.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Test cases
|
|
128
|
+
|
|
129
|
+
| Principal | Action | Resource | Expected | Reason |
|
|
130
|
+
|-----------|--------|----------|----------|--------|
|
|
131
|
+
| alice (owner) | READ | salary-review (private) | **Allow** | Owner exempt from private-doc-guard |
|
|
132
|
+
| alice (owner) | DELETE | q4-roadmap | **Allow** | Owner full access |
|
|
133
|
+
| bob (non-owner, pro) | READ | q4-roadmap (internal) | **Allow** | member-read-internal matches |
|
|
134
|
+
| bob (non-owner, pro) | READ | salary-review (private) | **Deny** | private-doc-guard forbid, bob is not owner |
|
|
135
|
+
| bob (pro) | SHARE | public-changelog | **Allow** | pro plan satisfies premium-share-guard |
|
|
136
|
+
| charlie (non-owner, free) | READ | public-changelog (public) | **Allow** | member-read-internal: "public" in allowlist |
|
|
137
|
+
| charlie (free) | SHARE | public-changelog | **Deny** | free plan fails premium-share-guard |
|
|
138
|
+
| charlie (non-owner, free) | READ | salary-review (private) | **Deny** | private-doc-guard forbid |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Common pitfalls in this pattern
|
|
143
|
+
|
|
144
|
+
**`principal.name` is not authentication.** Cedar doesn't verify identity — it evaluates the entity attributes you provide. If you pass `attrs: { name: "alice" }`, Cedar trusts it. The security boundary is in how you build your entity store, not in the Cedar policy.
|
|
145
|
+
|
|
146
|
+
**Array containment goes array-first.** Cedar uses `["a", "b"].contains(value)` — the array is on the LEFT, the value is on the right. Writing `value.contains("a")` is a type error because strings don't have a `contains` method in Cedar.
|
|
147
|
+
|
|
148
|
+
**`forbid` + `unless` is not the same as two separate permits.** The `unless` clause in `private-doc-guard` means "forbid UNLESS owner". Without the `unless`, the forbid would block the owner too and no `permit` could override it. Never assume a `permit` can override a `forbid` — it cannot.
|
|
149
|
+
|
|
150
|
+
**Plan-tier changes need policy updates.** If a user upgrades from free to pro, their `plan` attribute in the entity store must be updated before Cedar reflects the change. Cedar has no concept of time — it evaluates the current entity state only.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"uid": { "type": "SaaS::User", "id": "alice" },
|
|
4
|
+
"attrs": { "name": "alice", "plan": "enterprise" },
|
|
5
|
+
"parents": []
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"uid": { "type": "SaaS::User", "id": "bob" },
|
|
9
|
+
"attrs": { "name": "bob", "plan": "pro" },
|
|
10
|
+
"parents": []
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"uid": { "type": "SaaS::User", "id": "charlie" },
|
|
14
|
+
"attrs": { "name": "charlie", "plan": "free" },
|
|
15
|
+
"parents": []
|
|
16
|
+
},
|
|
17
|
+
{ "uid": { "type": "SaaS::Workspace", "id": "acme-corp" }, "attrs": {}, "parents": [] },
|
|
18
|
+
{
|
|
19
|
+
"uid": { "type": "SaaS::Document", "id": "q4-roadmap" },
|
|
20
|
+
"attrs": { "visibility": "internal", "owner_id": "alice" },
|
|
21
|
+
"parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"uid": { "type": "SaaS::Document", "id": "public-changelog" },
|
|
25
|
+
"attrs": { "visibility": "public", "owner_id": "alice" },
|
|
26
|
+
"parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"uid": { "type": "SaaS::Document", "id": "salary-review" },
|
|
30
|
+
"attrs": { "visibility": "private", "owner_id": "alice" },
|
|
31
|
+
"parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Private documents are blocked for everyone except the owner
|
|
2
|
+
// Uses optional attribute guard: check visibility before accessing
|
|
3
|
+
forbid (
|
|
4
|
+
principal,
|
|
5
|
+
action,
|
|
6
|
+
resource
|
|
7
|
+
)
|
|
8
|
+
when {
|
|
9
|
+
resource.visibility == "private"
|
|
10
|
+
}
|
|
11
|
+
unless {
|
|
12
|
+
principal.name == resource.owner_id
|
|
13
|
+
};
|