@linagora/ldap-rest-client 1.0.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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/index.d.mts +1399 -0
- package/dist/index.d.ts +1399 -0
- package/dist/index.js +1173 -0
- package/dist/index.mjs +1134 -0
- package/package.json +69 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ApiError: () => ApiError,
|
|
24
|
+
AuthenticationError: () => AuthenticationError,
|
|
25
|
+
AuthorizationError: () => AuthorizationError,
|
|
26
|
+
ConflictError: () => ConflictError,
|
|
27
|
+
GroupsResource: () => GroupsResource,
|
|
28
|
+
LdapRestClient: () => LdapRestClient,
|
|
29
|
+
LdapRestError: () => LdapRestError,
|
|
30
|
+
NetworkError: () => NetworkError,
|
|
31
|
+
NotFoundError: () => NotFoundError,
|
|
32
|
+
OrganizationsResource: () => OrganizationsResource,
|
|
33
|
+
RateLimitError: () => RateLimitError,
|
|
34
|
+
UsersResource: () => UsersResource,
|
|
35
|
+
ValidationError: () => ValidationError
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/config/ClientConfig.ts
|
|
40
|
+
var ConfigValidator = class {
|
|
41
|
+
static validate(config) {
|
|
42
|
+
if (!config.baseUrl || config.baseUrl.trim().length === 0) {
|
|
43
|
+
throw new Error("baseUrl is required");
|
|
44
|
+
}
|
|
45
|
+
if (config.auth?.type === "hmac") {
|
|
46
|
+
if (!config.auth.serviceId || config.auth.serviceId.trim().length === 0) {
|
|
47
|
+
throw new Error("serviceId is required for HMAC authentication");
|
|
48
|
+
}
|
|
49
|
+
if (!config.auth.secret || config.auth.secret.trim().length === 0) {
|
|
50
|
+
throw new Error("secret is required for HMAC authentication");
|
|
51
|
+
}
|
|
52
|
+
if (config.auth.secret.length < 32) {
|
|
53
|
+
console.warn(
|
|
54
|
+
`[LDAP-REST Client] Secret should be at least 32 characters (current: ${config.auth.secret.length})`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
new URL(config.baseUrl);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new Error("baseUrl must be a valid URL");
|
|
62
|
+
}
|
|
63
|
+
if (config.timeout !== void 0 && (config.timeout <= 0 || !Number.isFinite(config.timeout))) {
|
|
64
|
+
throw new Error("timeout must be a positive number");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
static normalize(config) {
|
|
68
|
+
return {
|
|
69
|
+
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
70
|
+
auth: config.auth ?? { type: "cookie" },
|
|
71
|
+
timeout: config.timeout ?? 3e4,
|
|
72
|
+
logger: config.logger
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
static toHttpConfig(config) {
|
|
76
|
+
return {
|
|
77
|
+
baseUrl: config.baseUrl,
|
|
78
|
+
timeout: config.timeout
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/lib/HmacAuth.ts
|
|
84
|
+
var import_crypto = require("crypto");
|
|
85
|
+
var HmacAuth = class {
|
|
86
|
+
/**
|
|
87
|
+
* Creates an HMAC authentication handler
|
|
88
|
+
*
|
|
89
|
+
* @param {HmacAuthConfig} config - HMAC authentication configuration containing serviceId and secret
|
|
90
|
+
*/
|
|
91
|
+
constructor(config) {
|
|
92
|
+
this.config = config;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generates authorization header for a request
|
|
96
|
+
*
|
|
97
|
+
* @param {SignatureParams} params - Request parameters to sign
|
|
98
|
+
* @returns {string} Authorization header value in format "HMAC-SHA256 service-id:timestamp:signature"
|
|
99
|
+
*/
|
|
100
|
+
sign = (params) => {
|
|
101
|
+
const signature = this.generateSignature(params);
|
|
102
|
+
return `HMAC-SHA256 ${signature.serviceId}:${signature.timestamp}:${signature.signature}`;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Generates HMAC signature components
|
|
106
|
+
*
|
|
107
|
+
* @param {SignatureParams} params - Request parameters
|
|
108
|
+
* @returns {HmacSignature} Signature components including service ID, timestamp, and signature
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
generateSignature = (params) => {
|
|
112
|
+
const timestamp = Date.now();
|
|
113
|
+
const bodyHash = this.hashBody(params.method, params.body);
|
|
114
|
+
const signingString = `${params.method.toUpperCase()}|${params.path}|${timestamp}|${bodyHash}`;
|
|
115
|
+
const signature = this.computeHmac(signingString);
|
|
116
|
+
return {
|
|
117
|
+
serviceId: this.config.serviceId,
|
|
118
|
+
timestamp,
|
|
119
|
+
signature
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Computes SHA256 hash of request body
|
|
124
|
+
*
|
|
125
|
+
* For GET/DELETE/HEAD requests, returns empty string.
|
|
126
|
+
* For other methods, returns SHA256 hash of body if provided.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} method - HTTP method
|
|
129
|
+
* @param {string} [body] - Request body
|
|
130
|
+
* @returns {string} SHA256 hash as hex string or empty string
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
hashBody = (method, body) => {
|
|
134
|
+
const upperMethod = method.toUpperCase();
|
|
135
|
+
if (upperMethod === "GET" || upperMethod === "DELETE" || upperMethod === "HEAD") {
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
if (!body || body.trim().length === 0) {
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
return (0, import_crypto.createHash)("sha256").update(body, "utf8").digest("hex");
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Computes HMAC-SHA256 signature
|
|
145
|
+
*
|
|
146
|
+
* @param {string} data - Data to sign
|
|
147
|
+
* @returns {string} HMAC-SHA256 signature as hex string
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
computeHmac = (data) => {
|
|
151
|
+
return (0, import_crypto.createHmac)("sha256", this.config.secret).update(data, "utf8").digest("hex");
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/errors/LdapRestError.ts
|
|
156
|
+
var LdapRestError = class extends Error {
|
|
157
|
+
/** Error class name (automatically set to constructor name) */
|
|
158
|
+
name;
|
|
159
|
+
/** HTTP status code associated with the error (if applicable) */
|
|
160
|
+
statusCode;
|
|
161
|
+
/** Machine-readable error code for programmatic handling */
|
|
162
|
+
code;
|
|
163
|
+
/**
|
|
164
|
+
* Creates a new LDAP-REST error
|
|
165
|
+
*
|
|
166
|
+
* @param {string} message - Human-readable error message
|
|
167
|
+
* @param {number} [statusCode] - HTTP status code
|
|
168
|
+
* @param {string} [code] - Machine-readable error code
|
|
169
|
+
*/
|
|
170
|
+
constructor(message, statusCode, code) {
|
|
171
|
+
super(message);
|
|
172
|
+
this.name = this.constructor.name;
|
|
173
|
+
this.statusCode = statusCode;
|
|
174
|
+
this.code = code;
|
|
175
|
+
Error.captureStackTrace(this, this.constructor);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/errors/ApiError.ts
|
|
180
|
+
var ApiError = class _ApiError extends LdapRestError {
|
|
181
|
+
/**
|
|
182
|
+
* Creates a new API error
|
|
183
|
+
*
|
|
184
|
+
* @param {string} message - Human-readable error message
|
|
185
|
+
* @param {number} statusCode - HTTP status code
|
|
186
|
+
* @param {string} code - Machine-readable error code
|
|
187
|
+
*/
|
|
188
|
+
constructor(message, statusCode, code) {
|
|
189
|
+
super(message, statusCode, code);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Creates an ApiError from an API response body
|
|
193
|
+
*
|
|
194
|
+
* @param {number} statusCode - HTTP status code from response
|
|
195
|
+
* @param {{ error: string; code: string }} body - Response body containing error details
|
|
196
|
+
* @returns {ApiError} New ApiError instance
|
|
197
|
+
*/
|
|
198
|
+
static fromResponse(statusCode, body) {
|
|
199
|
+
return new _ApiError(body.error, statusCode, body.code);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var ValidationError = class extends LdapRestError {
|
|
203
|
+
/**
|
|
204
|
+
* Creates a new validation error
|
|
205
|
+
*
|
|
206
|
+
* @param {string} message - Description of what validation failed
|
|
207
|
+
*/
|
|
208
|
+
constructor(message) {
|
|
209
|
+
super(message, 400, "VALIDATION_ERROR");
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var AuthenticationError = class extends LdapRestError {
|
|
213
|
+
/**
|
|
214
|
+
* Creates a new authentication error
|
|
215
|
+
*
|
|
216
|
+
* @param {string} message - Authentication failure reason
|
|
217
|
+
*/
|
|
218
|
+
constructor(message) {
|
|
219
|
+
super(message, 401, "AUTHENTICATION_ERROR");
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
var AuthorizationError = class extends LdapRestError {
|
|
223
|
+
/**
|
|
224
|
+
* Creates a new authorization error
|
|
225
|
+
*
|
|
226
|
+
* @param {string} message - Authorization failure reason
|
|
227
|
+
*/
|
|
228
|
+
constructor(message) {
|
|
229
|
+
super(message, 403, "AUTHORIZATION_ERROR");
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
var NotFoundError = class extends LdapRestError {
|
|
233
|
+
/**
|
|
234
|
+
* Creates a new not found error
|
|
235
|
+
*
|
|
236
|
+
* @param {string} message - Description of what was not found
|
|
237
|
+
* @param {string} [code] - Optional specific error code (defaults to 'NOT_FOUND')
|
|
238
|
+
*/
|
|
239
|
+
constructor(message, code) {
|
|
240
|
+
super(message, 404, code || "NOT_FOUND");
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var ConflictError = class extends LdapRestError {
|
|
244
|
+
/**
|
|
245
|
+
* Creates a new conflict error
|
|
246
|
+
*
|
|
247
|
+
* @param {string} message - Description of the conflict
|
|
248
|
+
* @param {string} [code] - Optional specific error code (e.g., 'USERNAME_EXISTS', 'EMAIL_EXISTS')
|
|
249
|
+
*/
|
|
250
|
+
constructor(message, code) {
|
|
251
|
+
super(message, 409, code || "CONFLICT");
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var RateLimitError = class extends LdapRestError {
|
|
255
|
+
/** Number of seconds to wait before retrying (from Retry-After header) */
|
|
256
|
+
retryAfter;
|
|
257
|
+
/**
|
|
258
|
+
* Creates a new rate limit error
|
|
259
|
+
*
|
|
260
|
+
* @param {string} message - Rate limit error message
|
|
261
|
+
* @param {number} [retryAfter] - Seconds to wait before retrying
|
|
262
|
+
*/
|
|
263
|
+
constructor(message, retryAfter) {
|
|
264
|
+
super(message, 429, "RATE_LIMIT_EXCEEDED");
|
|
265
|
+
this.retryAfter = retryAfter;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var NetworkError = class extends LdapRestError {
|
|
269
|
+
/** Original error that caused the network failure */
|
|
270
|
+
cause;
|
|
271
|
+
/**
|
|
272
|
+
* Creates a new network error
|
|
273
|
+
*
|
|
274
|
+
* @param {string} message - Network error description
|
|
275
|
+
* @param {Error} [cause] - Original error that caused the failure
|
|
276
|
+
*/
|
|
277
|
+
constructor(message, cause) {
|
|
278
|
+
super(message);
|
|
279
|
+
this.cause = cause;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/lib/HttpClient.ts
|
|
284
|
+
var HttpClient = class {
|
|
285
|
+
/**
|
|
286
|
+
* Creates an HTTP client instance
|
|
287
|
+
*
|
|
288
|
+
* @param {HttpConfig} config - HTTP configuration (baseUrl, timeout)
|
|
289
|
+
* @param {Auth | undefined} auth - Optional authentication handler (HMAC). If undefined, uses cookies.
|
|
290
|
+
* @param {Logger<unknown>} logger - Logger instance
|
|
291
|
+
*/
|
|
292
|
+
constructor(config, auth, logger) {
|
|
293
|
+
this.config = config;
|
|
294
|
+
this.auth = auth;
|
|
295
|
+
this.logger = logger;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Makes an authenticated HTTP request
|
|
299
|
+
*
|
|
300
|
+
* @template T - Response type
|
|
301
|
+
* @param {RequestOptions} options - Request options
|
|
302
|
+
* @returns {Promise<T>} Parsed response body
|
|
303
|
+
* @throws {ApiError} When API returns an error
|
|
304
|
+
* @throws {NetworkError} When network request fails
|
|
305
|
+
*/
|
|
306
|
+
request = async (options) => {
|
|
307
|
+
const url = `${this.config.baseUrl}${options.path}`;
|
|
308
|
+
const bodyString = options.body ? JSON.stringify(options.body) : void 0;
|
|
309
|
+
this.logger.debug("Sending request", {
|
|
310
|
+
method: options.method,
|
|
311
|
+
url,
|
|
312
|
+
hasBody: !!bodyString
|
|
313
|
+
});
|
|
314
|
+
const authHeader = this.auth?.sign({
|
|
315
|
+
method: options.method,
|
|
316
|
+
path: options.path,
|
|
317
|
+
body: bodyString
|
|
318
|
+
});
|
|
319
|
+
const headers = {
|
|
320
|
+
"Content-Type": "application/json",
|
|
321
|
+
...options.headers
|
|
322
|
+
};
|
|
323
|
+
if (authHeader) {
|
|
324
|
+
headers.Authorization = authHeader;
|
|
325
|
+
}
|
|
326
|
+
const controller = new AbortController();
|
|
327
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
328
|
+
try {
|
|
329
|
+
const response = await fetch(url, {
|
|
330
|
+
method: options.method,
|
|
331
|
+
headers,
|
|
332
|
+
body: bodyString,
|
|
333
|
+
signal: controller.signal,
|
|
334
|
+
credentials: authHeader ? void 0 : "include"
|
|
335
|
+
});
|
|
336
|
+
clearTimeout(timeoutId);
|
|
337
|
+
this.logger.info("Request completed", {
|
|
338
|
+
method: options.method,
|
|
339
|
+
path: options.path,
|
|
340
|
+
status: response.status
|
|
341
|
+
});
|
|
342
|
+
return this.handleResponse(response);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
clearTimeout(timeoutId);
|
|
345
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
346
|
+
this.logger.error("Request timeout", {
|
|
347
|
+
method: options.method,
|
|
348
|
+
path: options.path,
|
|
349
|
+
timeoutMs: this.config.timeout
|
|
350
|
+
});
|
|
351
|
+
throw new NetworkError(`Request timeout after ${this.config.timeout}ms`);
|
|
352
|
+
}
|
|
353
|
+
if (error instanceof NetworkError || error instanceof ApiError) {
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
this.logger.error("Network request failed", {
|
|
357
|
+
method: options.method,
|
|
358
|
+
path: options.path,
|
|
359
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
360
|
+
});
|
|
361
|
+
throw new NetworkError(
|
|
362
|
+
`Network request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
363
|
+
error instanceof Error ? error : void 0
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Handles HTTP response
|
|
369
|
+
*
|
|
370
|
+
* Maps HTTP status codes to specific error types and parses response body.
|
|
371
|
+
*
|
|
372
|
+
* @template T - Response type
|
|
373
|
+
* @param {Response} response - Fetch API response
|
|
374
|
+
* @returns {Promise<T>} Parsed response body
|
|
375
|
+
* @throws {ApiError} When API returns an error status
|
|
376
|
+
* @private
|
|
377
|
+
*/
|
|
378
|
+
handleResponse = async (response) => {
|
|
379
|
+
if (response.ok) {
|
|
380
|
+
if (response.status === 204) {
|
|
381
|
+
return { success: true };
|
|
382
|
+
}
|
|
383
|
+
const contentType = response.headers.get("content-type");
|
|
384
|
+
if (!contentType?.includes("application/json")) {
|
|
385
|
+
throw new ApiError("Expected JSON response", response.status, "INVALID_RESPONSE");
|
|
386
|
+
}
|
|
387
|
+
return response.json();
|
|
388
|
+
}
|
|
389
|
+
let errorBody;
|
|
390
|
+
try {
|
|
391
|
+
const json = await response.json();
|
|
392
|
+
errorBody = json;
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
const message = errorBody?.error ?? `HTTP ${response.status}: ${response.statusText}`;
|
|
396
|
+
const code = errorBody?.code ?? "UNKNOWN_ERROR";
|
|
397
|
+
switch (response.status) {
|
|
398
|
+
case 400:
|
|
399
|
+
throw new ValidationError(message);
|
|
400
|
+
case 401:
|
|
401
|
+
throw new AuthenticationError(message);
|
|
402
|
+
case 403:
|
|
403
|
+
throw new AuthorizationError(message);
|
|
404
|
+
case 404:
|
|
405
|
+
throw new NotFoundError(message, code);
|
|
406
|
+
case 409:
|
|
407
|
+
throw new ConflictError(message, code);
|
|
408
|
+
case 429: {
|
|
409
|
+
const retryAfter = response.headers.get("retry-after");
|
|
410
|
+
throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : void 0);
|
|
411
|
+
}
|
|
412
|
+
default:
|
|
413
|
+
if (errorBody) {
|
|
414
|
+
throw ApiError.fromResponse(response.status, errorBody);
|
|
415
|
+
}
|
|
416
|
+
throw new ApiError(message, response.status, code);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Performs a GET request
|
|
421
|
+
*
|
|
422
|
+
* @template T - Response type
|
|
423
|
+
* @param {string} path - Request path
|
|
424
|
+
* @param {Record<string, string>} [headers] - Additional headers
|
|
425
|
+
* @returns {Promise<T>} Parsed response body
|
|
426
|
+
*/
|
|
427
|
+
get = (path, headers) => {
|
|
428
|
+
return this.request({ method: "GET", path, headers });
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Performs a POST request
|
|
432
|
+
*
|
|
433
|
+
* @template T - Response type
|
|
434
|
+
* @param {string} path - Request path
|
|
435
|
+
* @param {unknown} [body] - Request body
|
|
436
|
+
* @param {Record<string, string>} [headers] - Additional headers
|
|
437
|
+
* @returns {Promise<T>} Parsed response body
|
|
438
|
+
*/
|
|
439
|
+
post = (path, body, headers) => {
|
|
440
|
+
return this.request({ method: "POST", path, body, headers });
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* Performs a PATCH request
|
|
444
|
+
*
|
|
445
|
+
* @template T - Response type
|
|
446
|
+
* @param {string} path - Request path
|
|
447
|
+
* @param {unknown} [body] - Request body
|
|
448
|
+
* @param {Record<string, string>} [headers] - Additional headers
|
|
449
|
+
* @returns {Promise<T>} Parsed response body
|
|
450
|
+
*/
|
|
451
|
+
patch = (path, body, headers) => {
|
|
452
|
+
return this.request({ method: "PATCH", path, body, headers });
|
|
453
|
+
};
|
|
454
|
+
/**
|
|
455
|
+
* Performs a DELETE request
|
|
456
|
+
*
|
|
457
|
+
* @template T - Response type
|
|
458
|
+
* @param {string} path - Request path
|
|
459
|
+
* @param {Record<string, string>} [headers] - Additional headers
|
|
460
|
+
* @returns {Promise<T>} Parsed response body
|
|
461
|
+
*/
|
|
462
|
+
delete = (path, headers) => {
|
|
463
|
+
return this.request({ method: "DELETE", path, headers });
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/lib/index.ts
|
|
468
|
+
var import_tslog = require("tslog");
|
|
469
|
+
|
|
470
|
+
// src/LdapRestClient.ts
|
|
471
|
+
var import_tslog2 = require("tslog");
|
|
472
|
+
|
|
473
|
+
// src/resources/BaseResource.ts
|
|
474
|
+
var BaseResource = class {
|
|
475
|
+
/**
|
|
476
|
+
* Creates a base resource instance
|
|
477
|
+
*
|
|
478
|
+
* @param {HttpClient} http - HTTP client for making requests
|
|
479
|
+
* @protected
|
|
480
|
+
*/
|
|
481
|
+
constructor(http) {
|
|
482
|
+
this.http = http;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Builds a query string from parameters
|
|
486
|
+
*
|
|
487
|
+
* Filters out undefined and null values, and properly encodes
|
|
488
|
+
* parameter names and values for URL use.
|
|
489
|
+
*
|
|
490
|
+
* @param {Record<string, string | number | boolean | undefined>} params - Query parameters
|
|
491
|
+
* @returns {string} Formatted query string with leading '?' or empty string
|
|
492
|
+
* @protected
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```typescript
|
|
496
|
+
* buildQueryString({ field: 'username', value: 'john' })
|
|
497
|
+
* // Returns: "?field=username&value=john"
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
buildQueryString = (params) => {
|
|
501
|
+
const entries = Object.entries(params).filter(
|
|
502
|
+
([_, value]) => value !== void 0 && value !== null
|
|
503
|
+
);
|
|
504
|
+
if (entries.length === 0) {
|
|
505
|
+
return "";
|
|
506
|
+
}
|
|
507
|
+
const queryParams = entries.map(
|
|
508
|
+
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
|
|
509
|
+
);
|
|
510
|
+
return `?${queryParams.join("&")}`;
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// src/resources/UsersResource.ts
|
|
515
|
+
var UsersResource = class extends BaseResource {
|
|
516
|
+
/**
|
|
517
|
+
* Creates a new user in the main LDAP branch
|
|
518
|
+
*
|
|
519
|
+
* @param {CreateUserRequest} data - User data including credentials and profile
|
|
520
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
521
|
+
* @throws {ConflictError} When username/email/phone already exists
|
|
522
|
+
* @throws {ApiError} On other API errors
|
|
523
|
+
*/
|
|
524
|
+
create = async (data) => {
|
|
525
|
+
return this.http.post("/api/v1/users", data);
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Updates an existing user
|
|
529
|
+
*
|
|
530
|
+
* @param {string} userId - User identifier (username)
|
|
531
|
+
* @param {UpdateUserRequest} data - Fields to update
|
|
532
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
533
|
+
* @throws {NotFoundError} When user is not found
|
|
534
|
+
* @throws {ApiError} On other API errors
|
|
535
|
+
*/
|
|
536
|
+
update = async (userId, data) => {
|
|
537
|
+
return this.http.patch(`/api/v1/users/${encodeURIComponent(userId)}`, data);
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Disables a user account
|
|
541
|
+
*
|
|
542
|
+
* Sets pwdAccountLockedTime to lock the account using LDAP PPolicy.
|
|
543
|
+
*
|
|
544
|
+
* @param {string} userId - User identifier (username)
|
|
545
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
546
|
+
* @throws {NotFoundError} When user is not found
|
|
547
|
+
* @throws {ApiError} On other API errors
|
|
548
|
+
*/
|
|
549
|
+
disable = async (userId) => {
|
|
550
|
+
return this.http.post(`/api/v1/users/${encodeURIComponent(userId)}/disable`);
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Deletes a user
|
|
554
|
+
*
|
|
555
|
+
* Permanently removes the user from LDAP.
|
|
556
|
+
*
|
|
557
|
+
* @param {string} userId - User identifier (username)
|
|
558
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
559
|
+
* @throws {NotFoundError} When user is not found
|
|
560
|
+
* @throws {ApiError} On other API errors
|
|
561
|
+
*/
|
|
562
|
+
delete = async (userId) => {
|
|
563
|
+
return this.http.delete(`/api/v1/users/${encodeURIComponent(userId)}`);
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Checks if a username, phone, or email is available
|
|
567
|
+
*
|
|
568
|
+
* @param {CheckAvailabilityParams} params - Field and value to check
|
|
569
|
+
* @returns {Promise<CheckAvailabilityResponse>} Availability status
|
|
570
|
+
* @throws {ApiError} On API errors
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* const result = await client.users.checkAvailability({
|
|
575
|
+
* field: 'username',
|
|
576
|
+
* value: 'johndoe'
|
|
577
|
+
* });
|
|
578
|
+
*
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
checkAvailability = async (params) => {
|
|
582
|
+
const query = this.buildQueryString(params);
|
|
583
|
+
return this.http.get(`/api/v1/users/check${query}`);
|
|
584
|
+
};
|
|
585
|
+
/**
|
|
586
|
+
* Fetches a user by identifier
|
|
587
|
+
*
|
|
588
|
+
* @param {FetchUserRequest} params - Fetch parameters (by, value, fields)
|
|
589
|
+
* @returns {Promise<User>} User data
|
|
590
|
+
* @throws {NotFoundError} When user is not found
|
|
591
|
+
* @throws {ApiError} On other API errors
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```typescript
|
|
595
|
+
* const user = await client.users.fetch({
|
|
596
|
+
* by: 'username',
|
|
597
|
+
* value: 'johndoe',
|
|
598
|
+
* fields: 'cn,mail,mobile'
|
|
599
|
+
* });
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
fetch = async (params) => {
|
|
603
|
+
const query = this.buildQueryString(params);
|
|
604
|
+
return this.http.get(`/api/v1/users${query}`);
|
|
605
|
+
};
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
// src/resources/OrganizationsResource.ts
|
|
609
|
+
var OrganizationsResource = class extends BaseResource {
|
|
610
|
+
/**
|
|
611
|
+
* Creates a new organization with dedicated LDAP branch
|
|
612
|
+
*
|
|
613
|
+
* @param {CreateOrganizationRequest} data - Organization data
|
|
614
|
+
* @returns {Promise<CreateOrganizationResponse>} Organization details including baseDN
|
|
615
|
+
* @throws {ConflictError} When organization already exists
|
|
616
|
+
* @throws {ApiError} On other API errors
|
|
617
|
+
*/
|
|
618
|
+
create = async (data) => {
|
|
619
|
+
return this.http.post("/api/v1/organizations", data);
|
|
620
|
+
};
|
|
621
|
+
/**
|
|
622
|
+
* Checks if an organization identifier is available
|
|
623
|
+
*
|
|
624
|
+
* @param {CheckAvailabilityParams} params - Field and value to check
|
|
625
|
+
* @returns {Promise<CheckAvailabilityResponse>} Availability status
|
|
626
|
+
* @throws {ApiError} On API errors
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* const result = await client.organizations.checkAvailability({
|
|
631
|
+
* field: 'domain',
|
|
632
|
+
* value: 'acme.example.com'
|
|
633
|
+
* });
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
checkAvailability = async (params) => {
|
|
637
|
+
const query = this.buildQueryString(params);
|
|
638
|
+
return this.http.get(`/api/v1/organizations/check${query}`);
|
|
639
|
+
};
|
|
640
|
+
/**
|
|
641
|
+
* Links a user as the first admin of an organization
|
|
642
|
+
*
|
|
643
|
+
* Associates an existing user account with an organization and grants
|
|
644
|
+
* admin privileges. Typically called after organization creation.
|
|
645
|
+
*
|
|
646
|
+
* @param {string} organizationId - Organization identifier
|
|
647
|
+
* @param {CreateAdminRequest} data - Admin user details (username and email)
|
|
648
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
649
|
+
* @throws {NotFoundError} When organization or user is not found
|
|
650
|
+
* @throws {ConflictError} When user is already an admin
|
|
651
|
+
* @throws {ApiError} On other API errors
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* ```typescript
|
|
655
|
+
* await client.organizations.createAdmin('org_abc123', {
|
|
656
|
+
* username: 'john.doe',
|
|
657
|
+
* mail: 'john.doe@acme.example.com'
|
|
658
|
+
* });
|
|
659
|
+
* ```
|
|
660
|
+
*/
|
|
661
|
+
createAdmin = async (organizationId, data) => {
|
|
662
|
+
return this.http.post(
|
|
663
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/admin`,
|
|
664
|
+
data
|
|
665
|
+
);
|
|
666
|
+
};
|
|
667
|
+
/**
|
|
668
|
+
* Gets a list of organizations for the authenticated user
|
|
669
|
+
*
|
|
670
|
+
* Requires SSO cookie authentication. Returns organizations where the
|
|
671
|
+
* authenticated user has admin role.
|
|
672
|
+
*
|
|
673
|
+
* @returns {Promise<Organization[]>} Array of organizations
|
|
674
|
+
* @throws {ApiError} On API errors
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* const organizations = await client.organizations.list();
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
list = async () => {
|
|
682
|
+
return this.http.get("/api/v1/organizations");
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* Gets details of a specific organization
|
|
686
|
+
*
|
|
687
|
+
* Requires SSO cookie authentication.
|
|
688
|
+
*
|
|
689
|
+
* @param {string} organizationId - Organization identifier
|
|
690
|
+
* @returns {Promise<Organization>} Organization details
|
|
691
|
+
* @throws {NotFoundError} When organization is not found
|
|
692
|
+
* @throws {ApiError} On other API errors
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* const org = await client.organizations.get('org_abc123');
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
get = async (organizationId) => {
|
|
700
|
+
return this.http.get(`/api/v1/organizations/${encodeURIComponent(organizationId)}`);
|
|
701
|
+
};
|
|
702
|
+
/**
|
|
703
|
+
* Updates an organization
|
|
704
|
+
*
|
|
705
|
+
* Requires SSO cookie authentication and admin role.
|
|
706
|
+
*
|
|
707
|
+
* @param {string} organizationId - Organization identifier
|
|
708
|
+
* @param {UpdateOrganizationRequest} data - Fields to update
|
|
709
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
710
|
+
* @throws {NotFoundError} When organization is not found
|
|
711
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
712
|
+
* @throws {ApiError} On other API errors
|
|
713
|
+
*
|
|
714
|
+
* @example
|
|
715
|
+
* ```typescript
|
|
716
|
+
* await client.organizations.update('org_abc123', {
|
|
717
|
+
* name: 'New Organization Name',
|
|
718
|
+
* status: 'active'
|
|
719
|
+
* });
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
update = async (organizationId, data) => {
|
|
723
|
+
return this.http.patch(`/api/v1/organizations/${encodeURIComponent(organizationId)}`, data);
|
|
724
|
+
};
|
|
725
|
+
// ===== B2B User Management Methods =====
|
|
726
|
+
/**
|
|
727
|
+
* Creates a new user in an organization's LDAP branch
|
|
728
|
+
*
|
|
729
|
+
* Requires SSO cookie authentication and admin role in the target organization.
|
|
730
|
+
*
|
|
731
|
+
* @param {string} organizationId - Organization identifier
|
|
732
|
+
* @param {CreateUserRequest} data - User data including credentials and profile
|
|
733
|
+
* @returns {Promise<CreateB2BUserResponse>} Response with user's baseDN
|
|
734
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
735
|
+
* @throws {NotFoundError} When organization is not found
|
|
736
|
+
* @throws {ConflictError} When username/email/phone already exists
|
|
737
|
+
* @throws {ApiError} On other API errors
|
|
738
|
+
*
|
|
739
|
+
* @example
|
|
740
|
+
* ```typescript
|
|
741
|
+
* const result = await client.organizations.createUser('org_abc123', {
|
|
742
|
+
* cn: 'john.doe',
|
|
743
|
+
* uid: 'john.doe',
|
|
744
|
+
* // ... other user fields
|
|
745
|
+
* });
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
createUser = async (organizationId, data) => {
|
|
749
|
+
return this.http.post(
|
|
750
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users`,
|
|
751
|
+
data
|
|
752
|
+
);
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* Updates a user in an organization
|
|
756
|
+
*
|
|
757
|
+
* Requires SSO cookie authentication and admin role in the target organization.
|
|
758
|
+
*
|
|
759
|
+
* @param {string} organizationId - Organization identifier
|
|
760
|
+
* @param {string} userId - User identifier (username)
|
|
761
|
+
* @param {UpdateUserRequest} data - Fields to update
|
|
762
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
763
|
+
* @throws {NotFoundError} When user or organization is not found
|
|
764
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
765
|
+
* @throws {ApiError} On other API errors
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* ```typescript
|
|
769
|
+
* await client.organizations.updateUser('org_abc123', 'john.doe', {
|
|
770
|
+
* mobile: '+33687654321'
|
|
771
|
+
* });
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
updateUser = async (organizationId, userId, data) => {
|
|
775
|
+
return this.http.patch(
|
|
776
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users/${encodeURIComponent(userId)}`,
|
|
777
|
+
data
|
|
778
|
+
);
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* Disables a user in an organization
|
|
782
|
+
*
|
|
783
|
+
* Locks the account by setting pwdAccountLockedTime using LDAP PPolicy.
|
|
784
|
+
* Requires SSO cookie authentication and admin role.
|
|
785
|
+
*
|
|
786
|
+
* @param {string} organizationId - Organization identifier
|
|
787
|
+
* @param {string} userId - User identifier (username)
|
|
788
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
789
|
+
* @throws {NotFoundError} When user or organization is not found
|
|
790
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
791
|
+
* @throws {ApiError} On other API errors
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```typescript
|
|
795
|
+
* await client.organizations.disableUser('org_abc123', 'john.doe');
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
disableUser = async (organizationId, userId) => {
|
|
799
|
+
return this.http.post(
|
|
800
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users/${encodeURIComponent(userId)}/disable`
|
|
801
|
+
);
|
|
802
|
+
};
|
|
803
|
+
/**
|
|
804
|
+
* Deletes a user from an organization
|
|
805
|
+
*
|
|
806
|
+
* Permanently removes the user from the organization's LDAP branch.
|
|
807
|
+
* Requires SSO cookie authentication and admin role.
|
|
808
|
+
*
|
|
809
|
+
* @param {string} organizationId - Organization identifier
|
|
810
|
+
* @param {string} userId - User identifier (username)
|
|
811
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
812
|
+
* @throws {NotFoundError} When user or organization is not found
|
|
813
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
814
|
+
* @throws {ApiError} On other API errors
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```typescript
|
|
818
|
+
* await client.organizations.deleteUser('org_abc123', 'john.doe');
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
deleteUser = async (organizationId, userId) => {
|
|
822
|
+
return this.http.delete(
|
|
823
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users/${encodeURIComponent(userId)}`
|
|
824
|
+
);
|
|
825
|
+
};
|
|
826
|
+
/**
|
|
827
|
+
* Fetches a user from an organization by identifier
|
|
828
|
+
*
|
|
829
|
+
* Requires SSO cookie authentication and admin role.
|
|
830
|
+
*
|
|
831
|
+
* @param {string} organizationId - Organization identifier
|
|
832
|
+
* @param {FetchUserRequest} params - Fetch parameters (by, value, fields)
|
|
833
|
+
* @returns {Promise<User>} User data
|
|
834
|
+
* @throws {NotFoundError} When user or organization is not found
|
|
835
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
836
|
+
* @throws {ApiError} On other API errors
|
|
837
|
+
*
|
|
838
|
+
* @example
|
|
839
|
+
* ```typescript
|
|
840
|
+
* const user = await client.organizations.getUser('org_abc123', {
|
|
841
|
+
* by: 'username',
|
|
842
|
+
* value: 'john.doe',
|
|
843
|
+
* fields: 'cn,mail,mobile'
|
|
844
|
+
* });
|
|
845
|
+
* ```
|
|
846
|
+
*/
|
|
847
|
+
getUser = async (organizationId, params) => {
|
|
848
|
+
const query = this.buildQueryString(params);
|
|
849
|
+
return this.http.get(
|
|
850
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users${query}`
|
|
851
|
+
);
|
|
852
|
+
};
|
|
853
|
+
/**
|
|
854
|
+
* Lists users in an organization with pagination and filtering
|
|
855
|
+
*
|
|
856
|
+
* Requires SSO cookie authentication and admin role.
|
|
857
|
+
*
|
|
858
|
+
* @param {string} organizationId - Organization identifier
|
|
859
|
+
* @param {ListUsersParams} params - Pagination and filter parameters
|
|
860
|
+
* @returns {Promise<ListUsersResponse>} Paginated list of users
|
|
861
|
+
* @throws {NotFoundError} When organization is not found
|
|
862
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
863
|
+
* @throws {ApiError} On other API errors
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* ```typescript
|
|
867
|
+
* const result = await client.organizations.listUsers('org_abc123', {
|
|
868
|
+
* page: 1,
|
|
869
|
+
* limit: 20,
|
|
870
|
+
* status: 'active',
|
|
871
|
+
* search: 'john',
|
|
872
|
+
* sortBy: 'createdAt',
|
|
873
|
+
* sortOrder: 'desc'
|
|
874
|
+
* });
|
|
875
|
+
* ```
|
|
876
|
+
*/
|
|
877
|
+
listUsers = async (organizationId, params) => {
|
|
878
|
+
const query = params ? this.buildQueryString(params) : "";
|
|
879
|
+
return this.http.get(
|
|
880
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users${query}`
|
|
881
|
+
);
|
|
882
|
+
};
|
|
883
|
+
/**
|
|
884
|
+
* Checks if a username, phone, or email is available in an organization
|
|
885
|
+
*
|
|
886
|
+
* Requires SSO cookie authentication and admin role.
|
|
887
|
+
*
|
|
888
|
+
* @param {string} organizationId - Organization identifier
|
|
889
|
+
* @param {CheckAvailabilityParams} params - Field and value to check
|
|
890
|
+
* @returns {Promise<CheckAvailabilityResponse>} Availability status
|
|
891
|
+
* @throws {NotFoundError} When organization is not found
|
|
892
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
893
|
+
* @throws {ApiError} On other API errors
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* ```typescript
|
|
897
|
+
* const result = await client.organizations.checkUserAvailability('org_abc123', {
|
|
898
|
+
* field: 'username',
|
|
899
|
+
* value: 'john.doe'
|
|
900
|
+
* });
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
903
|
+
checkUserAvailability = async (organizationId, params) => {
|
|
904
|
+
const query = this.buildQueryString(params);
|
|
905
|
+
return this.http.get(
|
|
906
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users/check${query}`
|
|
907
|
+
);
|
|
908
|
+
};
|
|
909
|
+
/**
|
|
910
|
+
* Changes a user's role in an organization
|
|
911
|
+
*
|
|
912
|
+
* Available roles: admin, moderator, member.
|
|
913
|
+
* Requires SSO cookie authentication and admin role.
|
|
914
|
+
* Cannot change your own role or remove the last admin.
|
|
915
|
+
*
|
|
916
|
+
* @param {string} organizationId - Organization identifier
|
|
917
|
+
* @param {string} userId - User identifier (username)
|
|
918
|
+
* @param {ChangeUserRoleRequest} data - New role
|
|
919
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
920
|
+
* @throws {NotFoundError} When user or organization is not found
|
|
921
|
+
* @throws {ForbiddenError} When attempting self-demotion or removing last admin
|
|
922
|
+
* @throws {ApiError} On other API errors
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* ```typescript
|
|
926
|
+
* await client.organizations.changeUserRole('org_abc123', 'john.doe', {
|
|
927
|
+
* role: 'moderator'
|
|
928
|
+
* });
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
changeUserRole = async (organizationId, userId, data) => {
|
|
932
|
+
return this.http.patch(
|
|
933
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/users/${encodeURIComponent(userId)}/role`,
|
|
934
|
+
data
|
|
935
|
+
);
|
|
936
|
+
};
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
// src/resources/GroupsResource.ts
|
|
940
|
+
var GroupsResource = class extends BaseResource {
|
|
941
|
+
/**
|
|
942
|
+
* Creates a new group in an organization
|
|
943
|
+
*
|
|
944
|
+
* Requires SSO cookie authentication and admin role in the target organization.
|
|
945
|
+
*
|
|
946
|
+
* @param {string} organizationId - Organization identifier
|
|
947
|
+
* @param {CreateGroupRequest} data - Group data (name and optional description)
|
|
948
|
+
* @returns {Promise<CreateGroupResponse>} Created group details
|
|
949
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
950
|
+
* @throws {NotFoundError} When organization is not found
|
|
951
|
+
* @throws {ConflictError} When group name already exists
|
|
952
|
+
* @throws {ApiError} On other API errors
|
|
953
|
+
*
|
|
954
|
+
* @examples
|
|
955
|
+
* ```typescript
|
|
956
|
+
* const result = await client.groups.create('org_abc123', {
|
|
957
|
+
* name: 'engineering',
|
|
958
|
+
* description: 'Engineering team'
|
|
959
|
+
* });
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
962
|
+
create = async (organizationId, data) => {
|
|
963
|
+
return this.http.post(
|
|
964
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups`,
|
|
965
|
+
data
|
|
966
|
+
);
|
|
967
|
+
};
|
|
968
|
+
/**
|
|
969
|
+
* Lists all groups in an organization with pagination
|
|
970
|
+
*
|
|
971
|
+
* Requires SSO cookie authentication and admin role.
|
|
972
|
+
*
|
|
973
|
+
* @param {string} organizationId - Organization identifier
|
|
974
|
+
* @param {ListGroupsParams} params - Pagination parameters (page, limit)
|
|
975
|
+
* @returns {Promise<ListGroupsResponse>} Paginated list of groups
|
|
976
|
+
* @throws {NotFoundError} When organization is not found
|
|
977
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
978
|
+
* @throws {ApiError} On other API errors
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```typescript
|
|
982
|
+
* const result = await client.groups.list('org_abc123', {
|
|
983
|
+
* page: 1,
|
|
984
|
+
* limit: 20
|
|
985
|
+
* });
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
list = async (organizationId, params) => {
|
|
989
|
+
const query = params ? this.buildQueryString(params) : "";
|
|
990
|
+
return this.http.get(
|
|
991
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups${query}`
|
|
992
|
+
);
|
|
993
|
+
};
|
|
994
|
+
/**
|
|
995
|
+
* Gets details of a specific group
|
|
996
|
+
*
|
|
997
|
+
* Requires SSO cookie authentication and admin role.
|
|
998
|
+
*
|
|
999
|
+
* @param {string} organizationId - Organization identifier
|
|
1000
|
+
* @param {string} groupId - Group identifier
|
|
1001
|
+
* @returns {Promise<Group>} Group details including members
|
|
1002
|
+
* @throws {NotFoundError} When organization or group is not found
|
|
1003
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
1004
|
+
* @throws {ApiError} On other API errors
|
|
1005
|
+
*
|
|
1006
|
+
* @example
|
|
1007
|
+
* ```typescript
|
|
1008
|
+
* const group = await client.groups.get('org_abc123', 'grp_xyz789');
|
|
1009
|
+
* ```
|
|
1010
|
+
*/
|
|
1011
|
+
get = async (organizationId, groupId) => {
|
|
1012
|
+
return this.http.get(
|
|
1013
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups/${encodeURIComponent(groupId)}`
|
|
1014
|
+
);
|
|
1015
|
+
};
|
|
1016
|
+
/**
|
|
1017
|
+
* Updates a group's properties
|
|
1018
|
+
*
|
|
1019
|
+
* Requires SSO cookie authentication and admin role.
|
|
1020
|
+
*
|
|
1021
|
+
* @param {string} organizationId - Organization identifier
|
|
1022
|
+
* @param {string} groupId - Group identifier
|
|
1023
|
+
* @param {UpdateGroupRequest} data - Fields to update (name, description)
|
|
1024
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
1025
|
+
* @throws {NotFoundError} When organization or group is not found
|
|
1026
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
1027
|
+
* @throws {ConflictError} When new group name already exists
|
|
1028
|
+
* @throws {ApiError} On other API errors
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* await client.groups.update('org_abc123', 'grp_xyz789', {
|
|
1033
|
+
* description: 'Updated description'
|
|
1034
|
+
* });
|
|
1035
|
+
* ```
|
|
1036
|
+
*/
|
|
1037
|
+
update = async (organizationId, groupId, data) => {
|
|
1038
|
+
return this.http.patch(
|
|
1039
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups/${encodeURIComponent(groupId)}`,
|
|
1040
|
+
data
|
|
1041
|
+
);
|
|
1042
|
+
};
|
|
1043
|
+
/**
|
|
1044
|
+
* Deletes a group from an organization
|
|
1045
|
+
*
|
|
1046
|
+
* Permanently removes the group. Members are not deleted, only the group itself.
|
|
1047
|
+
* Requires SSO cookie authentication and admin role.
|
|
1048
|
+
*
|
|
1049
|
+
* @param {string} organizationId - Organization identifier
|
|
1050
|
+
* @param {string} groupId - Group identifier
|
|
1051
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
1052
|
+
* @throws {NotFoundError} When organization or group is not found
|
|
1053
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
1054
|
+
* @throws {ApiError} On other API errors
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```typescript
|
|
1058
|
+
* await client.groups.delete('org_abc123', 'grp_xyz789');
|
|
1059
|
+
* ```
|
|
1060
|
+
*/
|
|
1061
|
+
delete = async (organizationId, groupId) => {
|
|
1062
|
+
return this.http.delete(
|
|
1063
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups/${encodeURIComponent(groupId)}`
|
|
1064
|
+
);
|
|
1065
|
+
};
|
|
1066
|
+
/**
|
|
1067
|
+
* Adds users to a group
|
|
1068
|
+
*
|
|
1069
|
+
* Adds one or more users to the group's member list.
|
|
1070
|
+
* Requires SSO cookie authentication and admin role.
|
|
1071
|
+
*
|
|
1072
|
+
* @param {string} organizationId - Organization identifier
|
|
1073
|
+
* @param {string} groupId - Group identifier
|
|
1074
|
+
* @param {AddGroupMembersRequest} data - Usernames to add
|
|
1075
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
1076
|
+
* @throws {NotFoundError} When organization, group, or users are not found
|
|
1077
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
1078
|
+
* @throws {ApiError} On other API errors
|
|
1079
|
+
*
|
|
1080
|
+
* @example
|
|
1081
|
+
* ```typescript
|
|
1082
|
+
* await client.groups.addMembers('org_abc123', 'grp_xyz789', {
|
|
1083
|
+
* usernames: ['alice.johnson', 'bob.wilson']
|
|
1084
|
+
* });
|
|
1085
|
+
* ```
|
|
1086
|
+
*/
|
|
1087
|
+
addMembers = async (organizationId, groupId, data) => {
|
|
1088
|
+
return this.http.post(
|
|
1089
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups/${encodeURIComponent(groupId)}/members`,
|
|
1090
|
+
data
|
|
1091
|
+
);
|
|
1092
|
+
};
|
|
1093
|
+
/**
|
|
1094
|
+
* Removes a user from a group
|
|
1095
|
+
*
|
|
1096
|
+
* Removes a specific user from the group's member list.
|
|
1097
|
+
* Requires SSO cookie authentication and admin role.
|
|
1098
|
+
*
|
|
1099
|
+
* @param {string} organizationId - Organization identifier
|
|
1100
|
+
* @param {string} groupId - Group identifier
|
|
1101
|
+
* @param {string} userId - User identifier (username) to remove
|
|
1102
|
+
* @returns {Promise<{ success: true }>} Success response
|
|
1103
|
+
* @throws {NotFoundError} When organization, group, or user is not found
|
|
1104
|
+
* @throws {ForbiddenError} When user lacks admin privileges
|
|
1105
|
+
* @throws {ApiError} On other API errors
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```typescript
|
|
1109
|
+
* await client.groups.removeMember('org_abc123', 'grp_xyz789', 'bob.wilson');
|
|
1110
|
+
* ```
|
|
1111
|
+
*/
|
|
1112
|
+
removeMember = async (organizationId, groupId, userId) => {
|
|
1113
|
+
return this.http.delete(
|
|
1114
|
+
`/api/v1/organizations/${encodeURIComponent(organizationId)}/groups/${encodeURIComponent(groupId)}/members/${encodeURIComponent(userId)}`
|
|
1115
|
+
);
|
|
1116
|
+
};
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// src/LdapRestClient.ts
|
|
1120
|
+
var LdapRestClient = class {
|
|
1121
|
+
users;
|
|
1122
|
+
organizations;
|
|
1123
|
+
groups;
|
|
1124
|
+
config;
|
|
1125
|
+
/**
|
|
1126
|
+
* Creates a new LDAP-REST client instance
|
|
1127
|
+
*
|
|
1128
|
+
* @param {ClientConfig} config - Client configuration
|
|
1129
|
+
* @throws {Error} When configuration is invalid
|
|
1130
|
+
*/
|
|
1131
|
+
constructor(config) {
|
|
1132
|
+
ConfigValidator.validate(config);
|
|
1133
|
+
this.config = ConfigValidator.normalize(config);
|
|
1134
|
+
const logger = new import_tslog2.Logger({
|
|
1135
|
+
name: "LDAP-REST-CLIENT",
|
|
1136
|
+
minLevel: 0,
|
|
1137
|
+
...this.config.logger
|
|
1138
|
+
});
|
|
1139
|
+
logger.info("Initializing LDAP-REST client", {
|
|
1140
|
+
baseUrl: this.config.baseUrl,
|
|
1141
|
+
authType: this.config.auth.type
|
|
1142
|
+
});
|
|
1143
|
+
const auth = this.config.auth.type === "hmac" ? new HmacAuth(this.config.auth) : void 0;
|
|
1144
|
+
const http = new HttpClient(ConfigValidator.toHttpConfig(this.config), auth, logger);
|
|
1145
|
+
this.users = new UsersResource(http);
|
|
1146
|
+
this.organizations = new OrganizationsResource(http);
|
|
1147
|
+
this.groups = new GroupsResource(http);
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Gets the configured base URL
|
|
1151
|
+
*
|
|
1152
|
+
* @returns {string} The base URL of the LDAP-REST API
|
|
1153
|
+
*/
|
|
1154
|
+
getBaseUrl = () => {
|
|
1155
|
+
return this.config.baseUrl;
|
|
1156
|
+
};
|
|
1157
|
+
};
|
|
1158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1159
|
+
0 && (module.exports = {
|
|
1160
|
+
ApiError,
|
|
1161
|
+
AuthenticationError,
|
|
1162
|
+
AuthorizationError,
|
|
1163
|
+
ConflictError,
|
|
1164
|
+
GroupsResource,
|
|
1165
|
+
LdapRestClient,
|
|
1166
|
+
LdapRestError,
|
|
1167
|
+
NetworkError,
|
|
1168
|
+
NotFoundError,
|
|
1169
|
+
OrganizationsResource,
|
|
1170
|
+
RateLimitError,
|
|
1171
|
+
UsersResource,
|
|
1172
|
+
ValidationError
|
|
1173
|
+
});
|