@owlmeans/iam 0.1.18-rc.45 → 0.1.18-rc.47
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 +9 -7
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/iam/SKILL.md +196 -75
- package/build/consts.d.ts +57 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +59 -0
- package/build/consts.js.map +1 -1
- package/build/errors.d.ts +13 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +21 -0
- package/build/errors.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/runtime.d.ts +187 -0
- package/build/runtime.d.ts.map +1 -0
- package/build/runtime.js +244 -0
- package/build/runtime.js.map +1 -0
- package/build/types.d.ts +251 -34
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +21 -4
- package/build/utils.d.ts.map +1 -1
- package/build/utils.js +26 -15
- package/build/utils.js.map +1 -1
- package/package.json +7 -3
- package/src/consts.ts +64 -0
- package/src/errors.ts +25 -0
- package/src/index.ts +1 -0
- package/src/runtime.ts +440 -0
- package/src/types.ts +289 -35
- package/src/utils.ts +35 -19
- package/tests/runtime.spec.ts +101 -0
- package/tests/tsconfig.json +12 -0
- package/tests/utils.spec.ts +47 -1
package/README.md
CHANGED
|
@@ -6,14 +6,16 @@ Provider-agnostic IAM abstraction for the OwlMeans framework — `IamService` in
|
|
|
6
6
|
|
|
7
7
|
- `IamService` — unified interface for provisioning clients, realms, and permissions across IAM backends (e.g. Keycloak, built-in)
|
|
8
8
|
- `IamClient` / `IamCredentialsPair` / `IamResourceSpec` — shared contract types used by server-side IAM implementations
|
|
9
|
-
- `IamPermissionArgs` — describes a named permission
|
|
10
|
-
- `
|
|
11
|
-
-
|
|
9
|
+
- `IamPermissionArgs` — describes a named permission: resource and organization binding (`resourceScoped`, `entityScoped`), title, area and `defaultClass`
|
|
10
|
+
- Tenancy facets on `IamService` — `organizations`, `groups`, `subjects` — and `unsupportedFacet` for a backend without them
|
|
11
|
+
- `makeIamRuntimeProtocols` — declarations of the provider's runtime IAM API (organizations, members, permissions, grants)
|
|
12
|
+
- `hasPermission` — utility to check whether a set of grants covers a given permission, resource and acting organization
|
|
13
|
+
- Errors: `IamError` and subtypes for client, permission, user, organization and group failures
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
bun add @owlmeans/iam@^0.1.18-rc.
|
|
18
|
+
bun add @owlmeans/iam@^0.1.18-rc.47
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
## Usage
|
|
@@ -22,8 +24,8 @@ bun add @owlmeans/iam@^0.1.18-rc.45
|
|
|
22
24
|
import type { IamService, IamClient } from '@owlmeans/iam'
|
|
23
25
|
import { hasPermission } from '@owlmeans/iam'
|
|
24
26
|
|
|
25
|
-
// Check whether an auth payload
|
|
26
|
-
const allowed = hasPermission(auth, {
|
|
27
|
+
// Check whether an auth payload grants a permission for one record, in the organization the request acts in
|
|
28
|
+
const allowed = hasPermission(auth, 'order--edit', { resourceId: 'ord-1', entitySlug: 'acme' })
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
Typically consumed via `@owlmeans/server-iam` which wires the concrete OIDC-backed implementation.
|
|
@@ -36,7 +38,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
36
38
|
your project's skill store (`.agents/skills/`):
|
|
37
39
|
|
|
38
40
|
```sh
|
|
39
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
41
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.46
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/iam",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.47",
|
|
5
|
+
"generatedAt": "2026-10-03T11:19:50.779Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: iam
|
|
3
|
-
description: "How to use @owlmeans/iam — the provider-agnostic IamService, the permission-definition
|
|
3
|
+
description: "How to use @owlmeans/iam — the provider-agnostic IamService, the permission-definition model with its four kinds (unbound, entity-bound, resource-bound, both) and default classes, the organization/group/subject facets, the runtime IAM API declarations, client-id uniqueness, issuer and redirect-URI rules, and hasPermission. Load when wiring IAM operations, granting or revoking permissions, working with tenanted clients, selecting the IAM backend, or implementing a new IAM provider. Applies to files matching **/services/iam*.ts, **/context.ts, **/types.ts."
|
|
4
4
|
metadata:
|
|
5
5
|
applyTo: "**/services/iam*.ts, **/context.ts, **/types.ts"
|
|
6
6
|
---
|
|
@@ -8,57 +8,43 @@ metadata:
|
|
|
8
8
|
|
|
9
9
|
# Using `@owlmeans/iam`
|
|
10
10
|
|
|
11
|
-
**Install:** `"@owlmeans/iam": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/iam": "^0.1.18-rc.47"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
Provider-agnostic IAM abstraction: the `IamService` interface, the permission and grant shapes, the
|
|
14
|
-
gate-param grammar,
|
|
14
|
+
gate-param grammar, `hasPermission`, and the declarations of the provider's runtime IAM API. It
|
|
15
|
+
contains no implementation and talks to no provider —
|
|
15
16
|
concrete backends (a Keycloak proxy, an integrated backend built on the OwlMeans OIDC provider) are
|
|
16
17
|
separate packages, and every consumer depends on this interface rather than on one of them.
|
|
17
18
|
|
|
18
|
-
##
|
|
19
|
+
## Where the halves live
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|--------|-------------|
|
|
26
|
-
| `appendIam(context, opts?)` | Wires the OIDC guard onto a web context **and** installs the consent-before-sign-in precondition |
|
|
27
|
-
| `withIamGuard(protocolTree, coguards?)` | Returns a decorated immutable protocol tree with IAM's OIDC guard applied |
|
|
28
|
-
| `iamEntrypoints(dispatcherProps?)` | Browser-local OIDC bindings and dispatcher-screen binding |
|
|
29
|
-
| `requireConsentForLogin(ctx, opts?)` | That precondition on its own, for a context wired some other way |
|
|
30
|
-
| `CONSENT_LOGIN_PRECONDITION` / `CLIENT_IAM_SERVICE` | The precondition alias and the service alias |
|
|
31
|
-
| `useLogin` / `useLogout`, `LoginOutcome` / `LoginIntent`, `LoginPlugin` / `LoginService` / … | Re-exported sign-in surface, so an app has one IAM import rather than three |
|
|
32
|
-
| Everything from `@owlmeans/iam` | The types above, plus `hasPermission` |
|
|
33
|
-
|
|
34
|
-
The protocol tree is immutable. Decorate it once with `withIamGuard(protocols)` before binding local
|
|
35
|
-
screens and API callers, then spread `iamEntrypoints()` once into that browser binding list. The
|
|
36
|
-
helper deliberately does not add declarations to a flattened entrypoint array.
|
|
37
|
-
|
|
38
|
-
And the consent precondition sits on `LoginService.begin` rather than on a hook, a screen or a login
|
|
39
|
-
plugin, because `begin` is the single funnel every sign-in mechanic passes through and the one place
|
|
40
|
-
the user's gesture is still live — a refusal resolves as `LoginOutcome.Gesture` and opens the consent dialog in
|
|
41
|
-
the same gesture. An application that genuinely sets no cookie passes `{ consent: { disabled: true } }`.
|
|
42
|
-
|
|
43
|
-
The server half of the same story is `@owlmeans/server-iam` — the IAM gate that asserts the grants
|
|
44
|
-
this package's screens hand out. Permission decisions are made there, never in the browser.
|
|
21
|
+
This package is contracts only. The browser half — `appendIam`, the session's organizations and the
|
|
22
|
+
switch, `hasPermission` re-exported — is `@owlmeans/client-iam` (`client-iam` skill); the server half
|
|
23
|
+
— the IAM gate, the session's organizations on the server and the runtime IAM client — is
|
|
24
|
+
`@owlmeans/server-iam` (`server-iam` skill). Permission decisions are made on the server, never in
|
|
25
|
+
the browser.
|
|
45
26
|
|
|
46
27
|
## Public API surface
|
|
47
28
|
|
|
48
29
|
| Symbol | Kind | Purpose |
|
|
49
30
|
|--------|------|---------|
|
|
50
31
|
| `IamService` | type | Unified IAM interface — all provisioning + authorization operations |
|
|
51
|
-
| `IamClient` | type | Provisioned OIDC client `{ id?, clientId, secret?, name?, realm? }` |
|
|
52
|
-
| `
|
|
32
|
+
| `IamClient` | type | Provisioned OIDC client `{ id?, clientId, secret?, name?, realm?, config? }` |
|
|
33
|
+
| `IamClientInfo` | type | `IamClient` without `secret` — what `getClient` answers |
|
|
34
|
+
| `IamClientConfig` | type | `{ operators?, users?, [key]: unknown }` — the two tenancy flags plus the client's arbitrary configuration |
|
|
35
|
+
| `IamClientOptions` | type | `{ redirectUris?, config? }` — explicit `ensureClient` hardening (omitting `redirectUris` is a keycloak-only legacy shape, see below); `config` merges shallowly |
|
|
53
36
|
| `IamCredentialsPair` | type | `{ token: string; realm: string }` |
|
|
54
|
-
| `IamPermissionArgs` | type | `{ permission?, resourceScoped?, title?, area?, managed?,
|
|
37
|
+
| `IamPermissionArgs` | type | `{ permission?, resourceScoped?, entityScoped?, title?, area?, managed?, defaultClass? }` — `permission` absent means unscoped resource name; omitted flags are kept, passed ones (`false` / `None` included) written |
|
|
55
38
|
| `IamResourceSpec` | type | `{ name: string; displayName?: string }` |
|
|
56
|
-
| `IamPermissionDefinition` | type | Declared permission `{ name, resource, action?, resourceScoped?, title?, area?, managed?,
|
|
57
|
-
| `IamPermissionFilter` | type | `{ areas?, managed?, resourceScoped? }` — narrows `listPermissions`; an `areas` entry of `null` matches untagged definitions |
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
39
|
+
| `IamPermissionDefinition` | type | Declared permission `{ name, resource, action?, resourceScoped?, entityScoped?, title?, area?, managed?, defaultClass? }` |
|
|
40
|
+
| `IamPermissionFilter` | type | `{ areas?, managed?, resourceScoped?, entityScoped? }` — narrows `listPermissions`; an `areas` entry of `null` matches untagged definitions |
|
|
41
|
+
| `IamDefaultClass` | enum | `None` / `User` / `Member` / `Owner` — who holds a definition without a grant (see "Permission kinds") |
|
|
42
|
+
| `IamGrantArgs` | type | `{ resources?, mode?, entitySlug? }` — `entitySlug` binds an `entityScoped` grant |
|
|
43
|
+
| `IamGrant` | type | `{ profileId? \| group?, clientId, permission, resources?, mode?, entitySlug?, origin?, through? }` — exactly one subject |
|
|
44
|
+
| `IamGrantOrigin` | enum | `Direct` / `Default` / `Group` — why a listed grant is held; `through` names the group |
|
|
45
|
+
| `IamGroupRef` | type | `{ entitySlug, key }` — a group by its organization's slug and its key |
|
|
60
46
|
| `IamGrantMode` | enum | `Blanket` / `Resources` / `All` — which FORM of a grant an operation addresses |
|
|
61
|
-
| `IamGrantBundle` | type | `{ filter?, permissions?, mode?, resources? }` — what `grantBundle` hands out |
|
|
47
|
+
| `IamGrantBundle` | type | `{ filter?, permissions?, mode?, resources?, entitySlug? }` — what `grantBundle` hands out |
|
|
62
48
|
| `IamRemovalPolicy` | enum | `Cascade` / `Refuse` — what to do about grants of a definition being deleted |
|
|
63
49
|
| `IamPermissionDeleteArgs`, `IamPermissionRemoval` | type | `{ policy?, managed? }` and `{ permission, clientId, found, revoked }` |
|
|
64
50
|
| `IamNormalizeArgs`, `IamNormalizationReport` | type | `{ dryRun? }` and the rename/merge plan `normalizePermissions` reports |
|
|
@@ -66,11 +52,24 @@ this package's screens hand out. Permission decisions are made there, never in t
|
|
|
66
52
|
| `PERMISSION_ACTION_SEPARATOR` | const | `'--'` — TWO hyphens, never one |
|
|
67
53
|
| `parsePermissionName` / `composePermissionName` / `isPermissionName` | fn | Read, build and validate a permission NAME |
|
|
68
54
|
| `ParsedPermissionName` | type | `{ name, resource, action?, problem? }` |
|
|
69
|
-
| `IamUser` | type |
|
|
55
|
+
| `IamUser` | type | Subject of one client `{ profileId, email?, name?, role, disabled?, grantCount?, home?, memberships? }` |
|
|
56
|
+
| `IamMembershipInfo` | type | `{ entitySlug, title?, owner, groups, home? }` — one organization of an `IamUser` |
|
|
70
57
|
| `IamUserInvite` | type | `{ email, name?, role? }` — find-or-create args |
|
|
71
|
-
| `IamUserUpdate` | type | `{ name?, role?, disabled? }` |
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
58
|
+
| `IamUserUpdate` | type | `{ name?, role?, disabled? }` — `disabled` lands on the primary row |
|
|
59
|
+
| `IamOrganization`, `IamOrganizationArgs`, `IamOrganizationUpdate` | type | `{ entitySlug, title?, members? }`; create `{ title?, profileId? }` (the new owner); update `{ title }` |
|
|
60
|
+
| `IamMember`, `IamMemberInvite`, `IamMemberUpdate` | type | `{ profileId, email?, name?, owner, groups, managed? }`; `{ email, name?, owner? }`; `{ owner?, groups? }` |
|
|
61
|
+
| `IamGroup`, `IamGroupArgs`, `IamGroupBundle` | type | `{ entitySlug, key, title?, managed?, members?, bundles }`; `{ title?, bundles? }`; `{ filter?, permissions? }` |
|
|
62
|
+
| `IamStaffSyncArgs`, `IamStaffSyncReport` | type | `{ bundles }` and `{ added, removed }` (profile ids) of `syncStaff` |
|
|
63
|
+
| `IamSubject`, `IamSignInArgs`, `IamSignInResult` | type | `{ profileId, email?, name?, organizations, permissions }` (claims input); `{ email, name? }`; `{ accountId, profileId }` |
|
|
64
|
+
| `IamOrganizationFacet`, `IamGroupFacet`, `IamSubjectFacet` | type | The three facets of `IamService` |
|
|
65
|
+
| `unsupportedFacet<T>(what)` | fn | A facet whose every method throws `IamUnsupported(what)` — synchronously |
|
|
66
|
+
| `IAM_MEMBERS_GROUP` | const | `'members'` — the managed staff group `syncStaff` keeps |
|
|
67
|
+
| `IAM_EMAIL_PROOF` | const | `'proof:email'` — the `profileId` of a credential that proves an address and identifies nobody yet |
|
|
68
|
+
| `hasPermission` | fn | `(auth, permission, { scope?, resourceId?, entitySlug? }?)` — checks `Authorization.permissions`; an unscoped set satisfies a resourceId check, an unbound set any organization |
|
|
69
|
+
| `HasPermissionOptions` | type | `{ scope?, resourceId?, entitySlug? }` |
|
|
70
|
+
| `makeIamRuntimeProtocols({ service, path? })` | fn | The runtime IAM API's protocol tree — see "Runtime IAM API" |
|
|
71
|
+
| `IAM_RUNTIME_GUARD`, `IAM_RUNTIME_PATH`, `IAM_RUNTIME_ROUTES` | const | Its guard alias, default base path (`'/runtime'`) and leaf-path table |
|
|
72
|
+
| `IamRuntime*` | type/schema | Its wire shapes and closed schemas (`IamRuntimeOrganization`, `IamRuntimeMember`, `IamRuntimePermission`, `IamRuntimeGrant`, `IamRuntimeGrantRequest`, … and `*Schema`) |
|
|
74
73
|
| Gate grammar | fn/type | `parseGateParam`, `parseGateSelector`, `formatGateParam`, `resolveGateResource`, `validateGateParams`, `routeParamsOf`, plus `RESOURCE_*_SEPARATOR`, `GateParamSource`, `DEFAULT_GATE_PARAM_SOURCES`, `GateParamErrorCode`, `GateResolutionFailure` and the `Gate*` types. The syntax itself is documented in the `server-iam` skill |
|
|
75
74
|
| `DEFAULT_ALIAS` | const | Default service alias `'iam-service'` |
|
|
76
75
|
| `IAM_MODE_KEYCLOAK` | const | `'keycloak'` |
|
|
@@ -82,9 +81,11 @@ this package's screens hand out. Permission decisions are made there, never in t
|
|
|
82
81
|
| `IamGrantError` | class | Grant subject missing or entity mismatch |
|
|
83
82
|
| `IamPermissionError` | class | A refusal about a DEFINITION: `permission:held:<name>`, `permission:managed:<name>` |
|
|
84
83
|
| `IamUserError` | class | An end-user operation was refused |
|
|
84
|
+
| `IamOrganizationError` | class | An organization or membership refusal — unknown organization, last owner, membership cap |
|
|
85
|
+
| `IamGroupError` | class | A group refusal — unknown key, a managed group written by an operator, another app's group |
|
|
85
86
|
| `IamUnsupported` | class | Operation not supported by the active backend (e.g. `resource-scoped-grant`, `user-management`) |
|
|
86
87
|
|
|
87
|
-
## Permission model (
|
|
88
|
+
## Permission model (kinds and grant forms)
|
|
88
89
|
|
|
89
90
|
Permissions are declared per entity client (project) with `ensurePermission` and granted to end-user
|
|
90
91
|
subjects with `grantPermission`:
|
|
@@ -113,12 +114,44 @@ nothing at request time. `managed: true` marks a definition the platform owns: a
|
|
|
113
114
|
revoke it from an undifferentiated list, and a repair that deletes "definitions no declaration names"
|
|
114
115
|
must not reach one.
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
### Permission kinds
|
|
118
|
+
|
|
119
|
+
What a grant binds to is a property of the DEFINITION, and grants follow it:
|
|
120
|
+
|
|
121
|
+
| Kind | Definition flags | Grant stored on | Applies |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| unbound | none | the subject's primary row, or a group | everywhere |
|
|
124
|
+
| entity-bound | `entityScoped` | the subject's row of THAT organization, or a group of it | only while acting in that organization |
|
|
125
|
+
| resource-bound | `resourceScoped` | the primary row, a titled set with `resources` | to those resource ids |
|
|
126
|
+
| both | both | that organization's row, a titled set with `resources` | both checks |
|
|
127
|
+
|
|
128
|
+
- Granting an `entityScoped` definition REQUIRES `args.entitySlug`, which names the subject's row
|
|
129
|
+
there — the subject must be a member. Any other definition refuses `args.entitySlug`. Turning
|
|
130
|
+
`entityScoped` off while grants exist is refused: every bound grant would silently widen.
|
|
131
|
+
- Stored sets carry the organization's id; CLAIM sets (provider → relying party only) carry its
|
|
132
|
+
`entitySlug` instead (`OidcPermissionSetClaim`). The relying party keeps the unbound sets and the
|
|
133
|
+
acting organization's, strips the binding, and drops the rest — a browser token never carries a
|
|
134
|
+
bound set of another organization.
|
|
135
|
+
- `hasPermission(auth, name, { entitySlug })` applies a set without `entitySlug` everywhere and a
|
|
136
|
+
bound one only when `opts.entitySlug` names its organization — fail-closed, so a bound set that
|
|
137
|
+
leaked into a token never counts where the caller named no organization.
|
|
138
|
+
|
|
139
|
+
### Default classes
|
|
140
|
+
|
|
141
|
+
`defaultClass` says who holds a definition without an explicit grant. It is evaluated when claims and
|
|
142
|
+
grant listings are built — never written onto a row:
|
|
143
|
+
|
|
144
|
+
- `User` — every signed-in subject of the client. Requires an unbound, unmanaged `user`-area definition.
|
|
145
|
+
- `Member` / `Owner` — every member / owner of the organization the subject acts in. Requires
|
|
146
|
+
`entityScoped`.
|
|
147
|
+
- `None` — nobody.
|
|
148
|
+
|
|
149
|
+
A class default is a BLANKET set, so `Owner` on a resource-scoped definition is its all-records form.
|
|
150
|
+
An explicit `false` on a row suppresses only the class default of that binding (an unbound `false`
|
|
151
|
+
suppresses `User`; a `false` on the row of an organization suppresses `Member`/`Owner` there).
|
|
152
|
+
Grants are additive — there is no deny rule over a group's grant. A listing marks each grant's
|
|
153
|
+
`origin`: `Direct`, `Default`, or `Group` with `through` naming the group. A backend that cannot
|
|
154
|
+
evaluate classes (Keycloak) refuses any `defaultClass` other than `None` with `IamUnsupported`.
|
|
122
155
|
|
|
123
156
|
## `IamService` interface
|
|
124
157
|
|
|
@@ -135,10 +168,14 @@ interface IamService extends InitializedService {
|
|
|
135
168
|
// "A client id is a global name" for the two rules it has to obey.
|
|
136
169
|
ensureClient: (entityId: string, clientId: string, options?: IamClientOptions) => Promise<IamClient>
|
|
137
170
|
|
|
171
|
+
// The entity's client without its secret, or null. Never provisions.
|
|
172
|
+
getClient: (entityId: string, clientId: string) => Promise<IamClientInfo | null>
|
|
173
|
+
|
|
138
174
|
// Reserve a free client id without provisioning it; false when anyone else holds it.
|
|
139
175
|
claimClient: (entityId: string, clientId: string) => Promise<boolean>
|
|
140
176
|
|
|
141
|
-
// Release a client id and everything keyed by it
|
|
177
|
+
// Release a client id and everything keyed by it — its subjects' rows, its groups, its
|
|
178
|
+
// definitions (called when a project/slot is deleted).
|
|
142
179
|
deleteClient: (entityId: string, clientId: string) => Promise<void>
|
|
143
180
|
|
|
144
181
|
// Declare a permission. Returns the canonical name. MERGES into an existing definition —
|
|
@@ -155,14 +192,16 @@ interface IamService extends InitializedService {
|
|
|
155
192
|
// A rename and a merge — never a delete, never an invention.
|
|
156
193
|
normalizePermissions: (entityId: string, clientId: string, args?: IamNormalizeArgs) => Promise<IamNormalizationReport>
|
|
157
194
|
|
|
158
|
-
// Provision a resource and assign it to the tenant's owner role
|
|
195
|
+
// Provision a resource and assign it to the tenant's owner role (keycloak). A no-op on the
|
|
196
|
+
// integrated backend, which has no owner role to assign to.
|
|
159
197
|
ensureResourceOwnership: (entityId: string, clientId: string, resource: IamResourceSpec) => Promise<void>
|
|
160
198
|
|
|
161
199
|
// --- Authorization (permission definitions & grants) ---
|
|
162
200
|
|
|
163
201
|
listPermissions: (entityId: string, clientId: string, filter?: IamPermissionFilter) => Promise<IamPermissionDefinition[]>
|
|
164
202
|
|
|
165
|
-
// args.resources selects the resource-scoped form
|
|
203
|
+
// args.resources selects the resource-scoped form, args.entitySlug the binding of an entityScoped
|
|
204
|
+
// definition; see "Permission model (kinds and grant forms)"
|
|
166
205
|
grantPermission: (entityId: string, clientId: string, profileId: string, permission: string, args?: IamGrantArgs) => Promise<IamGrant>
|
|
167
206
|
revokePermission: (entityId: string, clientId: string, profileId: string, permission: string, args?: IamGrantArgs) => Promise<void>
|
|
168
207
|
|
|
@@ -171,30 +210,108 @@ interface IamService extends InitializedService {
|
|
|
171
210
|
// grantPermission, which costs N·M writes and N·M lost-update windows on the same records.
|
|
172
211
|
grantBundle: (entityId: string, clientId: string, profileIds: string[], bundle: IamGrantBundle) => Promise<IamGrant[]>
|
|
173
212
|
|
|
213
|
+
// Direct, default-class and group grants, each with its `origin`.
|
|
174
214
|
listGrants: (entityId: string, clientId: string, profileId?: string) => Promise<IamGrant[]>
|
|
175
215
|
|
|
176
|
-
// ---
|
|
216
|
+
// --- Users of one client — see "Users are per client" ---
|
|
177
217
|
|
|
178
218
|
listUsers: (entityId: string, clientId?: string) => Promise<IamUser[]>
|
|
179
|
-
getUser: (entityId: string, profileId: string) => Promise<IamUser | null>
|
|
180
|
-
inviteUser: (entityId: string, invite: IamUserInvite) => Promise<IamUser>
|
|
181
|
-
updateUser: (entityId: string, profileId: string, update: IamUserUpdate) => Promise<IamUser>
|
|
182
|
-
removeUser: (entityId: string, profileId: string) => Promise<void>
|
|
219
|
+
getUser: (entityId: string, clientId: string, profileId: string) => Promise<IamUser | null>
|
|
220
|
+
inviteUser: (entityId: string, clientId: string, invite: IamUserInvite) => Promise<IamUser>
|
|
221
|
+
updateUser: (entityId: string, clientId: string, profileId: string, update: IamUserUpdate) => Promise<IamUser>
|
|
222
|
+
removeUser: (entityId: string, clientId: string, profileId: string) => Promise<void>
|
|
223
|
+
|
|
224
|
+
// The owning organization's staff, admitted to the client through the managed `members` group.
|
|
225
|
+
syncStaff: (entityId: string, clientId: string, args: IamStaffSyncArgs) => Promise<IamStaffSyncReport>
|
|
226
|
+
|
|
227
|
+
// --- Tenancy facets — see "Organizations, groups and subjects" ---
|
|
228
|
+
organizations: IamOrganizationFacet
|
|
229
|
+
groups: IamGroupFacet
|
|
230
|
+
subjects: IamSubjectFacet
|
|
183
231
|
}
|
|
184
232
|
```
|
|
185
233
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
234
|
+
The first parameter of every method is `entityId` — the organization that OWNS the client — and
|
|
235
|
+
every `(entityId, clientId)` method verifies that ownership before it reads or writes anything. The
|
|
236
|
+
organizations a client's subjects act in are a different thing, named by `entitySlug` inside the
|
|
237
|
+
facets.
|
|
238
|
+
|
|
239
|
+
## Users are per client
|
|
240
|
+
|
|
241
|
+
A person is ONE account (one per e-mail address); what a client sees of it are its rows of that app —
|
|
242
|
+
one per (account, app, organization), the PRIMARY one in the account's own organization. The
|
|
243
|
+
`profileId` a client knows is the pairwise subject, the same on every row of one (account, app) and
|
|
244
|
+
different for every app, so two apps of one customer cannot correlate their users by it.
|
|
245
|
+
|
|
246
|
+
- `listUsers(entityId, clientId)` lists every account holding a row of the client, in whichever
|
|
247
|
+
organization. Without `clientId` it is a read-only aggregate over the entity's clients, for a
|
|
248
|
+
console — nothing is written through it.
|
|
249
|
+
- `getUser` / `inviteUser` / `updateUser` / `removeUser` take the client. `removeUser` deletes this
|
|
250
|
+
account's rows of THAT app only — the account, and its rows of other apps, stay. `updateUser`
|
|
251
|
+
writes `disabled` on the primary row, which is what refuses the subject at the provider.
|
|
252
|
+
- `inviteUser` is idempotent by email, which is what lets an invitation and a first sign-in converge on
|
|
253
|
+
one record instead of accumulating twins.
|
|
254
|
+
|
|
255
|
+
A backend with no user store of its own — one where the customer manages users in the provider's own
|
|
256
|
+
console — throws `IamUnsupported('user-management')` from all five methods.
|
|
257
|
+
|
|
258
|
+
## Organizations, groups and subjects
|
|
259
|
+
|
|
260
|
+
The three facets of `IamService` carry tenancy. A backend that cannot provide one answers
|
|
261
|
+
`unsupportedFacet<IamOrganizationFacet>('organizations')` (and so on): every method of it throws
|
|
262
|
+
`IamUnsupported(what)` SYNCHRONOUSLY, because `subjects.identify` is synchronous and a rejected
|
|
263
|
+
promise would hand it the wrong type — an awaited call rejects either way.
|
|
264
|
+
|
|
265
|
+
- **Tenancy is the client's configuration.** `IamClientConfig.users` / `.operators` turn it on: with
|
|
266
|
+
neither flag, every subject acts in the client's owning organization (a single-tenant app); with
|
|
267
|
+
either, each subject acts in organizations of its own and the provider claims them under the
|
|
268
|
+
`organizations` scope (`@owlmeans/oidc`). The flags also gate the runtime API's writes by area.
|
|
269
|
+
- **`organizations`** — `list`, `get`, `create({ title?, profileId? })` (that subject becomes the
|
|
270
|
+
owner), `update({ title })`, `members`, `addMember` (find-or-create by e-mail, idempotent, refused
|
|
271
|
+
past the membership cap), `updateMember`, `removeMember`. The last owner cannot be demoted or
|
|
272
|
+
removed (`IamOrganizationError`).
|
|
273
|
+
- **`groups`** — a group is (organization, client, key), kept inside the organization record; one
|
|
274
|
+
client never reads or writes another's. `list`, `ensure`, `remove`, `members`, `addMembers`,
|
|
275
|
+
`removeMembers`, `grant` / `revoke` (bound to the group's organization), `grants`, and
|
|
276
|
+
`setBundles` — a bundle (`{ filter?, permissions? }`) grants every definition it selects to the
|
|
277
|
+
group's members at claim time. `IAM_MEMBERS_GROUP` is the managed group `syncStaff` keeps in the
|
|
278
|
+
owning organization; it is read-only to an operator (`IamGroupError`).
|
|
279
|
+
- **`subjects`** — the provider's half, keyed by the ACCOUNT, which never leaves the provider.
|
|
280
|
+
`signIn(clientId, { email })` finds or creates the account and its rows for the client;
|
|
281
|
+
`resolve(clientId, accountId)` builds the `IamSubject` the claims come from (`null` = not admitted:
|
|
282
|
+
no row, or a disabled primary row); `identify(clientId, accountId)` is the pairwise `sub`.
|
|
283
|
+
|
|
284
|
+
## Runtime IAM API
|
|
285
|
+
|
|
286
|
+
`makeIamRuntimeProtocols({ service, path? })` declares the provider-side API a tenanted client's
|
|
287
|
+
server calls on behalf of its signed-in subject. It is declarations only: the provider binds it, and
|
|
288
|
+
a relying party calls it through `iamRuntime` (`@owlmeans/server-iam`) over plain `fetch`.
|
|
289
|
+
|
|
290
|
+
| Route (under `path`, default `IAM_RUNTIME_PATH`) | Tree | Who |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `GET /organizations` | `organizations.list` | any subject — its own |
|
|
293
|
+
| `POST /organizations` `{ title? }` | `organizations.create` | any subject of a tenanted client; it becomes owner |
|
|
294
|
+
| `POST /organizations/:entitySlug` `{ title }` | `organizations.update` | owner |
|
|
295
|
+
| `GET /organizations/:entitySlug/members` | `members.list` | member |
|
|
296
|
+
| `POST /organizations/:entitySlug/members` `{ email, name?, owner? }` | `members.add` | owner; idempotent by e-mail |
|
|
297
|
+
| `POST …/members/:profileId` `{ owner?, groups? }` | `members.update` | owner |
|
|
298
|
+
| `POST …/members/:profileId/remove` | `members.remove` | owner, or the member itself |
|
|
299
|
+
| `GET …/permissions` | `permissions.list` | member |
|
|
300
|
+
| `GET …/grants` `?profileId` | `grants.list` | owner |
|
|
301
|
+
| `POST …/grants`, `POST …/grants/revoke` `{ profileId, permission, resources?, mode? }` | `grants.assign` / `grants.revoke` | owner |
|
|
302
|
+
|
|
303
|
+
- **Every route, the base included, is pinned to `service`** — a route's service is never inherited
|
|
304
|
+
from its parent — and every leaf hangs under ONE base carrying `IAM_RUNTIME_GUARD`. The serving
|
|
305
|
+
process registers that guard: it admits only a bearer access token the provider issued, and takes
|
|
306
|
+
the client and the account from it, never from the request.
|
|
307
|
+
- **The wire names organizations by `entitySlug` and subjects by the calling client's pairwise
|
|
308
|
+
`profileId`** — no organization or account id appears in any shape. Every schema is closed, since
|
|
309
|
+
the server's AJV strips an undeclared key silently. `IAM_RUNTIME_ROUTES` is the one leaf-path table
|
|
310
|
+
the declarations and the fetch client share.
|
|
311
|
+
- The serving process enforces the write rules: only `entityScoped`, unmanaged definitions of the
|
|
312
|
+
CALLING client, binding forced to the path's organization, the last owner protected, the
|
|
313
|
+
membership cap, and only for an area the client's tenancy config opens (`users` → `user`,
|
|
314
|
+
`operators` → `operator`). A non-owner write is `AuthForbidden(ORGANIZATION_OWNER_REFUSAL)`.
|
|
198
315
|
|
|
199
316
|
## A client id is a global name
|
|
200
317
|
|
|
@@ -215,8 +332,8 @@ cannot namespace it on a consumer's behalf. Uniqueness has to be *in the id*.
|
|
|
215
332
|
|
|
216
333
|
## `IamClient.realm` field
|
|
217
334
|
|
|
218
|
-
`realm`
|
|
219
|
-
reach into an adapter's own internals (`(client as any)._realm`) for the same value.
|
|
335
|
+
Every implementation sets `realm` to the entity id, so read `client.realm ?? fallbackEntityId` and
|
|
336
|
+
never reach into an adapter's own internals (`(client as any)._realm`) for the same value.
|
|
220
337
|
|
|
221
338
|
## Issuer & redirect URIs
|
|
222
339
|
|
|
@@ -285,14 +402,18 @@ the same options — otherwise they disagree about the issuer, and only one of t
|
|
|
285
402
|
- Do not expose `getCredentialsPair` to request handlers — it returns a raw admin token.
|
|
286
403
|
- `IamClientError` means the provisioned client was invalid (missing secret, null token). The adapter
|
|
287
404
|
already throws it; callers handle it rather than re-throwing a wrapped error.
|
|
288
|
-
- `
|
|
289
|
-
|
|
290
|
-
|
|
405
|
+
- An operation a backend cannot support throws `IamUnsupported('<what>')`; a whole facet it cannot
|
|
406
|
+
support is `unsupportedFacet(what)`. Callers turn that into a fallback — an "external console"
|
|
407
|
+
link, a disabled control — rather than an error screen.
|
|
408
|
+
- A wire value names an organization by `entitySlug` only; the `entityId` parameter is the client's
|
|
409
|
+
OWNER, resolved server-side, never taken from a token or a request.
|
|
291
410
|
|
|
292
411
|
## Related
|
|
293
412
|
|
|
294
|
-
- `@owlmeans/server-iam` — the IAM gate that asserts
|
|
295
|
-
see the `server-iam` skill
|
|
413
|
+
- `@owlmeans/server-iam` — the IAM gate that asserts every permission kind, the gate-param grammar,
|
|
414
|
+
the session's organizations and the runtime IAM client; see the `server-iam` skill
|
|
415
|
+
- `@owlmeans/client-iam` — browser wiring, the organization switch and `hasPermission`; see the
|
|
416
|
+
`client-iam` skill
|
|
296
417
|
- `@owlmeans/oidc` — provider descriptors and the `iamMode` config seam; see the `oidc` skill
|
|
297
418
|
- The concrete adapters (a Keycloak proxy, and the integrated backend built on
|
|
298
419
|
`@owlmeans/server-oidc-provider` plus `@owlmeans/server-auth-identity`) ship separately and are
|
package/build/consts.d.ts
CHANGED
|
@@ -93,4 +93,61 @@ export declare const IAM_AREAS: readonly ['user', 'operator', 'admin'];
|
|
|
93
93
|
export type IamArea = typeof IAM_AREAS[number];
|
|
94
94
|
/** Separates the resource from the action in a permission name. TWO hyphens, never one. */
|
|
95
95
|
export declare const PERMISSION_ACTION_SEPARATOR = "--";
|
|
96
|
+
/**
|
|
97
|
+
* Who holds a permission without an explicit grant — a property of the DEFINITION, evaluated when
|
|
98
|
+
* claims are built and never written onto a profile.
|
|
99
|
+
*
|
|
100
|
+
* `User` is every signed-in subject of the client (an unbound, unmanaged `user`-area definition
|
|
101
|
+
* only); `Member` and `Owner` are every member / owner of the organization the subject acts in, so
|
|
102
|
+
* they require an `entityScoped` definition. A class default is a BLANKET set: `Owner` on a
|
|
103
|
+
* resource-scoped definition is its all-records form.
|
|
104
|
+
*/
|
|
105
|
+
export declare enum IamDefaultClass {
|
|
106
|
+
None = "none",
|
|
107
|
+
User = "user",
|
|
108
|
+
Member = "member",
|
|
109
|
+
Owner = "owner"
|
|
110
|
+
}
|
|
111
|
+
/** Why a subject holds a grant, as a listing reports it. */
|
|
112
|
+
export declare enum IamGrantOrigin {
|
|
113
|
+
/** Written on the subject's own row. */
|
|
114
|
+
Direct = "direct",
|
|
115
|
+
/** The definition's `defaultClass` covers the subject. */
|
|
116
|
+
Default = "default",
|
|
117
|
+
/** Held by a group the subject is a member of — `IamGrant.through` names it. */
|
|
118
|
+
Group = "group"
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The managed group `syncStaff` keeps in a client's owning organization: the owner's own staff,
|
|
122
|
+
* admitted to the client with the bundles the sync hands it. Read-only to an operator.
|
|
123
|
+
*/
|
|
124
|
+
export declare const IAM_MEMBERS_GROUP = "members";
|
|
125
|
+
/**
|
|
126
|
+
* The `profileId` of a credential that proves an e-mail address and identifies nobody yet: what the
|
|
127
|
+
* end-user sign-in hands the provider's finalizer, which then resolves the account itself. It carries
|
|
128
|
+
* no `entitySlug` — a proof bound to an organization is something else.
|
|
129
|
+
*/
|
|
130
|
+
export declare const IAM_EMAIL_PROOF = "proof:email";
|
|
131
|
+
/**
|
|
132
|
+
* The guard of the runtime IAM API (`makeIamRuntimeProtocols`). The serving process registers it: it
|
|
133
|
+
* admits a bearer access token the provider itself issued, and takes the client and the account from
|
|
134
|
+
* that token, never from the request.
|
|
135
|
+
*/
|
|
136
|
+
export declare const IAM_RUNTIME_GUARD = "iam-runtime-guard";
|
|
137
|
+
/** Where the runtime IAM API mounts under its service unless the declaration names another path. */
|
|
138
|
+
export declare const IAM_RUNTIME_PATH = "/runtime";
|
|
139
|
+
/**
|
|
140
|
+
* The runtime API's leaf paths under its base. One table so the declarations and a fetch-only client
|
|
141
|
+
* (a target's server, which binds no entrypoint of the provider's tree) cannot disagree on a segment.
|
|
142
|
+
*/
|
|
143
|
+
export declare const IAM_RUNTIME_ROUTES: {
|
|
144
|
+
readonly organizations: '/organizations';
|
|
145
|
+
readonly organization: '/organizations/:entitySlug';
|
|
146
|
+
readonly members: '/organizations/:entitySlug/members';
|
|
147
|
+
readonly member: '/organizations/:entitySlug/members/:profileId';
|
|
148
|
+
readonly memberRemove: '/organizations/:entitySlug/members/:profileId/remove';
|
|
149
|
+
readonly permissions: '/organizations/:entitySlug/permissions';
|
|
150
|
+
readonly grants: '/organizations/:entitySlug/grants';
|
|
151
|
+
readonly grantsRevoke: '/organizations/:entitySlug/grants/revoke';
|
|
152
|
+
};
|
|
96
153
|
//# sourceMappingURL=consts.d.ts.map
|
package/build/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,gBAAgB,CAAA;AAE1C,eAAO,MAAM,iBAAiB,aAAa,CAAA;AAC3C,eAAO,MAAM,mBAAmB,eAAe,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG,OAAO,iBAAiB,GAAG,OAAO,mBAAmB,CAAA;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAE1C,8DAA8D;AAC9D,oBAAY,eAAe;IACzB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAEvD,CAAA;AAED,+FAA+F;AAC/F,oBAAY,kBAAkB;IAC5B,aAAa,8BAA8B;IAC3C,aAAa,8BAA8B;IAC3C,YAAY,6BAA6B;IACzC,oGAAoG;IACpG,cAAc,+BAA+B;IAC7C,oEAAoE;IACpE,iBAAiB,mCAAmC;CACrD;AAED,8CAA8C;AAC9C,oBAAY,qBAAqB;IAC/B,4CAA4C;IAC5C,WAAW,iBAAiB;IAC5B,iDAAiD;IACjD,aAAa,mBAAmB;IAChC,wFAAwF;IACxF,SAAS,eAAe;IACxB,yCAAyC;IACzC,SAAS,cAAc;CACxB;AAED;;;;;;;GAOG;AACH,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,mDAAmD;IACnD,GAAG,QAAQ;CACZ;AAED,+EAA+E;AAC/E,oBAAY,gBAAgB;IAC1B,uFAAuF;IACvF,OAAO,YAAY;IACnB,gFAAgF;IAChF,MAAM,WAAW;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,YAAI,MAAM,EAAE,UAAU,EAAE,OAAO,CAAU,CAAA;AAE/D,MAAM,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC,CAAA;AAE9C,2FAA2F;AAC3F,eAAO,MAAM,2BAA2B,OAAO,CAAA"}
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,gBAAgB,CAAA;AAE1C,eAAO,MAAM,iBAAiB,aAAa,CAAA;AAC3C,eAAO,MAAM,mBAAmB,eAAe,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG,OAAO,iBAAiB,GAAG,OAAO,mBAAmB,CAAA;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAE1C,8DAA8D;AAC9D,oBAAY,eAAe;IACzB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAEvD,CAAA;AAED,+FAA+F;AAC/F,oBAAY,kBAAkB;IAC5B,aAAa,8BAA8B;IAC3C,aAAa,8BAA8B;IAC3C,YAAY,6BAA6B;IACzC,oGAAoG;IACpG,cAAc,+BAA+B;IAC7C,oEAAoE;IACpE,iBAAiB,mCAAmC;CACrD;AAED,8CAA8C;AAC9C,oBAAY,qBAAqB;IAC/B,4CAA4C;IAC5C,WAAW,iBAAiB;IAC5B,iDAAiD;IACjD,aAAa,mBAAmB;IAChC,wFAAwF;IACxF,SAAS,eAAe;IACxB,yCAAyC;IACzC,SAAS,cAAc;CACxB;AAED;;;;;;;GAOG;AACH,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,mDAAmD;IACnD,GAAG,QAAQ;CACZ;AAED,+EAA+E;AAC/E,oBAAY,gBAAgB;IAC1B,uFAAuF;IACvF,OAAO,YAAY;IACnB,gFAAgF;IAChF,MAAM,WAAW;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,YAAI,MAAM,EAAE,UAAU,EAAE,OAAO,CAAU,CAAA;AAE/D,MAAM,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC,CAAA;AAE9C,2FAA2F;AAC3F,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAE/C;;;;;;;;GAQG;AACH,oBAAY,eAAe;IACzB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,KAAK,UAAU;CAChB;AAED,4DAA4D;AAC5D,oBAAY,cAAc;IACxB,wCAAwC;IACxC,MAAM,WAAW;IACjB,0DAA0D;IAC1D,OAAO,YAAY;IACnB,gFAAgF;IAChF,KAAK,UAAU;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAA;AAE1C;;;;GAIG;AACH,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AAEpD,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,kBAAkB;aAC7B,aAAa,EAAE,gBAAgB;aAC/B,YAAY,EAAE,4BAA4B;aAC1C,OAAO,EAAE,oCAAoC;aAC7C,MAAM,EAAE,+CAA+C;aACvD,YAAY,EAAE,sDAAsD;aACpE,WAAW,EAAE,wCAAwC;aACrD,MAAM,EAAE,mCAAmC;aAC3C,YAAY,EAAE,0CAA0C;CAChD,CAAA"}
|
package/build/consts.js
CHANGED
|
@@ -98,4 +98,63 @@ export var IamRemovalPolicy;
|
|
|
98
98
|
export const IAM_AREAS = ['user', 'operator', 'admin'];
|
|
99
99
|
/** Separates the resource from the action in a permission name. TWO hyphens, never one. */
|
|
100
100
|
export const PERMISSION_ACTION_SEPARATOR = '--';
|
|
101
|
+
/**
|
|
102
|
+
* Who holds a permission without an explicit grant — a property of the DEFINITION, evaluated when
|
|
103
|
+
* claims are built and never written onto a profile.
|
|
104
|
+
*
|
|
105
|
+
* `User` is every signed-in subject of the client (an unbound, unmanaged `user`-area definition
|
|
106
|
+
* only); `Member` and `Owner` are every member / owner of the organization the subject acts in, so
|
|
107
|
+
* they require an `entityScoped` definition. A class default is a BLANKET set: `Owner` on a
|
|
108
|
+
* resource-scoped definition is its all-records form.
|
|
109
|
+
*/
|
|
110
|
+
export var IamDefaultClass;
|
|
111
|
+
(function (IamDefaultClass) {
|
|
112
|
+
IamDefaultClass["None"] = "none";
|
|
113
|
+
IamDefaultClass["User"] = "user";
|
|
114
|
+
IamDefaultClass["Member"] = "member";
|
|
115
|
+
IamDefaultClass["Owner"] = "owner";
|
|
116
|
+
})(IamDefaultClass || (IamDefaultClass = {}));
|
|
117
|
+
/** Why a subject holds a grant, as a listing reports it. */
|
|
118
|
+
export var IamGrantOrigin;
|
|
119
|
+
(function (IamGrantOrigin) {
|
|
120
|
+
/** Written on the subject's own row. */
|
|
121
|
+
IamGrantOrigin["Direct"] = "direct";
|
|
122
|
+
/** The definition's `defaultClass` covers the subject. */
|
|
123
|
+
IamGrantOrigin["Default"] = "default";
|
|
124
|
+
/** Held by a group the subject is a member of — `IamGrant.through` names it. */
|
|
125
|
+
IamGrantOrigin["Group"] = "group";
|
|
126
|
+
})(IamGrantOrigin || (IamGrantOrigin = {}));
|
|
127
|
+
/**
|
|
128
|
+
* The managed group `syncStaff` keeps in a client's owning organization: the owner's own staff,
|
|
129
|
+
* admitted to the client with the bundles the sync hands it. Read-only to an operator.
|
|
130
|
+
*/
|
|
131
|
+
export const IAM_MEMBERS_GROUP = 'members';
|
|
132
|
+
/**
|
|
133
|
+
* The `profileId` of a credential that proves an e-mail address and identifies nobody yet: what the
|
|
134
|
+
* end-user sign-in hands the provider's finalizer, which then resolves the account itself. It carries
|
|
135
|
+
* no `entitySlug` — a proof bound to an organization is something else.
|
|
136
|
+
*/
|
|
137
|
+
export const IAM_EMAIL_PROOF = 'proof:email';
|
|
138
|
+
/**
|
|
139
|
+
* The guard of the runtime IAM API (`makeIamRuntimeProtocols`). The serving process registers it: it
|
|
140
|
+
* admits a bearer access token the provider itself issued, and takes the client and the account from
|
|
141
|
+
* that token, never from the request.
|
|
142
|
+
*/
|
|
143
|
+
export const IAM_RUNTIME_GUARD = 'iam-runtime-guard';
|
|
144
|
+
/** Where the runtime IAM API mounts under its service unless the declaration names another path. */
|
|
145
|
+
export const IAM_RUNTIME_PATH = '/runtime';
|
|
146
|
+
/**
|
|
147
|
+
* The runtime API's leaf paths under its base. One table so the declarations and a fetch-only client
|
|
148
|
+
* (a target's server, which binds no entrypoint of the provider's tree) cannot disagree on a segment.
|
|
149
|
+
*/
|
|
150
|
+
export const IAM_RUNTIME_ROUTES = {
|
|
151
|
+
organizations: '/organizations',
|
|
152
|
+
organization: '/organizations/:entitySlug',
|
|
153
|
+
members: '/organizations/:entitySlug/members',
|
|
154
|
+
member: '/organizations/:entitySlug/members/:profileId',
|
|
155
|
+
memberRemove: '/organizations/:entitySlug/members/:profileId/remove',
|
|
156
|
+
permissions: '/organizations/:entitySlug/permissions',
|
|
157
|
+
grants: '/organizations/:entitySlug/grants',
|
|
158
|
+
grantsRevoke: '/organizations/:entitySlug/grants/revoke',
|
|
159
|
+
};
|
|
101
160
|
//# sourceMappingURL=consts.js.map
|
package/build/consts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAA;AAE1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAA;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAA;AAI/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAE3C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAE5C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAE1C,8DAA8D;AAC9D,MAAM,CAAN,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,oCAAiB,CAAA;IACjB,kCAAe,CAAA;IACf,gCAAa,CAAA;IACb,sCAAmB,CAAA;IACnB,gCAAa,CAAA;AACf,CAAC,EANW,eAAe,KAAf,eAAe,QAM1B;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK;CAC9C,CAAA;AAED,+FAA+F;AAC/F,MAAM,CAAN,IAAY,kBAQX;AARD,WAAY,kBAAkB;IAC5B,iEAA2C,CAAA;IAC3C,iEAA2C,CAAA;IAC3C,+DAAyC,CAAA;IACzC,oGAAoG;IACpG,mEAA6C,CAAA;IAC7C,oEAAoE;IACpE,0EAAoD,CAAA;AACtD,CAAC,EARW,kBAAkB,KAAlB,kBAAkB,QAQ7B;AAED,8CAA8C;AAC9C,MAAM,CAAN,IAAY,qBASX;AATD,WAAY,qBAAqB;IAC/B,4CAA4C;IAC5C,qDAA4B,CAAA;IAC5B,iDAAiD;IACjD,yDAAgC,CAAA;IAChC,wFAAwF;IACxF,iDAAwB,CAAA;IACxB,yCAAyC;IACzC,gDAAuB,CAAA;AACzB,CAAC,EATW,qBAAqB,KAArB,qBAAqB,QAShC;AAED;;;;;;;GAOG;AACH,MAAM,CAAN,IAAY,YAKX;AALD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,uCAAuB,CAAA;IACvB,mDAAmD;IACnD,2BAAW,CAAA;AACb,CAAC,EALW,YAAY,KAAZ,YAAY,QAKvB;AAED,+EAA+E;AAC/E,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,uFAAuF;IACvF,uCAAmB,CAAA;IACnB,gFAAgF;IAChF,qCAAiB,CAAA;AACnB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAU,CAAA;AAI/D,2FAA2F;AAC3F,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAA"}
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"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"}
|
package/build/errors.d.ts
CHANGED
|
@@ -36,4 +36,17 @@ export declare class IamUserError extends IamError {
|
|
|
36
36
|
static typeName: string;
|
|
37
37
|
constructor(message?: string);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A refusal about an organization or a membership in it — an unknown organization, the last owner,
|
|
41
|
+
* the membership cap.
|
|
42
|
+
*/
|
|
43
|
+
export declare class IamOrganizationError extends IamError {
|
|
44
|
+
static typeName: string;
|
|
45
|
+
constructor(message?: string);
|
|
46
|
+
}
|
|
47
|
+
/** A refusal about a group: an unknown key, a managed group written by an operator, another app's group. */
|
|
48
|
+
export declare class IamGroupError extends IamError {
|
|
49
|
+
static typeName: string;
|
|
50
|
+
constructor(message?: string);
|
|
51
|
+
}
|
|
39
52
|
//# sourceMappingURL=errors.d.ts.map
|
package/build/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,QAAS,SAAQ,cAAc;IAC1C,OAAuB,QAAQ,SAAkC;IAEjE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAAwC;IAEvE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,OAAuB,QAAQ,SAA0C;IAEzE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,OAAuB,QAAQ,SAA4C;IAE3E,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAA6C;IAE5E,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,YAAa,SAAQ,QAAQ;IACxC,OAAuB,QAAQ,SAAsC;IAErE,YAAY,OAAO,SAAU,EAG5B;CACF"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,QAAS,SAAQ,cAAc;IAC1C,OAAuB,QAAQ,SAAkC;IAEjE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAAwC;IAEvE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,OAAuB,QAAQ,SAA0C;IAEzE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,OAAuB,QAAQ,SAA4C;IAE3E,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAA6C;IAE5E,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,qBAAa,YAAa,SAAQ,QAAQ;IACxC,OAAuB,QAAQ,SAAsC;IAErE,YAAY,OAAO,SAAU,EAG5B;CACF;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,OAAuB,QAAQ,SAA8C;IAE7E,YAAY,OAAO,SAAU,EAG5B;CACF;AAED,4GAA4G;AAC5G,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,OAAO,SAAU,EAG5B;CACF"}
|