@astrale-domains/shell 0.1.5 → 0.1.7
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/README.md +73 -173
- package/dist/domain.d.ts +477 -31
- package/dist/domain.js +87 -78
- package/dist/functions/application/index.js +2 -1
- package/dist/functions/application/legacy/index.js +1 -0
- package/dist/functions/application/legacy/list-entrypoints.js +8 -0
- package/dist/functions/authority/capabilities.js +1 -0
- package/dist/functions/authority/composition.js +47 -0
- package/dist/functions/authority/index.js +2 -0
- package/dist/functions/authority/roles.js +98 -0
- package/dist/functions/directory/disconnect-directory.js +35 -0
- package/dist/functions/directory/index.js +1 -0
- package/dist/functions/group/change-group-membership.js +43 -9
- package/dist/functions/group/create-group.js +23 -0
- package/dist/functions/group/delete-group.js +33 -0
- package/dist/functions/group/deprovision-group.js +27 -0
- package/dist/functions/group/group-errors.js +43 -0
- package/dist/functions/group/group-membership-errors.js +2 -0
- package/dist/functions/group/group.js +51 -0
- package/dist/functions/group/index.js +8 -2
- package/dist/functions/group/legacy/index.js +1 -0
- package/dist/functions/group/legacy/membership.js +60 -0
- package/dist/functions/group/membership-laws.js +40 -0
- package/dist/functions/group/provision-group.js +33 -0
- package/dist/functions/group/update-group.js +55 -0
- package/dist/functions/invitation/index.js +2 -0
- package/dist/functions/invitation/invitation-errors.js +13 -0
- package/dist/functions/invitation/invitation-methods.js +41 -0
- package/dist/functions/invitation/send-invitation.js +81 -0
- package/dist/functions/user/account.js +35 -0
- package/dist/functions/user/create-user.js +45 -0
- package/dist/functions/user/deactivate-user.js +19 -0
- package/dist/functions/user/delete-user.js +34 -0
- package/dist/functions/user/deprovision-user.js +21 -0
- package/dist/functions/user/index.js +15 -1
- package/dist/functions/user/invite-errors.js +18 -0
- package/dist/functions/user/invite-user.js +42 -34
- package/dist/functions/user/legacy/claimed-account.js +11 -0
- package/dist/functions/user/legacy/index.js +4 -0
- package/dist/functions/user/legacy/invite.js +20 -0
- package/dist/functions/user/legacy/migrate-accounts.js +98 -0
- package/dist/functions/user/legacy/record-profile.js +19 -0
- package/dist/functions/user/protect-administrators.js +16 -0
- package/dist/functions/user/provision-user.js +70 -0
- package/dist/functions/user/reactivate-user.js +19 -0
- package/dist/functions/user/read-account.js +12 -0
- package/dist/functions/user/request-invitation.js +17 -0
- package/dist/functions/user/update-profile.js +22 -0
- package/dist/functions/user/update-user.js +68 -0
- package/dist/functions/user/user-errors.js +69 -0
- package/dist/functions/user/write-account.js +57 -0
- package/dist/functions/view-access/index.js +2 -1
- package/dist/functions/view-access/{assign-views.js → legacy/assign-views.js} +11 -7
- package/dist/functions/view-access/legacy/index.js +1 -0
- package/dist/functions/view-access/{view-access-errors.js → legacy/view-access-errors.js} +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/integrations/user-invitation/invite-user-request.d.ts +6 -0
- package/dist/legacy/index.js +1 -0
- package/dist/legacy/release.js +33 -0
- package/dist/mutations/composition/grant-role.js +13 -0
- package/dist/mutations/composition/index.js +1 -0
- package/dist/mutations/group/legacy/index.js +1 -0
- package/dist/mutations/group/legacy/membership-class.js +7 -0
- package/dist/mutations/group/set-group-membership.js +23 -14
- package/dist/mutations/identity/create-profile.js +37 -0
- package/dist/mutations/identity/delete-identity.js +28 -0
- package/dist/mutations/identity/index.js +4 -0
- package/dist/mutations/identity/write-account.js +109 -0
- package/dist/mutations/identity/write-group.js +72 -0
- package/dist/mutations/invitation/index.js +1 -0
- package/dist/mutations/invitation/write-invitation.js +62 -0
- package/dist/mutations/user/legacy/index.js +1 -0
- package/dist/mutations/user/{record-user-profile.js → legacy/record-user-profile.js} +5 -2
- package/dist/providers/admin-enrollment/create-admin-enrollment-provider.js +6 -3
- package/dist/providers/admin-enrollment/legacy/index.js +1 -0
- package/dist/providers/admin-enrollment/legacy/send-without-profile.js +16 -0
- package/dist/queries/application/legacy/index.js +1 -0
- package/dist/queries/application/legacy/read-installed-entrypoints.js +47 -0
- package/dist/queries/composition/index.js +2 -0
- package/dist/queries/composition/read-extensions.js +34 -0
- package/dist/queries/composition/read-role-nodes.js +41 -0
- package/dist/queries/group/index.js +1 -1
- package/dist/queries/group/legacy/index.js +2 -0
- package/dist/queries/group/legacy/read-core-groups.js +46 -0
- package/dist/queries/group/legacy/read-member-of-group.js +24 -0
- package/dist/queries/group/read-group-membership-endpoints.js +11 -0
- package/dist/queries/group/read-group-membership.js +6 -21
- package/dist/queries/identity/index.js +1 -0
- package/dist/queries/identity/read-identities.js +29 -0
- package/dist/queries/invitation/index.js +1 -0
- package/dist/queries/invitation/read-invitation.js +25 -0
- package/dist/queries/profile/index.js +1 -0
- package/dist/queries/profile/read-profile.js +15 -0
- package/dist/queries/role/index.js +1 -0
- package/dist/queries/role/read-role-node.js +13 -0
- package/dist/queries/user/legacy/index.js +1 -0
- package/dist/queries/user/legacy/read-user-profile.js +27 -0
- package/dist/runtime.d.ts +18203 -630
- package/dist/runtime.js +32 -7
- package/dist/schema/index.d.ts +7 -4
- package/dist/schema/index.js +8 -4
- package/dist/schema/legacy/index.js +1 -0
- package/dist/schema/legacy/schema.js +32 -0
- package/dist/schema/modules/application/legacy/entrypoint-entry.js +11 -0
- package/dist/schema/modules/application/legacy/functions/index.js +1 -0
- package/dist/schema/modules/application/legacy/functions/list-entrypoints.js +21 -0
- package/dist/schema/modules/application/legacy/index.js +2 -0
- package/dist/schema/modules/directory/functions/disconnect-directory.d.ts +13 -0
- package/dist/schema/modules/directory/functions/disconnect-directory.js +15 -0
- package/dist/schema/modules/directory/functions/index.d.ts +1 -0
- package/dist/schema/modules/directory/functions/index.js +1 -0
- package/dist/schema/modules/directory/index.d.ts +1 -0
- package/dist/schema/modules/directory/index.js +1 -0
- package/dist/schema/modules/group/classes/group-profile-link.d.ts +34 -0
- package/dist/schema/modules/group/classes/group-profile-link.js +21 -0
- package/dist/schema/modules/group/classes/group-profile.d.ts +15 -0
- package/dist/schema/modules/group/classes/group-profile.js +20 -0
- package/dist/schema/modules/group/classes/group.d.ts +73 -1
- package/dist/schema/modules/group/classes/group.js +28 -7
- package/dist/schema/modules/group/classes/index.d.ts +2 -1
- package/dist/schema/modules/group/classes/index.js +3 -1
- package/dist/schema/modules/group/functions/assign-member.js +17 -0
- package/dist/schema/modules/group/functions/create-group.js +20 -0
- package/dist/schema/modules/group/functions/delete-group.js +12 -0
- package/dist/schema/modules/group/functions/deprovision-group.js +14 -0
- package/dist/schema/modules/group/functions/index.js +7 -2
- package/dist/schema/modules/group/functions/provision-group.js +14 -0
- package/dist/schema/modules/group/functions/unassign-member.js +17 -0
- package/dist/schema/modules/group/functions/update-group.js +20 -0
- package/dist/schema/modules/group/index.d.ts +3 -3
- package/dist/schema/modules/group/index.js +4 -3
- package/dist/schema/modules/group/legacy/admission.d.ts +9 -0
- package/dist/schema/modules/group/legacy/admission.js +9 -0
- package/dist/schema/modules/group/legacy/classes/index.d.ts +1 -0
- package/dist/schema/modules/group/legacy/classes/index.js +1 -0
- package/dist/schema/modules/group/{classes → legacy/classes}/member-of-group.d.ts +10 -1
- package/dist/schema/modules/group/legacy/classes/member-of-group.js +32 -0
- package/dist/schema/modules/group/legacy/core-groups.d.ts +22 -0
- package/dist/schema/modules/group/legacy/core-groups.js +28 -0
- package/dist/schema/modules/group/legacy/index.d.ts +5 -0
- package/dist/schema/modules/group/legacy/index.js +6 -0
- package/dist/schema/modules/group/legacy/policies/belongs-to-group.d.ts +5 -0
- package/dist/schema/modules/group/legacy/policies/belongs-to-group.js +9 -0
- package/dist/schema/modules/group/legacy/policies/index.d.ts +2 -0
- package/dist/schema/modules/group/legacy/policies/index.js +2 -0
- package/dist/schema/modules/group/legacy/policies/owns-membership-group.d.ts +6 -0
- package/dist/schema/modules/group/legacy/policies/owns-membership-group.js +12 -0
- package/dist/schema/modules/group/legacy/relaxations.d.ts +21 -0
- package/dist/schema/modules/group/legacy/relaxations.js +27 -0
- package/dist/schema/modules/group/policies/describes-group-of-subject.d.ts +2 -0
- package/dist/schema/modules/group/policies/describes-group-of-subject.js +7 -0
- package/dist/schema/modules/group/policies/index.d.ts +2 -1
- package/dist/schema/modules/group/policies/index.js +2 -1
- package/dist/schema/modules/group/policies/is-group-profile-source.d.ts +2 -0
- package/dist/schema/modules/group/policies/is-group-profile-source.js +6 -0
- package/dist/schema/modules/group/types/group.js +10 -0
- package/dist/schema/modules/group/types/index.js +1 -0
- package/dist/schema/modules/invitation/classes/index.js +2 -0
- package/dist/schema/modules/invitation/classes/invitation-user.js +17 -0
- package/dist/schema/modules/invitation/classes/invitation.js +30 -0
- package/dist/schema/modules/invitation/functions/accept.js +16 -0
- package/dist/schema/modules/invitation/functions/cancel.js +12 -0
- package/dist/schema/modules/invitation/functions/index.js +4 -0
- package/dist/schema/modules/invitation/functions/resend.js +12 -0
- package/dist/schema/modules/invitation/functions/send.js +20 -0
- package/dist/schema/modules/invitation/index.js +2 -0
- package/dist/schema/modules/invitation/types/index.js +1 -0
- package/dist/schema/modules/invitation/types/invitation-status.js +5 -0
- package/dist/schema/modules/namespace/classes/alias.d.ts +2 -1
- package/dist/schema/modules/namespace/classes/alias.js +11 -2
- package/dist/schema/modules/namespace/functions/link.js +3 -2
- package/dist/schema/modules/namespace/functions/rename.js +3 -2
- package/dist/schema/modules/namespace/functions/unlink.js +3 -2
- package/dist/schema/modules/role/classes/index.d.ts +1 -0
- package/dist/schema/modules/role/classes/index.js +1 -0
- package/dist/schema/modules/role/classes/role.d.ts +16 -0
- package/dist/schema/modules/role/classes/role.js +19 -0
- package/dist/schema/modules/role/core/index.d.ts +1 -0
- package/dist/schema/modules/role/core/index.js +1 -0
- package/dist/schema/modules/role/core/roles.d.ts +37 -0
- package/dist/schema/modules/role/core/roles.js +30 -0
- package/dist/schema/modules/role/index.d.ts +2 -0
- package/dist/schema/modules/role/index.js +2 -0
- package/dist/schema/modules/user/classes/index.d.ts +2 -0
- package/dist/schema/modules/user/classes/index.js +3 -0
- package/dist/schema/modules/user/classes/user-profile-link.d.ts +35 -0
- package/dist/schema/modules/user/classes/user-profile-link.js +22 -0
- package/dist/schema/modules/user/classes/user-profile.d.ts +41 -0
- package/dist/schema/modules/user/classes/user-profile.js +30 -0
- package/dist/schema/modules/user/classes/user.d.ts +133 -8
- package/dist/schema/modules/user/classes/user.js +46 -15
- package/dist/schema/modules/user/functions/create-user.js +21 -0
- package/dist/schema/modules/user/functions/deactivate-user.js +12 -0
- package/dist/schema/modules/user/functions/delete-user.js +12 -0
- package/dist/schema/modules/user/functions/deprovision-user.js +14 -0
- package/dist/schema/modules/user/functions/index.js +8 -1
- package/dist/schema/modules/user/functions/invite-user.js +23 -4
- package/dist/schema/modules/user/functions/provision-user.js +25 -0
- package/dist/schema/modules/user/functions/reactivate-user.js +12 -0
- package/dist/schema/modules/user/functions/update-profile.js +16 -0
- package/dist/schema/modules/user/functions/update-user.js +22 -0
- package/dist/schema/modules/user/index.d.ts +2 -1
- package/dist/schema/modules/user/index.js +2 -1
- package/dist/schema/modules/user/legacy/account-properties.js +24 -0
- package/dist/schema/modules/user/legacy/functions/index.js +2 -0
- package/dist/schema/modules/user/legacy/functions/migrate-accounts.js +23 -0
- package/dist/schema/modules/user/legacy/functions/record-profile.js +18 -0
- package/dist/schema/modules/user/legacy/index.js +5 -0
- package/dist/schema/modules/user/legacy/invite.js +15 -0
- package/dist/schema/modules/user/legacy/relaxations.js +37 -0
- package/dist/schema/modules/user/policies/describes-subject.d.ts +2 -0
- package/dist/schema/modules/user/policies/describes-subject.js +7 -0
- package/dist/schema/modules/user/policies/index.d.ts +2 -0
- package/dist/schema/modules/user/policies/index.js +2 -0
- package/dist/schema/modules/user/policies/is-profile-source.d.ts +2 -0
- package/dist/schema/modules/user/policies/is-profile-source.js +6 -0
- package/dist/schema/modules/user/types/account.d.ts +12 -0
- package/dist/schema/modules/user/types/account.js +12 -0
- package/dist/schema/modules/user/types/index.d.ts +1 -1
- package/dist/schema/modules/user/types/index.js +1 -1
- package/dist/schema/modules/view-access/classes/view-access.d.ts +7 -11
- package/dist/schema/modules/view-access/classes/view-access.js +17 -12
- package/dist/schema/modules/view-access/index.d.ts +1 -2
- package/dist/schema/modules/view-access/index.js +1 -2
- package/dist/schema/modules/view-access/legacy/functions/assign-views.js +22 -0
- package/dist/schema/modules/view-access/legacy/functions/index.js +1 -0
- package/dist/schema/modules/view-access/legacy/index.js +2 -0
- package/dist/schema/modules/view-access/legacy/view-assignment.js +24 -0
- package/dist/schema/modules/view-access/policies/holds-view-access.d.ts +2 -0
- package/dist/schema/modules/view-access/policies/holds-view-access.js +5 -0
- package/dist/schema/modules/view-access/policies/index.d.ts +1 -0
- package/dist/schema/modules/view-access/policies/index.js +1 -0
- package/dist/schema/policies/has-role.d.ts +6 -0
- package/dist/schema/policies/has-role.js +9 -0
- package/dist/schema/policies/index.d.ts +1 -0
- package/dist/schema/policies/index.js +1 -4
- package/dist/schema/policies/legacy/administers-shell-class.js +21 -0
- package/dist/schema/policies/legacy/index.js +4 -0
- package/dist/schema/policies/legacy/is-self.js +9 -0
- package/dist/schema/policies/legacy/owns-class.js +12 -0
- package/dist/schema/policies/legacy/uses-function.js +9 -0
- package/dist/schema/schema.d.ts +1849 -118
- package/dist/schema/schema.js +35 -15
- package/dist/schema/types/attributes.js +15 -0
- package/dist/schema/types/index.js +1 -0
- package/dist/utils/attributes/attributes.js +12 -0
- package/dist/utils/attributes/index.js +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/same-json/index.js +1 -0
- package/dist/utils/same-json/same-json.js +9 -0
- package/package.json +4 -4
- package/dist/functions/application/list-entrypoints.js +0 -6
- package/dist/functions/user/record-profile.js +0 -18
- package/dist/mutations/user/index.js +0 -1
- package/dist/queries/application/index.js +0 -1
- package/dist/queries/application/read-installed-entrypoints.js +0 -28
- package/dist/queries/user/index.js +0 -1
- package/dist/queries/user/read-user-profile.js +0 -9
- package/dist/schema/modules/application/functions/index.d.ts +0 -1
- package/dist/schema/modules/application/functions/index.js +0 -1
- package/dist/schema/modules/application/functions/list-entrypoints.d.ts +0 -15
- package/dist/schema/modules/application/functions/list-entrypoints.js +0 -16
- package/dist/schema/modules/application/index.d.ts +0 -2
- package/dist/schema/modules/application/index.js +0 -2
- package/dist/schema/modules/application/types/entrypoint-entry.d.ts +0 -8
- package/dist/schema/modules/application/types/entrypoint-entry.js +0 -7
- package/dist/schema/modules/application/types/index.d.ts +0 -1
- package/dist/schema/modules/application/types/index.js +0 -1
- package/dist/schema/modules/group/classes/member-of-group.js +0 -12
- package/dist/schema/modules/group/core/administrators-group.d.ts +0 -1
- package/dist/schema/modules/group/core/administrators-group.js +0 -6
- package/dist/schema/modules/group/core/applications-group.d.ts +0 -1
- package/dist/schema/modules/group/core/applications-group.js +0 -6
- package/dist/schema/modules/group/core/index.d.ts +0 -3
- package/dist/schema/modules/group/core/index.js +0 -3
- package/dist/schema/modules/group/core/members-group.d.ts +0 -1
- package/dist/schema/modules/group/core/members-group.js +0 -6
- package/dist/schema/modules/group/functions/assign-member-to-group.js +0 -11
- package/dist/schema/modules/group/functions/unassign-member-from-group.js +0 -11
- package/dist/schema/modules/group/policies/belongs-to-group.d.ts +0 -1
- package/dist/schema/modules/group/policies/belongs-to-group.js +0 -5
- package/dist/schema/modules/user/functions/record-profile.js +0 -14
- package/dist/schema/modules/user/types/user-profile.d.ts +0 -7
- package/dist/schema/modules/user/types/user-profile.js +0 -7
- package/dist/schema/modules/view-access/core/index.d.ts +0 -1
- package/dist/schema/modules/view-access/core/index.js +0 -1
- package/dist/schema/modules/view-access/core/view-only-group.d.ts +0 -5
- package/dist/schema/modules/view-access/core/view-only-group.js +0 -10
- package/dist/schema/modules/view-access/functions/assign-views.d.ts +0 -16
- package/dist/schema/modules/view-access/functions/assign-views.js +0 -17
- package/dist/schema/modules/view-access/functions/index.d.ts +0 -1
- package/dist/schema/modules/view-access/functions/index.js +0 -1
- package/dist/schema/modules/view-access/types/index.js +0 -1
- package/dist/schema/modules/view-access/types/view-assignment.js +0 -16
- package/dist/schema/policies/administers-shell-class.js +0 -11
- package/dist/schema/policies/is-self.js +0 -5
- package/dist/schema/policies/owns-class.js +0 -7
- package/dist/schema/policies/uses-function.js +0 -5
- /package/dist/functions/group/{assign-member-to-group.js → assign-member.js} +0 -0
- /package/dist/functions/group/{unassign-member-from-group.js → unassign-member.js} +0 -0
package/dist/domain.js
CHANGED
|
@@ -1,96 +1,105 @@
|
|
|
1
1
|
import { defineDomain, defineFrontend, vite, requirements, } from '@astrale-os/sdk/domain';
|
|
2
|
-
import { K
|
|
2
|
+
import { K } from '@astrale-os/sdk/schema';
|
|
3
3
|
import { ShellSchema, S } from './schema/index.js';
|
|
4
|
+
// Seam L-SHELL-7, L-SHELL-9, L-SHELL-11: the bindings legacy groups and callers still need.
|
|
5
|
+
import { legacyRelease } from './legacy/index.js';
|
|
4
6
|
import runtime, {} from './runtime.js';
|
|
5
7
|
const frontend = defineFrontend({
|
|
6
8
|
schema: ShellSchema,
|
|
7
9
|
source: vite({ config: './frontend/vite.config.ts' }),
|
|
8
10
|
routes: { application: { path: '/', handshake: 'shell' } },
|
|
9
11
|
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The Roles the Shell release binds: `admin` is the only granter of every Shell Role and of the
|
|
14
|
+
* grantable Kernel Roles (R2); compositions are release bindings, not Core Edges (AM1).
|
|
15
|
+
*/
|
|
16
|
+
const domainRequirements = requirements(legacyRelease({
|
|
17
|
+
roles: [
|
|
18
|
+
// Profiles, member ⊂ admin, and their atomic Roles (AM1); admin grants them all.
|
|
19
|
+
{
|
|
20
|
+
role: S.core.nodes['workspace.member'],
|
|
21
|
+
members: [S.core.nodes['workspace.admin']],
|
|
22
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
23
|
+
},
|
|
24
|
+
{ role: S.core.nodes['workspace.admin'], granters: [S.core.nodes['workspace.admin']] },
|
|
25
|
+
// A member reads profiles only (user-profile.reader, group-profile.reader); the accounts and
|
|
26
|
+
// groups themselves are read by administrators.
|
|
27
|
+
{
|
|
28
|
+
role: S.core.nodes['user.reader'],
|
|
29
|
+
members: [S.core.nodes['workspace.admin']],
|
|
30
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
role: S.core.nodes['user.writer'],
|
|
34
|
+
members: [S.core.nodes['workspace.admin']],
|
|
35
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: S.core.nodes['group.reader'],
|
|
39
|
+
members: [S.core.nodes['workspace.admin']],
|
|
40
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
role: S.core.nodes['group.writer'],
|
|
44
|
+
members: [S.core.nodes['workspace.admin']],
|
|
45
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
46
|
+
},
|
|
47
|
+
{ role: S.core.nodes['directory.provisioner'], granters: [S.core.nodes['workspace.admin']] },
|
|
48
|
+
// A member reads every profile; it changes its own once an administrator allows it.
|
|
49
|
+
{
|
|
50
|
+
role: S.core.nodes['user-profile.reader'],
|
|
51
|
+
members: [S.core.nodes['workspace.member']],
|
|
52
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
53
|
+
},
|
|
54
|
+
// Off by default: an administrator lets members change their own profile from the console
|
|
55
|
+
// (workspace.member → self-writer). Left out of the release so an installation keeps that
|
|
56
|
+
// choice.
|
|
57
|
+
{
|
|
58
|
+
role: S.core.nodes['user-profile.self-writer'],
|
|
59
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
60
|
+
},
|
|
27
61
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
S.classes.User.static.methods.invite,
|
|
32
|
-
S.classes.User.methods.recordProfile,
|
|
33
|
-
S.classes.Group.static.methods.assignMember,
|
|
34
|
-
S.classes.Group.static.methods.unassignMember,
|
|
35
|
-
S.classes.Namespace.methods.link,
|
|
36
|
-
S.classes.Namespace.methods.unlink,
|
|
37
|
-
S.classes.Namespace.methods.rename,
|
|
38
|
-
// Core Edges cannot author Kernel extends_with, so administrators hold it directly.
|
|
39
|
-
S.functions.listEntrypoints,
|
|
40
|
-
S.functions.assignViews,
|
|
41
|
-
],
|
|
42
|
-
classes: [
|
|
43
|
-
{ class: S.classes.User, operations: ['create', 'read', 'traverse', 'update', 'delete'] },
|
|
44
|
-
{
|
|
45
|
-
class: S.classes.Group,
|
|
46
|
-
operations: ['create', 'read', 'traverse', 'update', 'delete'],
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
class: S.classes.member_of_group,
|
|
50
|
-
operations: ['create', 'read', 'traverse', 'update', 'delete'],
|
|
51
|
-
},
|
|
52
|
-
{ class: S.classes.Namespace, operations: ['read', 'traverse'] },
|
|
53
|
-
{ class: S.classes.alias, operations: ['create', 'read', 'traverse', 'update', 'delete'] },
|
|
54
|
-
// assignViews gives and withdraws Views; a Mutation may too. view_access carries no property.
|
|
55
|
-
{ class: S.classes.view_access, operations: ['create', 'read', 'traverse', 'delete'] },
|
|
56
|
-
{ class: K.classes.extends_with, operations: ['create', 'read', 'traverse', 'delete'] },
|
|
57
|
-
],
|
|
58
|
-
authority: {
|
|
59
|
-
grant: ['create', 'read', 'update', 'delete', 'traverse', 'use', 'revoke'],
|
|
60
|
-
revoke: ['create', 'read', 'update', 'delete', 'traverse', 'use', 'grant'],
|
|
61
|
-
},
|
|
62
|
+
role: S.core.nodes['group-profile.reader'],
|
|
63
|
+
members: [S.core.nodes['workspace.member']],
|
|
64
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
62
65
|
},
|
|
66
|
+
// Each profile holds the Kernel profile of its rank; Kernel admin is granted by Root alone.
|
|
67
|
+
// Kernel reader is no profile's: admin grants it to whoever only opens the Views it is given
|
|
68
|
+
// (AM10).
|
|
69
|
+
{ role: K.core.nodes.reader, granters: [S.core.nodes['workspace.admin']] },
|
|
63
70
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
K.functions.download,
|
|
68
|
-
// Upload exposes no data: its receipt binds to the caller, and attaching it still takes a
|
|
69
|
-
// Mutation under the caller's rights. The Kernel bounds it per blob and per Instance quota.
|
|
70
|
-
K.functions.upload,
|
|
71
|
-
K.functions.introspect,
|
|
72
|
-
K.functions.query,
|
|
73
|
-
K.functions.mutate,
|
|
74
|
-
K.classes.View.static.methods.resolve,
|
|
75
|
-
// The workspace opens only on this catalogue: an invited member must be able to read it.
|
|
76
|
-
S.functions.listEntrypoints,
|
|
77
|
-
// The GUI records the email and name each account signs in with; isSelf bounds it.
|
|
78
|
-
S.classes.User.methods.recordProfile,
|
|
79
|
-
],
|
|
71
|
+
role: K.core.nodes.writer,
|
|
72
|
+
members: [S.core.nodes['workspace.member']],
|
|
73
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
80
74
|
},
|
|
75
|
+
{ role: K.core.nodes.admin, members: [S.core.nodes['workspace.admin']] },
|
|
76
|
+
// Shell itself reads compositions (U4, U5, G3) and runs its own Queries and Mutations.
|
|
81
77
|
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
subject: S.core.nodes.applications,
|
|
86
|
-
functions: [
|
|
87
|
-
S.functions.listEntrypoints,
|
|
88
|
-
K.classes.View.static.methods.resolve,
|
|
89
|
-
K.functions.query,
|
|
90
|
-
],
|
|
78
|
+
role: K.core.nodes['authority.reader'],
|
|
79
|
+
members: [requirements.domain],
|
|
80
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
91
81
|
},
|
|
82
|
+
{
|
|
83
|
+
role: K.core.nodes['graph.reader'],
|
|
84
|
+
members: [requirements.domain],
|
|
85
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
role: K.core.nodes['graph.writer'],
|
|
89
|
+
members: [requirements.domain],
|
|
90
|
+
granters: [S.core.nodes['workspace.admin']],
|
|
91
|
+
},
|
|
92
|
+
// admin grants the other parts of reader and writer and the readers outside them (R2).
|
|
93
|
+
{ role: K.core.nodes['graph.manager'], granters: [S.core.nodes['workspace.admin']] },
|
|
94
|
+
{ role: K.core.nodes['blob.reader'], granters: [S.core.nodes['workspace.admin']] },
|
|
95
|
+
{ role: K.core.nodes['blob.writer'], granters: [S.core.nodes['workspace.admin']] },
|
|
96
|
+
{ role: K.core.nodes['blob.manager'], granters: [S.core.nodes['workspace.admin']] },
|
|
97
|
+
{ role: K.core.nodes['view.reader'], granters: [S.core.nodes['workspace.admin']] },
|
|
98
|
+
{ role: K.core.nodes['domain.reader'], granters: [S.core.nodes['workspace.admin']] },
|
|
99
|
+
{ role: K.core.nodes['execution.reader'], granters: [S.core.nodes['workspace.admin']] },
|
|
100
|
+
{ role: K.core.nodes['journal.reader'], granters: [S.core.nodes['workspace.admin']] },
|
|
92
101
|
],
|
|
93
|
-
});
|
|
102
|
+
}));
|
|
94
103
|
export const domain = defineDomain({
|
|
95
104
|
schema: ShellSchema,
|
|
96
105
|
runtime,
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
// Seam L-SHELL-11: the legacy handler the Runtime registers.
|
|
2
|
+
export { legacyListEntrypoints } from './legacy/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { legacyListEntrypoints } from './list-entrypoints.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineAction } from '@astrale-os/sdk/action';
|
|
2
|
+
import { legacyReadInstalledEntrypoints } from '../../../queries/application/legacy/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated One graph read with Shell's authority: every Domain with an `entrypoint` Edge, its
|
|
5
|
+
* name and its entrypoint View (L-SHELL-11). Replaced by the visitor's own Query (V2). Removal:
|
|
6
|
+
* Shell major (delivery step 5) after 1Pact step 4.
|
|
7
|
+
*/
|
|
8
|
+
export const legacyListEntrypoints = defineAction()('listEntrypoints', async ({ query }) => ({ entrypoints: [...(await query(legacyReadInstalledEntrypoints, {}))] }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readExtensions } from '../../queries/composition/index.js';
|
|
2
|
+
/** A composition deeper than this is refused as a defect rather than walked further. */
|
|
3
|
+
const MAXIMUM_DEPTH = 32;
|
|
4
|
+
/** Identities one frontier Query starts from. */
|
|
5
|
+
const FRONTIER = 256;
|
|
6
|
+
/**
|
|
7
|
+
* Every Identity reached from `start` along Kernel `extends_with`, with its Class: `outgoing`, what
|
|
8
|
+
* `start` holds; `incoming`, who holds `start`. The Kernel refuses cycles, so the walk ends.
|
|
9
|
+
*/
|
|
10
|
+
export async function walkComposition({ query }, start, direction) {
|
|
11
|
+
const reached = new Map();
|
|
12
|
+
let frontier = [start];
|
|
13
|
+
for (let depth = 0; frontier.length > 0; depth += 1) {
|
|
14
|
+
if (depth === MAXIMUM_DEPTH)
|
|
15
|
+
throw new RangeError('The composition is deeper than Shell walks.');
|
|
16
|
+
const next = [];
|
|
17
|
+
for (let index = 0; index < frontier.length; index += FRONTIER) {
|
|
18
|
+
const [first, ...others] = frontier.slice(index, index + FRONTIER);
|
|
19
|
+
if (first === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
const edges = await query(readExtensions, {
|
|
22
|
+
identities: [first, ...others],
|
|
23
|
+
direction,
|
|
24
|
+
});
|
|
25
|
+
for (const edge of edges) {
|
|
26
|
+
const [far, farClass] = direction === 'outgoing'
|
|
27
|
+
? [edge.target, edge.targetClass]
|
|
28
|
+
: [edge.source, edge.sourceClass];
|
|
29
|
+
if (far === start || reached.has(far))
|
|
30
|
+
continue;
|
|
31
|
+
reached.set(far, farClass);
|
|
32
|
+
next.push(far);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
frontier = next;
|
|
36
|
+
}
|
|
37
|
+
return reached;
|
|
38
|
+
}
|
|
39
|
+
/** The `extends_with` Edges into one Identity: its direct members. */
|
|
40
|
+
export async function readMembers({ query }, identity) {
|
|
41
|
+
return query(readExtensions, { identities: [identity], direction: 'incoming' });
|
|
42
|
+
}
|
|
43
|
+
/** The Identities one Identity extends directly. */
|
|
44
|
+
export async function readExtensionTargets({ query }, identity) {
|
|
45
|
+
const edges = await query(readExtensions, { identities: [identity], direction: 'outgoing' });
|
|
46
|
+
return new Set(edges.map(({ target }) => target));
|
|
47
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readRoleNodes } from '../../queries/composition/index.js';
|
|
2
|
+
import { readIdentities } from '../../queries/identity/index.js';
|
|
3
|
+
import { readRoleNode } from '../../queries/role/index.js';
|
|
4
|
+
import { walkComposition } from './composition.js';
|
|
5
|
+
/** Kernel reader and writer and their parts: every other Kernel Role administers (U4). */
|
|
6
|
+
const KERNEL_ORDINARY = new Set([
|
|
7
|
+
'reader',
|
|
8
|
+
'writer',
|
|
9
|
+
'graph.reader',
|
|
10
|
+
'graph.writer',
|
|
11
|
+
'graph.manager',
|
|
12
|
+
'blob.reader',
|
|
13
|
+
'blob.writer',
|
|
14
|
+
'blob.manager',
|
|
15
|
+
'view.reader',
|
|
16
|
+
'domain.reader',
|
|
17
|
+
]);
|
|
18
|
+
const SHELL_ADMINISTRATIVE = new Set([
|
|
19
|
+
'workspace.admin',
|
|
20
|
+
'user.writer',
|
|
21
|
+
'group.writer',
|
|
22
|
+
'directory.provisioner',
|
|
23
|
+
]);
|
|
24
|
+
const keyOf = (ref) => `${ref.origin}:${ref.name}`;
|
|
25
|
+
/** One handler's view of authority; each composition and the Role catalogue is read once. */
|
|
26
|
+
export function authority(capabilities) {
|
|
27
|
+
const S = capabilities.domain;
|
|
28
|
+
const kernel = S.dependencies.kernel.origin;
|
|
29
|
+
let catalogue;
|
|
30
|
+
const roles = () => (catalogue ??= capabilities.query(readRoleNodes, {}));
|
|
31
|
+
const compositions = new Map();
|
|
32
|
+
const composition = (identity) => {
|
|
33
|
+
let walked = compositions.get(identity);
|
|
34
|
+
if (walked === undefined) {
|
|
35
|
+
walked = walkComposition(capabilities, identity, 'outgoing');
|
|
36
|
+
compositions.set(identity, walked);
|
|
37
|
+
}
|
|
38
|
+
return walked;
|
|
39
|
+
};
|
|
40
|
+
const administrative = async () => {
|
|
41
|
+
const { byKey } = await roles();
|
|
42
|
+
const ids = new Set();
|
|
43
|
+
for (const [key, id] of byKey) {
|
|
44
|
+
const [origin, name] = [key.slice(0, key.indexOf(':')), key.slice(key.indexOf(':') + 1)];
|
|
45
|
+
if (origin === S.origin && SHELL_ADMINISTRATIVE.has(name))
|
|
46
|
+
ids.add(id);
|
|
47
|
+
if (origin === kernel && !KERNEL_ORDINARY.has(name))
|
|
48
|
+
ids.add(id);
|
|
49
|
+
}
|
|
50
|
+
return ids;
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
async roleNode(role) {
|
|
54
|
+
return (await roles()).byKey.get(keyOf(S.core.nodes[role].ref));
|
|
55
|
+
},
|
|
56
|
+
async callerHolds(role) {
|
|
57
|
+
const caller = capabilities.graph.caller;
|
|
58
|
+
if (caller === undefined)
|
|
59
|
+
return false;
|
|
60
|
+
return caller.query(readRoleNode, { role: S.core.nodes[role].ref });
|
|
61
|
+
},
|
|
62
|
+
async heldRole(identity) {
|
|
63
|
+
const { byKey } = await roles();
|
|
64
|
+
const reached = await composition(identity);
|
|
65
|
+
const held = [...byKey].filter(([, id]) => reached.has(id)).map(([key]) => key);
|
|
66
|
+
const admin = keyOf(S.core.nodes['workspace.admin'].ref);
|
|
67
|
+
const role = held.includes(admin) ? admin : held[0];
|
|
68
|
+
if (role === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
return role.startsWith(`${S.origin}:`) ? role.slice(S.origin.length + 1) : role;
|
|
71
|
+
},
|
|
72
|
+
async holdsAdministration(identity) {
|
|
73
|
+
const ids = await administrative();
|
|
74
|
+
for (const reached of (await composition(identity)).keys())
|
|
75
|
+
if (ids.has(reached))
|
|
76
|
+
return true;
|
|
77
|
+
return false;
|
|
78
|
+
},
|
|
79
|
+
async reaches(from, to) {
|
|
80
|
+
return from === to || (await composition(from)).has(to);
|
|
81
|
+
},
|
|
82
|
+
async activeAdministrators() {
|
|
83
|
+
const admin = (await roles()).byKey.get(keyOf(S.core.nodes['workspace.admin'].ref));
|
|
84
|
+
if (admin === undefined)
|
|
85
|
+
return [];
|
|
86
|
+
const holders = [...(await walkComposition(capabilities, admin, 'incoming')).keys()];
|
|
87
|
+
const [first, ...others] = holders;
|
|
88
|
+
if (first === undefined)
|
|
89
|
+
return [];
|
|
90
|
+
const accounts = await capabilities.query(readIdentities, {
|
|
91
|
+
kind: 'User',
|
|
92
|
+
ids: [first, ...others],
|
|
93
|
+
});
|
|
94
|
+
const frozen = S.classes.User.properties.frozen.key;
|
|
95
|
+
return accounts.filter(({ props }) => props[frozen] !== true).map(({ id }) => id);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineAction } from '@astrale-os/sdk/action';
|
|
2
|
+
import { toGroup } from '../group/index.js';
|
|
3
|
+
import { toAccount, userErrors, writeAccount } from '../user/index.js';
|
|
4
|
+
import { updateGroup } from '../../mutations/identity/index.js';
|
|
5
|
+
import { readIdentities } from '../../queries/identity/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* The directory is disconnected (`dsync.deleted`): its accounts and groups become local. Their
|
|
8
|
+
* `directory` and `externalId` are erased; an account the directory had frozen stays frozen. Replaying it changes nothing (A3).
|
|
9
|
+
*/
|
|
10
|
+
export const disconnectDirectory = defineAction()('disconnectDirectory', { errors: userErrors }, async (context) => {
|
|
11
|
+
const { directory } = context.input;
|
|
12
|
+
const [users, groups] = await Promise.all([
|
|
13
|
+
context.query(readIdentities, { kind: 'User', where: { directory } }),
|
|
14
|
+
context.query(readIdentities, { kind: 'Group', where: { directory } }),
|
|
15
|
+
]);
|
|
16
|
+
for (const node of users) {
|
|
17
|
+
const account = toAccount(context.domain, node);
|
|
18
|
+
const written = await writeAccount(context, account, {
|
|
19
|
+
directory: null,
|
|
20
|
+
externalId: null,
|
|
21
|
+
});
|
|
22
|
+
if (written === 'DUPLICATE_KEY' || written === 'CHANGED') {
|
|
23
|
+
throw context.error('ACCOUNT_CHANGED', {});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const node of groups) {
|
|
27
|
+
const group = toGroup(context.domain, node);
|
|
28
|
+
await context.mutate(updateGroup, {
|
|
29
|
+
group: group.id,
|
|
30
|
+
set: {},
|
|
31
|
+
unset: ['directory', 'externalId'],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return { users: users.length, groups: groups.length };
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { disconnectDirectory } from './disconnect-directory.js';
|
|
@@ -1,31 +1,65 @@
|
|
|
1
1
|
import { setGroupMembership } from '../../mutations/group/index.js';
|
|
2
|
-
import { readGroupMembership, readGroupMembershipEndpoints
|
|
2
|
+
import { readGroupMembership, readGroupMembershipEndpoints } from '../../queries/group/index.js';
|
|
3
3
|
import { canonicalNodeId } from '../../utils/canonical-node-path/index.js';
|
|
4
|
-
|
|
4
|
+
import { readGroup } from './group.js';
|
|
5
|
+
// Seam L-SHELL-8, L-SHELL-9: legacy groups also keep `member_of_group`; a Domain's own Group
|
|
6
|
+
// subclass changes its members under that Domain's authority.
|
|
7
|
+
import { legacyMembershipEdges, legacyChangeForeignMembership } from './legacy/index.js';
|
|
8
|
+
import { membershipRefusal } from './membership-laws.js';
|
|
9
|
+
/**
|
|
10
|
+
* Add or withdraw one direct member of a Shell group: Kernel `extends_with` from the member to the
|
|
11
|
+
* group (G1), under G2, G3 and acyclicity. The write converges what it observed, guarded by it;
|
|
12
|
+
* after an unknown outcome it observes again, and an unconfirmed outcome stays explicit.
|
|
13
|
+
*/
|
|
5
14
|
export async function changeGroupMembership(context, present) {
|
|
6
15
|
const invalid = () => context.error('INVALID_GROUP_MEMBERSHIP_REFERENCE', {});
|
|
7
16
|
const membership = {
|
|
8
17
|
member: canonicalNodeId(context.input.member, invalid),
|
|
9
18
|
group: canonicalNodeId(context.input.group, invalid),
|
|
10
19
|
};
|
|
20
|
+
const endpoints = await context.step.run('observe-endpoints', () => context.query(readGroupMembershipEndpoints, membership));
|
|
21
|
+
if (!endpoints.visible)
|
|
22
|
+
throw context.error('GROUP_MEMBERSHIP_ENDPOINTS_UNAVAILABLE', {});
|
|
23
|
+
if (endpoints.groupClass !== context.domain.classes.Group.key) {
|
|
24
|
+
return legacyChangeForeignMembership(context, membership, present);
|
|
25
|
+
}
|
|
26
|
+
const refusal = await context.step.run('check-laws', async () => {
|
|
27
|
+
const group = await readGroup(context, membership.group);
|
|
28
|
+
if (group === undefined)
|
|
29
|
+
return { code: 'GROUP_NOT_FOUND' };
|
|
30
|
+
return (await membershipRefusal(context, group, membership.member, present)) ?? null;
|
|
31
|
+
});
|
|
32
|
+
if (refusal !== null) {
|
|
33
|
+
if (refusal.code === 'GROUP_ROLE_PROTECTED') {
|
|
34
|
+
throw context.error(refusal.code, { role: refusal.role });
|
|
35
|
+
}
|
|
36
|
+
throw context.error(refusal.code, {});
|
|
37
|
+
}
|
|
11
38
|
const observe = async () => {
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
39
|
+
const current = await context.query(readGroupMembershipEndpoints, membership);
|
|
40
|
+
if (!current.visible)
|
|
14
41
|
throw context.error('GROUP_MEMBERSHIP_ENDPOINTS_UNAVAILABLE', {});
|
|
15
|
-
const
|
|
16
|
-
|
|
42
|
+
const authority = await context.query(readGroupMembership, membership);
|
|
43
|
+
const edges = [
|
|
44
|
+
{ edge: 'authority', current: authority.present, present, guard: authority.guard },
|
|
45
|
+
...(await legacyMembershipEdges(context, membership, present)),
|
|
46
|
+
];
|
|
47
|
+
return { endpoints: current.guard, edges };
|
|
17
48
|
};
|
|
49
|
+
const matches = (edges) => edges.every(({ current, present: wanted }) => current === wanted);
|
|
18
50
|
const current = await context.step.run('observe-membership', observe);
|
|
19
|
-
if (matches(current
|
|
51
|
+
if (matches(current.edges))
|
|
20
52
|
return {};
|
|
21
|
-
const result = await context.step.run('mutate-membership', () => context.mutate(setGroupMembership, { ...membership, current
|
|
53
|
+
const result = await context.step.run('mutate-membership', () => context.mutate(setGroupMembership, { ...membership, ...current }));
|
|
22
54
|
if (result.ok)
|
|
23
55
|
return {};
|
|
24
56
|
if (result.outcomeKnown) {
|
|
57
|
+
if (result.rule === 'cycle')
|
|
58
|
+
throw context.error('GROUP_MEMBERSHIP_CYCLE', {});
|
|
25
59
|
throw context.error('GROUP_MEMBERSHIP_CONFLICT', { cause: result.code });
|
|
26
60
|
}
|
|
27
61
|
const recovered = await context.step.run('recover-membership', observe);
|
|
28
|
-
if (matches(recovered
|
|
62
|
+
if (matches(recovered.edges))
|
|
29
63
|
return {};
|
|
30
64
|
throw context.error('GROUP_MEMBERSHIP_UNCONFIRMED', {
|
|
31
65
|
cause: result.code,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineAction } from '@astrale-os/sdk/action';
|
|
2
|
+
import { Path } from '@astrale-os/sdk/graph/path';
|
|
3
|
+
import { createGroup } from '../../mutations/identity/index.js';
|
|
4
|
+
import { MAX_ATTRIBUTES, boundedAttributes } from '../../utils/index.js';
|
|
5
|
+
import { groupErrors } from './group-errors.js';
|
|
6
|
+
/** Create a local group. An empty group holds nothing: creating one grants no right. */
|
|
7
|
+
export const create = defineAction()('Group.create', { errors: groupErrors }, async ({ input, mutate, error }) => {
|
|
8
|
+
const attributes = input.attributes === undefined ? undefined : boundedAttributes(input.attributes);
|
|
9
|
+
if (input.attributes !== undefined && attributes === undefined) {
|
|
10
|
+
throw error('GROUP_ATTRIBUTES_TOO_MANY', { max: MAX_ATTRIBUTES });
|
|
11
|
+
}
|
|
12
|
+
const result = await mutate(createGroup, {
|
|
13
|
+
values: {
|
|
14
|
+
name: input.name,
|
|
15
|
+
...(input.description === undefined ? {} : { description: input.description }),
|
|
16
|
+
...(input.icon === undefined || input.icon === null ? {} : { icon: input.icon }),
|
|
17
|
+
...(attributes === undefined ? {} : { attributes }),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (!result.ok)
|
|
21
|
+
throw error('GROUP_CHANGED', {});
|
|
22
|
+
return { group: Path.id(result.group).raw };
|
|
23
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineAction } from '@astrale-os/sdk/action';
|
|
2
|
+
import { readMembers } from '../authority/index.js';
|
|
3
|
+
import { deleteIdentity } from '../../mutations/identity/index.js';
|
|
4
|
+
import { readProfile } from '../../queries/profile/index.js';
|
|
5
|
+
import { groupErrors } from './group-errors.js';
|
|
6
|
+
import { isProvidedGroup, readGroup } from './group.js';
|
|
7
|
+
import { roleProtected } from './membership-laws.js';
|
|
8
|
+
/**
|
|
9
|
+
* Withdraw a local group's memberships, incoming and outgoing, and delete it (G4). Only its directory
|
|
10
|
+
* deletes a provided group (G2); only an admin deletes a group that holds a Role (G3).
|
|
11
|
+
*/
|
|
12
|
+
export const remove = defineAction()('Group.delete', { errors: groupErrors }, async (context) => {
|
|
13
|
+
const { error, self } = context;
|
|
14
|
+
const group = await readGroup(context, self);
|
|
15
|
+
if (group === undefined)
|
|
16
|
+
throw error('GROUP_NOT_FOUND', {});
|
|
17
|
+
if (isProvidedGroup(group))
|
|
18
|
+
throw error('GROUP_DIRECTORY_MANAGED', {});
|
|
19
|
+
const role = await roleProtected(context, group);
|
|
20
|
+
if (role !== undefined)
|
|
21
|
+
throw error('GROUP_ROLE_PROTECTED', { role });
|
|
22
|
+
const members = await readMembers(context, self);
|
|
23
|
+
const profile = await context.query(readProfile, { kind: 'Group', node: self });
|
|
24
|
+
const result = await context.mutate(deleteIdentity, {
|
|
25
|
+
kind: 'Group',
|
|
26
|
+
node: self,
|
|
27
|
+
members,
|
|
28
|
+
...(profile === undefined ? {} : { profile }),
|
|
29
|
+
});
|
|
30
|
+
if (!result.ok)
|
|
31
|
+
throw error('GROUP_CHANGED', {});
|
|
32
|
+
return {};
|
|
33
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineAction } from '@astrale-os/sdk/action';
|
|
2
|
+
import { readMembers } from '../authority/index.js';
|
|
3
|
+
import { deleteIdentity } from '../../mutations/identity/index.js';
|
|
4
|
+
import { readProfile } from '../../queries/profile/index.js';
|
|
5
|
+
import { groupErrors } from './group-errors.js';
|
|
6
|
+
import { findGroup } from './group.js';
|
|
7
|
+
/**
|
|
8
|
+
* The directory deleted the group (`dsync.group.deleted`): its memberships leave and it is deleted
|
|
9
|
+
* (G4). A group the directory no longer has is left as it is (A3).
|
|
10
|
+
*/
|
|
11
|
+
export const deprovision = defineAction()('Group.deprovision', { errors: groupErrors }, async (context) => {
|
|
12
|
+
const { input, error } = context;
|
|
13
|
+
const group = await findGroup(context, input);
|
|
14
|
+
if (group === undefined)
|
|
15
|
+
return {};
|
|
16
|
+
const members = await readMembers(context, group.id);
|
|
17
|
+
const profile = await context.query(readProfile, { kind: 'Group', node: group.id });
|
|
18
|
+
const result = await context.mutate(deleteIdentity, {
|
|
19
|
+
kind: 'Group',
|
|
20
|
+
node: group.id,
|
|
21
|
+
members,
|
|
22
|
+
...(profile === undefined ? {} : { profile }),
|
|
23
|
+
});
|
|
24
|
+
if (!result.ok)
|
|
25
|
+
throw error('GROUP_CHANGED', {});
|
|
26
|
+
return {};
|
|
27
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const none = z.strictObject({});
|
|
3
|
+
/**
|
|
4
|
+
* The refusals of the group methods. The SDK declares no FORBIDDEN business family, so a law that
|
|
5
|
+
* forbids a change for the state of the group or its caller answers CONFLICT.
|
|
6
|
+
*/
|
|
7
|
+
export const groupErrors = {
|
|
8
|
+
GROUP_NOT_FOUND: {
|
|
9
|
+
family: 'NOT_FOUND',
|
|
10
|
+
message: 'No group is at this path.',
|
|
11
|
+
details: none,
|
|
12
|
+
},
|
|
13
|
+
GROUP_ATTRIBUTES_TOO_MANY: {
|
|
14
|
+
family: 'VALIDATION_ERROR',
|
|
15
|
+
message: 'A group carries at most 64 attributes.',
|
|
16
|
+
details: z.strictObject({ max: z.number() }),
|
|
17
|
+
},
|
|
18
|
+
GROUP_DIRECTORY_MANAGED: {
|
|
19
|
+
family: 'CONFLICT',
|
|
20
|
+
message: "This group is managed by your organization's directory. Change it in the directory.",
|
|
21
|
+
details: none,
|
|
22
|
+
},
|
|
23
|
+
GROUP_NOT_DIRECTORY_MANAGED: {
|
|
24
|
+
family: 'CONFLICT',
|
|
25
|
+
message: 'This group is local: only group.writer changes its members.',
|
|
26
|
+
details: none,
|
|
27
|
+
},
|
|
28
|
+
GROUP_ROLE_PROTECTED: {
|
|
29
|
+
family: 'CONFLICT',
|
|
30
|
+
message: 'This group grants a role. Only an administrator may change its members.',
|
|
31
|
+
details: z.strictObject({ role: z.string() }),
|
|
32
|
+
},
|
|
33
|
+
GROUP_MEMBERSHIP_CYCLE: {
|
|
34
|
+
family: 'CONFLICT',
|
|
35
|
+
message: 'This member already holds the group: the membership would close a cycle.',
|
|
36
|
+
details: none,
|
|
37
|
+
},
|
|
38
|
+
GROUP_CHANGED: {
|
|
39
|
+
family: 'CONFLICT',
|
|
40
|
+
message: 'The group changed concurrently; retry the operation.',
|
|
41
|
+
details: none,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { groupErrors } from './group-errors.js';
|
|
2
3
|
export const groupMembershipErrors = {
|
|
4
|
+
...groupErrors,
|
|
3
5
|
GROUP_MEMBERSHIP_ENDPOINTS_UNAVAILABLE: {
|
|
4
6
|
family: 'CONFLICT',
|
|
5
7
|
message: 'Shell must be able to traverse both membership endpoints before confirming membership.',
|