@owlmeans/iam 0.1.18-rc.8 → 0.1.18
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 +185 -15
- package/agent-meta/manifest.json +92 -4
- package/agent-meta/skills/iam/SKILL.md +91 -177
- package/agent-meta/skills/iam/parts/clients.md +93 -0
- package/agent-meta/skills/iam/parts/permissions.md +106 -0
- package/agent-meta/skills/iam/parts/service.md +115 -0
- package/agent-meta/skills/iam/parts/tenancy.md +72 -0
- package/build/consts.d.ts +170 -1
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +221 -0
- package/build/consts.js.map +1 -1
- package/build/consts.local.d.ts +58 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +21 -0
- package/build/consts.local.js.map +1 -0
- package/build/errors.d.ts +26 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +38 -0
- package/build/errors.js.map +1 -1
- package/build/facet.d.ts +10 -0
- package/build/facet.d.ts.map +1 -0
- package/build/facet.js +16 -0
- package/build/facet.js.map +1 -0
- package/build/gate/consts.local.d.ts +2 -0
- package/build/gate/consts.local.d.ts.map +1 -0
- package/build/gate/consts.local.js +3 -0
- package/build/gate/consts.local.js.map +1 -0
- package/build/gate/index.d.ts +4 -0
- package/build/gate/index.d.ts.map +1 -0
- package/build/gate/index.js +4 -0
- package/build/gate/index.js.map +1 -0
- package/build/gate/param.d.ts +6 -0
- package/build/gate/param.d.ts.map +1 -0
- package/build/gate/param.js +83 -0
- package/build/gate/param.js.map +1 -0
- package/build/gate/resolve.d.ts +10 -0
- package/build/gate/resolve.d.ts.map +1 -0
- package/build/gate/resolve.js +93 -0
- package/build/gate/resolve.js.map +1 -0
- package/build/gate/types.d.ts +109 -0
- package/build/gate/types.d.ts.map +1 -0
- package/build/gate/types.js +2 -0
- package/build/gate/types.js.map +1 -0
- package/build/gate/types.local.d.ts +5 -0
- package/build/gate/types.local.d.ts.map +1 -0
- package/build/gate/types.local.js +2 -0
- package/build/gate/types.local.js.map +1 -0
- package/build/gate/validate.d.ts +4 -0
- package/build/gate/validate.d.ts.map +1 -0
- package/build/gate/validate.js +100 -0
- package/build/gate/validate.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -0
- package/build/index.js.map +1 -1
- package/build/permission/types.d.ts +35 -0
- package/build/permission/types.d.ts.map +1 -0
- package/build/permission/types.js +2 -0
- package/build/permission/types.js.map +1 -0
- package/build/permission.d.ts +8 -0
- package/build/permission.d.ts.map +1 -0
- package/build/permission.js +34 -0
- package/build/permission.js.map +1 -0
- package/build/runtime/types.d.ts +90 -0
- package/build/runtime/types.d.ts.map +1 -0
- package/build/runtime/types.js +2 -0
- package/build/runtime/types.js.map +1 -0
- package/build/runtime.d.ts +75 -0
- package/build/runtime.d.ts.map +1 -0
- package/build/runtime.js +75 -0
- package/build/runtime.js.map +1 -0
- package/build/schemas.d.ts +13 -0
- package/build/schemas.d.ts.map +1 -0
- package/build/schemas.js +108 -0
- package/build/schemas.js.map +1 -0
- package/build/types.d.ts +429 -30
- package/build/types.d.ts.map +1 -1
- package/build/utils/types.d.ts +12 -0
- package/build/utils/types.d.ts.map +1 -0
- package/build/utils/types.js +2 -0
- package/build/utils/types.js.map +1 -0
- package/build/utils.d.ts +7 -6
- package/build/utils.d.ts.map +1 -1
- package/build/utils.js +10 -1
- package/build/utils.js.map +1 -1
- package/package.json +12 -7
- package/src/consts.local.ts +26 -0
- package/src/consts.ts +241 -1
- package/src/errors.ts +44 -0
- package/src/facet.ts +17 -0
- package/src/gate/consts.local.ts +3 -0
- package/src/gate/index.ts +3 -0
- package/src/gate/param.ts +108 -0
- package/src/gate/resolve.ts +114 -0
- package/src/gate/types.local.ts +4 -0
- package/src/gate/types.ts +117 -0
- package/src/gate/validate.ts +123 -0
- package/src/index.ts +9 -0
- package/src/permission/types.ts +33 -0
- package/src/permission.ts +48 -0
- package/src/runtime/types.ts +107 -0
- package/src/runtime.ts +142 -0
- package/src/schemas.ts +122 -0
- package/src/types.ts +492 -31
- package/src/utils/types.ts +11 -0
- package/src/utils.ts +12 -7
- package/tests/gate-param.spec.ts +233 -0
- package/tests/runtime.spec.ts +101 -0
- package/tests/tsconfig.json +12 -0
- package/tests/utils.spec.ts +47 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { GateParamSource, GateResolutionFailure } from '../consts.js';
|
|
2
|
+
const isRecord = (value) => value != null && typeof value === 'object';
|
|
3
|
+
/**
|
|
4
|
+
* A resource id is a scalar. Anything else is refused rather than coerced.
|
|
5
|
+
*
|
|
6
|
+
* Stringifying an object would produce `"[object Object]"`, which then either matches nothing or —
|
|
7
|
+
* far worse — matches another record that stringified the same way.
|
|
8
|
+
*/
|
|
9
|
+
const scalar = (value) => {
|
|
10
|
+
switch (typeof value) {
|
|
11
|
+
case 'string':
|
|
12
|
+
return value;
|
|
13
|
+
case 'number':
|
|
14
|
+
case 'boolean':
|
|
15
|
+
case 'bigint':
|
|
16
|
+
return `${value}`;
|
|
17
|
+
default:
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const sourceOf = (req, source) => {
|
|
22
|
+
switch (source) {
|
|
23
|
+
case GateParamSource.Params: return req.params;
|
|
24
|
+
case GateParamSource.Query: return req.query;
|
|
25
|
+
case GateParamSource.Body: return req.body;
|
|
26
|
+
case GateParamSource.Headers: return req.headers;
|
|
27
|
+
case GateParamSource.Auth: return req.auth;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Walk one source for the selector's path.
|
|
32
|
+
*
|
|
33
|
+
* Headers get two adjustments they need and nothing else does: the first segment is lowercased,
|
|
34
|
+
* because HTTP header names are case-insensitive and every server normalises them differently; and a
|
|
35
|
+
* single-element array unwraps, because a repeated header arrives as an array and silently picking
|
|
36
|
+
* one of several values would be an authorization decision made by accident.
|
|
37
|
+
*/
|
|
38
|
+
const walk = (container, path, source) => {
|
|
39
|
+
if (!isRecord(container)) {
|
|
40
|
+
return { reason: GateResolutionFailure.SourceMissing };
|
|
41
|
+
}
|
|
42
|
+
let cursor = container;
|
|
43
|
+
for (const [index, rawSegment] of path.entries()) {
|
|
44
|
+
if (!isRecord(cursor)) {
|
|
45
|
+
return { reason: GateResolutionFailure.NotProvided };
|
|
46
|
+
}
|
|
47
|
+
const segment = source === GateParamSource.Headers && index === 0
|
|
48
|
+
? rawSegment.toLowerCase()
|
|
49
|
+
: rawSegment;
|
|
50
|
+
cursor = cursor[segment];
|
|
51
|
+
}
|
|
52
|
+
if (source === GateParamSource.Headers && Array.isArray(cursor)) {
|
|
53
|
+
if (cursor.length !== 1) {
|
|
54
|
+
return { reason: GateResolutionFailure.NotScalar };
|
|
55
|
+
}
|
|
56
|
+
cursor = cursor[0];
|
|
57
|
+
}
|
|
58
|
+
if (cursor == null) {
|
|
59
|
+
return { reason: GateResolutionFailure.NotProvided };
|
|
60
|
+
}
|
|
61
|
+
return { value: cursor };
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the resource id a selector points at.
|
|
65
|
+
*
|
|
66
|
+
* Sources are tried in the selector's own order — for the bare form, route params before the query —
|
|
67
|
+
* and the first that yields a value wins. When none does, the reason travels back so the caller can
|
|
68
|
+
* tell a MISCONFIGURED gate (the request never carried that key) from a legitimately DENIED one.
|
|
69
|
+
*/
|
|
70
|
+
export const resolveGateResource = (req, selector) => {
|
|
71
|
+
let reason = GateResolutionFailure.SourceMissing;
|
|
72
|
+
for (const source of selector.sources) {
|
|
73
|
+
const result = walk(sourceOf(req, source), selector.path, source);
|
|
74
|
+
if (result.reason != null) {
|
|
75
|
+
// A hard refusal is more informative than "keep looking" — report it rather than letting a
|
|
76
|
+
// later, emptier source overwrite it with a vaguer reason.
|
|
77
|
+
if (result.reason === GateResolutionFailure.NotScalar) {
|
|
78
|
+
return { reason: result.reason, from: source };
|
|
79
|
+
}
|
|
80
|
+
if (reason === GateResolutionFailure.SourceMissing) {
|
|
81
|
+
reason = result.reason;
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const value = scalar(result.value);
|
|
86
|
+
if (value == null) {
|
|
87
|
+
return { reason: GateResolutionFailure.NotScalar, from: source };
|
|
88
|
+
}
|
|
89
|
+
return { id: value, from: source };
|
|
90
|
+
}
|
|
91
|
+
return { reason };
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/gate/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { GateParamSource, GateParamErrorCode, GateResolutionFailure } from '../consts.js';
|
|
2
|
+
/**
|
|
3
|
+
* The parts of a request a gate selector may read.
|
|
4
|
+
*
|
|
5
|
+
* Structural on purpose: `AbstractRequest` is assignable to it, so `@owlmeans/server-iam` passes its
|
|
6
|
+
* request straight through, while tooling outside the server stack can resolve a selector against a
|
|
7
|
+
* plain object without taking a dependency on `@owlmeans/entrypoint`.
|
|
8
|
+
*/
|
|
9
|
+
export interface GateRequestLike {
|
|
10
|
+
params?: unknown;
|
|
11
|
+
query?: unknown;
|
|
12
|
+
body?: unknown;
|
|
13
|
+
headers?: unknown;
|
|
14
|
+
auth?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** Where one gate param reads its resource id from. */
|
|
17
|
+
export interface GateResourceSelector {
|
|
18
|
+
/** The selector text exactly as written, for diagnostics. */
|
|
19
|
+
readonly selector: string;
|
|
20
|
+
/** Undefined for the bare form, which searches `sources` in order. */
|
|
21
|
+
readonly source?: GateParamSource;
|
|
22
|
+
/** Length 1 and UNSPLIT for the bare form — a bare key may legally contain dots. */
|
|
23
|
+
readonly path: string[];
|
|
24
|
+
/** The explicit source, or `DEFAULT_GATE_PARAM_SOURCES`. */
|
|
25
|
+
readonly sources: GateParamSource[];
|
|
26
|
+
}
|
|
27
|
+
export interface GateParamProblem {
|
|
28
|
+
code: GateParamErrorCode;
|
|
29
|
+
detail: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ParsedGateParam {
|
|
32
|
+
permission: string;
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated The bare-form flat key, kept so existing readers keep compiling. Read `resource`,
|
|
35
|
+
* which carries the source and the path for both forms.
|
|
36
|
+
*/
|
|
37
|
+
resourceParam?: string;
|
|
38
|
+
resource?: GateResourceSelector;
|
|
39
|
+
error?: GateParamProblem;
|
|
40
|
+
}
|
|
41
|
+
export interface GateResourceResolution {
|
|
42
|
+
id?: string;
|
|
43
|
+
from?: GateParamSource;
|
|
44
|
+
reason?: GateResolutionFailure;
|
|
45
|
+
}
|
|
46
|
+
/** What an entrypoint declares, so a selector can be checked against it before it is ever deployed. */
|
|
47
|
+
export interface GateParamAudit {
|
|
48
|
+
routePath?: string;
|
|
49
|
+
routeParams?: string[];
|
|
50
|
+
filter?: {
|
|
51
|
+
query?: object;
|
|
52
|
+
params?: object;
|
|
53
|
+
body?: object;
|
|
54
|
+
headers?: object;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface GateParamIssue {
|
|
58
|
+
param: string;
|
|
59
|
+
code: GateParamErrorCode;
|
|
60
|
+
detail: string;
|
|
61
|
+
}
|
|
62
|
+
/** The gate-param grammar: `<permission>@<selector>` read, written and taken apart. */
|
|
63
|
+
export interface GateParamHelper {
|
|
64
|
+
/**
|
|
65
|
+
* Read the `@…` half of a gate param.
|
|
66
|
+
*
|
|
67
|
+
* Two forms, and the difference between them is deliberate:
|
|
68
|
+
*
|
|
69
|
+
* `@enquiryId` — bare. A FLAT key, searched in `params` then `query`. It is never split on
|
|
70
|
+
* a dot, because a query key legally contains one and splitting it would
|
|
71
|
+
* change the meaning of selectors already deployed.
|
|
72
|
+
* `@body:order.id` — qualified. An explicit source and a nested path.
|
|
73
|
+
*
|
|
74
|
+
* The rule worth remembering: the bare form is lenient and flat, the qualified form is strict and
|
|
75
|
+
* nested.
|
|
76
|
+
*/
|
|
77
|
+
parseGateSelector: (selector: string) => GateResourceSelector | GateParamProblem;
|
|
78
|
+
/**
|
|
79
|
+
* Split a gate param into the permission it checks and where the resource id comes from.
|
|
80
|
+
*
|
|
81
|
+
* Splits at the FIRST `@`. Everything before it is the permission name looked up in the subject's
|
|
82
|
+
* grants; everything after says where to read the resource id at request time. The suffix is the
|
|
83
|
+
* gate's syntax and nothing else's — a permission stored or granted under a name containing one is a
|
|
84
|
+
* key nothing ever looks up.
|
|
85
|
+
*/
|
|
86
|
+
parseGateParam: (param: string) => ParsedGateParam;
|
|
87
|
+
/** Compose a gate param. The inverse of `parseGateParam` for every selector it accepts. */
|
|
88
|
+
formatGateParam: (permission: string, selector?: string | {
|
|
89
|
+
source?: GateParamSource;
|
|
90
|
+
path: string[];
|
|
91
|
+
}) => string;
|
|
92
|
+
}
|
|
93
|
+
/** Gate params checked against the entrypoint that declares them. */
|
|
94
|
+
export interface GateValidationHelper {
|
|
95
|
+
/** The `:name` segments of a route template. */
|
|
96
|
+
routeParamsOf: (path: string) => string[];
|
|
97
|
+
/**
|
|
98
|
+
* Check gate params against what their entrypoint declares.
|
|
99
|
+
*
|
|
100
|
+
* Reports only what is provable from the declaration itself. Anything that would need a guess is
|
|
101
|
+
* left alone: a check that fires on correct code stops being read.
|
|
102
|
+
*
|
|
103
|
+
* Meant to run where a mistake can still be fixed — while code is being generated, or once per alias
|
|
104
|
+
* as a diagnostic. It must never decide a request: with `some()` semantics across params, one
|
|
105
|
+
* malformed sibling must not be able to refuse a legitimately passing one.
|
|
106
|
+
*/
|
|
107
|
+
validateGateParams: (params: string[], audit?: GateParamAudit) => GateParamIssue[];
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gate/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAE9F;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAA;IACjC,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,kBAAkB,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,KAAK,CAAC,EAAE,gBAAgB,CAAA;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAC/B;AAED,uGAAuG;AACvG,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,kBAAkB,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;;OAYG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,oBAAoB,GAAG,gBAAgB,CAAA;IAChF;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,eAAe,CAAA;IAClD,2FAA2F;IAC3F,eAAe,EAAE,CACf,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,KAC7D,MAAM,CAAA;CACZ;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;IACzC;;;;;;;;;OASG;IACH,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,cAAc,KAAK,cAAc,EAAE,CAAA;CACnF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/gate/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.local.d.ts","sourceRoot":"","sources":["../../src/gate/types.local.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.local.js","sourceRoot":"","sources":["../../src/gate/types.local.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/gate/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwD,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAI5G,eAAO,MAAM,0BAA0B,QAAO,oBAkH7C,CAAA;AAED,eAAO,MAAM,oBAAoB,sBAA+B,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { normalizePath, SEP, PARAM } from '@owlmeans/route';
|
|
2
|
+
import { GateParamSource, GateParamErrorCode } from '../consts.js';
|
|
3
|
+
import { gateParamHelper } from './param.js';
|
|
4
|
+
export const createGateValidationHelper = () => {
|
|
5
|
+
const routeParamsOf = (path) => normalizePath(path).split(SEP)
|
|
6
|
+
.filter(segment => segment.startsWith(PARAM))
|
|
7
|
+
.map(segment => segment.slice(PARAM.length));
|
|
8
|
+
/**
|
|
9
|
+
* Whether a key can survive validation to reach the gate.
|
|
10
|
+
*
|
|
11
|
+
* The server compiles entrypoint filters with AJV's `removeAdditional`, which strips an undeclared
|
|
12
|
+
* key BEFORE the handler — and therefore before the gate — runs. So a selector naming a key the
|
|
13
|
+
* filter does not declare denies every request, with a clean build and nothing logged.
|
|
14
|
+
*
|
|
15
|
+
* The `additionalProperties === false` qualifier is what makes this check usable rather than noise:
|
|
16
|
+
* AJV only strips when a schema says so. An entrypoint that declares no filter for a source strips
|
|
17
|
+
* nothing, and reporting those would fire on nearly every endpoint and be ignored within a week.
|
|
18
|
+
*/
|
|
19
|
+
const unreachable = (schema, key) => {
|
|
20
|
+
if (schema == null) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const { properties, additionalProperties } = schema;
|
|
24
|
+
if (additionalProperties !== false) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return properties == null || !(key in properties);
|
|
28
|
+
};
|
|
29
|
+
const filterFor = (audit, source) => {
|
|
30
|
+
switch (source) {
|
|
31
|
+
case GateParamSource.Query: return audit.filter?.query;
|
|
32
|
+
case GateParamSource.Body: return audit.filter?.body;
|
|
33
|
+
case GateParamSource.Headers: return audit.filter?.headers;
|
|
34
|
+
case GateParamSource.Params: return audit.filter?.params;
|
|
35
|
+
case GateParamSource.Auth: return undefined;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const auditSelector = (audit, selector) => {
|
|
39
|
+
const key = selector.path[0];
|
|
40
|
+
if (key == null) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// The bare form searches route params first and the query second, so it is only provably wrong
|
|
44
|
+
// when BOTH are impossible. A declared route param settles it; otherwise fall through to the
|
|
45
|
+
// query check below, which only fires on an explicitly closed schema.
|
|
46
|
+
const routeParams = audit.routeParams
|
|
47
|
+
?? (audit.routePath != null ? routeParamsOf(audit.routePath) : undefined);
|
|
48
|
+
if (selector.source == null) {
|
|
49
|
+
if (routeParams?.includes(key) === true) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (unreachable(audit.filter?.query, key) && unreachable(audit.filter?.params, key)) {
|
|
53
|
+
return {
|
|
54
|
+
code: GateParamErrorCode.UnknownRouteParam,
|
|
55
|
+
detail: `"${key}" is not a route param of "${audit.routePath ?? '?'}"`
|
|
56
|
+
+ ' and is declared by neither the params nor the query filter'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (selector.source === GateParamSource.Params) {
|
|
62
|
+
if (routeParams != null && !routeParams.includes(key) && unreachable(audit.filter?.params, key)) {
|
|
63
|
+
return {
|
|
64
|
+
code: GateParamErrorCode.UnknownRouteParam,
|
|
65
|
+
detail: `"${key}" is not a route param of "${audit.routePath ?? '?'}"`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (unreachable(filterFor(audit, selector.source), key)) {
|
|
71
|
+
return {
|
|
72
|
+
code: GateParamErrorCode.UnreachableKey,
|
|
73
|
+
detail: `the ${selector.source} filter declares no "${key}" and forbids additional properties,`
|
|
74
|
+
+ ' so it is removed by validation before the gate can read it'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
const validateGateParams = (params, audit) => {
|
|
80
|
+
const issues = [];
|
|
81
|
+
for (const param of params) {
|
|
82
|
+
const parsed = gateParamHelper.parseGateParam(param);
|
|
83
|
+
if (parsed.error != null) {
|
|
84
|
+
issues.push({ param, code: parsed.error.code, detail: parsed.error.detail });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (parsed.resource == null || audit == null) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const issue = auditSelector(audit, parsed.resource);
|
|
91
|
+
if (issue != null) {
|
|
92
|
+
issues.push({ param, ...issue });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return issues;
|
|
96
|
+
};
|
|
97
|
+
return { routeParamsOf, validateGateParams };
|
|
98
|
+
};
|
|
99
|
+
export const gateValidationHelper = createGateValidationHelper();
|
|
100
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/gate/validate.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
export type * from './types.js';
|
|
2
|
+
export type * from './gate/types.js';
|
|
3
|
+
export type * from './permission/types.js';
|
|
4
|
+
export type * from './runtime/types.js';
|
|
5
|
+
export type * from './utils/types.js';
|
|
2
6
|
export * from './consts.js';
|
|
3
7
|
export * from './errors.js';
|
|
4
8
|
export * from './utils.js';
|
|
9
|
+
export * from './facet.js';
|
|
10
|
+
export * from './gate/index.js';
|
|
11
|
+
export * from './permission.js';
|
|
12
|
+
export * from './runtime.js';
|
|
13
|
+
export { IamRuntimeOrganizationSchema, IamRuntimeOrganizationListSchema, IamRuntimeMemberSchema, IamRuntimeMemberListSchema, IamRuntimeMemberUpdateSchema, IamRuntimePermissionSchema, IamRuntimePermissionListSchema, IamRuntimeGrantSchema, IamRuntimeGrantListSchema, IamRuntimeGrantRequestSchema } from './schemas.js';
|
|
5
14
|
//# 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,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,iBAAiB,CAAA;AACpC,mBAAmB,uBAAuB,CAAA;AAC1C,mBAAmB,oBAAoB,CAAA;AACvC,mBAAmB,kBAAkB,CAAA;AACrC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export * from './consts.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
3
|
export * from './utils.js';
|
|
4
|
+
export * from './facet.js';
|
|
5
|
+
export * from './gate/index.js';
|
|
6
|
+
export * from './permission.js';
|
|
7
|
+
export * from './runtime.js';
|
|
8
|
+
export { IamRuntimeOrganizationSchema, IamRuntimeOrganizationListSchema, IamRuntimeMemberSchema, IamRuntimeMemberListSchema, IamRuntimeMemberUpdateSchema, IamRuntimePermissionSchema, IamRuntimePermissionListSchema, IamRuntimeGrantSchema, IamRuntimeGrantListSchema, IamRuntimeGrantRequestSchema } from './schemas.js';
|
|
4
9
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { GateParamProblem } from '../gate/types.js';
|
|
2
|
+
/** A permission NAME taken apart. Never carries a selector — `@` is the gate's syntax alone. */
|
|
3
|
+
export interface ParsedPermissionName {
|
|
4
|
+
/** The whole name, exactly as it must be stored and granted. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** What IAM stores as `resource`. The whole name when there is no action separator. */
|
|
7
|
+
resource: string;
|
|
8
|
+
/** What IAM stores as `action`. Absent for a bare, unsplittable legacy name. */
|
|
9
|
+
action?: string;
|
|
10
|
+
/** Set when the argument carried an `@` — which a NAME never legally does. */
|
|
11
|
+
problem?: GateParamProblem;
|
|
12
|
+
}
|
|
13
|
+
/** Permission NAMES: `<resource>--<action>`, never carrying a gate selector. */
|
|
14
|
+
export interface PermissionNameHelper {
|
|
15
|
+
/**
|
|
16
|
+
* Read a permission NAME.
|
|
17
|
+
*
|
|
18
|
+
* The name is `<resource>--<action>`, with TWO hyphens. One hyphen is not a separator: `enquiry-view`
|
|
19
|
+
* is a resource called `enquiry-view` carrying no action, and it must keep parsing that way — a name
|
|
20
|
+
* already granted somewhere cannot be re-interpreted without orphaning the grant.
|
|
21
|
+
*
|
|
22
|
+
* A name never carries a gate selector. `@` is the gate's syntax, and a name containing one is
|
|
23
|
+
* exactly the corruption this function exists to make visible, so it is reported through `problem`
|
|
24
|
+
* rather than quietly split.
|
|
25
|
+
*/
|
|
26
|
+
parsePermissionName: (name: string) => ParsedPermissionName;
|
|
27
|
+
/** Compose a permission name. The inverse of `parsePermissionName`. */
|
|
28
|
+
composePermissionName: (parts: {
|
|
29
|
+
resource: string;
|
|
30
|
+
action?: string;
|
|
31
|
+
}) => string;
|
|
32
|
+
/** True when the value is a usable permission name — well-formed and carrying no gate selector. */
|
|
33
|
+
isPermissionName: (value: string) => boolean;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/permission/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,gGAAgG;AAChG,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAA;IACZ,uFAAuF;IACvF,QAAQ,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAC3B;AAED,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;OAUG;IACH,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,oBAAoB,CAAA;IAC3D,uEAAuE;IACvE,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IAC/E,mGAAmG;IACnG,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;CAC7C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/permission/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ParsedPermissionName, PermissionNameHelper } from './permission/types.js';
|
|
2
|
+
export declare const createPermissionNameHelper: () => PermissionNameHelper;
|
|
3
|
+
export declare const permissionNameHelper: PermissionNameHelper;
|
|
4
|
+
/** @deprecated compat:factory-refactor — use `permissionNameHelper.parsePermissionName(…)` */
|
|
5
|
+
export declare const parsePermissionName: (name: string) => ParsedPermissionName;
|
|
6
|
+
/** @deprecated compat:factory-refactor — use `permissionNameHelper.isPermissionName(…)` */
|
|
7
|
+
export declare const isPermissionName: (value: string) => boolean;
|
|
8
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAEvF,eAAO,MAAM,0BAA0B,QAAO,oBAmC7C,CAAA;AAED,eAAO,MAAM,oBAAoB,sBAA+B,CAAA;AAEhE,8FAA8F;AAC9F,eAAO,MAAM,mBAAmB,SAAU,MAAM,KAAG,oBACH,CAAA;AAEhD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,UAAW,MAAM,KAAG,OAAuD,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PERMISSION_ACTION_SEPARATOR, RESOURCE_PARAM_SEPARATOR, GateParamErrorCode } from './consts.js';
|
|
2
|
+
export const createPermissionNameHelper = () => {
|
|
3
|
+
const parsePermissionName = (name) => {
|
|
4
|
+
const problem = name.includes(RESOURCE_PARAM_SEPARATOR)
|
|
5
|
+
? {
|
|
6
|
+
code: GateParamErrorCode.UnreachableKey,
|
|
7
|
+
detail: `"${name}" carries a gate selector; "${RESOURCE_PARAM_SEPARATOR}" is never part of a`
|
|
8
|
+
+ ' permission name, and nothing ever looks up a name that contains one'
|
|
9
|
+
}
|
|
10
|
+
: undefined;
|
|
11
|
+
const idx = name.indexOf(PERMISSION_ACTION_SEPARATOR);
|
|
12
|
+
if (idx < 0) {
|
|
13
|
+
return { name, resource: name, ...(problem != null ? { problem } : {}) };
|
|
14
|
+
}
|
|
15
|
+
const action = name.slice(idx + PERMISSION_ACTION_SEPARATOR.length);
|
|
16
|
+
return {
|
|
17
|
+
name,
|
|
18
|
+
resource: name.slice(0, idx),
|
|
19
|
+
...(action !== '' ? { action } : {}),
|
|
20
|
+
...(problem != null ? { problem } : {})
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const composePermissionName = (parts) => parts.action == null || parts.action === ''
|
|
24
|
+
? parts.resource
|
|
25
|
+
: `${parts.resource}${PERMISSION_ACTION_SEPARATOR}${parts.action}`;
|
|
26
|
+
const isPermissionName = (value) => value !== '' && !value.includes(RESOURCE_PARAM_SEPARATOR);
|
|
27
|
+
return { parsePermissionName, composePermissionName, isPermissionName };
|
|
28
|
+
};
|
|
29
|
+
export const permissionNameHelper = createPermissionNameHelper();
|
|
30
|
+
/** @deprecated compat:factory-refactor — use `permissionNameHelper.parsePermissionName(…)` */
|
|
31
|
+
export const parsePermissionName = (name) => permissionNameHelper.parsePermissionName(name);
|
|
32
|
+
/** @deprecated compat:factory-refactor — use `permissionNameHelper.isPermissionName(…)` */
|
|
33
|
+
export const isPermissionName = (value) => permissionNameHelper.isPermissionName(value);
|
|
34
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAGvG,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAyB,EAAE;IACnE,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAwB,EAAE;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YACrD,CAAC,CAAC;gBACA,IAAI,EAAE,kBAAkB,CAAC,cAAc;gBACvC,MAAM,EAAE,IAAI,IAAI,+BAA+B,wBAAwB,sBAAsB;sBACzF,sEAAsE;aAC3E;YACD,CAAC,CAAC,SAAS,CAAA;QAEb,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAA;QACrD,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QAC1E,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAA;QAEnE,OAAO;YACL,IAAI;YACJ,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YAC5B,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAA;IACH,CAAC,CAAA;IAED,MAAM,qBAAqB,GAAG,CAC5B,KAA4C,EACpC,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE;QACtD,CAAC,CAAC,KAAK,CAAC,QAAQ;QAChB,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,GAAG,2BAA2B,GAAG,KAAK,CAAC,MAAM,EAAE,CAAA;IAEpE,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAW,EAAE,CAClD,KAAK,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAA;IAE3D,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAA;AACzE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,EAAE,CAAA;AAEhE,8FAA8F;AAC9F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAwB,EAAE,CACxE,oBAAoB,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAA;AAEhD,2FAA2F;AAC3F,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { IamDefaultClass, IamGrantMode, IamGrantOrigin } from '../consts.js';
|
|
2
|
+
/** One organization of the calling subject. */
|
|
3
|
+
export interface IamRuntimeOrganization {
|
|
4
|
+
entitySlug: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
owner: boolean;
|
|
7
|
+
groups?: string[];
|
|
8
|
+
home?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface IamRuntimeOrganizationList {
|
|
11
|
+
items: IamRuntimeOrganization[];
|
|
12
|
+
}
|
|
13
|
+
export interface IamRuntimeOrganizationCreate {
|
|
14
|
+
title?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface IamRuntimeOrganizationUpdate {
|
|
17
|
+
title: string;
|
|
18
|
+
}
|
|
19
|
+
export interface IamRuntimeOrganizationParams {
|
|
20
|
+
entitySlug: string;
|
|
21
|
+
}
|
|
22
|
+
export interface IamRuntimeMember {
|
|
23
|
+
profileId: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
owner: boolean;
|
|
27
|
+
groups: string[];
|
|
28
|
+
managed?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface IamRuntimeMemberList {
|
|
31
|
+
items: IamRuntimeMember[];
|
|
32
|
+
}
|
|
33
|
+
/** Find-or-create by e-mail; adding someone twice answers the same member. */
|
|
34
|
+
export interface IamRuntimeMemberInvite {
|
|
35
|
+
email: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
owner?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface IamRuntimeMemberUpdate {
|
|
40
|
+
owner?: boolean;
|
|
41
|
+
groups?: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface IamRuntimeMemberParams {
|
|
44
|
+
entitySlug: string;
|
|
45
|
+
profileId: string;
|
|
46
|
+
}
|
|
47
|
+
/** A definition of the calling client that can be granted in an organization. */
|
|
48
|
+
export interface IamRuntimePermission {
|
|
49
|
+
name: string;
|
|
50
|
+
title?: string;
|
|
51
|
+
area?: string;
|
|
52
|
+
resourceScoped?: boolean;
|
|
53
|
+
managed?: boolean;
|
|
54
|
+
defaultClass?: IamDefaultClass;
|
|
55
|
+
}
|
|
56
|
+
export interface IamRuntimePermissionList {
|
|
57
|
+
items: IamRuntimePermission[];
|
|
58
|
+
}
|
|
59
|
+
/** A grant of a member in the organization; `through` names the group a `Group`-origin one comes from. */
|
|
60
|
+
export interface IamRuntimeGrant {
|
|
61
|
+
profileId: string;
|
|
62
|
+
permission: string;
|
|
63
|
+
resources?: string[];
|
|
64
|
+
mode?: IamGrantMode;
|
|
65
|
+
origin?: IamGrantOrigin;
|
|
66
|
+
through?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface IamRuntimeGrantList {
|
|
69
|
+
items: IamRuntimeGrant[];
|
|
70
|
+
}
|
|
71
|
+
export interface IamRuntimeGrantQuery {
|
|
72
|
+
profileId?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Grant or revoke. The binding is the path's organization — the body cannot name another. */
|
|
75
|
+
export interface IamRuntimeGrantRequest {
|
|
76
|
+
profileId: string;
|
|
77
|
+
permission: string;
|
|
78
|
+
resources?: string[];
|
|
79
|
+
mode?: IamGrantMode;
|
|
80
|
+
}
|
|
81
|
+
export interface IamRuntimeAck {
|
|
82
|
+
ok: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface IamRuntimeProtocolOptions {
|
|
85
|
+
/** The service alias that serves the API — every route, the base included, is pinned to it. */
|
|
86
|
+
service: string;
|
|
87
|
+
/** The base path under that service. Defaults to `IAM_RUNTIME_PATH`. */
|
|
88
|
+
path?: string;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAEjF,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,sBAAsB,EAAE,CAAA;CAChC;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,EAAE,CAAA;CAC1B;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,oBAAoB,EAAE,CAAA;CAC9B;AAED,0GAA0G;AAC1G,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,8FAA8F;AAC9F,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAA;CACZ;AAED,MAAM,WAAW,yBAAyB;IACxC,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":""}
|