@owlmeans/server-iam 0.1.18-rc.56 → 0.1.18-rc.57
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 -4
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-iam/SKILL.md +12 -12
- package/build/gate.d.ts +2 -2
- package/build/gate.d.ts.map +1 -1
- package/build/gate.js +69 -69
- package/build/gate.js.map +1 -1
- package/build/index.d.ts +3 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/build/organization/types.d.ts +21 -0
- package/build/organization/types.d.ts.map +1 -0
- package/build/organization/types.js +2 -0
- package/build/organization/types.js.map +1 -0
- package/build/organization.d.ts +5 -14
- package/build/organization.d.ts.map +1 -1
- package/build/organization.js +20 -25
- package/build/organization.js.map +1 -1
- package/build/runtime.d.ts +2 -0
- package/build/runtime.d.ts.map +1 -1
- package/build/runtime.js +32 -29
- package/build/runtime.js.map +1 -1
- package/build/types.local.d.ts +2 -1
- package/build/types.local.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/gate.ts +75 -75
- package/src/index.ts +3 -2
- package/src/organization/types.ts +21 -0
- package/src/organization.ts +30 -31
- package/src/runtime.ts +40 -33
- package/src/types.local.ts +1 -1
- package/tests/organization.spec.ts +8 -8
- package/tests/runtime.spec.ts +5 -4
package/README.md
CHANGED
|
@@ -7,14 +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` /
|
|
11
|
-
- `
|
|
10
|
+
- `makeOrganizationScope(context, request)` — `.organizationOf(slug)` / `.organizationsOf()`: the organizations of a tenanted session, from its session record
|
|
11
|
+
- `makeIamRuntimeClient(context, request)` — a typed client of the provider's runtime IAM API, acting as the request's subject
|
|
12
12
|
- Designed for IAM consumers such as the viable target template backend
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
bun add @owlmeans/server-iam@^0.1.18-rc.
|
|
17
|
+
bun add @owlmeans/server-iam@^0.1.18-rc.57
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
@@ -47,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
47
47
|
your project's skill store (`.agents/skills/`):
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
50
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.49
|
|
51
51
|
```
|
|
52
52
|
|
|
53
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-10-
|
|
4
|
+
"version": "0.1.18-rc.57",
|
|
5
|
+
"generatedAt": "2026-10-05T21:03:06.380Z",
|
|
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), 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 (
|
|
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 (makeOrganizationScope: organizationOf/organizationsOf), the request-bound runtime IAM client (makeIamRuntimeClient), 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,7 +8,7 @@ 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.57"` 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
14
|
IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is active. For a
|
|
@@ -22,13 +22,13 @@ as the signed-in subject.
|
|
|
22
22
|
| `appendIam(context)` | fn | Registers `makeOidcClientService()`, `makeOidcWrappingService()`, `makeIamGate()` (under `OIDC_GATE`), and the OIDC guard |
|
|
23
23
|
| `makeIamGate(alias?, opts?)` | fn | The IAM `GateService` — claims-first assertion with UMA2 fallback |
|
|
24
24
|
| `IamGateOptions` | type | `{ strictResourceScope?: boolean }` — refuse a scoped param on the fallback path instead of widening it |
|
|
25
|
-
| `parseGateParam` /
|
|
25
|
+
| `gateParamHelper` (`.parseGateParam` / `.parseGateSelector` / `.formatGateParam`) / `gateValidationHelper.validateGateParams` / `resolveGateResource` | helper / fn | Re-exports from `@owlmeans/iam`, which owns the grammar |
|
|
26
26
|
| `RESOURCE_PARAM_SEPARATOR` / `RESOURCE_SOURCE_SEPARATOR` / `RESOURCE_PATH_SEPARATOR` | const | `'@'`, `':'` and `'.'`, re-exported |
|
|
27
27
|
| `GateParamSource` / `GateParamErrorCode` / `GateResolutionFailure` | enum | Re-exported; the sources a selector may name, and why one failed |
|
|
28
28
|
| `hasPermission` | fn | Re-export from `@owlmeans/iam` |
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
29
|
+
| `makeOrganizationScope(context, request).organizationOf(entitySlug)` | scope | 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
|
+
| `makeOrganizationScope(context, request).organizationsOf()` | scope | Every organization of the request's subject, as `ResolvedEntity[]` |
|
|
31
|
+
| `makeIamRuntimeClient(context, request)` | fn | The request-bound `IamRuntimeClient` of the provider's runtime IAM API — see "The runtime IAM client" |
|
|
32
32
|
| `IamRuntimeClient` | type | `organizations.{list,create,update}`, `members.{list,add,update,remove}`, `permissions.list`, `grants.{list,assign,revoke}` |
|
|
33
33
|
| `ORGANIZATION_REFUSAL` / `ORGANIZATION_OWNER_REFUSAL` | const | Re-exported from `@owlmeans/oidc`: the `AuthForbidden` reasons of a non-member and of a non-owner |
|
|
34
34
|
| `SERVER_IAM_SERVICE` | const | `'server-iam-service'` — exported and referenced nowhere; the gate registers under `OIDC_GATE`, not under this |
|
|
@@ -74,10 +74,10 @@ Sources: `params`, `query`, `body`, `headers`, `auth`.
|
|
|
74
74
|
**The `@` belongs to the gate and nowhere else.** It is never part of a permission's stored NAME. A
|
|
75
75
|
definition or grant written as `enquiry--view@enquiryId` is a key nothing looks up, so the permission
|
|
76
76
|
reads as granted in every screen and every request is still refused, with nothing logged.
|
|
77
|
-
`ensurePermission` refuses such a name; `validateGateParams` catches the mirror-image mistake at
|
|
77
|
+
`ensurePermission` refuses such a name; `gateValidationHelper.validateGateParams` catches the mirror-image mistake at
|
|
78
78
|
generation time.
|
|
79
79
|
|
|
80
|
-
### `validateGateParams(params, audit?)`
|
|
80
|
+
### `gateValidationHelper.validateGateParams(params, audit?)`
|
|
81
81
|
|
|
82
82
|
Reports what is provable from an entrypoint's own declaration: a selector naming a route param the
|
|
83
83
|
path does not carry, or a `body`/`query`/`headers` key the `Filter` does not declare **while that
|
|
@@ -103,7 +103,7 @@ await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
|
|
|
103
103
|
1. `req.auth == null` → `AuthForbidden('auth')`.
|
|
104
104
|
2. **Claims mode** — when `req.auth.permissions` is a valid `PermissionSet[]` (minted into the id_token
|
|
105
105
|
by the integrated IAM provider and mapped into `Auth` by `@owlmeans/server-oidc-rp`), params are
|
|
106
|
-
asserted locally via `hasPermission`, with `entitySlug: req.entity?.slug ?? entitySlugOf(auth)` —
|
|
106
|
+
asserted locally via `hasPermission`, with `entitySlug: req.entity?.slug ?? authHelper.entitySlugOf(auth)` —
|
|
107
107
|
the organization the request acts in.
|
|
108
108
|
3. **Fallback** — otherwise `@…` suffixes are stripped and the check delegates to the UMA2 gate model
|
|
109
109
|
from `@owlmeans/server-oidc-rp` (`createGateModel().loadPermissions`) — exactly the check
|
|
@@ -144,7 +144,7 @@ store must not grant against slugs, because a rename would then orphan every gra
|
|
|
144
144
|
|
|
145
145
|
A relying party of a tenanted client has no organization registry of its own: the provider's
|
|
146
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
|
|
147
|
+
re-read on every validation. `makeOrganizationScope(context, request).organizationsOf()` answers all of it and `.organizationOf(slug)` one entry by
|
|
148
148
|
slug, as `ResolvedEntity` values keyed by the frozen `entityKey` — use them for a handler that acts in
|
|
149
149
|
an organization its URL names rather than the session's acting one (which the guard already attaches
|
|
150
150
|
as `req.entity`). A session of a client without the scope acts in none and has none to offer; an
|
|
@@ -152,7 +152,7 @@ unauthenticated request or a session that is gone is `AuthorizationError` (401).
|
|
|
152
152
|
|
|
153
153
|
## The runtime IAM client
|
|
154
154
|
|
|
155
|
-
`
|
|
155
|
+
`makeIamRuntimeClient(context, request)` calls the provider's runtime IAM API (`makeIamRuntimeProtocols` in
|
|
156
156
|
`@owlmeans/iam`) as that request's subject:
|
|
157
157
|
|
|
158
158
|
- **Base** — the provider's discovery field `IAM_API_METADATA` (`owlmeans_iam_api`), read through
|
|
@@ -182,7 +182,7 @@ the token; nothing sent here can widen them.
|
|
|
182
182
|
- The OIDC provider entry must request the `permissions` scope (`extraScopes`) for claims mode to
|
|
183
183
|
engage; without it the gate transparently uses the fallback. A tenanted client also requests
|
|
184
184
|
`organizations`.
|
|
185
|
-
- Reach the runtime IAM API only through `
|
|
185
|
+
- Reach the runtime IAM API only through `makeIamRuntimeClient`; never forward the browser's wrapped token or
|
|
186
186
|
build a bearer from anything but the session record.
|
|
187
187
|
|
|
188
188
|
## Related instructions
|
package/build/gate.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { IamGateOptions } from './types.js';
|
|
|
5
5
|
* tooling can all read it. Re-exported here because this module was its home and is the documented
|
|
6
6
|
* import path.
|
|
7
7
|
*/
|
|
8
|
-
export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure,
|
|
9
|
-
export type { ParsedGateParam, GateResourceSelector, GateParamAudit } from '@owlmeans/iam';
|
|
8
|
+
export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure, gateParamHelper, gateValidationHelper, resolveGateResource } from '@owlmeans/iam';
|
|
9
|
+
export type { ParsedGateParam, GateResourceSelector, GateParamAudit, GateParamHelper, GateValidationHelper } from '@owlmeans/iam';
|
|
10
10
|
/**
|
|
11
11
|
* IAM gate: claims-first with UMA2 fallback. Registered under the OIDC_GATE alias by
|
|
12
12
|
* appendIam() so target code never knows which IAM backend is active.
|
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;AAM1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAM1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,eAAe,EAAE,oBAAoB,EAAE,mBAAmB,EAC3D,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAejI;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,SAAqB,cAAc,KAAG,WAkK9E,CAAA"}
|
package/build/gate.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { createLazyService } from '@owlmeans/context';
|
|
2
|
-
import {
|
|
2
|
+
import { authHelper, AuthForbidden } from '@owlmeans/auth';
|
|
3
3
|
import { OIDC_GATE } from '@owlmeans/oidc';
|
|
4
4
|
import { createGateModel, extractPermissionSets } from '@owlmeans/server-oidc-rp';
|
|
5
|
-
import { hasPermission,
|
|
5
|
+
import { hasPermission, gateParamHelper, gateValidationHelper, resolveGateResource, GateResolutionFailure } from '@owlmeans/iam';
|
|
6
6
|
import { logger, logThrottle } from '@owlmeans/log';
|
|
7
7
|
/**
|
|
8
8
|
* The gate-param grammar lives in `@owlmeans/iam` so the browser, the adapters and code-generation
|
|
9
9
|
* tooling can all read it. Re-exported here because this module was its home and is the documented
|
|
10
10
|
* import path.
|
|
11
11
|
*/
|
|
12
|
-
export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure,
|
|
12
|
+
export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure, gateParamHelper, gateValidationHelper, resolveGateResource } from '@owlmeans/iam';
|
|
13
13
|
/**
|
|
14
14
|
* Structural faults are logged once per alias+param+reason.
|
|
15
15
|
*
|
|
@@ -18,68 +18,8 @@ export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPA
|
|
|
18
18
|
*/
|
|
19
19
|
const reported = new Set();
|
|
20
20
|
const log = logger('server-iam:gate');
|
|
21
|
-
const reportOnce = (key, message, data) => {
|
|
22
|
-
if (reported.has(key)) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
reported.add(key);
|
|
26
|
-
log.error(message, data);
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* A refusal of the gate. Logged once a minute per route, reason and subject — a hot endpoint
|
|
30
|
-
* refuses on every request, and one line per window says the same thing.
|
|
31
|
-
*/
|
|
32
|
-
const forbidden = (req, reason, params) => {
|
|
33
|
-
const auth = req.auth;
|
|
34
|
-
if (logThrottle(`access.forbidden\0${req.alias ?? ''}\0${reason}\0${auth?.userId ?? ''}`, 60_000)) {
|
|
35
|
-
log.warn('Access forbidden', {
|
|
36
|
-
alias: req.alias, reason, params, userId: auth?.userId, entitySlug: req.entity?.slug ?? (auth != null ? entitySlugOf(auth) : undefined),
|
|
37
|
-
}, { event: 'access.forbidden' });
|
|
38
|
-
}
|
|
39
|
-
return new AuthForbidden(reason);
|
|
40
|
-
};
|
|
41
21
|
/** Aliases whose declarations have already been checked. */
|
|
42
22
|
const audited = new Set();
|
|
43
|
-
/**
|
|
44
|
-
* Check an entrypoint's gate params against what it declares, once, the first time it is reached.
|
|
45
|
-
*
|
|
46
|
-
* Structurally unable to affect the outcome: it only ever logs. A selector naming a key the
|
|
47
|
-
* entrypoint's filter does not declare is stripped by validation before the gate runs, so the
|
|
48
|
-
* endpoint denies every request with a clean build and nothing else to go on — this is the line that
|
|
49
|
-
* says why.
|
|
50
|
-
*/
|
|
51
|
-
const auditEntrypoint = (ctx, req, params) => {
|
|
52
|
-
if (req.alias == null || audited.has(req.alias)) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
audited.add(req.alias);
|
|
56
|
-
try {
|
|
57
|
-
const entrypoint = ctx.entrypoint(req.alias);
|
|
58
|
-
if (entrypoint == null) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
let routePath;
|
|
62
|
-
try {
|
|
63
|
-
routePath = entrypoint.mount();
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
// The mount needs a service with an address; an entrypoint that has none still has a filter
|
|
67
|
-
// worth auditing.
|
|
68
|
-
}
|
|
69
|
-
const audit = {
|
|
70
|
-
...(routePath != null ? { routePath } : {}),
|
|
71
|
-
...(entrypoint.filter != null ? { filter: entrypoint.filter } : {})
|
|
72
|
-
};
|
|
73
|
-
for (const issue of validateGateParams(params, audit)) {
|
|
74
|
-
log.error('Gate param is misconfigured', {
|
|
75
|
-
param: issue.param, alias: req.alias, code: issue.code, detail: issue.detail,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// An entrypoint that cannot be looked up is not this gate's problem to report.
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
23
|
/**
|
|
84
24
|
* IAM gate: claims-first with UMA2 fallback. Registered under the OIDC_GATE alias by
|
|
85
25
|
* appendIam() so target code never knows which IAM backend is active.
|
|
@@ -96,6 +36,66 @@ const auditEntrypoint = (ctx, req, params) => {
|
|
|
96
36
|
* have passed must not be refused because of it.
|
|
97
37
|
*/
|
|
98
38
|
export const makeIamGate = (alias = OIDC_GATE, opts) => {
|
|
39
|
+
const reportOnce = (key, message, data) => {
|
|
40
|
+
if (reported.has(key)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
reported.add(key);
|
|
44
|
+
log.error(message, data);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A refusal of the gate. Logged once a minute per route, reason and subject — a hot endpoint
|
|
48
|
+
* refuses on every request, and one line per window says the same thing.
|
|
49
|
+
*/
|
|
50
|
+
const forbidden = (req, reason, params) => {
|
|
51
|
+
const auth = req.auth;
|
|
52
|
+
if (logThrottle(`access.forbidden\0${req.alias ?? ''}\0${reason}\0${auth?.userId ?? ''}`, 60_000)) {
|
|
53
|
+
log.warn('Access forbidden', {
|
|
54
|
+
alias: req.alias, reason, params, userId: auth?.userId, entitySlug: req.entity?.slug ?? (auth != null ? authHelper.entitySlugOf(auth) : undefined),
|
|
55
|
+
}, { event: 'access.forbidden' });
|
|
56
|
+
}
|
|
57
|
+
return new AuthForbidden(reason);
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Check an entrypoint's gate params against what it declares, once, the first time it is reached.
|
|
61
|
+
*
|
|
62
|
+
* Structurally unable to affect the outcome: it only ever logs. A selector naming a key the
|
|
63
|
+
* entrypoint's filter does not declare is stripped by validation before the gate runs, so the
|
|
64
|
+
* endpoint denies every request with a clean build and nothing else to go on — this is the line that
|
|
65
|
+
* says why.
|
|
66
|
+
*/
|
|
67
|
+
const auditEntrypoint = (ctx, req, params) => {
|
|
68
|
+
if (req.alias == null || audited.has(req.alias)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
audited.add(req.alias);
|
|
72
|
+
try {
|
|
73
|
+
const entrypoint = ctx.entrypoint(req.alias);
|
|
74
|
+
if (entrypoint == null) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let routePath;
|
|
78
|
+
try {
|
|
79
|
+
routePath = entrypoint.mount();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// The mount needs a service with an address; an entrypoint that has none still has a filter
|
|
83
|
+
// worth auditing.
|
|
84
|
+
}
|
|
85
|
+
const audit = {
|
|
86
|
+
...(routePath != null ? { routePath } : {}),
|
|
87
|
+
...(entrypoint.filter != null ? { filter: entrypoint.filter } : {})
|
|
88
|
+
};
|
|
89
|
+
for (const issue of gateValidationHelper.validateGateParams(params, audit)) {
|
|
90
|
+
log.error('Gate param is misconfigured', {
|
|
91
|
+
param: issue.param, alias: req.alias, code: issue.code, detail: issue.detail,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// An entrypoint that cannot be looked up is not this gate's problem to report.
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
99
|
const service = createLazyService(alias, {
|
|
100
100
|
assert: async (req, _, params) => {
|
|
101
101
|
await service.ready();
|
|
@@ -114,13 +114,13 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
|
|
|
114
114
|
// The organization the request acts in. A relying party of a tenanted client attaches it
|
|
115
115
|
// through the guard; otherwise it is the token's. A set bound to any other organization
|
|
116
116
|
// never satisfies the check.
|
|
117
|
-
const entitySlug = req.entity?.slug ?? entitySlugOf(auth);
|
|
117
|
+
const entitySlug = req.entity?.slug ?? authHelper.entitySlugOf(auth);
|
|
118
118
|
const granted = params.some(param => {
|
|
119
|
-
const { permission, resource, error } = parseGateParam(param);
|
|
119
|
+
const { permission, resource, error } = gateParamHelper.parseGateParam(param);
|
|
120
120
|
// Grants are stored against the entity's stable id wherever one is resolvable; the
|
|
121
121
|
// slug is only a fallback for deployments with no organization store of their own,
|
|
122
122
|
// where the slug IS the identifier.
|
|
123
|
-
const fixed = permission.replaceAll('{entity}', req.entity?.id ?? entitySlugOf(auth) ?? '-');
|
|
123
|
+
const fixed = permission.replaceAll('{entity}', req.entity?.id ?? authHelper.entitySlugOf(auth) ?? '-');
|
|
124
124
|
if (error != null) {
|
|
125
125
|
structural.push(`"${param}" does not parse [${error.code}]: ${error.detail}`);
|
|
126
126
|
return false;
|
|
@@ -147,16 +147,16 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
|
|
|
147
147
|
// UMA2 fallback. Resource scoping is NOT enforced here: a Keycloak-backed deployment cannot
|
|
148
148
|
// store a resource-scoped grant, so a scoped param widens to a project-wide check and the same
|
|
149
149
|
// declaration means two different things depending on which backend is active.
|
|
150
|
-
const scoped = params.filter(param => parseGateParam(param).resource != null);
|
|
150
|
+
const scoped = params.filter(param => gateParamHelper.parseGateParam(param).resource != null);
|
|
151
151
|
scoped.forEach(param => reportOnce(`uma2\0${req.alias}\0${param}`, 'Gate param is resource-scoped, but resource scoping is not enforced in UMA2 mode — it is checked as a '
|
|
152
152
|
+ 'project-wide permission', { param, alias: req.alias }));
|
|
153
153
|
const usable = opts?.strictResourceScope === true
|
|
154
|
-
? params.filter(param => parseGateParam(param).resource == null)
|
|
154
|
+
? params.filter(param => gateParamHelper.parseGateParam(param).resource == null)
|
|
155
155
|
: params;
|
|
156
156
|
if (usable.length < 1) {
|
|
157
157
|
throw forbidden(req, 'permission', params);
|
|
158
158
|
}
|
|
159
|
-
const stripped = usable.map(param => parseGateParam(param).permission);
|
|
159
|
+
const stripped = usable.map(param => gateParamHelper.parseGateParam(param).permission);
|
|
160
160
|
const model = createGateModel(ctx);
|
|
161
161
|
const permissions = await model.loadPermissions(auth, stripped);
|
|
162
162
|
if (permissions.length < 1) {
|
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,
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export * from './consts.js';
|
|
|
5
5
|
export * from './append.js';
|
|
6
6
|
export * from './gate.js';
|
|
7
7
|
export type * from './types.js';
|
|
8
|
-
export { organizationOf, organizationsOf } from './organization.js';
|
|
9
|
-
export
|
|
8
|
+
export { makeOrganizationScope, organizationOf, organizationsOf } from './organization.js';
|
|
9
|
+
export type * from './organization/types.js';
|
|
10
|
+
export { makeIamRuntimeClient, iamRuntime } from './runtime.js';
|
|
10
11
|
export type { IamRuntimeClient } from './types.js';
|
|
11
12
|
//# 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,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,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,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAC1F,mBAAmB,yBAAyB,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -3,6 +3,6 @@ export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc
|
|
|
3
3
|
export * from './consts.js';
|
|
4
4
|
export * from './append.js';
|
|
5
5
|
export * from './gate.js';
|
|
6
|
-
export { organizationOf, organizationsOf } from './organization.js';
|
|
7
|
-
export { iamRuntime } from './runtime.js';
|
|
6
|
+
export { makeOrganizationScope, organizationOf, organizationsOf } from './organization.js';
|
|
7
|
+
export { makeIamRuntimeClient, iamRuntime } from './runtime.js';
|
|
8
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,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AAEzB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,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;AAEzB,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAE1F,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ResolvedEntity } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { OIDCAuthCache } from '@owlmeans/server-oidc-rp';
|
|
3
|
+
/** The organizations of one request's subject, read from its session — built once per handler. */
|
|
4
|
+
export interface OrganizationScope {
|
|
5
|
+
/** The session record behind the request's wrapped token — the guard has already refreshed it. */
|
|
6
|
+
sessionOf: () => Promise<OIDCAuthCache>;
|
|
7
|
+
/**
|
|
8
|
+
* Every organization of the request's subject, as request entities keyed by their frozen IAM key.
|
|
9
|
+
*
|
|
10
|
+
* The session record is the authority — the provider's last claim, re-read on every validation —
|
|
11
|
+
* never the browser: a relying party of a tenanted client has no organization registry of its own.
|
|
12
|
+
*/
|
|
13
|
+
organizationsOf: () => Promise<ResolvedEntity[]>;
|
|
14
|
+
/**
|
|
15
|
+
* One organization of the request's subject by its slug — for a handler that acts in an
|
|
16
|
+
* organization the URL names rather than in the session's acting one. One the subject is not in is
|
|
17
|
+
* refused as `AuthForbidden(ORGANIZATION_REFUSAL)`, exactly as the switch refuses it.
|
|
18
|
+
*/
|
|
19
|
+
organizationOf: (entitySlug: string) => Promise<ResolvedEntity>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/organization/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE7D,kGAAkG;AAClG,MAAM,WAAW,iBAAiB;IAChC,kGAAkG;IAClG,SAAS,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IACvC;;;;;OAKG;IACH,eAAe,EAAE,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;IAChD;;;;OAIG;IACH,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAA;CAChE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/organization/types.ts"],"names":[],"mappings":""}
|
package/build/organization.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import type { AbstractRequest, ResolvedEntity } from '@owlmeans/entrypoint';
|
|
2
|
-
import type { Config, Context
|
|
3
|
-
|
|
4
|
-
export declare const
|
|
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
|
-
*/
|
|
2
|
+
import type { Config, Context } from '@owlmeans/server-oidc-rp';
|
|
3
|
+
import type { OrganizationScope } from './organization/types.js';
|
|
4
|
+
export declare const makeOrganizationScope: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => OrganizationScope;
|
|
5
|
+
/** @deprecated compat:factory-refactor — use `makeOrganizationScope(context, request).organizationsOf()` */
|
|
11
6
|
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
|
-
*/
|
|
7
|
+
/** @deprecated compat:factory-refactor — use `makeOrganizationScope(context, request).organizationOf(…)` */
|
|
17
8
|
export declare const organizationOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest, entitySlug: string) => Promise<ResolvedEntity>;
|
|
18
9
|
//# sourceMappingURL=organization.d.ts.map
|
|
@@ -1 +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,
|
|
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,EAAiB,MAAM,0BAA0B,CAAA;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAShE,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACjE,CAAC,WAAW,eAAe,KACnC,iBAqBF,CAAA;AAED,4GAA4G;AAC5G,eAAO,MAAM,eAAe,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACjE,CAAC,WAAW,eAAe,KACnC,OAAO,CAAC,cAAc,EAAE,CAA0E,CAAA;AAErG,4GAA4G;AAC5G,eAAO,MAAM,cAAc,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,eAAe,cAAc,MAAM,KACvD,OAAO,CAAC,cAAc,CAAmF,CAAA"}
|
package/build/organization.js
CHANGED
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
import { AuthForbidden, AuthorizationError } from '@owlmeans/auth';
|
|
2
2
|
import { ORGANIZATION_REFUSAL } from '@owlmeans/oidc';
|
|
3
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
4
|
/**
|
|
12
5
|
* The organizations the provider last claimed for the session's subject. A session that acts in
|
|
13
6
|
* none — a client without the organizations scope — has none to offer, whatever its record holds.
|
|
14
7
|
*/
|
|
15
8
|
const claimedOf = (record) => record.acting == null ? [] : record.organizations ?? [];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return resolvedEntityOf(org);
|
|
9
|
+
export const makeOrganizationScope = (context, request) => {
|
|
10
|
+
const sessionOf = async () => {
|
|
11
|
+
if (request.auth == null) {
|
|
12
|
+
throw new AuthorizationError('auth');
|
|
13
|
+
}
|
|
14
|
+
return sessionRecord(context, request.auth.token);
|
|
15
|
+
};
|
|
16
|
+
const organizationsOf = async () => claimedOf(await sessionOf()).map(resolvedEntityOf);
|
|
17
|
+
const organizationOf = async (entitySlug) => {
|
|
18
|
+
const org = claimedOf(await sessionOf()).find(org => org.entitySlug === entitySlug);
|
|
19
|
+
if (org == null) {
|
|
20
|
+
throw new AuthForbidden(ORGANIZATION_REFUSAL);
|
|
21
|
+
}
|
|
22
|
+
return resolvedEntityOf(org);
|
|
23
|
+
};
|
|
24
|
+
return { sessionOf, organizationsOf, organizationOf };
|
|
34
25
|
};
|
|
26
|
+
/** @deprecated compat:factory-refactor — use `makeOrganizationScope(context, request).organizationsOf()` */
|
|
27
|
+
export const organizationsOf = async (context, request) => await makeOrganizationScope(context, request).organizationsOf();
|
|
28
|
+
/** @deprecated compat:factory-refactor — use `makeOrganizationScope(context, request).organizationOf(…)` */
|
|
29
|
+
export const organizationOf = async (context, request, entitySlug) => await makeOrganizationScope(context, request).organizationOf(entitySlug);
|
|
35
30
|
//# sourceMappingURL=organization.js.map
|
|
@@ -1 +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;
|
|
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;AAI1E;;;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,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAAU,EAAE,OAAwB,EACjB,EAAE;IACrB,MAAM,SAAS,GAAG,KAAK,IAA4B,EAAE;QACnD,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;QACtC,CAAC;QAED,OAAO,aAAa,CAAO,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzD,CAAC,CAAA;IAED,MAAM,eAAe,GAAG,KAAK,IAA+B,EAAE,CAAC,SAAS,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAEjH,MAAM,cAAc,GAAG,KAAK,EAAE,UAAkB,EAA2B,EAAE;QAC3E,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,UAAU,CAAC,CAAA;QACnF,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,aAAa,CAAC,oBAAoB,CAAC,CAAA;QAC/C,CAAC;QAED,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC,CAAA;IAED,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,CAAA;AACvD,CAAC,CAAA;AAED,4GAA4G;AAC5G,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,OAAU,EAAE,OAAwB,EACT,EAAE,CAAC,MAAM,qBAAqB,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,eAAe,EAAE,CAAA;AAErG,4GAA4G;AAC5G,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAAU,EAAE,OAAwB,EAAE,UAAkB,EAC/B,EAAE,CAAC,MAAM,qBAAqB,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA"}
|
package/build/runtime.d.ts
CHANGED
|
@@ -9,5 +9,7 @@ import type { IamRuntimeClient } from './types.js';
|
|
|
9
9
|
* sent. The base URL is the provider's discovery field `IAM_API_METADATA`. Calls travel over plain
|
|
10
10
|
* `fetch`, because the API is another service's tree and a relying party binds none of it.
|
|
11
11
|
*/
|
|
12
|
+
export declare const makeIamRuntimeClient: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => IamRuntimeClient;
|
|
13
|
+
/** @deprecated compat:factory-refactor — use `makeIamRuntimeClient(context, request)` */
|
|
12
14
|
export declare const iamRuntime: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => IamRuntimeClient;
|
|
13
15
|
//# sourceMappingURL=runtime.d.ts.map
|
package/build/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAwC,KAAK,MAAM,EAAE,KAAK,OAAO,EAA0B,MAAM,0BAA0B,CAAA;AAIlI,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAwC,KAAK,MAAM,EAAE,KAAK,OAAO,EAA0B,MAAM,0BAA0B,CAAA;AAIlI,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAmDlD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,eAAe,KACnC,gBAqHF,CAAA;AAED,yFAAyF;AACzF,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACtD,CAAC,WAAW,eAAe,KACnC,gBAAgE,CAAA"}
|