@plantops/contracts 0.1.0
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 +11 -0
- package/dist/audit.d.ts +149 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +87 -0
- package/dist/bindings.d.ts +125 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +40 -0
- package/dist/clients.d.ts +240 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +49 -0
- package/dist/constants.d.ts +91 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +92 -0
- package/dist/entitlements.d.ts +135 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +43 -0
- package/dist/errors.d.ts +68 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +83 -0
- package/dist/grants.d.ts +70 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +13 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/jwt.d.ts +155 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +49 -0
- package/dist/lib/contracts.d.ts +2 -0
- package/dist/lib/contracts.d.ts.map +1 -0
- package/dist/lib/contracts.js +3 -0
- package/dist/manifest.d.ts +136 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +22 -0
- package/dist/nav.d.ts +53 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +25 -0
- package/dist/pagination.d.ts +28 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +27 -0
- package/dist/registry.d.ts +183 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +24 -0
- package/dist/roles.d.ts +215 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +45 -0
- package/dist/scopes.d.ts +220 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +108 -0
- package/dist/service-accounts.d.ts +81 -0
- package/dist/service-accounts.d.ts.map +1 -0
- package/dist/service-accounts.js +35 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/type-assertions.d.ts +18 -0
- package/dist/type-assertions.d.ts.map +1 -0
- package/dist/type-assertions.js +8 -0
- package/dist/users.d.ts +364 -0
- package/dist/users.d.ts.map +1 -0
- package/dist/users.js +134 -0
- package/package.json +39 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry contract — the platform-admin catalog surface (Doc 06 §4).
|
|
3
|
+
*
|
|
4
|
+
* Three tables, one vocabulary: `application` is what a module *is*,
|
|
5
|
+
* `permission` is what it can be granted, `nav_node` is what it shows. All three
|
|
6
|
+
* are **catalog**, not tenant data (Doc 07 §6): they carry no `client_id`, every
|
|
7
|
+
* authenticated subject can read them, and only a platform admin writes them.
|
|
8
|
+
*
|
|
9
|
+
* ## Natural keys, and why the DTOs carry both halves
|
|
10
|
+
*
|
|
11
|
+
* Every row here has two identifiers: a uuid, and a `key` that is unique within
|
|
12
|
+
* its parent (`application.key` globally, `permission.key` and `nav_node.key`
|
|
13
|
+
* per application). Both are returned, deliberately. The uuid is what a
|
|
14
|
+
* `role_permission` or `menu_permission` row references; the key is what a
|
|
15
|
+
* manifest is written against (Doc 02 §2) and what an operator recognises. A
|
|
16
|
+
* contract that returned only one of them would force every consumer to keep its
|
|
17
|
+
* own lookup table to get back the other.
|
|
18
|
+
*
|
|
19
|
+
* Field naming is snake_case, matching {@link ServiceAccountDTO} and the bodies
|
|
20
|
+
* Doc 06 §3 writes out — these are published shapes, and a convention that
|
|
21
|
+
* changes between two endpoints of the same API is worse than either
|
|
22
|
+
* convention.
|
|
23
|
+
*/
|
|
24
|
+
import type { NavNodeKind } from './nav.js';
|
|
25
|
+
/**
|
|
26
|
+
* One registered application (Doc 01 §3.1).
|
|
27
|
+
*
|
|
28
|
+
* `is_active = false` is the global off switch of Doc 02 §7: the app is hidden
|
|
29
|
+
* everywhere and **all of its data is preserved**. There is no delete, because a
|
|
30
|
+
* deleted application would cascade away the permissions that existing role
|
|
31
|
+
* mappings and audit rows still refer to.
|
|
32
|
+
*/
|
|
33
|
+
export interface ApplicationDTO {
|
|
34
|
+
id: string;
|
|
35
|
+
/** Machine key, e.g. `gatepass`. Globally unique and never editable. */
|
|
36
|
+
key: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string | null;
|
|
39
|
+
is_active: boolean;
|
|
40
|
+
/** Free-form per-application settings (Doc 01 §3.1). */
|
|
41
|
+
config: Record<string, unknown>;
|
|
42
|
+
/** ISO-8601. */
|
|
43
|
+
created_at: string;
|
|
44
|
+
updated_at: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One atomic permission (Doc 01 §3.2).
|
|
48
|
+
*
|
|
49
|
+
* The key is the dotted `app.resource.action` form Doc 02 §2 writes; the pair
|
|
50
|
+
* `(application_id, key)` is unique, which is the 409 in Doc 06 §4.
|
|
51
|
+
*/
|
|
52
|
+
export interface PermissionDTO {
|
|
53
|
+
id: string;
|
|
54
|
+
application_id: string;
|
|
55
|
+
key: string;
|
|
56
|
+
name: string;
|
|
57
|
+
description: string | null;
|
|
58
|
+
is_active: boolean;
|
|
59
|
+
/** ISO-8601. */
|
|
60
|
+
created_at: string;
|
|
61
|
+
updated_at: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One nav node as the **catalog** holds it (Doc 01 §3.3).
|
|
65
|
+
*
|
|
66
|
+
* Distinct from {@link NavNodeDTO}, which is the *resolved* tree a subject sees
|
|
67
|
+
* (Doc 05 §4): that one is pruned to what the subject may see and therefore
|
|
68
|
+
* omits `is_active`, `is_public` and `requires`, because a pruned tree has
|
|
69
|
+
* already answered the question those fields exist to decide. This one is the
|
|
70
|
+
* platform admin's view of what exists, visible or not.
|
|
71
|
+
*/
|
|
72
|
+
export interface NavNodeCatalogDTO {
|
|
73
|
+
id: string;
|
|
74
|
+
application_id: string;
|
|
75
|
+
/** `null` = top level. A parent always belongs to the same application. */
|
|
76
|
+
parent_id: string | null;
|
|
77
|
+
kind: NavNodeKind;
|
|
78
|
+
/** Unique within the application; the manifest upsert's natural key. */
|
|
79
|
+
key: string;
|
|
80
|
+
label: string;
|
|
81
|
+
route: string | null;
|
|
82
|
+
/** Icon *key* — the frontend maps it to its own icon set (Doc 05 §7). */
|
|
83
|
+
icon: string | null;
|
|
84
|
+
sort_order: number;
|
|
85
|
+
is_active: boolean;
|
|
86
|
+
/** Leaf-only opt-in: an unmapped leaf is visible anyway (Doc 05 §3). */
|
|
87
|
+
is_public: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Keys of the permissions mapped to this node, OR semantics (Doc 01 §4.4).
|
|
90
|
+
*
|
|
91
|
+
* Included so the tree is inspectable in one call: the mapping is the whole
|
|
92
|
+
* point of a nav node, and a catalog view that omits it sends every caller
|
|
93
|
+
* back for a second request to find out whether a node is reachable at all.
|
|
94
|
+
*/
|
|
95
|
+
requires: string[];
|
|
96
|
+
/** ISO-8601. */
|
|
97
|
+
created_at: string;
|
|
98
|
+
updated_at: string;
|
|
99
|
+
children: NavNodeCatalogDTO[];
|
|
100
|
+
}
|
|
101
|
+
/** `GET /iam/applications/:id/nav` — the application's full catalog tree. */
|
|
102
|
+
export interface NavCatalogResponse {
|
|
103
|
+
application_id: string;
|
|
104
|
+
/** Top-level nodes, each with its descendants, ordered by `sort_order`. */
|
|
105
|
+
tree: NavNodeCatalogDTO[];
|
|
106
|
+
}
|
|
107
|
+
/** `POST /iam/applications` body (Doc 06 §4). */
|
|
108
|
+
export interface CreateApplicationRequest {
|
|
109
|
+
key: string;
|
|
110
|
+
name: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
config?: Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* `PATCH /iam/applications/:id` body (Doc 06 §4) — update, or activate /
|
|
116
|
+
* deactivate.
|
|
117
|
+
*
|
|
118
|
+
* `key` is absent on purpose: it is the natural key every manifest upload and
|
|
119
|
+
* every permission address is written against (Doc 02 §2), so renaming it would
|
|
120
|
+
* silently re-point an entire catalog.
|
|
121
|
+
*/
|
|
122
|
+
export interface UpdateApplicationRequest {
|
|
123
|
+
name?: string;
|
|
124
|
+
description?: string | null;
|
|
125
|
+
is_active?: boolean;
|
|
126
|
+
config?: Record<string, unknown>;
|
|
127
|
+
}
|
|
128
|
+
/** One entry of the `POST /iam/applications/:id/permissions` body. */
|
|
129
|
+
export interface CreatePermissionInput {
|
|
130
|
+
key: string;
|
|
131
|
+
name: string;
|
|
132
|
+
description?: string;
|
|
133
|
+
}
|
|
134
|
+
/** `POST /iam/applications/:id/permissions` body — bulk (Doc 02 §2 step 2). */
|
|
135
|
+
export interface CreatePermissionsRequest {
|
|
136
|
+
permissions: CreatePermissionInput[];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* One entry of the `POST /iam/applications/:id/nav` body.
|
|
140
|
+
*
|
|
141
|
+
* The parent is named by **either** `parent_id` or `parent_key`, never both.
|
|
142
|
+
* `parent_key` may name a node created earlier in the same request, which is
|
|
143
|
+
* what lets one call build a module and its menus in the order Doc 02 §2 step 3
|
|
144
|
+
* describes.
|
|
145
|
+
*/
|
|
146
|
+
export interface CreateNavNodeInput {
|
|
147
|
+
kind: NavNodeKind;
|
|
148
|
+
key: string;
|
|
149
|
+
label: string;
|
|
150
|
+
route?: string;
|
|
151
|
+
icon?: string;
|
|
152
|
+
sort_order?: number;
|
|
153
|
+
is_public?: boolean;
|
|
154
|
+
parent_id?: string;
|
|
155
|
+
parent_key?: string;
|
|
156
|
+
}
|
|
157
|
+
/** `POST /iam/applications/:id/nav` body — bulk (Doc 02 §2 step 3). */
|
|
158
|
+
export interface CreateNavNodesRequest {
|
|
159
|
+
nodes: CreateNavNodeInput[];
|
|
160
|
+
}
|
|
161
|
+
/** One nav node and the permission keys being mapped to (or unmapped from) it. */
|
|
162
|
+
export interface NavPermissionMapping {
|
|
163
|
+
nav_key: string;
|
|
164
|
+
permission_keys: string[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* `POST` / `DELETE /iam/applications/:id/nav-permissions` body (Doc 02 §2 step 4).
|
|
168
|
+
*
|
|
169
|
+
* Both directions take the same shape because they are the same statement in
|
|
170
|
+
* opposite directions, and both are idempotent: mapping what is already mapped
|
|
171
|
+
* and unmapping what is not are no-ops that audit nothing.
|
|
172
|
+
*/
|
|
173
|
+
export interface NavPermissionsRequest {
|
|
174
|
+
mappings: NavPermissionMapping[];
|
|
175
|
+
}
|
|
176
|
+
/** What a mapping call changed — the counts, plus the pairs it touched. */
|
|
177
|
+
export interface NavPermissionsResult {
|
|
178
|
+
/** Rows actually inserted (`POST`) or deleted (`DELETE`). */
|
|
179
|
+
changed: number;
|
|
180
|
+
/** Pairs already in the requested state, which changed nothing. */
|
|
181
|
+
unchanged: number;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry contract — the platform-admin catalog surface (Doc 06 §4).
|
|
3
|
+
*
|
|
4
|
+
* Three tables, one vocabulary: `application` is what a module *is*,
|
|
5
|
+
* `permission` is what it can be granted, `nav_node` is what it shows. All three
|
|
6
|
+
* are **catalog**, not tenant data (Doc 07 §6): they carry no `client_id`, every
|
|
7
|
+
* authenticated subject can read them, and only a platform admin writes them.
|
|
8
|
+
*
|
|
9
|
+
* ## Natural keys, and why the DTOs carry both halves
|
|
10
|
+
*
|
|
11
|
+
* Every row here has two identifiers: a uuid, and a `key` that is unique within
|
|
12
|
+
* its parent (`application.key` globally, `permission.key` and `nav_node.key`
|
|
13
|
+
* per application). Both are returned, deliberately. The uuid is what a
|
|
14
|
+
* `role_permission` or `menu_permission` row references; the key is what a
|
|
15
|
+
* manifest is written against (Doc 02 §2) and what an operator recognises. A
|
|
16
|
+
* contract that returned only one of them would force every consumer to keep its
|
|
17
|
+
* own lookup table to get back the other.
|
|
18
|
+
*
|
|
19
|
+
* Field naming is snake_case, matching {@link ServiceAccountDTO} and the bodies
|
|
20
|
+
* Doc 06 §3 writes out — these are published shapes, and a convention that
|
|
21
|
+
* changes between two endpoints of the same API is worse than either
|
|
22
|
+
* convention.
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
package/dist/roles.d.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role contract — the WHAT of the access equation (Doc 01 §4.2, Doc 06 §7).
|
|
3
|
+
*
|
|
4
|
+
* A role is a tenant-defined **bundle of catalog permissions**. It is the only
|
|
5
|
+
* one of the three dimensions a client composes for itself: the permissions come
|
|
6
|
+
* from the platform's catalog (Doc 02 §2), the scope nodes come from the client's
|
|
7
|
+
* own org tree ({@link ScopeNodeDTO}), and a `role_binding` ties a subject to one
|
|
8
|
+
* of each (Doc 01 §4.5).
|
|
9
|
+
*
|
|
10
|
+
* ## Why a role carries no permissions inline
|
|
11
|
+
*
|
|
12
|
+
* {@link RoleDTO} reports how many permissions it maps, not which — the mapping
|
|
13
|
+
* is its own endpoint, `GET /iam/roles/:id/permissions`, returning
|
|
14
|
+
* {@link RolePermissionsResponse}. Two reasons, and the second is the real one:
|
|
15
|
+
*
|
|
16
|
+
* - a role list is a list of names and counts (Doc 09 §3.2), and inlining a
|
|
17
|
+
* two-hundred-key permission set into every row of it is a payload nobody
|
|
18
|
+
* reads; and
|
|
19
|
+
* - the mapping needs *more* than the permission — the owning application, and
|
|
20
|
+
* whether that application is still enabled for the tenant — which the role
|
|
21
|
+
* list has no business carrying.
|
|
22
|
+
*
|
|
23
|
+
* ## What "enabled" has to do with any of this
|
|
24
|
+
*
|
|
25
|
+
* Doc 02 §6: a role may only map permissions belonging to applications **enabled
|
|
26
|
+
* for that role's client**. That is a rule about the moment a mapping is
|
|
27
|
+
* *created*. It is deliberately not a rule about the moment one is *read*,
|
|
28
|
+
* because Doc 02 §7 preserves mappings when an application is disabled — they go
|
|
29
|
+
* inert, not away, so that re-enabling is a toggle rather than a re-setup. So
|
|
30
|
+
* {@link RolePermissionDTO} publishes `application_enabled`: a mapped permission
|
|
31
|
+
* that grants nothing today is a thing an operator has to be able to see, and a
|
|
32
|
+
* response that silently omitted it would make a disabled application look like
|
|
33
|
+
* a role someone had quietly edited.
|
|
34
|
+
*
|
|
35
|
+
* Field naming is snake_case, matching every other published shape here.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* How many permissions one `PUT /iam/roles/:id/permissions` may set.
|
|
39
|
+
*
|
|
40
|
+
* The same bound the catalog's own bulk seed uses, and for the same reason: the
|
|
41
|
+
* whole array is validated and written inside one request transaction, so an
|
|
42
|
+
* unbounded body is an unbounded transaction. A role that needs more than two
|
|
43
|
+
* hundred atomic permissions is a role that wants splitting.
|
|
44
|
+
*/
|
|
45
|
+
export declare const MAX_PERMISSIONS_PER_ROLE = 200;
|
|
46
|
+
/**
|
|
47
|
+
* One tenant-defined role (Doc 01 §4.2).
|
|
48
|
+
*
|
|
49
|
+
* `is_system` marks a role the tenant did not create and may not rename or
|
|
50
|
+
* delete — today that is the `Client Admin` role seeded with the tenant's first
|
|
51
|
+
* administrator (Doc 02 §3 step 3). Its *permissions* are still set through the
|
|
52
|
+
* ordinary endpoint, because a system role with no way to be given permissions
|
|
53
|
+
* would be a role that can never do anything.
|
|
54
|
+
*
|
|
55
|
+
* The two counts are Doc 09 §3.2's list columns, which is the whole reason they
|
|
56
|
+
* are on the DTO rather than left to the caller to assemble.
|
|
57
|
+
*/
|
|
58
|
+
export interface RoleDTO {
|
|
59
|
+
id: string;
|
|
60
|
+
client_id: string;
|
|
61
|
+
/** Unique within the client — `unique (client_id, name)` (migration 0003). */
|
|
62
|
+
name: string;
|
|
63
|
+
description: string | null;
|
|
64
|
+
/** Seeded by the platform; not renamable and not deletable by the tenant. */
|
|
65
|
+
is_system: boolean;
|
|
66
|
+
/** Rows in `role_permission`, enabled or not. Doc 09 §3.2's "# permissions". */
|
|
67
|
+
permission_count: number;
|
|
68
|
+
/**
|
|
69
|
+
* Distinct subjects bound to this role anywhere in the tree — Doc 09 §3.2's
|
|
70
|
+
* "# users bound".
|
|
71
|
+
*
|
|
72
|
+
* Distinct *subjects*, not bindings: one user bound at three plants is one
|
|
73
|
+
* person with access, which is what the column is read as. It counts service
|
|
74
|
+
* accounts too, since a machine identity holding a role is exactly as much a
|
|
75
|
+
* consequence of deleting it (Doc 01 §4.5).
|
|
76
|
+
*/
|
|
77
|
+
bound_subject_count: number;
|
|
78
|
+
/** ISO-8601. */
|
|
79
|
+
created_at: string;
|
|
80
|
+
updated_at: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* One permission as a role maps it (Doc 01 §4.3).
|
|
84
|
+
*
|
|
85
|
+
* A {@link PermissionDTO} widened with the two facts that decide whether the
|
|
86
|
+
* mapping currently grants anything: which application it belongs to, and
|
|
87
|
+
* whether that application is still enabled for this tenant. Both are joined in
|
|
88
|
+
* rather than left for the caller to fetch, because the permission picker of
|
|
89
|
+
* Doc 09 §3.2 groups by application and cannot render a row without them.
|
|
90
|
+
*/
|
|
91
|
+
export interface RolePermissionDTO {
|
|
92
|
+
id: string;
|
|
93
|
+
key: string;
|
|
94
|
+
name: string;
|
|
95
|
+
description: string | null;
|
|
96
|
+
/** The catalog's own switch — `false` once a manifest retires the key (Doc 02 §7). */
|
|
97
|
+
is_active: boolean;
|
|
98
|
+
application_id: string;
|
|
99
|
+
application_key: string;
|
|
100
|
+
application_name: string;
|
|
101
|
+
/**
|
|
102
|
+
* Whether the owning application is enabled for this role's client.
|
|
103
|
+
*
|
|
104
|
+
* `false` means the mapping is **inert but preserved** (Doc 02 §7): it grants
|
|
105
|
+
* nothing today and grants again the moment the application is re-enabled.
|
|
106
|
+
* A new mapping cannot be created in this state — that is the Doc 02 §6 rule —
|
|
107
|
+
* but an existing one is never silently dropped.
|
|
108
|
+
*/
|
|
109
|
+
application_enabled: boolean;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `GET` / `PUT /iam/roles/:id/permissions` → the role's whole mapping
|
|
113
|
+
* (Doc 06 §7).
|
|
114
|
+
*
|
|
115
|
+
* Not paginated, for the reason `/iam/permissions/resolve` is not: this is one
|
|
116
|
+
* editable unit — a picker either shows the whole selection or shows a lie —
|
|
117
|
+
* and it is bounded by {@link MAX_PERMISSIONS_PER_ROLE} rather than by a page
|
|
118
|
+
* size. Ordered by application key, then permission key, so the grouping the UI
|
|
119
|
+
* renders is the order it arrives in.
|
|
120
|
+
*/
|
|
121
|
+
export interface RolePermissionsResponse {
|
|
122
|
+
role_id: string;
|
|
123
|
+
permissions: RolePermissionDTO[];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* `GET /iam/roles/permission-catalog` → everything a role of this tenant may be
|
|
127
|
+
* given (Doc 06 §7, Doc 09 §3.2).
|
|
128
|
+
*
|
|
129
|
+
* ## Why this exists at all
|
|
130
|
+
*
|
|
131
|
+
* Doc 09 §3.2's permission picker offers "only apps enabled for the client", and
|
|
132
|
+
* until this endpoint there was no way for a tenant administrator to find out
|
|
133
|
+
* what those were: the catalog lives behind `iam.platform.permission.read`, which
|
|
134
|
+
* is platform authority no client admin holds, and
|
|
135
|
+
* `GET /iam/roles/:id/permissions` answers a narrower question — what one role
|
|
136
|
+
* already carries. A picker built from that could only ever offer what was
|
|
137
|
+
* already chosen.
|
|
138
|
+
*
|
|
139
|
+
* ## Every entry is one `PUT /iam/roles/:id/permissions` will accept
|
|
140
|
+
*
|
|
141
|
+
* The set is exactly the four conditions that endpoint validates (Doc 02 §6):
|
|
142
|
+
* the permission is active, its application is active in the registry, and that
|
|
143
|
+
* application is enabled for this client. Anything the picker offers is
|
|
144
|
+
* therefore mappable, which is what makes a 409 from a save a real conflict —
|
|
145
|
+
* something changed underneath — rather than the ordinary result of choosing a
|
|
146
|
+
* row that was on screen.
|
|
147
|
+
*
|
|
148
|
+
* ## It reuses {@link RolePermissionDTO}
|
|
149
|
+
*
|
|
150
|
+
* `is_active` and `application_enabled` are constant `true` for every entry
|
|
151
|
+
* here, by construction. They are on the wire anyway so that a picker can hold
|
|
152
|
+
* catalog entries and a role's own *inert* mappings — a preserved
|
|
153
|
+
* `role_permission` whose application is currently disabled (Doc 02 §7) — in one
|
|
154
|
+
* list without a union type. Those two are the rows that differ, and they are
|
|
155
|
+
* the rows the screen has to render differently.
|
|
156
|
+
*/
|
|
157
|
+
export interface PermissionCatalogResponse {
|
|
158
|
+
/** The caller's tenant, from the token — never from a query parameter. */
|
|
159
|
+
client_id: string;
|
|
160
|
+
/** Ordered by application name, then by permission key. */
|
|
161
|
+
permissions: RolePermissionDTO[];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* `POST /iam/roles` body (Doc 06 §7).
|
|
165
|
+
*
|
|
166
|
+
* The client is the caller's own, taken from the token's `cid` and from nowhere
|
|
167
|
+
* else — there is no `client_id` field here and there will not be one, for the
|
|
168
|
+
* reason `/iam/scopes` has no `?clientId=`: it would look like it selected a
|
|
169
|
+
* tenant while RLS quietly ignored it.
|
|
170
|
+
*
|
|
171
|
+
* `is_system` is likewise absent. A tenant that could set it would be able to
|
|
172
|
+
* make a role it cannot afterwards delete.
|
|
173
|
+
*/
|
|
174
|
+
export interface CreateRoleRequest {
|
|
175
|
+
name: string;
|
|
176
|
+
description?: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* `PATCH /iam/roles/:id` body — Doc 06 §7's "rename", plus the description.
|
|
180
|
+
*
|
|
181
|
+
* The description rides along because it is the same row's editable prose and
|
|
182
|
+
* Doc 10 §4 gives roles a `role.updated` action to record it under — the test
|
|
183
|
+
* `updateScopeNodeSchema` fails, which is why the scope tree's PATCH is narrower
|
|
184
|
+
* than this one rather than the other way round.
|
|
185
|
+
*
|
|
186
|
+
* Permissions are **not** here. They are a `PUT` of their own, because setting
|
|
187
|
+
* them is a replacement of a set with cross-tenant validation attached
|
|
188
|
+
* (Doc 02 §6), not a field of the role.
|
|
189
|
+
*/
|
|
190
|
+
export interface UpdateRoleRequest {
|
|
191
|
+
name?: string;
|
|
192
|
+
description?: string;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* `PUT /iam/roles/:id/permissions` body (Doc 06 §7).
|
|
196
|
+
*
|
|
197
|
+
* A `PUT` of the whole set, not a pair of add/remove calls: "which permissions
|
|
198
|
+
* does this role have" is one question with one answer, and an editor that saves
|
|
199
|
+
* a picker's state has exactly one set to send. An empty array is legal and
|
|
200
|
+
* means what it says — the role maps nothing.
|
|
201
|
+
*
|
|
202
|
+
* ## Ids, not keys
|
|
203
|
+
*
|
|
204
|
+
* A permission `key` is unique only within its application (`unique
|
|
205
|
+
* (application_id, key)`), so a bare key names a permission only if you already
|
|
206
|
+
* know which application is meant. Accepting keys would therefore mean accepting
|
|
207
|
+
* pairs, and a role editor gets its ids from
|
|
208
|
+
* `GET /iam/applications/:id/permissions` — the same call that renders the
|
|
209
|
+
* picker — so the pair would be assembled from data the caller already holds and
|
|
210
|
+
* then taken apart again on arrival.
|
|
211
|
+
*/
|
|
212
|
+
export interface SetRolePermissionsRequest {
|
|
213
|
+
permission_ids: string[];
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6EAA6E;IAC7E,SAAS,EAAE,OAAO,CAAC;IACnB,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;;OAQG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,sFAAsF;IACtF,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B"}
|
package/dist/roles.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role contract — the WHAT of the access equation (Doc 01 §4.2, Doc 06 §7).
|
|
3
|
+
*
|
|
4
|
+
* A role is a tenant-defined **bundle of catalog permissions**. It is the only
|
|
5
|
+
* one of the three dimensions a client composes for itself: the permissions come
|
|
6
|
+
* from the platform's catalog (Doc 02 §2), the scope nodes come from the client's
|
|
7
|
+
* own org tree ({@link ScopeNodeDTO}), and a `role_binding` ties a subject to one
|
|
8
|
+
* of each (Doc 01 §4.5).
|
|
9
|
+
*
|
|
10
|
+
* ## Why a role carries no permissions inline
|
|
11
|
+
*
|
|
12
|
+
* {@link RoleDTO} reports how many permissions it maps, not which — the mapping
|
|
13
|
+
* is its own endpoint, `GET /iam/roles/:id/permissions`, returning
|
|
14
|
+
* {@link RolePermissionsResponse}. Two reasons, and the second is the real one:
|
|
15
|
+
*
|
|
16
|
+
* - a role list is a list of names and counts (Doc 09 §3.2), and inlining a
|
|
17
|
+
* two-hundred-key permission set into every row of it is a payload nobody
|
|
18
|
+
* reads; and
|
|
19
|
+
* - the mapping needs *more* than the permission — the owning application, and
|
|
20
|
+
* whether that application is still enabled for the tenant — which the role
|
|
21
|
+
* list has no business carrying.
|
|
22
|
+
*
|
|
23
|
+
* ## What "enabled" has to do with any of this
|
|
24
|
+
*
|
|
25
|
+
* Doc 02 §6: a role may only map permissions belonging to applications **enabled
|
|
26
|
+
* for that role's client**. That is a rule about the moment a mapping is
|
|
27
|
+
* *created*. It is deliberately not a rule about the moment one is *read*,
|
|
28
|
+
* because Doc 02 §7 preserves mappings when an application is disabled — they go
|
|
29
|
+
* inert, not away, so that re-enabling is a toggle rather than a re-setup. So
|
|
30
|
+
* {@link RolePermissionDTO} publishes `application_enabled`: a mapped permission
|
|
31
|
+
* that grants nothing today is a thing an operator has to be able to see, and a
|
|
32
|
+
* response that silently omitted it would make a disabled application look like
|
|
33
|
+
* a role someone had quietly edited.
|
|
34
|
+
*
|
|
35
|
+
* Field naming is snake_case, matching every other published shape here.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* How many permissions one `PUT /iam/roles/:id/permissions` may set.
|
|
39
|
+
*
|
|
40
|
+
* The same bound the catalog's own bulk seed uses, and for the same reason: the
|
|
41
|
+
* whole array is validated and written inside one request transaction, so an
|
|
42
|
+
* unbounded body is an unbounded transaction. A role that needs more than two
|
|
43
|
+
* hundred atomic permissions is a role that wants splitting.
|
|
44
|
+
*/
|
|
45
|
+
export const MAX_PERMISSIONS_PER_ROLE = 200;
|