@twinfinity/permission 6.1.0 → 6.1.2-ci.30738
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/dist/BuildInfo.js +3 -3
- package/dist/PermissionClient.d.ts +21 -2
- package/dist/PermissionClient.d.ts.map +1 -1
- package/dist/PermissionClient.js +49 -7
- package/dist/PermissionClient.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +56 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/PermissionClient.html +11 -1
- package/docs/classes/PermissionConflictError.html +1 -1
- package/docs/classes/PermissionError.html +1 -1
- package/docs/classes/PermissionForbiddenError.html +1 -1
- package/docs/classes/PermissionNotFoundError.html +1 -1
- package/docs/classes/PermissionValidationError.html +1 -1
- package/docs/enums/PrincipalType.html +2 -1
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +1 -1
- package/docs/interfaces/GetUserOptions.html +5 -0
- package/docs/interfaces/GroupMember.html +11 -4
- package/docs/interfaces/IPermissionClient.html +12 -2
- package/docs/interfaces/ListUsersOptions.html +20 -0
- package/docs/interfaces/User.html +11 -4
- package/package.json +2 -2
- package/src/PermissionClient.ts +75 -6
- package/src/types.ts +58 -3
package/src/PermissionClient.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
GroupMember,
|
|
7
7
|
PutGroupRequest,
|
|
8
8
|
DeleteGroupRequest,
|
|
9
|
+
ListUsersOptions,
|
|
10
|
+
GetUserOptions,
|
|
9
11
|
PermissionErrorBody
|
|
10
12
|
} from './types';
|
|
11
13
|
import {
|
|
@@ -17,7 +19,24 @@ import {
|
|
|
17
19
|
} from './errors';
|
|
18
20
|
|
|
19
21
|
export interface IPermissionClient {
|
|
22
|
+
/**
|
|
23
|
+
* Lists users with keyset pagination and optional filters. `q` is a case-insensitive fuzzy
|
|
24
|
+
* search across external username, email, first and last name; `externalUsername` and
|
|
25
|
+
* `email` are case-insensitive exact matches. All filters are AND-combined.
|
|
26
|
+
*
|
|
27
|
+
* @throws {PermissionValidationError} On invalid pagination token, out-of-range limit, or too-long filter (400).
|
|
28
|
+
* @throws {PermissionForbiddenError} On insufficient permissions, including `includeDeleted` without global `Edit` (403).
|
|
29
|
+
*/
|
|
30
|
+
listUsers(options?: ListUsersOptions, signal?: AbortSignal): Promise<PaginatedResponse<User>>;
|
|
20
31
|
listUsers(page?: string, limit?: number, q?: string, signal?: AbortSignal): Promise<PaginatedResponse<User>>;
|
|
32
|
+
/**
|
|
33
|
+
* Gets a single user by their unique ID. Soft-deleted users are reported as not found
|
|
34
|
+
* unless `includeDeleted` is set.
|
|
35
|
+
*
|
|
36
|
+
* @throws {PermissionNotFoundError} When no such user exists, or the user is soft-deleted and `includeDeleted` is false (404).
|
|
37
|
+
* @throws {PermissionForbiddenError} On insufficient permissions, including `includeDeleted` without global `Edit` (403).
|
|
38
|
+
*/
|
|
39
|
+
getUser(userId: string, options?: GetUserOptions, signal?: AbortSignal): Promise<User>;
|
|
21
40
|
listGroups(page?: string, limit?: number, q?: string, signal?: AbortSignal): Promise<PaginatedResponse<Group>>;
|
|
22
41
|
putGroup(id: string, request: PutGroupRequest, signal?: AbortSignal): Promise<Group>;
|
|
23
42
|
deleteGroup(id: string, request: DeleteGroupRequest, signal?: AbortSignal): Promise<Group>;
|
|
@@ -73,21 +92,40 @@ export class PermissionClient implements IPermissionClient {
|
|
|
73
92
|
this._groupsUrl = `${base}/_ps/groups`;
|
|
74
93
|
}
|
|
75
94
|
|
|
95
|
+
listUsers(options?: ListUsersOptions, signal?: AbortSignal): Promise<PaginatedResponse<User>>;
|
|
96
|
+
listUsers(page?: string, limit?: number, q?: string, signal?: AbortSignal): Promise<PaginatedResponse<User>>;
|
|
76
97
|
async listUsers(
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
pageOrOptions?: string | ListUsersOptions,
|
|
99
|
+
limitOrSignal?: number | AbortSignal,
|
|
79
100
|
q?: string,
|
|
80
101
|
signal?: AbortSignal
|
|
81
102
|
): Promise<PaginatedResponse<User>> {
|
|
103
|
+
let options: ListUsersOptions;
|
|
104
|
+
let abortSignal: AbortSignal | undefined;
|
|
105
|
+
const secondArgIsSignal = limitOrSignal !== undefined && typeof limitOrSignal !== 'number';
|
|
106
|
+
if (typeof pageOrOptions === 'object') {
|
|
107
|
+
options = pageOrOptions;
|
|
108
|
+
abortSignal = secondArgIsSignal ? limitOrSignal : undefined;
|
|
109
|
+
} else if (secondArgIsSignal) {
|
|
110
|
+
options = { page: pageOrOptions };
|
|
111
|
+
abortSignal = limitOrSignal;
|
|
112
|
+
} else {
|
|
113
|
+
options = { page: pageOrOptions, limit: limitOrSignal, q };
|
|
114
|
+
abortSignal = signal;
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
const params = new URLSearchParams();
|
|
83
|
-
if (page !== undefined) params.set('page', page);
|
|
84
|
-
params.set('limit', limit.toString());
|
|
85
|
-
if (q) params.set('q', q);
|
|
118
|
+
if (options.page !== undefined) params.set('page', options.page);
|
|
119
|
+
params.set('limit', (options.limit ?? 500).toString());
|
|
120
|
+
if (options.q) params.set('q', options.q);
|
|
121
|
+
if (options.externalUsername) params.set('externalUsername', options.externalUsername);
|
|
122
|
+
if (options.email) params.set('email', options.email);
|
|
123
|
+
if (options.includeDeleted) params.set('includeDeleted', 'true');
|
|
86
124
|
|
|
87
125
|
const url = `${this._usersUrl}?${params}`;
|
|
88
126
|
|
|
89
127
|
const response = await this._httpClient.fetch(HttpMethod.Get, url, {
|
|
90
|
-
signal
|
|
128
|
+
signal: abortSignal
|
|
91
129
|
});
|
|
92
130
|
|
|
93
131
|
if (response.ok) {
|
|
@@ -112,6 +150,37 @@ export class PermissionClient implements IPermissionClient {
|
|
|
112
150
|
);
|
|
113
151
|
}
|
|
114
152
|
|
|
153
|
+
async getUser(userId: string, options?: GetUserOptions, signal?: AbortSignal): Promise<User> {
|
|
154
|
+
const params = new URLSearchParams();
|
|
155
|
+
if (options?.includeDeleted) params.set('includeDeleted', 'true');
|
|
156
|
+
const query = params.toString();
|
|
157
|
+
|
|
158
|
+
const url = `${this._usersUrl}/${encodeURIComponent(userId)}${query ? `?${query}` : ''}`;
|
|
159
|
+
|
|
160
|
+
const response = await this._httpClient.fetch(HttpMethod.Get, url, { signal });
|
|
161
|
+
|
|
162
|
+
if (response.ok) {
|
|
163
|
+
return (await response.json()) as User;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (response.status === 404) {
|
|
167
|
+
const body = await readErrorBody(response);
|
|
168
|
+
throw new PermissionNotFoundError(body?.detail ?? `User '${userId}' not found`, body);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (response.status === 403) {
|
|
172
|
+
const body = await readErrorBody(response);
|
|
173
|
+
throw new PermissionForbiddenError(body?.detail ?? 'Insufficient permissions to get user', body);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const errorText = await readBodyText(response);
|
|
177
|
+
throw new PermissionError(
|
|
178
|
+
`Failed to get user: ${response.status} ${response.statusText} - ${errorText}`,
|
|
179
|
+
response.status,
|
|
180
|
+
response.statusText
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
115
184
|
async listGroups(
|
|
116
185
|
page: string | undefined = undefined,
|
|
117
186
|
limit = 500,
|
package/src/types.ts
CHANGED
|
@@ -20,9 +20,63 @@ export interface PaginatedResponse<T> {
|
|
|
20
20
|
export interface User {
|
|
21
21
|
id: string;
|
|
22
22
|
username: string;
|
|
23
|
+
/**
|
|
24
|
+
* The human-readable username from the user's source pool (e.g. PS UserName). Omitted when
|
|
25
|
+
* not available — the backend drops null fields from responses. Unlike `username` (an opaque
|
|
26
|
+
* per-pool key), this is the name users recognize; it is not unique.
|
|
27
|
+
*/
|
|
28
|
+
externalUsername?: string;
|
|
23
29
|
email: string;
|
|
24
|
-
firstName
|
|
25
|
-
lastName
|
|
30
|
+
firstName?: string;
|
|
31
|
+
lastName?: string;
|
|
32
|
+
/**
|
|
33
|
+
* `true` when the user is soft-deleted; omitted otherwise. Only ever present on responses
|
|
34
|
+
* requested with `includeDeleted: true`, since deleted users are hidden by default.
|
|
35
|
+
*/
|
|
36
|
+
deleted?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Filters for {@link IPermissionClient.listUsers | listUsers}. All filters are AND-combined.
|
|
41
|
+
*/
|
|
42
|
+
export interface ListUsersOptions {
|
|
43
|
+
/** Pagination token from a previous response's `pagination.nextPage`. */
|
|
44
|
+
page?: string;
|
|
45
|
+
/** Maximum number of items to return (max 1000). Defaults to 500. */
|
|
46
|
+
limit?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Case-insensitive free-text search matched against external username, email, first name,
|
|
49
|
+
* and last name (never the opaque `username`). Empty strings are treated as "no filter".
|
|
50
|
+
*/
|
|
51
|
+
q?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Case-insensitive exact match on external username. Unlike the fuzzy `q` search this never
|
|
54
|
+
* matches substrings. May yield 0..n users — the field is not unique. Empty strings are
|
|
55
|
+
* treated as "no filter".
|
|
56
|
+
*/
|
|
57
|
+
externalUsername?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Case-insensitive exact match on email. Unlike the fuzzy `q` search this never matches
|
|
60
|
+
* substrings. May yield 0..n users — the field is not unique. Empty strings are treated
|
|
61
|
+
* as "no filter".
|
|
62
|
+
*/
|
|
63
|
+
email?: string;
|
|
64
|
+
/**
|
|
65
|
+
* When true, includes soft-deleted users. Requires `Edit` on the global
|
|
66
|
+
* principal-management scope; the request fails with 403 otherwise.
|
|
67
|
+
*/
|
|
68
|
+
includeDeleted?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Options for {@link IPermissionClient.getUser | getUser}.
|
|
73
|
+
*/
|
|
74
|
+
export interface GetUserOptions {
|
|
75
|
+
/**
|
|
76
|
+
* When true, allows returning a soft-deleted user. Requires `Edit` on the global
|
|
77
|
+
* principal-management scope; the request fails with 403 otherwise.
|
|
78
|
+
*/
|
|
79
|
+
includeDeleted?: boolean;
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
/**
|
|
@@ -37,7 +91,8 @@ export interface GroupMember extends User {
|
|
|
37
91
|
export enum PrincipalType {
|
|
38
92
|
Unknown = 'Unknown',
|
|
39
93
|
DomainGroup = 'DomainGroup',
|
|
40
|
-
Group = 'Group'
|
|
94
|
+
Group = 'Group',
|
|
95
|
+
User = 'User'
|
|
41
96
|
}
|
|
42
97
|
|
|
43
98
|
/**
|