@owlmeans/server-iam 0.1.18-rc.52 → 0.1.18-rc.54
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 +4 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-iam/SKILL.md +54 -5
- package/build/gate.d.ts +2 -1
- package/build/gate.d.ts.map +1 -1
- package/build/gate.js +8 -3
- package/build/gate.js.map +1 -1
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/build/organization.d.ts +18 -0
- package/build/organization.d.ts.map +1 -0
- package/build/organization.js +35 -0
- package/build/organization.js.map +1 -0
- package/build/runtime.d.ts +41 -0
- package/build/runtime.d.ts.map +1 -0
- package/build/runtime.js +165 -0
- package/build/runtime.js.map +1 -0
- package/package.json +8 -7
- package/src/gate.ts +9 -3
- package/src/index.ts +4 -0
- package/src/organization.ts +50 -0
- package/src/runtime.ts +234 -0
- package/tests/context.ts +100 -0
- package/tests/gate.spec.ts +79 -3
- package/tests/organization.spec.ts +52 -0
- package/tests/runtime.spec.ts +168 -0
- package/tests/tsconfig.json +12 -0
package/README.md
CHANGED
|
@@ -7,12 +7,14 @@ One-call OIDC RP wiring and IAM gate for OwlMeans servers — `appendIam()` and
|
|
|
7
7
|
- `appendIam(context)` — registers the OIDC client, wrapping service, and IAM gate in a single call, replacing verbose manual wiring
|
|
8
8
|
- `makeIamGate()` — produces a guard that asserts unscoped or resource-scoped permissions against the IAM backend (claims-first, UMA2 fallback)
|
|
9
9
|
- Re-exports `hasPermission` from `@owlmeans/iam` for inline permission checks
|
|
10
|
+
- `organizationOf` / `organizationsOf` — the organizations of a tenanted session, from its session record
|
|
11
|
+
- `iamRuntime(context, request)` — a typed client of the provider's runtime IAM API, acting as the request's subject
|
|
10
12
|
- Designed for IAM consumers such as the viable target template backend
|
|
11
13
|
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
14
16
|
```bash
|
|
15
|
-
bun add @owlmeans/server-iam@^0.1.18-rc.
|
|
17
|
+
bun add @owlmeans/server-iam@^0.1.18-rc.54
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
## Usage
|
|
@@ -45,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
45
47
|
your project's skill store (`.agents/skills/`):
|
|
46
48
|
|
|
47
49
|
```sh
|
|
48
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
50
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.46
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
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/server-iam",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.54",
|
|
5
|
+
"generatedAt": "2026-10-03T11:19:50.777Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: server-iam
|
|
3
|
-
description: "How to use @owlmeans/server-iam — one-call OIDC RP wiring (appendIam)
|
|
3
|
+
description: "How to use @owlmeans/server-iam — one-call OIDC RP wiring (appendIam), the IAM gate that asserts unbound, organization-bound and resource-scoped permissions (claims-first, UMA2 fallback), the session's organizations (organizationOf/organizationsOf), the request-bound runtime IAM client (iamRuntime), plus the gate-param grammar it re-exports. Use when gating server endpoints, declaring gate params, acting in a tenanted client's organizations, managing members or grants from a target's server, or diagnosing a permission refusal. Applies to files matching **/owlmeans.ts, **/gate*.ts, **/auth-guard*.ts."
|
|
4
4
|
metadata:
|
|
5
5
|
applyTo: "**/owlmeans.ts, **/gate*.ts, **/auth-guard*.ts"
|
|
6
6
|
---
|
|
@@ -8,10 +8,12 @@ metadata:
|
|
|
8
8
|
|
|
9
9
|
# Using `@owlmeans/server-iam`
|
|
10
10
|
|
|
11
|
-
**Install:** `"@owlmeans/server-iam": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/server-iam": "^0.1.18-rc.54"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
Boilerplate-less server-side consumer of the OwlMeans IAM. One call wires the OIDC RP stack and the
|
|
14
|
-
IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is active.
|
|
14
|
+
IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is active. For a
|
|
15
|
+
tenanted client it also reads the session's organizations and calls the provider's runtime IAM API
|
|
16
|
+
as the signed-in subject.
|
|
15
17
|
|
|
16
18
|
## Public API surface
|
|
17
19
|
|
|
@@ -24,6 +26,11 @@ IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is
|
|
|
24
26
|
| `RESOURCE_PARAM_SEPARATOR` / `RESOURCE_SOURCE_SEPARATOR` / `RESOURCE_PATH_SEPARATOR` | const | `'@'`, `':'` and `'.'`, re-exported |
|
|
25
27
|
| `GateParamSource` / `GateParamErrorCode` / `GateResolutionFailure` | enum | Re-exported; the sources a selector may name, and why one failed |
|
|
26
28
|
| `hasPermission` | fn | Re-export from `@owlmeans/iam` |
|
|
29
|
+
| `organizationOf(context, request, entitySlug)` | fn | One organization of the request's subject as a `ResolvedEntity` (`{ id: entityKey, slug, iamKey: entityKey }`); one the subject is not in is `AuthForbidden(ORGANIZATION_REFUSAL)` |
|
|
30
|
+
| `organizationsOf(context, request)` | fn | Every organization of the request's subject, as `ResolvedEntity[]` |
|
|
31
|
+
| `iamRuntime(context, request)` | fn | The request-bound `IamRuntimeClient` of the provider's runtime IAM API — see "The runtime IAM client" |
|
|
32
|
+
| `IamRuntimeClient` | type | `organizations.{list,create,update}`, `members.{list,add,update,remove}`, `permissions.list`, `grants.{list,assign,revoke}` |
|
|
33
|
+
| `ORGANIZATION_REFUSAL` / `ORGANIZATION_OWNER_REFUSAL` | const | Re-exported from `@owlmeans/oidc`: the `AuthForbidden` reasons of a non-member and of a non-owner |
|
|
27
34
|
| `SERVER_IAM_SERVICE` | const | `'server-iam-service'` — exported and referenced nowhere; the gate registers under `OIDC_GATE`, not under this |
|
|
28
35
|
| Every `@owlmeans/iam` type | type | Re-exported wholesale, so a server needs one IAM import |
|
|
29
36
|
|
|
@@ -96,11 +103,16 @@ await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
|
|
|
96
103
|
1. `req.auth == null` → `AuthForbidden('auth')`.
|
|
97
104
|
2. **Claims mode** — when `req.auth.permissions` is a valid `PermissionSet[]` (minted into the id_token
|
|
98
105
|
by the integrated IAM provider and mapped into `Auth` by `@owlmeans/server-oidc-rp`), params are
|
|
99
|
-
asserted locally via `hasPermission
|
|
106
|
+
asserted locally via `hasPermission`, with `entitySlug: req.entity?.slug ?? entitySlugOf(auth)` —
|
|
107
|
+
the organization the request acts in.
|
|
100
108
|
3. **Fallback** — otherwise `@…` suffixes are stripped and the check delegates to the UMA2 gate model
|
|
101
109
|
from `@owlmeans/server-oidc-rp` (`createGateModel().loadPermissions`) — exactly the check
|
|
102
110
|
`makeOidcGate` performs, which is what a Keycloak-backed deployment gets.
|
|
103
111
|
|
|
112
|
+
In claims mode all four permission kinds (`iam` skill, "Permission kinds") are decided locally by
|
|
113
|
+
`hasPermission`; a bound set reaching the gate is a leak the relying party should have stripped, and
|
|
114
|
+
it counts only when the request acts in exactly its organization.
|
|
115
|
+
|
|
104
116
|
Which branch runs is decided per request by the token alone: a conforming `permissions` claim selects
|
|
105
117
|
claims mode, its absence selects the fallback. Nothing configures it, and there is no environment
|
|
106
118
|
variable behind it.
|
|
@@ -128,13 +140,50 @@ Grants are stored against the id wherever one is resolvable. The slug fallback e
|
|
|
128
140
|
with no organization store of their own, where the slug *is* the identifier; a deployment that has a
|
|
129
141
|
store must not grant against slugs, because a rename would then orphan every grant.
|
|
130
142
|
|
|
143
|
+
## Organizations of the session
|
|
144
|
+
|
|
145
|
+
A relying party of a tenanted client has no organization registry of its own: the provider's
|
|
146
|
+
`organizations` claim, kept in the session record (`@owlmeans/server-oidc-rp`), is the only source,
|
|
147
|
+
re-read on every validation. `organizationsOf` answers all of it and `organizationOf` one entry by
|
|
148
|
+
slug, as `ResolvedEntity` values keyed by the frozen `entityKey` — use them for a handler that acts in
|
|
149
|
+
an organization its URL names rather than the session's acting one (which the guard already attaches
|
|
150
|
+
as `req.entity`). A session of a client without the scope acts in none and has none to offer; an
|
|
151
|
+
unauthenticated request or a session that is gone is `AuthorizationError` (401).
|
|
152
|
+
|
|
153
|
+
## The runtime IAM client
|
|
154
|
+
|
|
155
|
+
`iamRuntime(context, request)` calls the provider's runtime IAM API (`makeIamRuntimeProtocols` in
|
|
156
|
+
`@owlmeans/iam`) as that request's subject:
|
|
157
|
+
|
|
158
|
+
- **Base** — the provider's discovery field `IAM_API_METADATA` (`owlmeans_iam_api`), read through
|
|
159
|
+
the OIDC client service's adapter `getMetadata()` for the session's client, and cached per context
|
|
160
|
+
and client; a failed lookup is forgotten. A provider that advertises none (Keycloak) is
|
|
161
|
+
`IamClientError('runtime-api:<client>')`.
|
|
162
|
+
- **Bearer** — the provider access token of the request's session record, read afresh on every call
|
|
163
|
+
so a token the guard just refreshed is the one sent. A record without one is
|
|
164
|
+
`AuthForbidden('record')`, and nothing is sent.
|
|
165
|
+
- **Transport** — plain `fetch` over `IAM_RUNTIME_ROUTES`: the API is another service's tree, and a
|
|
166
|
+
relying party binds none of it.
|
|
167
|
+
- **Refusals** — a development server's marshalled error is rebuilt into its class. A production body
|
|
168
|
+
is only an incident id, so the status and the route decide: a 403 from an owner route is
|
|
169
|
+
`AuthForbidden(ORGANIZATION_OWNER_REFUSAL)`, from any other route `AuthForbidden(ORGANIZATION_REFUSAL)`;
|
|
170
|
+
a 401 is `AuthorizationError('iam-runtime')`; anything else `IamError('runtime:status:<n>')`. The
|
|
171
|
+
incident id travels on the error.
|
|
172
|
+
|
|
173
|
+
`members.add` is find-or-create by e-mail and idempotent — that is the provider's job, so a caller
|
|
174
|
+
never checks for an existing member first. Owner and member rights are decided by the provider from
|
|
175
|
+
the token; nothing sent here can widen them.
|
|
176
|
+
|
|
131
177
|
## Rules
|
|
132
178
|
|
|
133
179
|
- Register the gate via `appendIam()`; do not also register `makeOidcGate()` — both use the
|
|
134
180
|
`OIDC_GATE` alias and the IAM gate already covers the UMA2 path.
|
|
135
181
|
- Never branch on the IAM backend in consumer code — the gate's claims/fallback split is the only seam.
|
|
136
182
|
- The OIDC provider entry must request the `permissions` scope (`extraScopes`) for claims mode to
|
|
137
|
-
engage; without it the gate transparently uses the fallback.
|
|
183
|
+
engage; without it the gate transparently uses the fallback. A tenanted client also requests
|
|
184
|
+
`organizations`.
|
|
185
|
+
- Reach the runtime IAM API only through `iamRuntime`; never forward the browser's wrapped token or
|
|
186
|
+
build a bearer from anything but the session record.
|
|
138
187
|
|
|
139
188
|
## Related instructions
|
|
140
189
|
|
package/build/gate.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ export interface IamGateOptions {
|
|
|
23
23
|
* appendIam() so target code never knows which IAM backend is active.
|
|
24
24
|
*
|
|
25
25
|
* 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
|
|
26
|
-
* params are asserted locally against it — both unscoped and resource-scoped forms
|
|
26
|
+
* params are asserted locally against it — both unscoped and resource-scoped forms, and a set
|
|
27
|
+
* bound to an organization only for the organization the request acts in.
|
|
27
28
|
* 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
|
|
28
29
|
* @-suffixes are stripped and the check delegates to the UMA2 gate model from
|
|
29
30
|
* @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
|
package/build/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAY1F;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE1F,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAiED
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAY1F;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE1F,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAiED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,SAAqB,cAAc,KAAG,WAiG9E,CAAA"}
|
package/build/gate.js
CHANGED
|
@@ -70,7 +70,8 @@ const auditEntrypoint = (ctx, req, params) => {
|
|
|
70
70
|
* appendIam() so target code never knows which IAM backend is active.
|
|
71
71
|
*
|
|
72
72
|
* 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
|
|
73
|
-
* params are asserted locally against it — both unscoped and resource-scoped forms
|
|
73
|
+
* params are asserted locally against it — both unscoped and resource-scoped forms, and a set
|
|
74
|
+
* bound to an organization only for the organization the request acts in.
|
|
74
75
|
* 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
|
|
75
76
|
* @-suffixes are stripped and the check delegates to the UMA2 gate model from
|
|
76
77
|
* @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
|
|
@@ -95,6 +96,10 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
|
|
|
95
96
|
// the subject simply lacks the grant. The two look identical from the outside and are
|
|
96
97
|
// repaired in completely different places.
|
|
97
98
|
const structural = [];
|
|
99
|
+
// The organization the request acts in. A relying party of a tenanted client attaches it
|
|
100
|
+
// through the guard; otherwise it is the token's. A set bound to any other organization
|
|
101
|
+
// never satisfies the check.
|
|
102
|
+
const entitySlug = req.entity?.slug ?? entitySlugOf(auth);
|
|
98
103
|
const granted = params.some(param => {
|
|
99
104
|
const { permission, resource, error } = parseGateParam(param);
|
|
100
105
|
// Grants are stored against the entity's stable id wherever one is resolvable; the
|
|
@@ -114,9 +119,9 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
|
|
|
114
119
|
}
|
|
115
120
|
return false;
|
|
116
121
|
}
|
|
117
|
-
return hasPermission(auth, fixed, { resourceId: resolution.id });
|
|
122
|
+
return hasPermission(auth, fixed, { resourceId: resolution.id, entitySlug });
|
|
118
123
|
}
|
|
119
|
-
return hasPermission(auth, fixed);
|
|
124
|
+
return hasPermission(auth, fixed, { entitySlug });
|
|
120
125
|
});
|
|
121
126
|
if (!granted) {
|
|
122
127
|
structural.forEach(detail => reportOnce(`${req.alias}\0${detail}`, `Gate on entrypoint "${req.alias}" refused a request it could not evaluate: ${detail}`));
|
package/build/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAEjF,OAAO,EACL,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,qBAAqB,EAC9F,MAAM,eAAe,CAAA;AAGtB;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AAgBtB;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;AAElC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,OAAe,EAAQ,EAAE;IACxD,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAM;IACR,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AACxB,CAAC,CAAA;AAED,4DAA4D;AAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;AAEjC;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,CAAC,GAAY,EAAE,GAAoB,EAAE,MAAgB,EAAQ,EAAE;IACrF,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,OAAM;IACR,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAEtB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAmB,GAAG,CAAC,KAAK,CAAC,CAAA;QAC9D,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,OAAM;QACR,CAAC;QAED,IAAI,SAA6B,CAAA;QACjC,IAAI,CAAC;YACH,SAAS,GAAG,UAAU,CAAC,KAAK,EAAE,CAAA;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,4FAA4F;YAC5F,kBAAkB;QACpB,CAAC;QAED,MAAM,KAAK,GAAmB;YAC5B,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAkC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChG,CAAA;QAED,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,CAAC,KAAK,CACX,eAAe,KAAK,CAAC,KAAK,oBAAoB,GAAG,CAAC,KAAK,oBAAoB;kBACzE,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CACtC,CAAA;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;IACjF,CAAC;AACH,CAAC,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAEjF,OAAO,EACL,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,qBAAqB,EAC9F,MAAM,eAAe,CAAA;AAGtB;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AAgBtB;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;AAElC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,OAAe,EAAQ,EAAE;IACxD,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAM;IACR,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AACxB,CAAC,CAAA;AAED,4DAA4D;AAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;AAEjC;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,CAAC,GAAY,EAAE,GAAoB,EAAE,MAAgB,EAAQ,EAAE;IACrF,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,OAAM;IACR,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAEtB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAmB,GAAG,CAAC,KAAK,CAAC,CAAA;QAC9D,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,OAAM;QACR,CAAC;QAED,IAAI,SAA6B,CAAA;QACjC,IAAI,CAAC;YACH,SAAS,GAAG,UAAU,CAAC,KAAK,EAAE,CAAA;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,4FAA4F;YAC5F,kBAAkB;QACpB,CAAC;QAED,MAAM,KAAK,GAAmB;YAC5B,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAkC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChG,CAAA;QAED,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,CAAC,KAAK,CACX,eAAe,KAAK,CAAC,KAAK,oBAAoB,GAAG,CAAC,KAAK,oBAAoB;kBACzE,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CACtC,CAAA;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;IACjF,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAK,GAAW,SAAS,EAAE,IAAqB,EAAe,EAAE;IAC3F,MAAM,OAAO,GAAgB,iBAAiB,CAAc,KAAK,EAAE;QACjE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE;YAC/B,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;YACrB,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAmB,CAAA;YAEhD,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,CAAA;YACjC,CAAC;YAED,MAAM,IAAI,GAAG,GAAG,CAAC,IAAY,CAAA;YAC7B,MAAM,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAEpD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACjB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;gBAEjC,2FAA2F;gBAC3F,sFAAsF;gBACtF,2CAA2C;gBAC3C,MAAM,UAAU,GAAa,EAAE,CAAA;gBAE/B,yFAAyF;gBACzF,wFAAwF;gBACxF,6BAA6B;gBAC7B,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,CAAA;gBAEzD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;oBAClC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;oBAC7D,mFAAmF;oBACnF,mFAAmF;oBACnF,oCAAoC;oBACpC,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;oBAE5F,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;wBAClB,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,qBAAqB,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;wBAC7E,OAAO,KAAK,CAAA;oBACd,CAAC;oBAED,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;wBACrB,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;wBACrD,IAAI,UAAU,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;4BAC1B,IAAI,UAAU,CAAC,MAAM,KAAK,qBAAqB,CAAC,WAAW,EAAE,CAAC;gCAC5D,UAAU,CAAC,IAAI,CACb,IAAI,KAAK,8BAA8B,UAAU,CAAC,MAAM,GAAG;sCACzD,SAAS,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzC,CAAA;4BACH,CAAC;4BACD,OAAO,KAAK,CAAA;wBACd,CAAC;wBAED,OAAO,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;oBAC9E,CAAC;oBAED,OAAO,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;gBACnD,CAAC,CAAC,CAAA;gBAEF,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CACrC,GAAG,GAAG,CAAC,KAAK,KAAK,MAAM,EAAE,EACzB,uBAAuB,GAAG,CAAC,KAAK,8CAA8C,MAAM,EAAE,CACvF,CAAC,CAAA;oBAEF,MAAM,IAAI,aAAa,CAAC,YAAY,CAAC,CAAA;gBACvC,CAAC;gBAED,OAAM;YACR,CAAC;YAED,4FAA4F;YAC5F,+FAA+F;YAC/F,+EAA+E;YAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAA;YAC7E,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,CAChC,SAAS,GAAG,CAAC,KAAK,KAAK,KAAK,EAAE,EAC9B,eAAe,KAAK,oBAAoB,GAAG,CAAC,KAAK,4CAA4C;kBAC3F,4EAA4E,CAC/E,CAAC,CAAA;YAEF,MAAM,MAAM,GAAG,IAAI,EAAE,mBAAmB,KAAK,IAAI;gBAC/C,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC;gBAChE,CAAC,CAAC,MAAM,CAAA;YAEV,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,aAAa,CAAC,YAAY,CAAC,CAAA;YACvC,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAA;YACtE,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;YAClC,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;YAE/D,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,aAAa,CAAC,YAAY,CAAC,CAAA;YACvC,CAAC;QACH,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export type * from '@owlmeans/iam';
|
|
2
2
|
export { hasPermission } from '@owlmeans/iam';
|
|
3
|
+
export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc';
|
|
3
4
|
export * from './consts.js';
|
|
4
5
|
export * from './append.js';
|
|
5
6
|
export * from './gate.js';
|
|
7
|
+
export { organizationOf, organizationsOf } from './organization.js';
|
|
8
|
+
export { iamRuntime } from './runtime.js';
|
|
9
|
+
export type { IamRuntimeClient } from './runtime.js';
|
|
6
10
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,eAAe,CAAA;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,eAAe,CAAA;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { hasPermission } from '@owlmeans/iam';
|
|
2
|
+
export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc';
|
|
2
3
|
export * from './consts.js';
|
|
3
4
|
export * from './append.js';
|
|
4
5
|
export * from './gate.js';
|
|
6
|
+
export { organizationOf, organizationsOf } from './organization.js';
|
|
7
|
+
export { iamRuntime } from './runtime.js';
|
|
5
8
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AbstractRequest, ResolvedEntity } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { Config, Context, OIDCAuthCache } from '@owlmeans/server-oidc-rp';
|
|
3
|
+
/** The session record behind the request's wrapped token — the guard has already refreshed it. */
|
|
4
|
+
export declare const sessionOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => Promise<OIDCAuthCache>;
|
|
5
|
+
/**
|
|
6
|
+
* Every organization of the request's subject, as request entities keyed by their frozen IAM key.
|
|
7
|
+
*
|
|
8
|
+
* The session record is the authority — the provider's last claim, re-read on every validation —
|
|
9
|
+
* never the browser: a relying party of a tenanted client has no organization registry of its own.
|
|
10
|
+
*/
|
|
11
|
+
export declare const organizationsOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => Promise<ResolvedEntity[]>;
|
|
12
|
+
/**
|
|
13
|
+
* One organization of the request's subject by its slug — for a handler that acts in an
|
|
14
|
+
* organization the URL names rather than in the session's acting one. One the subject is not in is
|
|
15
|
+
* refused as `AuthForbidden(ORGANIZATION_REFUSAL)`, exactly as the switch refuses it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const organizationOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest, entitySlug: string) => Promise<ResolvedEntity>;
|
|
18
|
+
//# sourceMappingURL=organization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../src/organization.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAI3E,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE9E,kGAAkG;AAClG,eAAO,MAAM,SAAS,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAC3D,CAAC,WAAW,eAAe,KACnC,OAAO,CAAC,aAAa,CAMvB,CAAA;AASD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACjE,CAAC,WAAW,eAAe,KACnC,OAAO,CAAC,cAAc,EAAE,CAA6E,CAAA;AAExG;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,eAAe,cAAc,MAAM,KACvD,OAAO,CAAC,cAAc,CAOxB,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AuthForbidden, AuthorizationError } from '@owlmeans/auth';
|
|
2
|
+
import { ORGANIZATION_REFUSAL } from '@owlmeans/oidc';
|
|
3
|
+
import { resolvedEntityOf, sessionRecord } from '@owlmeans/server-oidc-rp';
|
|
4
|
+
/** The session record behind the request's wrapped token — the guard has already refreshed it. */
|
|
5
|
+
export const sessionOf = async (context, request) => {
|
|
6
|
+
if (request.auth == null) {
|
|
7
|
+
throw new AuthorizationError('auth');
|
|
8
|
+
}
|
|
9
|
+
return sessionRecord(context, request.auth.token);
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The organizations the provider last claimed for the session's subject. A session that acts in
|
|
13
|
+
* none — a client without the organizations scope — has none to offer, whatever its record holds.
|
|
14
|
+
*/
|
|
15
|
+
const claimedOf = (record) => record.acting == null ? [] : record.organizations ?? [];
|
|
16
|
+
/**
|
|
17
|
+
* Every organization of the request's subject, as request entities keyed by their frozen IAM key.
|
|
18
|
+
*
|
|
19
|
+
* The session record is the authority — the provider's last claim, re-read on every validation —
|
|
20
|
+
* never the browser: a relying party of a tenanted client has no organization registry of its own.
|
|
21
|
+
*/
|
|
22
|
+
export const organizationsOf = async (context, request) => claimedOf(await sessionOf(context, request)).map(resolvedEntityOf);
|
|
23
|
+
/**
|
|
24
|
+
* One organization of the request's subject by its slug — for a handler that acts in an
|
|
25
|
+
* organization the URL names rather than in the session's acting one. One the subject is not in is
|
|
26
|
+
* refused as `AuthForbidden(ORGANIZATION_REFUSAL)`, exactly as the switch refuses it.
|
|
27
|
+
*/
|
|
28
|
+
export const organizationOf = async (context, request, entitySlug) => {
|
|
29
|
+
const org = claimedOf(await sessionOf(context, request)).find(org => org.entitySlug === entitySlug);
|
|
30
|
+
if (org == null) {
|
|
31
|
+
throw new AuthForbidden(ORGANIZATION_REFUSAL);
|
|
32
|
+
}
|
|
33
|
+
return resolvedEntityOf(org);
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=organization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organization.js","sourceRoot":"","sources":["../src/organization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAG1E,kGAAkG;AAClG,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,OAAU,EAAE,OAAwB,EACZ,EAAE;IAC1B,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACtC,CAAC;IAED,OAAO,aAAa,CAAO,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACzD,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,MAAqB,EAA2B,EAAE,CACnE,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAA;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,OAAU,EAAE,OAAwB,EACT,EAAE,CAAC,SAAS,CAAC,MAAM,SAAS,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;AAExG;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAAU,EAAE,OAAwB,EAAE,UAAkB,EAC/B,EAAE;IAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,SAAS,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,UAAU,CAAC,CAAA;IACzG,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC/C,CAAC;IAED,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;AAC9B,CAAC,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { IamRuntimeGrant, IamRuntimeGrantQuery, IamRuntimeGrantRequest, IamRuntimeMember, IamRuntimeMemberInvite, IamRuntimeMemberUpdate, IamRuntimeOrganization, IamRuntimeOrganizationCreate, IamRuntimeOrganizationUpdate, IamRuntimePermission } from '@owlmeans/iam';
|
|
3
|
+
import type { Config, Context } from '@owlmeans/server-oidc-rp';
|
|
4
|
+
/**
|
|
5
|
+
* The runtime IAM API as the signed-in subject of one request: its organizations, their members,
|
|
6
|
+
* the grantable definitions and the grants. Every call acts as that subject — the provider decides
|
|
7
|
+
* owner and member rights from the access token, never from anything sent here.
|
|
8
|
+
*/
|
|
9
|
+
export interface IamRuntimeClient {
|
|
10
|
+
organizations: {
|
|
11
|
+
list: () => Promise<IamRuntimeOrganization[]>;
|
|
12
|
+
/** The subject becomes the new organization's owner. */
|
|
13
|
+
create: (body?: IamRuntimeOrganizationCreate) => Promise<IamRuntimeOrganization>;
|
|
14
|
+
update: (entitySlug: string, body: IamRuntimeOrganizationUpdate) => Promise<IamRuntimeOrganization>;
|
|
15
|
+
};
|
|
16
|
+
members: {
|
|
17
|
+
list: (entitySlug: string) => Promise<IamRuntimeMember[]>;
|
|
18
|
+
/** Find-or-create by e-mail: adding an address twice answers the same member. */
|
|
19
|
+
add: (entitySlug: string, invite: IamRuntimeMemberInvite) => Promise<IamRuntimeMember>;
|
|
20
|
+
update: (entitySlug: string, profileId: string, update: IamRuntimeMemberUpdate) => Promise<IamRuntimeMember>;
|
|
21
|
+
remove: (entitySlug: string, profileId: string) => Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
permissions: {
|
|
24
|
+
list: (entitySlug: string) => Promise<IamRuntimePermission[]>;
|
|
25
|
+
};
|
|
26
|
+
grants: {
|
|
27
|
+
list: (entitySlug: string, query?: IamRuntimeGrantQuery) => Promise<IamRuntimeGrant[]>;
|
|
28
|
+
assign: (entitySlug: string, grant: IamRuntimeGrantRequest) => Promise<IamRuntimeGrant>;
|
|
29
|
+
revoke: (entitySlug: string, grant: IamRuntimeGrantRequest) => Promise<void>;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A typed client of the provider's runtime IAM API, bound to one request.
|
|
34
|
+
*
|
|
35
|
+
* It calls as the request's subject: the bearer is the provider access token of that request's
|
|
36
|
+
* session record, read afresh on every call, so a token the guard has just refreshed is the one
|
|
37
|
+
* sent. The base URL is the provider's discovery field `IAM_API_METADATA`. Calls travel over plain
|
|
38
|
+
* `fetch`, because the API is another service's tree and a relying party binds none of it.
|
|
39
|
+
*/
|
|
40
|
+
export declare const iamRuntime: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => IamRuntimeClient;
|
|
41
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAG3D,OAAO,KAAK,EACK,eAAe,EAAuB,oBAAoB,EAAE,sBAAsB,EACjG,gBAAgB,EAAE,sBAAsB,EAAwB,sBAAsB,EAAE,sBAAsB,EAC9G,4BAA4B,EAA8B,4BAA4B,EAAE,oBAAoB,EAE7G,MAAM,eAAe,CAAA;AAGtB,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAqB,MAAM,0BAA0B,CAAA;AAGlF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE;QACb,IAAI,EAAE,MAAM,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAA;QAC7C,wDAAwD;QACxD,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,4BAA4B,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;QAChF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;KACpG,CAAA;IACD,OAAO,EAAE;QACP,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;QACzD,iFAAiF;QACjF,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;QACtF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC5G,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACjE,CAAA;IACD,WAAW,EAAE;QACX,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAA;KAC9D,CAAA;IACD,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;QACtF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;QACvF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KAC7E,CAAA;CACF;AA+FD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACtD,CAAC,WAAW,eAAe,KACnC,gBAsFF,CAAA"}
|
package/build/runtime.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { AuthForbidden, AuthorizationError, AuthUnavailable } from '@owlmeans/auth';
|
|
2
|
+
import { ResilientError } from '@owlmeans/error';
|
|
3
|
+
import { IAM_RUNTIME_ROUTES, IamClientError, IamError } from '@owlmeans/iam';
|
|
4
|
+
import { IAM_API_METADATA, ORGANIZATION_OWNER_REFUSAL, ORGANIZATION_REFUSAL } from '@owlmeans/oidc';
|
|
5
|
+
import { DEFAULT_ALIAS as OIDC_CLIENT_SERVICE } from '@owlmeans/server-oidc-rp';
|
|
6
|
+
import { sessionOf } from './organization.js';
|
|
7
|
+
/** `@owlmeans/server-api`'s incident header; a production error body is nothing but this id. */
|
|
8
|
+
const INCIDENT_ID_HEADER = 'x-incident-id';
|
|
9
|
+
/**
|
|
10
|
+
* Discovered runtime bases, per context and per client. Discovery is a network round trip the OIDC
|
|
11
|
+
* client service does not memoise, and the base never changes while a provider runs. A failed
|
|
12
|
+
* lookup is forgotten, so the next request asks again rather than inheriting the error.
|
|
13
|
+
*/
|
|
14
|
+
const bases = new WeakMap();
|
|
15
|
+
const runtimeBase = (context, clientId) => {
|
|
16
|
+
const known = bases.get(context) ?? new Map();
|
|
17
|
+
bases.set(context, known);
|
|
18
|
+
const cached = known.get(clientId);
|
|
19
|
+
if (cached != null) {
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
const attempt = (async () => {
|
|
23
|
+
const client = await context.service(OIDC_CLIENT_SERVICE).getClient(clientId);
|
|
24
|
+
const base = client.getMetadata()[IAM_API_METADATA];
|
|
25
|
+
if (typeof base !== 'string' || base === '') {
|
|
26
|
+
// A provider that advertises no runtime API — Keycloak, or an integrated provider that does not
|
|
27
|
+
// serve it — has nothing to call.
|
|
28
|
+
throw new IamClientError(`runtime-api:${clientId}`);
|
|
29
|
+
}
|
|
30
|
+
return base.replace(/\/+$/, '');
|
|
31
|
+
})();
|
|
32
|
+
known.set(clientId, attempt);
|
|
33
|
+
attempt.catch(() => {
|
|
34
|
+
if (known.get(clientId) === attempt) {
|
|
35
|
+
known.delete(clientId);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return attempt;
|
|
39
|
+
};
|
|
40
|
+
const pathOf = (path, params = {}) => path.replace(/:(\w+)/g, (_, key) => {
|
|
41
|
+
const value = params[key];
|
|
42
|
+
if (value == null || value === '') {
|
|
43
|
+
throw new IamError(`runtime:param:${key}`);
|
|
44
|
+
}
|
|
45
|
+
return encodeURIComponent(value);
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* The error a refused call rejects with.
|
|
49
|
+
*
|
|
50
|
+
* A development server sends the marshalled error, which is rebuilt into its own class. A production
|
|
51
|
+
* one sends only an incident id, so the status and the route's access level are all there is to go
|
|
52
|
+
* on: a 403 from an owner-level route is the subject not owning the organization, from any other
|
|
53
|
+
* route the subject not being in it.
|
|
54
|
+
*/
|
|
55
|
+
const failureOf = async (response, access) => {
|
|
56
|
+
const text = await response.text().catch(() => '');
|
|
57
|
+
const incidentId = response.headers.get(INCIDENT_ID_HEADER) ?? undefined;
|
|
58
|
+
if (text.includes(ResilientError.separator)) {
|
|
59
|
+
try {
|
|
60
|
+
const error = ResilientError.ensure(text, true);
|
|
61
|
+
error.incidentId ??= incidentId;
|
|
62
|
+
return error;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Not a marshalled error after all — fall through to the status.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const error = response.status === 401 ? new AuthorizationError('iam-runtime')
|
|
69
|
+
: response.status === 403 ? new AuthForbidden(access === 'owner' ? ORGANIZATION_OWNER_REFUSAL : ORGANIZATION_REFUSAL)
|
|
70
|
+
: new IamError(`runtime:status:${response.status}`);
|
|
71
|
+
if (incidentId != null) {
|
|
72
|
+
error.incidentId = incidentId;
|
|
73
|
+
}
|
|
74
|
+
return error;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* A typed client of the provider's runtime IAM API, bound to one request.
|
|
78
|
+
*
|
|
79
|
+
* It calls as the request's subject: the bearer is the provider access token of that request's
|
|
80
|
+
* session record, read afresh on every call, so a token the guard has just refreshed is the one
|
|
81
|
+
* sent. The base URL is the provider's discovery field `IAM_API_METADATA`. Calls travel over plain
|
|
82
|
+
* `fetch`, because the API is another service's tree and a relying party binds none of it.
|
|
83
|
+
*/
|
|
84
|
+
export const iamRuntime = (context, request) => {
|
|
85
|
+
const call = async ({ method, path, access, params, query, body }) => {
|
|
86
|
+
const record = await sessionOf(context, request);
|
|
87
|
+
const clientId = record.client ?? context.service(OIDC_CLIENT_SERVICE).getDefault();
|
|
88
|
+
if (clientId == null) {
|
|
89
|
+
throw new AuthUnavailable('oidc-client');
|
|
90
|
+
}
|
|
91
|
+
const token = record.payload?.access_token;
|
|
92
|
+
if (token == null || token === '') {
|
|
93
|
+
throw new AuthForbidden('record');
|
|
94
|
+
}
|
|
95
|
+
const url = new URL(`${await runtimeBase(context, clientId)}${pathOf(path, params)}`);
|
|
96
|
+
Object.entries(query ?? {}).forEach(([key, value]) => {
|
|
97
|
+
if (value != null) {
|
|
98
|
+
url.searchParams.set(key, String(value));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const response = await fetch(url, {
|
|
102
|
+
method,
|
|
103
|
+
headers: {
|
|
104
|
+
authorization: `Bearer ${token}`,
|
|
105
|
+
accept: 'application/json',
|
|
106
|
+
...(method === 'POST' ? { 'content-type': 'application/json' } : {}),
|
|
107
|
+
},
|
|
108
|
+
// A JSON content type with an empty body is refused by the server, so a POST always sends one.
|
|
109
|
+
...(method === 'POST' ? { body: JSON.stringify(body ?? {}) } : {}),
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw await failureOf(response, access);
|
|
113
|
+
}
|
|
114
|
+
return await response.json();
|
|
115
|
+
};
|
|
116
|
+
const R = IAM_RUNTIME_ROUTES;
|
|
117
|
+
return {
|
|
118
|
+
organizations: {
|
|
119
|
+
list: async () => (await call({
|
|
120
|
+
method: 'GET', path: R.organizations, access: 'subject',
|
|
121
|
+
})).items,
|
|
122
|
+
create: async (body) => call({
|
|
123
|
+
method: 'POST', path: R.organizations, access: 'subject', body: body ?? {},
|
|
124
|
+
}),
|
|
125
|
+
update: async (entitySlug, body) => call({
|
|
126
|
+
method: 'POST', path: R.organization, access: 'owner', params: { entitySlug }, body,
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
members: {
|
|
130
|
+
list: async (entitySlug) => (await call({
|
|
131
|
+
method: 'GET', path: R.members, access: 'member', params: { entitySlug },
|
|
132
|
+
})).items,
|
|
133
|
+
add: async (entitySlug, invite) => call({
|
|
134
|
+
method: 'POST', path: R.members, access: 'owner', params: { entitySlug }, body: invite,
|
|
135
|
+
}),
|
|
136
|
+
update: async (entitySlug, profileId, update) => call({
|
|
137
|
+
method: 'POST', path: R.member, access: 'owner', params: { entitySlug, profileId }, body: update,
|
|
138
|
+
}),
|
|
139
|
+
remove: async (entitySlug, profileId) => {
|
|
140
|
+
await call({
|
|
141
|
+
method: 'POST', path: R.memberRemove, access: 'owner', params: { entitySlug, profileId },
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
permissions: {
|
|
146
|
+
list: async (entitySlug) => (await call({
|
|
147
|
+
method: 'GET', path: R.permissions, access: 'member', params: { entitySlug },
|
|
148
|
+
})).items,
|
|
149
|
+
},
|
|
150
|
+
grants: {
|
|
151
|
+
list: async (entitySlug, query) => (await call({
|
|
152
|
+
method: 'GET', path: R.grants, access: 'owner', params: { entitySlug }, query,
|
|
153
|
+
})).items,
|
|
154
|
+
assign: async (entitySlug, grant) => call({
|
|
155
|
+
method: 'POST', path: R.grants, access: 'owner', params: { entitySlug }, body: grant,
|
|
156
|
+
}),
|
|
157
|
+
revoke: async (entitySlug, grant) => {
|
|
158
|
+
await call({
|
|
159
|
+
method: 'POST', path: R.grantsRevoke, access: 'owner', params: { entitySlug }, body: grant,
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/server-iam",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.54",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
25
|
+
"@owlmeans/auth": "^0.1.18-rc.38",
|
|
26
26
|
"@owlmeans/context": "^0.1.18-rc.32",
|
|
27
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/
|
|
29
|
-
"@owlmeans/
|
|
30
|
-
"@owlmeans/
|
|
31
|
-
"@owlmeans/server-
|
|
27
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.39",
|
|
28
|
+
"@owlmeans/error": "^0.1.18-rc.36",
|
|
29
|
+
"@owlmeans/iam": "^0.1.18-rc.47",
|
|
30
|
+
"@owlmeans/oidc": "^0.1.18-rc.52",
|
|
31
|
+
"@owlmeans/server-context": "^0.1.18-rc.40",
|
|
32
|
+
"@owlmeans/server-oidc-rp": "^0.1.18-rc.52"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@owlmeans/dep-config": "workspace:*",
|
package/src/gate.ts
CHANGED
|
@@ -104,7 +104,8 @@ const auditEntrypoint = (ctx: Context, req: AbstractRequest, params: string[]):
|
|
|
104
104
|
* appendIam() so target code never knows which IAM backend is active.
|
|
105
105
|
*
|
|
106
106
|
* 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
|
|
107
|
-
* params are asserted locally against it — both unscoped and resource-scoped forms
|
|
107
|
+
* params are asserted locally against it — both unscoped and resource-scoped forms, and a set
|
|
108
|
+
* bound to an organization only for the organization the request acts in.
|
|
108
109
|
* 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
|
|
109
110
|
* @-suffixes are stripped and the check delegates to the UMA2 gate model from
|
|
110
111
|
* @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
|
|
@@ -134,6 +135,11 @@ export const makeIamGate = (alias: string = OIDC_GATE, opts?: IamGateOptions): G
|
|
|
134
135
|
// repaired in completely different places.
|
|
135
136
|
const structural: string[] = []
|
|
136
137
|
|
|
138
|
+
// The organization the request acts in. A relying party of a tenanted client attaches it
|
|
139
|
+
// through the guard; otherwise it is the token's. A set bound to any other organization
|
|
140
|
+
// never satisfies the check.
|
|
141
|
+
const entitySlug = req.entity?.slug ?? entitySlugOf(auth)
|
|
142
|
+
|
|
137
143
|
const granted = params.some(param => {
|
|
138
144
|
const { permission, resource, error } = parseGateParam(param)
|
|
139
145
|
// Grants are stored against the entity's stable id wherever one is resolvable; the
|
|
@@ -158,10 +164,10 @@ export const makeIamGate = (alias: string = OIDC_GATE, opts?: IamGateOptions): G
|
|
|
158
164
|
return false
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
return hasPermission(auth, fixed, { resourceId: resolution.id })
|
|
167
|
+
return hasPermission(auth, fixed, { resourceId: resolution.id, entitySlug })
|
|
162
168
|
}
|
|
163
169
|
|
|
164
|
-
return hasPermission(auth, fixed)
|
|
170
|
+
return hasPermission(auth, fixed, { entitySlug })
|
|
165
171
|
})
|
|
166
172
|
|
|
167
173
|
if (!granted) {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export type * from '@owlmeans/iam'
|
|
2
2
|
export { hasPermission } from '@owlmeans/iam'
|
|
3
|
+
export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc'
|
|
3
4
|
export * from './consts.js'
|
|
4
5
|
export * from './append.js'
|
|
5
6
|
export * from './gate.js'
|
|
7
|
+
export { organizationOf, organizationsOf } from './organization.js'
|
|
8
|
+
export { iamRuntime } from './runtime.js'
|
|
9
|
+
export type { IamRuntimeClient } from './runtime.js'
|