@stigmer/server 3.23.0 → 3.24.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/authorization/authorizer.d.ts +4 -8
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +5 -9
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +10 -25
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +7 -8
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +4 -20
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +5 -9
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +4 -5
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +2 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +2 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +3 -4
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +8 -10
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +0 -3
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +2 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +2 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +2 -2
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +2 -3
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +0 -6
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +16 -62
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +13 -32
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +3 -11
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +4 -4
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +3 -1
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +3 -1
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +3 -4
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +6 -11
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +8 -16
- package/dist/domain/agentshare/constants.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +3 -1
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +18 -25
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +41 -142
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +2 -3
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +2 -1
- package/dist/domain/executioncontext/controller.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +3 -4
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +1 -3
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +7 -5
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +8 -14
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +3 -1
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +3 -2
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +0 -2
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/workflow/agent-call-references.d.ts +81 -0
- package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
- package/dist/domain/workflow/agent-call-references.js +117 -0
- package/dist/domain/workflow/agent-call-references.js.map +1 -0
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +14 -6
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +3 -3
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
- package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +4 -2
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +3 -4
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +6 -5
- package/dist/extensions/authorization-queries.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +1 -1
- package/dist/extensions/resource-authorization.d.ts +3 -2
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-labels.d.ts +10 -9
- package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
- package/dist/pipeline/apiresource-labels.js +10 -9
- package/dist/pipeline/apiresource-labels.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +12 -3
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +21 -10
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +9 -11
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +204 -20
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +356 -43
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/query/search/controller.js +0 -1
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +1 -5
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +3 -13
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +5 -4
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +7 -6
- package/dist/query/search/handler.js.map +1 -1
- package/dist/query/search/query-store.d.ts.map +1 -1
- package/dist/query/search/query-store.js +0 -2
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +3 -6
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +3 -1
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +35 -1
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +4 -9
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/public-visibility-retired.d.ts +67 -0
- package/dist/store/public-visibility-retired.d.ts.map +1 -0
- package/dist/store/public-visibility-retired.js +55 -0
- package/dist/store/public-visibility-retired.js.map +1 -0
- package/dist/store/sqlite/migrations.d.ts +3 -1
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +36 -1
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +4 -8
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +24 -26
- package/src/authorization/__tests__/authorizer.test.ts +10 -9
- package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
- package/src/authorization/__tests__/evaluator.test.ts +14 -73
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +19 -27
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
- package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +1 -15
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
- package/src/authorization/__tests__/list-read-scope.test.ts +18 -17
- package/src/authorization/__tests__/store-tests.test.ts +2 -6
- package/src/authorization/__tests__/tuples.test.ts +12 -11
- package/src/authorization/authorizer.ts +4 -9
- package/src/authorization/derived-tuples.ts +14 -31
- package/src/authorization/evaluator.ts +8 -44
- package/src/authorization/list-read-scope.ts +5 -11
- package/src/authorization/model/agent.ts +2 -5
- package/src/authorization/model/agent_instance.ts +0 -2
- package/src/authorization/model/mcp_server.ts +0 -2
- package/src/authorization/model/plugin.ts +1 -4
- package/src/authorization/model/rewrite.ts +8 -13
- package/src/authorization/model/skill.ts +0 -2
- package/src/authorization/model/workflow.ts +0 -2
- package/src/authorization/model/workflow_instance.ts +0 -2
- package/src/authorization/organization-directory.ts +1 -3
- package/src/authorization/store-test-kit.ts +16 -75
- package/src/authorization/tuples.ts +16 -39
- package/src/domain/agent/__tests__/agent.test.ts +11 -4
- package/src/domain/agent/controller.ts +7 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
- package/src/domain/agentchannel/controller.ts +6 -1
- package/src/domain/agentexecution/controller.ts +6 -1
- package/src/domain/agentinstance/controller.ts +6 -12
- package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +16 -31
- package/src/domain/agentshare/constants.ts +14 -26
- package/src/domain/agentshare/controller.ts +6 -1
- package/src/domain/agentshare/steps.ts +41 -194
- package/src/domain/environment/controller.ts +5 -8
- package/src/domain/executioncontext/controller.ts +5 -1
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
- package/src/domain/mcpserver/controller.ts +6 -12
- package/src/domain/plugin/controller.ts +1 -8
- package/src/domain/plugin/push.ts +8 -16
- package/src/domain/schedule/controller.ts +6 -1
- package/src/domain/session/controller.ts +12 -4
- package/src/domain/skill/controller.ts +0 -7
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +373 -0
- package/src/domain/workflow/agent-call-references.ts +204 -0
- package/src/domain/workflow/controller.ts +28 -10
- package/src/domain/workflow/registry/data/task-kind-registry.json +3 -3
- package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
- package/src/domain/workflowexecution/controller.ts +7 -2
- package/src/domain/workflowinstance/controller.ts +6 -12
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +9 -6
- package/src/extensions/authorization-queries.ts +6 -5
- package/src/extensions/list-read-scope.ts +1 -1
- package/src/extensions/resource-authorization.ts +3 -5
- package/src/index.ts +0 -1
- package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
- package/src/pipeline/apiresource-labels.ts +10 -9
- package/src/pipeline/apiresource-meta.ts +21 -10
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
- package/src/pipeline/steps/__tests__/references.test.ts +737 -0
- package/src/pipeline/steps/authorization-tuples.ts +9 -11
- package/src/pipeline/steps/references.ts +622 -70
- package/src/query/search/__tests__/criteria.test.ts +0 -4
- package/src/query/search/__tests__/query-store.test.ts +11 -40
- package/src/query/search/controller.ts +0 -1
- package/src/query/search/criteria.ts +0 -14
- package/src/query/search/handler.ts +6 -7
- package/src/query/search/query-store.ts +0 -2
- package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
- package/src/store/__tests__/store-contract.ts +6 -51
- package/src/store/interface.ts +3 -6
- package/src/store/postgres/__tests__/migrations.test.ts +187 -6
- package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
- package/src/store/postgres/migrations.ts +48 -1
- package/src/store/postgres/store.ts +4 -11
- package/src/store/public-visibility-retired.ts +119 -0
- package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
- package/src/store/sqlite/migrations.ts +45 -1
- package/src/store/sqlite/store.ts +4 -10
- package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
- package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
- package/dist/pipeline/steps/visibility-gates.js +0 -139
- package/dist/pipeline/steps/visibility-gates.js.map +0 -1
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
- package/src/pipeline/steps/visibility-gates.ts +0 -174
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* - owner SELF: <row>#owner@identity_account:<row id>
|
|
13
13
|
* - creator: <row>#creator@identity_account:<creator stamp> (kinds flagged requires_creator_tuple)
|
|
14
14
|
* - org-viewer: <row>#viewer@organization:<org>#viewer (visibility_org; cloud#257's shape)
|
|
15
|
-
* - public-viewer: <row>#viewer@identity_account:* with allow_public
|
|
16
15
|
* - platform-viewer: none — it fans out over identity providers, a kind this edition does not serve
|
|
17
16
|
*
|
|
18
17
|
* Two facts are this edition's own and are stated here, nowhere else:
|
|
@@ -91,11 +90,7 @@ import type {
|
|
|
91
90
|
Tuple,
|
|
92
91
|
TupleSource,
|
|
93
92
|
} from "./tuples.js";
|
|
94
|
-
import {
|
|
95
|
-
ACCOUNT_TYPE,
|
|
96
|
-
ALLOW_PUBLIC_CONDITION,
|
|
97
|
-
formatObjectRef,
|
|
98
|
-
} from "./tuples.js";
|
|
93
|
+
import { ACCOUNT_TYPE, formatObjectRef } from "./tuples.js";
|
|
99
94
|
|
|
100
95
|
/**
|
|
101
96
|
* Kinds whose `[identity_account]` owner is recorded as an IamPolicy row
|
|
@@ -184,12 +179,6 @@ function visibilitySubject(
|
|
|
184
179
|
object: { type: "organization", id: org },
|
|
185
180
|
relation: "viewer",
|
|
186
181
|
};
|
|
187
|
-
case "public-viewer":
|
|
188
|
-
return {
|
|
189
|
-
form: "wildcard",
|
|
190
|
-
type: ACCOUNT_TYPE,
|
|
191
|
-
condition: ALLOW_PUBLIC_CONDITION,
|
|
192
|
-
};
|
|
193
182
|
case "platform-viewer":
|
|
194
183
|
return undefined;
|
|
195
184
|
default: {
|
|
@@ -361,24 +350,18 @@ function tupleOfRow(row: IamPolicy): Tuple {
|
|
|
361
350
|
type: principal?.kind ?? "",
|
|
362
351
|
id: principal?.id ?? "",
|
|
363
352
|
};
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
form: "
|
|
377
|
-
type: principalObject.type,
|
|
378
|
-
condition: undefined,
|
|
379
|
-
};
|
|
380
|
-
} else {
|
|
381
|
-
subject = { form: "object", object: principalObject };
|
|
382
|
-
}
|
|
353
|
+
// A principal id of `*` (a wildcard row a cloud database may still hold
|
|
354
|
+
// from the retired public level) is read as an ordinary object id here:
|
|
355
|
+
// no person carries `*` among their aliases, so the row matches nobody
|
|
356
|
+
// and reads as the inert grant it is, never as a fault that would turn
|
|
357
|
+
// the resource unreadable.
|
|
358
|
+
const subject: Subject =
|
|
359
|
+
principal !== undefined && principal.relation !== ""
|
|
360
|
+
? {
|
|
361
|
+
form: "userset",
|
|
362
|
+
object: principalObject,
|
|
363
|
+
relation: principal.relation,
|
|
364
|
+
}
|
|
365
|
+
: { form: "object", object: principalObject };
|
|
383
366
|
return { object, relation: spec?.relation ?? "", subject };
|
|
384
367
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The evaluator — OpenFGA's check, over the built-in model and a tuple
|
|
3
3
|
* source, small because the model uses four rewrite forms and nothing
|
|
4
|
-
* else (model/rewrite.ts). `checkRelation(object, relation, person
|
|
4
|
+
* else (model/rewrite.ts). `checkRelation(object, relation, person)`
|
|
5
5
|
* answers "does this person hold this relation on this object" the way
|
|
6
6
|
* the cloud's engine answers it over stored tuples, so the two editions
|
|
7
|
-
* share one meaning of authorization.
|
|
7
|
+
* share one meaning of authorization. There is no context parameter: the
|
|
8
|
+
* model declares no condition, so a check has no posture to state.
|
|
8
9
|
*
|
|
9
10
|
* The walk, in the order a `.fga` line reads:
|
|
10
11
|
* - `this`: the tuples on (object, relation). A tuple counts only if
|
|
@@ -12,9 +13,7 @@
|
|
|
12
13
|
* OpenFGA makes at write time, made here at read time, so a derived
|
|
13
14
|
* tuple the model no longer admits denies instead of allowing. An
|
|
14
15
|
* object subject matches when it names one of the person's aliases;
|
|
15
|
-
* a userset subject is resolved on its own object
|
|
16
|
-
* honoured only under its condition (`allow_public` reads the
|
|
17
|
-
* context's `allow`; an unknown condition is a fault).
|
|
16
|
+
* a userset subject is resolved on its own object.
|
|
18
17
|
* - `computed`: the same object, another relation.
|
|
19
18
|
* - `from`: every object the tupleset relation links to (again within
|
|
20
19
|
* the tupleset line's type restriction), resolved for the relation.
|
|
@@ -44,19 +43,8 @@
|
|
|
44
43
|
*/
|
|
45
44
|
import type { Model } from "./model/index.js";
|
|
46
45
|
import type { Rewrite, SubjectType } from "./model/rewrite.js";
|
|
47
|
-
import type {
|
|
48
|
-
|
|
49
|
-
ObjectRef,
|
|
50
|
-
Person,
|
|
51
|
-
Subject,
|
|
52
|
-
TupleSource,
|
|
53
|
-
} from "./tuples.js";
|
|
54
|
-
import {
|
|
55
|
-
ACCOUNT_TYPE,
|
|
56
|
-
ALLOW_PUBLIC_CONDITION,
|
|
57
|
-
formatObjectRef,
|
|
58
|
-
pairKey,
|
|
59
|
-
} from "./tuples.js";
|
|
46
|
+
import type { ObjectRef, Person, Subject, TupleSource } from "./tuples.js";
|
|
47
|
+
import { ACCOUNT_TYPE, formatObjectRef, pairKey } from "./tuples.js";
|
|
60
48
|
|
|
61
49
|
/**
|
|
62
50
|
* OpenFGA's default resolution-depth limit (its `resolveNodeLimit`), so
|
|
@@ -77,8 +65,7 @@ export type EvaluationFault =
|
|
|
77
65
|
| "undeclared-target-kind"
|
|
78
66
|
| "internal-caller-offered"
|
|
79
67
|
| "resolution-depth-exceeded"
|
|
80
|
-
| "resolution-cycle"
|
|
81
|
-
| "unknown-condition";
|
|
68
|
+
| "resolution-cycle";
|
|
82
69
|
|
|
83
70
|
/** A fault in the model or its evaluation — never a denial; the driver maps it to `unavailable`. */
|
|
84
71
|
export class AuthorizationEvaluationError extends Error {
|
|
@@ -101,7 +88,6 @@ export async function checkRelation(
|
|
|
101
88
|
object: ObjectRef,
|
|
102
89
|
relation: string,
|
|
103
90
|
person: Person,
|
|
104
|
-
context: CheckContext,
|
|
105
91
|
): Promise<boolean> {
|
|
106
92
|
if (deps.model.byType(object.type) === undefined) {
|
|
107
93
|
throw new AuthorizationEvaluationError(
|
|
@@ -109,7 +95,7 @@ export async function checkRelation(
|
|
|
109
95
|
`no declaration for kind '${object.type}' (target ${formatObjectRef(object)})`,
|
|
110
96
|
);
|
|
111
97
|
}
|
|
112
|
-
return new Walk(deps, person
|
|
98
|
+
return new Walk(deps, person).resolve(object, relation, 0);
|
|
113
99
|
}
|
|
114
100
|
|
|
115
101
|
/** One check's state: the memo and the in-progress set, over one source. */
|
|
@@ -120,7 +106,6 @@ class Walk {
|
|
|
120
106
|
constructor(
|
|
121
107
|
private readonly deps: EvaluationDeps,
|
|
122
108
|
private readonly person: Person,
|
|
123
|
-
private readonly context: CheckContext,
|
|
124
109
|
) {}
|
|
125
110
|
|
|
126
111
|
resolve(
|
|
@@ -234,15 +219,6 @@ class Walk {
|
|
|
234
219
|
allowed.relation === subject.relation,
|
|
235
220
|
) && (await this.resolve(subject.object, subject.relation, depth + 1))
|
|
236
221
|
);
|
|
237
|
-
case "wildcard": {
|
|
238
|
-
const allowed = restriction.some(
|
|
239
|
-
(entry) =>
|
|
240
|
-
entry.form === "wildcard" &&
|
|
241
|
-
entry.type === subject.type &&
|
|
242
|
-
entry.condition === subject.condition,
|
|
243
|
-
);
|
|
244
|
-
return allowed && this.conditionHolds(subject.condition);
|
|
245
|
-
}
|
|
246
222
|
default: {
|
|
247
223
|
const exhaustive: never = subject;
|
|
248
224
|
throw new Error(`unknown subject form: ${JSON.stringify(exhaustive)}`);
|
|
@@ -250,18 +226,6 @@ class Walk {
|
|
|
250
226
|
}
|
|
251
227
|
}
|
|
252
228
|
|
|
253
|
-
private conditionHolds(condition: string | undefined): boolean {
|
|
254
|
-
switch (condition) {
|
|
255
|
-
case ALLOW_PUBLIC_CONDITION:
|
|
256
|
-
return this.context.allow;
|
|
257
|
-
default:
|
|
258
|
-
throw new AuthorizationEvaluationError(
|
|
259
|
-
"unknown-condition",
|
|
260
|
-
`the model declares no condition '${String(condition)}'`,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
229
|
/** The object types the tupleset line admits — `[organization]`, `[session]` — from its `this` nodes. */
|
|
266
230
|
private objectTypesOf(
|
|
267
231
|
object: ObjectRef,
|
|
@@ -11,12 +11,10 @@
|
|
|
11
11
|
* row a second time. A self-host's lists show a person exactly what a
|
|
12
12
|
* `get` would let them read.
|
|
13
13
|
*
|
|
14
|
-
* Both verbs ask
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* listing; the organization's own people still reach it through the
|
|
19
|
-
* org-viewer shape public also emits.
|
|
14
|
+
* Both verbs ask the same question a point check asks, `can_view` on the
|
|
15
|
+
* candidate: the model has no subject whose answer differs between a
|
|
16
|
+
* listing and a get, so a list is exactly the set of rows a get would
|
|
17
|
+
* read.
|
|
20
18
|
*
|
|
21
19
|
* - `restrictListEntries`: one derived source for the caller, seeded
|
|
22
20
|
* with every candidate's facts; one `checkRelation` per unique id —
|
|
@@ -84,14 +82,11 @@ import { builtInModel } from "./model/index.js";
|
|
|
84
82
|
import type { KindDeclaration } from "./model/rewrite.js";
|
|
85
83
|
import type { AccountsByCaller } from "./person.js";
|
|
86
84
|
import { resolvePerson } from "./person.js";
|
|
87
|
-
import type {
|
|
85
|
+
import type { ObjectRef, Person } from "./tuples.js";
|
|
88
86
|
|
|
89
87
|
/** The relation every list lane reads through (the cloud's LIST_RELATION). */
|
|
90
88
|
const LIST_RELATION = "can_view";
|
|
91
89
|
|
|
92
|
-
/** The listing context: the public wildcard suppressed (the module header). */
|
|
93
|
-
const LISTING: CheckContext = { allow: false };
|
|
94
|
-
|
|
95
90
|
export interface BuiltInListReadScopeDeps {
|
|
96
91
|
readonly store: Store;
|
|
97
92
|
/** The port the grant path writes through — the person's rows are read from the same instance. */
|
|
@@ -138,7 +133,6 @@ export function newBuiltInListReadScope(
|
|
|
138
133
|
object,
|
|
139
134
|
LIST_RELATION,
|
|
140
135
|
person,
|
|
141
|
-
LISTING,
|
|
142
136
|
);
|
|
143
137
|
return allowed ? facts.id : undefined;
|
|
144
138
|
}),
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* read by whoever the visibility axis lets in. The `organization#member`
|
|
5
5
|
* and `organization#viewer` usersets on `viewer` are the org-visibility
|
|
6
6
|
* tuple's two shapes (the cloud writes `#viewer` since cloud#257; `#member`
|
|
7
|
-
* is the legacy shape it still honours),
|
|
8
|
-
*
|
|
9
|
-
* open-source tuple ever names.
|
|
7
|
+
* is the legacy shape it still honours), and `platform_viewer` is an
|
|
8
|
+
* identity-provider userset no open-source tuple ever names.
|
|
10
9
|
*/
|
|
11
10
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
12
11
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
@@ -17,7 +16,6 @@ import {
|
|
|
17
16
|
direct,
|
|
18
17
|
from,
|
|
19
18
|
objectOf,
|
|
20
|
-
publicWith,
|
|
21
19
|
union,
|
|
22
20
|
usersetOf,
|
|
23
21
|
} from "./rewrite.js";
|
|
@@ -44,7 +42,6 @@ export const agentDeclaration = declareKind({
|
|
|
44
42
|
union(
|
|
45
43
|
direct(
|
|
46
44
|
objectOf("identity_account"),
|
|
47
|
-
publicWith("identity_account", "allow_public"),
|
|
48
45
|
usersetOf("organization", "member"),
|
|
49
46
|
usersetOf("organization", "viewer"),
|
|
50
47
|
),
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
direct,
|
|
20
20
|
from,
|
|
21
21
|
objectOf,
|
|
22
|
-
publicWith,
|
|
23
22
|
union,
|
|
24
23
|
usersetOf,
|
|
25
24
|
} from "./rewrite.js";
|
|
@@ -38,7 +37,6 @@ export const agentInstanceDeclaration = declareKind({
|
|
|
38
37
|
union(
|
|
39
38
|
direct(
|
|
40
39
|
objectOf("identity_account"),
|
|
41
|
-
publicWith("identity_account", "allow_public"),
|
|
42
40
|
usersetOf("organization", "member"),
|
|
43
41
|
usersetOf("organization", "viewer"),
|
|
44
42
|
),
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
direct,
|
|
13
13
|
from,
|
|
14
14
|
objectOf,
|
|
15
|
-
publicWith,
|
|
16
15
|
union,
|
|
17
16
|
usersetOf,
|
|
18
17
|
} from "./rewrite.js";
|
|
@@ -39,7 +38,6 @@ export const mcpServerDeclaration = declareKind({
|
|
|
39
38
|
union(
|
|
40
39
|
direct(
|
|
41
40
|
objectOf("identity_account"),
|
|
42
|
-
publicWith("identity_account", "allow_public"),
|
|
43
41
|
usersetOf("organization", "member"),
|
|
44
42
|
usersetOf("organization", "viewer"),
|
|
45
43
|
),
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Transcript of fga/model/agentic/plugin.fga — the unit of install, shaped
|
|
3
3
|
* exactly like a skill: an organization-owned blueprint whose owner is the
|
|
4
4
|
* installer (and, through the organization, its admins), whose viewers are
|
|
5
|
-
* the org's members and viewers
|
|
6
|
-
* platform viewer. Its verbs are the blueprint set: `can_view`, `can_edit`
|
|
5
|
+
* the org's members and viewers and the platform viewer. Its verbs are the blueprint set: `can_view`, `can_edit`
|
|
7
6
|
* (visibility), `can_delete` (uninstall), `can_grant_access`,
|
|
8
7
|
* `can_view_access`; `can_use` is carried as the model's word for
|
|
9
8
|
* "install from" though no wire permission names it yet.
|
|
@@ -21,7 +20,6 @@ import {
|
|
|
21
20
|
direct,
|
|
22
21
|
from,
|
|
23
22
|
objectOf,
|
|
24
|
-
publicWith,
|
|
25
23
|
union,
|
|
26
24
|
usersetOf,
|
|
27
25
|
} from "./rewrite.js";
|
|
@@ -48,7 +46,6 @@ export const pluginDeclaration = declareKind({
|
|
|
48
46
|
union(
|
|
49
47
|
direct(
|
|
50
48
|
objectOf("identity_account"),
|
|
51
|
-
publicWith("identity_account", "allow_public"),
|
|
52
49
|
usersetOf("organization", "member"),
|
|
53
50
|
usersetOf("organization", "viewer"),
|
|
54
51
|
),
|
|
@@ -6,12 +6,17 @@
|
|
|
6
6
|
* define organization: [organization] → direct(objectOf("organization"))
|
|
7
7
|
* define owner: [identity_account] or admin from organization
|
|
8
8
|
* → union(direct(objectOf("identity_account")), from("admin", "organization"))
|
|
9
|
-
* define viewer: [identity_account,
|
|
10
|
-
*
|
|
11
|
-
* → union(direct(objectOf(...),
|
|
9
|
+
* define viewer: [identity_account, organization#member, organization#viewer]
|
|
10
|
+
* or owner or platform_viewer
|
|
11
|
+
* → union(direct(objectOf(...), usersetOf(...), usersetOf(...)),
|
|
12
12
|
* computed("owner"), computed("platform_viewer"))
|
|
13
13
|
* define can_view: viewer → computed("viewer")
|
|
14
14
|
*
|
|
15
|
+
* A subject type is an object type or a userset; the model admits no
|
|
16
|
+
* wildcard (`type:*`) and declares no condition, so neither has a form
|
|
17
|
+
* here. The day a line needs one, the transcript cannot be written and
|
|
18
|
+
* the gap is a design act.
|
|
19
|
+
*
|
|
15
20
|
* A declaration file (model/<kind>.ts) is a TRANSCRIPT of its `.fga`
|
|
16
21
|
* file: the same relations, in the file's order, each line rewritten in
|
|
17
22
|
* these builders so a reader with the model open sees the same text, and
|
|
@@ -53,12 +58,6 @@ export type SubjectType =
|
|
|
53
58
|
readonly form: "userset";
|
|
54
59
|
readonly type: string;
|
|
55
60
|
readonly relation: string;
|
|
56
|
-
}
|
|
57
|
-
/** `[identity_account:* with allow_public]` — every object of the type, under the condition. */
|
|
58
|
-
| {
|
|
59
|
-
readonly form: "wildcard";
|
|
60
|
-
readonly type: string;
|
|
61
|
-
readonly condition: string;
|
|
62
61
|
};
|
|
63
62
|
|
|
64
63
|
export type Rewrite =
|
|
@@ -87,10 +86,6 @@ export function usersetOf(type: string, relation: string): SubjectType {
|
|
|
87
86
|
return { form: "userset", type, relation };
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
export function publicWith(type: string, condition: string): SubjectType {
|
|
91
|
-
return { form: "wildcard", type, condition };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
89
|
export function direct(...subjects: ReadonlyArray<SubjectType>): Rewrite {
|
|
95
90
|
return { node: "this", subjects };
|
|
96
91
|
}
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
direct,
|
|
15
15
|
from,
|
|
16
16
|
objectOf,
|
|
17
|
-
publicWith,
|
|
18
17
|
union,
|
|
19
18
|
usersetOf,
|
|
20
19
|
} from "./rewrite.js";
|
|
@@ -41,7 +40,6 @@ export const skillDeclaration = declareKind({
|
|
|
41
40
|
union(
|
|
42
41
|
direct(
|
|
43
42
|
objectOf("identity_account"),
|
|
44
|
-
publicWith("identity_account", "allow_public"),
|
|
45
43
|
usersetOf("organization", "member"),
|
|
46
44
|
usersetOf("organization", "viewer"),
|
|
47
45
|
),
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
direct,
|
|
15
15
|
from,
|
|
16
16
|
objectOf,
|
|
17
|
-
publicWith,
|
|
18
17
|
union,
|
|
19
18
|
usersetOf,
|
|
20
19
|
} from "./rewrite.js";
|
|
@@ -41,7 +40,6 @@ export const workflowDeclaration = declareKind({
|
|
|
41
40
|
union(
|
|
42
41
|
direct(
|
|
43
42
|
objectOf("identity_account"),
|
|
44
|
-
publicWith("identity_account", "allow_public"),
|
|
45
43
|
usersetOf("organization", "member"),
|
|
46
44
|
usersetOf("organization", "viewer"),
|
|
47
45
|
),
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
direct,
|
|
31
31
|
from,
|
|
32
32
|
objectOf,
|
|
33
|
-
publicWith,
|
|
34
33
|
union,
|
|
35
34
|
usersetOf,
|
|
36
35
|
} from "./rewrite.js";
|
|
@@ -84,7 +83,6 @@ export const workflowInstanceDeclaration = declareKind({
|
|
|
84
83
|
union(
|
|
85
84
|
direct(
|
|
86
85
|
objectOf("identity_account"),
|
|
87
|
-
publicWith("identity_account", "allow_public"),
|
|
88
86
|
usersetOf("organization", "member"),
|
|
89
87
|
usersetOf("organization", "viewer"),
|
|
90
88
|
),
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
* annotation, which is why this driver meets that caller at all: a
|
|
33
33
|
* skip lane hands the directory the identity the interceptor stamped,
|
|
34
34
|
* and no Authorize arm ran before it. Every other caller gets the
|
|
35
|
-
* evaluated list.
|
|
36
|
-
* though no organization line admits a wildcard.
|
|
35
|
+
* evaluated list.
|
|
37
36
|
* The controller loads each id it is handed and skips the ones whose row
|
|
38
37
|
* is gone (grants can outlive rows); the source has already loaded the
|
|
39
38
|
* row once for the evaluation — two primary-key reads per organization,
|
|
@@ -102,7 +101,6 @@ export function newBuiltInOrganizationDirectory(
|
|
|
102
101
|
{ type: ORGANIZATION, id },
|
|
103
102
|
"can_view",
|
|
104
103
|
person,
|
|
105
|
-
{ allow: false },
|
|
106
104
|
)
|
|
107
105
|
) {
|
|
108
106
|
visible.push(id);
|
|
@@ -41,26 +41,18 @@
|
|
|
41
41
|
* `skipped` is therefore complete once every case has run; a consumer
|
|
42
42
|
* pins it after the cases, which is the order a test file declares them.
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* that
|
|
49
|
-
*
|
|
50
|
-
* key anywhere is a thrown fault, so a change to the format upstream is
|
|
51
|
-
* loud here rather than half-run.
|
|
44
|
+
* The parser is strict on purpose: an unknown key anywhere is a thrown
|
|
45
|
+
* fault, so a change to the format upstream is loud here rather than
|
|
46
|
+
* half-run. The model declares no condition, so the format's `condition`
|
|
47
|
+
* on a tuple and `context` on a check are among the keys refused: a
|
|
48
|
+
* document that still carries them is written against a model this
|
|
49
|
+
* evaluator does not answer, and saying so beats running it half-blind.
|
|
52
50
|
*/
|
|
53
51
|
import assert from "node:assert/strict";
|
|
54
52
|
|
|
55
53
|
import { checkRelation } from "./evaluator.js";
|
|
56
54
|
import type { Model } from "./model/index.js";
|
|
57
|
-
import type {
|
|
58
|
-
CheckContext,
|
|
59
|
-
Person,
|
|
60
|
-
Subject,
|
|
61
|
-
Tuple,
|
|
62
|
-
TupleSource,
|
|
63
|
-
} from "./tuples.js";
|
|
55
|
+
import type { Person, Subject, Tuple, TupleSource } from "./tuples.js";
|
|
64
56
|
import {
|
|
65
57
|
ACCOUNT_TYPE,
|
|
66
58
|
newInMemoryTupleSource,
|
|
@@ -76,20 +68,17 @@ export interface StoreTestTuple {
|
|
|
76
68
|
readonly user: string;
|
|
77
69
|
readonly relation: string;
|
|
78
70
|
readonly object: string;
|
|
79
|
-
readonly condition?: { readonly name: string };
|
|
80
71
|
}
|
|
81
72
|
|
|
82
73
|
export interface StoreTestCheck {
|
|
83
74
|
readonly user: string;
|
|
84
75
|
readonly object: string;
|
|
85
|
-
readonly context?: CheckContext;
|
|
86
76
|
readonly assertions: Readonly<Record<string, boolean>>;
|
|
87
77
|
}
|
|
88
78
|
|
|
89
79
|
export interface StoreTestListObjects {
|
|
90
80
|
readonly user: string;
|
|
91
81
|
readonly type: string;
|
|
92
|
-
readonly context?: CheckContext;
|
|
93
82
|
readonly assertions: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
94
83
|
}
|
|
95
84
|
|
|
@@ -132,28 +121,12 @@ export function parseStoreTestDocument(
|
|
|
132
121
|
}
|
|
133
122
|
|
|
134
123
|
function parseTuple(value: unknown, where: string): StoreTestTuple {
|
|
135
|
-
const entry = record(value, where, [
|
|
136
|
-
|
|
137
|
-
"relation",
|
|
138
|
-
"object",
|
|
139
|
-
"condition",
|
|
140
|
-
]);
|
|
141
|
-
const tuple: StoreTestTuple = {
|
|
124
|
+
const entry = record(value, where, ["user", "relation", "object"]);
|
|
125
|
+
return {
|
|
142
126
|
user: text(entry["user"], `${where}.user`),
|
|
143
127
|
relation: text(entry["relation"], `${where}.relation`),
|
|
144
128
|
object: text(entry["object"], `${where}.object`),
|
|
145
129
|
};
|
|
146
|
-
if (entry["condition"] === undefined) {
|
|
147
|
-
return tuple;
|
|
148
|
-
}
|
|
149
|
-
const condition = record(entry["condition"], `${where}.condition`, [
|
|
150
|
-
"name",
|
|
151
|
-
"context",
|
|
152
|
-
]);
|
|
153
|
-
return {
|
|
154
|
-
...tuple,
|
|
155
|
-
condition: { name: text(condition["name"], `${where}.condition.name`) },
|
|
156
|
-
};
|
|
157
130
|
}
|
|
158
131
|
|
|
159
132
|
function parseTest(value: unknown, where: string): StoreTest {
|
|
@@ -173,12 +146,7 @@ function parseTest(value: unknown, where: string): StoreTest {
|
|
|
173
146
|
}
|
|
174
147
|
|
|
175
148
|
function parseCheck(value: unknown, where: string): StoreTestCheck {
|
|
176
|
-
const entry = record(value, where, [
|
|
177
|
-
"user",
|
|
178
|
-
"object",
|
|
179
|
-
"context",
|
|
180
|
-
"assertions",
|
|
181
|
-
]);
|
|
149
|
+
const entry = record(value, where, ["user", "object", "assertions"]);
|
|
182
150
|
const assertions = record(entry["assertions"], `${where}.assertions`);
|
|
183
151
|
const parsed: Record<string, boolean> = {};
|
|
184
152
|
for (const [relation, expected] of Object.entries(assertions)) {
|
|
@@ -187,17 +155,15 @@ function parseCheck(value: unknown, where: string): StoreTestCheck {
|
|
|
187
155
|
}
|
|
188
156
|
parsed[relation] = expected;
|
|
189
157
|
}
|
|
190
|
-
const context = parseContext(entry["context"], `${where}.context`);
|
|
191
158
|
return {
|
|
192
159
|
user: text(entry["user"], `${where}.user`),
|
|
193
160
|
object: text(entry["object"], `${where}.object`),
|
|
194
|
-
...(context === undefined ? {} : { context }),
|
|
195
161
|
assertions: parsed,
|
|
196
162
|
};
|
|
197
163
|
}
|
|
198
164
|
|
|
199
165
|
function parseListObjects(value: unknown, where: string): StoreTestListObjects {
|
|
200
|
-
const entry = record(value, where, ["user", "type", "
|
|
166
|
+
const entry = record(value, where, ["user", "type", "assertions"]);
|
|
201
167
|
const assertions = record(entry["assertions"], `${where}.assertions`);
|
|
202
168
|
const parsed: Record<string, ReadonlyArray<string>> = {};
|
|
203
169
|
for (const [relation, expected] of Object.entries(assertions)) {
|
|
@@ -205,26 +171,13 @@ function parseListObjects(value: unknown, where: string): StoreTestListObjects {
|
|
|
205
171
|
(item, index) => text(item, `${where}.assertions.${relation}[${index}]`),
|
|
206
172
|
);
|
|
207
173
|
}
|
|
208
|
-
const context = parseContext(entry["context"], `${where}.context`);
|
|
209
174
|
return {
|
|
210
175
|
user: text(entry["user"], `${where}.user`),
|
|
211
176
|
type: text(entry["type"], `${where}.type`),
|
|
212
|
-
...(context === undefined ? {} : { context }),
|
|
213
177
|
assertions: parsed,
|
|
214
178
|
};
|
|
215
179
|
}
|
|
216
180
|
|
|
217
|
-
function parseContext(value: unknown, where: string): CheckContext | undefined {
|
|
218
|
-
if (value === undefined) {
|
|
219
|
-
return undefined;
|
|
220
|
-
}
|
|
221
|
-
const entry = record(value, where, ["allow"]);
|
|
222
|
-
if (typeof entry["allow"] !== "boolean") {
|
|
223
|
-
throw new Error(`${where}.allow: expected a boolean`);
|
|
224
|
-
}
|
|
225
|
-
return { allow: entry["allow"] };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
181
|
function record(
|
|
229
182
|
value: unknown,
|
|
230
183
|
where: string,
|
|
@@ -297,9 +250,6 @@ export interface StoreTestKit {
|
|
|
297
250
|
readonly skipped: ReadonlyArray<StoreTestSkip>;
|
|
298
251
|
}
|
|
299
252
|
|
|
300
|
-
/** The listing posture, for a check the document leaves without a context (the module header). */
|
|
301
|
-
const CONTEXT_WHEN_ABSENT: CheckContext = { allow: false };
|
|
302
|
-
|
|
303
253
|
/**
|
|
304
254
|
* The document's tests as runnable cases over `model`, plus every
|
|
305
255
|
* assertion group the kit does not run.
|
|
@@ -346,7 +296,6 @@ export function storeTestCases(
|
|
|
346
296
|
for (const item of runnable) {
|
|
347
297
|
const object = parseObjectRef(item.object);
|
|
348
298
|
const person = personOf(item.user);
|
|
349
|
-
const context = item.context ?? CONTEXT_WHEN_ABSENT;
|
|
350
299
|
for (const [relation, expected] of Object.entries(item.assertions)) {
|
|
351
300
|
const recording = recordUndeclaredTypes(source, model);
|
|
352
301
|
const actual = await checkRelation(
|
|
@@ -354,7 +303,6 @@ export function storeTestCases(
|
|
|
354
303
|
object,
|
|
355
304
|
relation,
|
|
356
305
|
person,
|
|
357
|
-
context,
|
|
358
306
|
);
|
|
359
307
|
if (recording.reached.size > 0) {
|
|
360
308
|
skipped.push({
|
|
@@ -371,7 +319,7 @@ export function storeTestCases(
|
|
|
371
319
|
assert.equal(
|
|
372
320
|
actual,
|
|
373
321
|
expected,
|
|
374
|
-
`${document.name} :: ${test.name}: ${item.user} ${relation} ${item.object}
|
|
322
|
+
`${document.name} :: ${test.name}: ${item.user} ${relation} ${item.object} expected ${String(expected)}, got ${String(actual)}`,
|
|
375
323
|
);
|
|
376
324
|
}
|
|
377
325
|
}
|
|
@@ -389,10 +337,9 @@ interface RecordingTupleSource extends TupleSource {
|
|
|
389
337
|
/**
|
|
390
338
|
* A view of `inner` that records, for one walk, every subject type the
|
|
391
339
|
* evaluator read that `model` does not declare. A person
|
|
392
|
-
* (`identity_account:<id
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* type is.
|
|
340
|
+
* (`identity_account:<id>`) is a subject the evaluator compares, never a
|
|
341
|
+
* type it resolves, so it is not a "reach" whatever the model declares; a
|
|
342
|
+
* userset or a parent object of an undeclared type is.
|
|
396
343
|
*/
|
|
397
344
|
function recordUndeclaredTypes(
|
|
398
345
|
inner: TupleSource,
|
|
@@ -423,8 +370,6 @@ function resolvedTypeOf(subject: Subject): string | undefined {
|
|
|
423
370
|
: subject.object.type;
|
|
424
371
|
case "userset":
|
|
425
372
|
return subject.object.type;
|
|
426
|
-
case "wildcard":
|
|
427
|
-
return undefined;
|
|
428
373
|
default: {
|
|
429
374
|
const exhaustive: never = subject;
|
|
430
375
|
throw new Error(`unknown subject form: ${JSON.stringify(exhaustive)}`);
|
|
@@ -433,14 +378,10 @@ function resolvedTypeOf(subject: Subject): string | undefined {
|
|
|
433
378
|
}
|
|
434
379
|
|
|
435
380
|
function tupleOf(entry: StoreTestTuple): Tuple {
|
|
436
|
-
const subject = parseSubject(entry.user);
|
|
437
381
|
return {
|
|
438
382
|
object: parseObjectRef(entry.object),
|
|
439
383
|
relation: entry.relation,
|
|
440
|
-
subject:
|
|
441
|
-
subject.form === "wildcard" && entry.condition !== undefined
|
|
442
|
-
? { ...subject, condition: entry.condition.name }
|
|
443
|
-
: subject,
|
|
384
|
+
subject: parseSubject(entry.user),
|
|
444
385
|
};
|
|
445
386
|
}
|
|
446
387
|
|