ldap-rest 0.3.0 → 0.3.2

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.
@@ -3,6 +3,395 @@ import DmPlugin, { type Role } from '../../abstract/plugin';
3
3
  import type { DM } from '../../bin';
4
4
  import type ldapActions from '../../lib/ldapActions';
5
5
  import { writeScimErrorFromException } from './errors';
6
+ /**
7
+ * Reusable SCIM 2.0 schemas surfaced by this plugin.
8
+ * Picked up by scripts/generate-openapi.ts and merged into `components.schemas`.
9
+ *
10
+ * @openapi-component
11
+ * ScimUser:
12
+ * type: object
13
+ * description: |
14
+ * SCIM 2.0 User resource (RFC 7643 §4.1).
15
+ * required: [schemas, userName]
16
+ * properties:
17
+ * schemas:
18
+ * type: array
19
+ * items: { type: string }
20
+ * example: ['urn:ietf:params:scim:schemas:core:2.0:User']
21
+ * id:
22
+ * type: string
23
+ * description: Opaque server-assigned identifier.
24
+ * example: 2819c223-7f76-453a-919d-413861904646
25
+ * externalId:
26
+ * type: string
27
+ * description: Provisioner-assigned identifier.
28
+ * example: 701984
29
+ * userName:
30
+ * type: string
31
+ * description: Unique user identifier (login name).
32
+ * example: bjensen
33
+ * name:
34
+ * type: object
35
+ * properties:
36
+ * formatted: { type: string, example: Ms. Barbara J Jensen III }
37
+ * familyName: { type: string, example: Jensen }
38
+ * givenName: { type: string, example: Barbara }
39
+ * middleName: { type: string, example: Jane }
40
+ * honorificPrefix: { type: string, example: Ms. }
41
+ * honorificSuffix: { type: string, example: III }
42
+ * displayName:
43
+ * type: string
44
+ * example: Babs Jensen
45
+ * nickName:
46
+ * type: string
47
+ * example: Babs
48
+ * profileUrl:
49
+ * type: string
50
+ * example: https://login.example.com/bjensen
51
+ * title:
52
+ * type: string
53
+ * example: Tour Guide
54
+ * userType:
55
+ * type: string
56
+ * example: Employee
57
+ * preferredLanguage:
58
+ * type: string
59
+ * example: en-US
60
+ * locale:
61
+ * type: string
62
+ * example: en-US
63
+ * timezone:
64
+ * type: string
65
+ * example: America/Los_Angeles
66
+ * active:
67
+ * type: boolean
68
+ * example: true
69
+ * emails:
70
+ * type: array
71
+ * items:
72
+ * type: object
73
+ * properties:
74
+ * value: { type: string }
75
+ * display: { type: string }
76
+ * type: { type: string }
77
+ * primary: { type: boolean }
78
+ * example:
79
+ * - value: bjensen@example.com
80
+ * type: work
81
+ * primary: true
82
+ * phoneNumbers:
83
+ * type: array
84
+ * items:
85
+ * type: object
86
+ * properties:
87
+ * value: { type: string }
88
+ * type: { type: string }
89
+ * primary: { type: boolean }
90
+ * addresses:
91
+ * type: array
92
+ * items:
93
+ * type: object
94
+ * properties:
95
+ * streetAddress: { type: string }
96
+ * locality: { type: string }
97
+ * region: { type: string }
98
+ * postalCode: { type: string }
99
+ * country: { type: string }
100
+ * type: { type: string }
101
+ * primary: { type: boolean }
102
+ * groups:
103
+ * type: array
104
+ * description: Groups the user belongs to (read-only).
105
+ * items:
106
+ * type: object
107
+ * properties:
108
+ * value: { type: string }
109
+ * $ref: { type: string }
110
+ * display: { type: string }
111
+ * meta:
112
+ * type: object
113
+ * properties:
114
+ * resourceType: { type: string, example: User }
115
+ * created: { type: string, format: date-time }
116
+ * lastModified: { type: string, format: date-time }
117
+ * location: { type: string }
118
+ * version: { type: string }
119
+ * ScimGroup:
120
+ * type: object
121
+ * description: |
122
+ * SCIM 2.0 Group resource (RFC 7643 §4.2).
123
+ * required: [schemas, displayName]
124
+ * properties:
125
+ * schemas:
126
+ * type: array
127
+ * items: { type: string }
128
+ * example: ['urn:ietf:params:scim:schemas:core:2.0:Group']
129
+ * id:
130
+ * type: string
131
+ * description: Opaque server-assigned identifier.
132
+ * example: e9e30dba-f08f-4109-8486-d5c6a331660a
133
+ * externalId:
134
+ * type: string
135
+ * example: 00g1emaKYZTWRINFRGETl
136
+ * displayName:
137
+ * type: string
138
+ * example: Tour Guides
139
+ * members:
140
+ * type: array
141
+ * description: Members of the group.
142
+ * items:
143
+ * type: object
144
+ * properties:
145
+ * value: { type: string, description: User or Group id }
146
+ * $ref: { type: string }
147
+ * display: { type: string }
148
+ * example:
149
+ * - value: 2819c223-7f76-453a-919d-413861904646
150
+ * display: Babs Jensen
151
+ * - value: 902c246b-6245-4190-8e05-00816be7344a
152
+ * display: Mandy Pepperidge
153
+ * meta:
154
+ * type: object
155
+ * properties:
156
+ * resourceType: { type: string, example: Group }
157
+ * created: { type: string, format: date-time }
158
+ * lastModified: { type: string, format: date-time }
159
+ * location: { type: string }
160
+ * version: { type: string }
161
+ * ScimListResponse:
162
+ * type: object
163
+ * description: |
164
+ * SCIM 2.0 list query response (urn:ietf:params:scim:api:messages:2.0:ListResponse).
165
+ * required: [schemas, totalResults, Resources]
166
+ * properties:
167
+ * schemas:
168
+ * type: array
169
+ * items: { type: string }
170
+ * example: ['urn:ietf:params:scim:api:messages:2.0:ListResponse']
171
+ * totalResults:
172
+ * type: integer
173
+ * description: Total number of matching resources.
174
+ * example: 2
175
+ * startIndex:
176
+ * type: integer
177
+ * description: 1-based index of the first result returned.
178
+ * example: 1
179
+ * itemsPerPage:
180
+ * type: integer
181
+ * description: Number of resources returned in this page.
182
+ * example: 2
183
+ * Resources:
184
+ * type: array
185
+ * items: {}
186
+ * description: The matching resources.
187
+ * ScimError:
188
+ * type: object
189
+ * description: |
190
+ * SCIM 2.0 error response (urn:ietf:params:scim:api:messages:2.0:Error).
191
+ * required: [schemas, status]
192
+ * properties:
193
+ * schemas:
194
+ * type: array
195
+ * items: { type: string }
196
+ * example: ['urn:ietf:params:scim:api:messages:2.0:Error']
197
+ * status:
198
+ * type: string
199
+ * description: HTTP status code as a string.
200
+ * example: '404'
201
+ * scimType:
202
+ * type: string
203
+ * description: |
204
+ * SCIM error type keyword (RFC 7644 §3.12). One of: invalidFilter,
205
+ * tooMany, uniqueness, mutability, invalidSyntax, invalidPath,
206
+ * noTarget, invalidValue, invalidVers, sensitive.
207
+ * example: mutability
208
+ * detail:
209
+ * type: string
210
+ * description: Human-readable error description.
211
+ * example: Resource 2819c223-7f76-453a-919d-413861904646 not found.
212
+ * example:
213
+ * schemas: ['urn:ietf:params:scim:api:messages:2.0:Error']
214
+ * status: '404'
215
+ * detail: Resource 2819c223-7f76-453a-919d-413861904646 not found.
216
+ * ScimPatchOp:
217
+ * type: object
218
+ * description: |
219
+ * SCIM 2.0 patch request body (urn:ietf:params:scim:api:messages:2.0:PatchOp,
220
+ * RFC 7644 §3.5.2).
221
+ * required: [schemas, Operations]
222
+ * properties:
223
+ * schemas:
224
+ * type: array
225
+ * items: { type: string }
226
+ * example: ['urn:ietf:params:scim:api:messages:2.0:PatchOp']
227
+ * Operations:
228
+ * type: array
229
+ * items:
230
+ * type: object
231
+ * required: [op]
232
+ * properties:
233
+ * op:
234
+ * type: string
235
+ * enum: [add, remove, replace, Add, Remove, Replace]
236
+ * path:
237
+ * type: string
238
+ * description: SCIM attribute path (e.g. "emails[type eq \"work\"].value").
239
+ * value:
240
+ * description: New value for the target attribute.
241
+ * example:
242
+ * schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp']
243
+ * Operations:
244
+ * - op: replace
245
+ * path: active
246
+ * value: false
247
+ * - op: replace
248
+ * path: emails[type eq "work"].value
249
+ * value: bjensen@newdomain.example.com
250
+ * ScimBulkRequest:
251
+ * type: object
252
+ * description: |
253
+ * SCIM 2.0 Bulk request (urn:ietf:params:scim:api:messages:2.0:BulkRequest,
254
+ * RFC 7644 §3.7).
255
+ * required: [schemas, Operations]
256
+ * properties:
257
+ * schemas:
258
+ * type: array
259
+ * items: { type: string }
260
+ * example: ['urn:ietf:params:scim:api:messages:2.0:BulkRequest']
261
+ * failOnErrors:
262
+ * type: integer
263
+ * description: |
264
+ * Stop processing after this many errors. Omit or set to 0 to
265
+ * continue on errors and process all operations.
266
+ * example: 1
267
+ * Operations:
268
+ * type: array
269
+ * items:
270
+ * type: object
271
+ * required: [method, path]
272
+ * properties:
273
+ * method:
274
+ * type: string
275
+ * enum: [POST, PUT, PATCH, DELETE]
276
+ * bulkId:
277
+ * type: string
278
+ * description: |
279
+ * Temporary client-assigned identifier for this operation.
280
+ * Subsequent operations in the same request may reference the
281
+ * created resource as "bulkId:<value>".
282
+ * path:
283
+ * type: string
284
+ * description: Resource path relative to the SCIM prefix (e.g. /Users or /Groups/123).
285
+ * data:
286
+ * description: Request body for POST/PUT/PATCH operations.
287
+ * example:
288
+ * schemas: ['urn:ietf:params:scim:api:messages:2.0:BulkRequest']
289
+ * failOnErrors: 1
290
+ * Operations:
291
+ * - method: POST
292
+ * bulkId: qwerty
293
+ * path: /Users
294
+ * data:
295
+ * schemas: ['urn:ietf:params:scim:schemas:core:2.0:User']
296
+ * userName: bjensen
297
+ * name:
298
+ * familyName: Jensen
299
+ * givenName: Barbara
300
+ * - method: DELETE
301
+ * path: /Users/2819c223-7f76-453a-919d-413861904646
302
+ * ScimBulkResponse:
303
+ * type: object
304
+ * description: |
305
+ * SCIM 2.0 Bulk response (urn:ietf:params:scim:api:messages:2.0:BulkResponse).
306
+ * required: [schemas, Operations]
307
+ * properties:
308
+ * schemas:
309
+ * type: array
310
+ * items: { type: string }
311
+ * example: ['urn:ietf:params:scim:api:messages:2.0:BulkResponse']
312
+ * Operations:
313
+ * type: array
314
+ * items:
315
+ * type: object
316
+ * required: [method, status]
317
+ * properties:
318
+ * method:
319
+ * type: string
320
+ * enum: [POST, PUT, PATCH, DELETE]
321
+ * bulkId:
322
+ * type: string
323
+ * location:
324
+ * type: string
325
+ * description: URL of the created/modified resource.
326
+ * version:
327
+ * type: string
328
+ * status:
329
+ * type: string
330
+ * description: HTTP status code for this individual operation.
331
+ * response:
332
+ * $ref: '#/components/schemas/ScimError'
333
+ * description: Present only when the operation failed.
334
+ * example:
335
+ * schemas: ['urn:ietf:params:scim:api:messages:2.0:BulkResponse']
336
+ * Operations:
337
+ * - method: POST
338
+ * bulkId: qwerty
339
+ * location: https://example.com/scim/v2/Users/2819c223-7f76-453a-919d-413861904646
340
+ * status: '201'
341
+ * - method: DELETE
342
+ * status: '204'
343
+ * ScimResourceType:
344
+ * type: object
345
+ * description: SCIM 2.0 ResourceType definition (RFC 7643 §6).
346
+ * required: [schemas, id, name, endpoint, schema]
347
+ * properties:
348
+ * schemas:
349
+ * type: array
350
+ * items: { type: string }
351
+ * example: ['urn:ietf:params:scim:schemas:core:2.0:ResourceType']
352
+ * id: { type: string, example: User }
353
+ * name: { type: string, example: User }
354
+ * endpoint: { type: string, example: /Users }
355
+ * description: { type: string, example: User Account }
356
+ * schema:
357
+ * type: string
358
+ * example: urn:ietf:params:scim:schemas:core:2.0:User
359
+ * schemaExtensions:
360
+ * type: array
361
+ * items:
362
+ * type: object
363
+ * properties:
364
+ * schema: { type: string }
365
+ * required: { type: boolean }
366
+ * meta:
367
+ * type: object
368
+ * properties:
369
+ * resourceType: { type: string, example: ResourceType }
370
+ * location: { type: string }
371
+ * ScimSchema:
372
+ * type: object
373
+ * description: SCIM 2.0 Schema definition (RFC 7643 §7).
374
+ * required: [schemas, id, name, attributes]
375
+ * properties:
376
+ * schemas:
377
+ * type: array
378
+ * items: { type: string }
379
+ * example: ['urn:ietf:params:scim:schemas:core:2.0:Schema']
380
+ * id:
381
+ * type: string
382
+ * example: urn:ietf:params:scim:schemas:core:2.0:User
383
+ * name: { type: string, example: User }
384
+ * description: { type: string, example: User Account }
385
+ * attributes:
386
+ * type: array
387
+ * items: {}
388
+ * description: Schema attribute definitions.
389
+ * meta:
390
+ * type: object
391
+ * properties:
392
+ * resourceType: { type: string, example: Schema }
393
+ * location: { type: string }
394
+ */
6
395
  export default class Scim extends DmPlugin {
7
396
  name: string;
8
397
  roles: Role[];
@@ -1,5 +1,38 @@
1
1
  import type { Express } from 'express';
2
2
  import DmPlugin, { type Role } from '../abstract/plugin';
3
+ /**
4
+ * @openapi-component
5
+ * LdapSchema:
6
+ * type: object
7
+ * description: |
8
+ * A JSON schema file served from the `schemas/` sub-directory of the
9
+ * static path. These schemas describe LDAP entity types (users, groups,
10
+ * organizations, …) and are consumed by browser-embedded editors to
11
+ * render attribute forms and validate input before submission.
12
+ *
13
+ * The server replaces `__FOO_BAR__` placeholders on the fly with the
14
+ * value of the corresponding `--foo-bar` CLI option so that a single
15
+ * schema file can be shared between server-side validation and
16
+ * client-side rendering.
17
+ * additionalProperties: true
18
+ * example:
19
+ * entity:
20
+ * name: standardUser
21
+ * mainAttribute: uid
22
+ * objectClass: [top, inetOrgPerson]
23
+ * singularName: user
24
+ * pluralName: users
25
+ * base: ou=users,dc=example,dc=com
26
+ * strict: true
27
+ * attributes:
28
+ * uid:
29
+ * type: string
30
+ * required: true
31
+ * role: identifier
32
+ * cn:
33
+ * type: string
34
+ * required: true
35
+ */
3
36
  export default class Static extends DmPlugin {
4
37
  name: string;
5
38
  roles: Role[];
@@ -1,6 +1,69 @@
1
1
  import type { Express } from 'express';
2
2
  import DmPlugin, { type Role } from '../../abstract/plugin';
3
3
  import type { DM } from '../../bin';
4
+ /**
5
+ * @openapi-component
6
+ * AppAccount:
7
+ * type: object
8
+ * description: |
9
+ * An application-specific identity attached to a user. App accounts
10
+ * allow a single principal user to authenticate as different named
11
+ * "devices" or "applications" each with its own credential, while
12
+ * sharing the same mail address.
13
+ * required: [uid]
14
+ * properties:
15
+ * uid:
16
+ * type: string
17
+ * description: |
18
+ * Unique identifier of the app account. Format:
19
+ * `<username>_c<8-digits>` (e.g. `alice_c04729183`).
20
+ * example: alice_c04729183
21
+ * name:
22
+ * type: string
23
+ * description: Human-readable label (stored as LDAP `description`).
24
+ * example: Work laptop
25
+ * example:
26
+ * uid: alice_c04729183
27
+ * name: Work laptop
28
+ * AppAccountCreated:
29
+ * type: object
30
+ * description: |
31
+ * Response returned when a new app account is successfully created.
32
+ * The one-time cleartext password is included here and never returned
33
+ * again — callers must store it immediately.
34
+ * required: [uid, pwd, mail]
35
+ * properties:
36
+ * uid:
37
+ * type: string
38
+ * description: Identifier of the newly created app account.
39
+ * example: alice_c04729183
40
+ * pwd:
41
+ * type: string
42
+ * description: |
43
+ * One-time cleartext password generated for this account.
44
+ * Format: 6 blocks of 4 characters separated by `-`
45
+ * (e.g. `Ab3@-xYz!-9pQ#-Sv4$-mN8!-pQ5@`).
46
+ * OpenLDAP hashes it via ppolicy before storage.
47
+ * example: Ab3@-xYz!-9pQ#-Sv4$-mN8!-pQ5@
48
+ * mail:
49
+ * type: string
50
+ * description: Mail address of the principal account this app account belongs to.
51
+ * example: alice@example.com
52
+ * example:
53
+ * uid: alice_c04729183
54
+ * pwd: Ab3@-xYz!-9pQ#-Sv4$-mN8!-pQ5@
55
+ * mail: alice@example.com
56
+ * AppAccountCreate:
57
+ * type: object
58
+ * description: Request body for creating a new app account.
59
+ * properties:
60
+ * name:
61
+ * type: string
62
+ * description: Optional human-readable label for the new account.
63
+ * example: Work laptop
64
+ * example:
65
+ * name: Work laptop
66
+ */
4
67
  export default class AppAccountsApi extends DmPlugin {
5
68
  name: string;
6
69
  roles: Role[];
@@ -0,0 +1,54 @@
1
+ import DmPlugin, { type Role } from '../../abstract/plugin';
2
+ import type { DM } from '../../bin';
3
+ import { Hooks } from '../../hooks';
4
+ interface RabbitPublisher {
5
+ init(): Promise<void>;
6
+ publish(exchange: string, routingKey: string, message: Record<string, unknown>): Promise<void>;
7
+ close(clearSubscriptions?: boolean): Promise<void>;
8
+ }
9
+ export default class CozyProvision extends DmPlugin {
10
+ name: string;
11
+ roles: Role[];
12
+ private readonly cozyAdminUrl;
13
+ private readonly cozyAdminPassphrase;
14
+ private readonly cozyAdminUser;
15
+ private readonly cozyOrgId;
16
+ private readonly cozyOrgDomain;
17
+ private readonly cozyDefaultLocale;
18
+ private readonly rabbitmqUrl;
19
+ private readonly authExchange;
20
+ private readonly b2bExchange;
21
+ private publisher;
22
+ private publisherInit;
23
+ constructor(server: DM);
24
+ hooks: Hooks;
25
+ /**
26
+ * POST /instances on the cozy-stack admin API. Returns true on success
27
+ * (including 409 idempotent "already exists").
28
+ */
29
+ private createCozyInstance;
30
+ /**
31
+ * Publish user.created on the `auth` topic exchange. Payload mirrors what
32
+ * cozy-stack expects: a sub identifier, an optional email, and the
33
+ * organizationDomain so consumers can match the instance.
34
+ */
35
+ private publishUserCreated;
36
+ /**
37
+ * Publish domain.user.deleted on the `b2b` topic exchange so peer cozy
38
+ * instances can drop the deleted user's contact card.
39
+ */
40
+ private publishUserDeleted;
41
+ /**
42
+ * Lazy-init the AMQP publisher. The `@linagora/rabbitmq-client` dependency
43
+ * is optional — if the package or the broker is unreachable, return null
44
+ * so callers can no-op cleanly.
45
+ *
46
+ * Override this in tests to inject a stub publisher.
47
+ */
48
+ protected getPublisher(): Promise<RabbitPublisher | null>;
49
+ private extractId;
50
+ private extractPrimaryEmail;
51
+ private extractLocale;
52
+ private registerShutdown;
53
+ }
54
+ export {};
@@ -5,6 +5,53 @@ import type { DM } from '../../bin';
5
5
  import type { AttributeValue } from '../../lib/ldapActions';
6
6
  import { Hooks } from '../../hooks';
7
7
  import type { ChangesToNotify } from '../ldap/onChange';
8
+ /**
9
+ * OpenAPI schemas specific to the James integration.
10
+ *
11
+ * @openapi-component
12
+ * JamesQuotaUsage:
13
+ * type: object
14
+ * description: |
15
+ * Quota information for a mailbox user as returned by the James
16
+ * WebAdmin API (`GET /quota/users/{mail}`). Contains separate
17
+ * counters for message count and storage size.
18
+ * properties:
19
+ * count:
20
+ * type: object
21
+ * description: Message-count quota.
22
+ * properties:
23
+ * used:
24
+ * type: integer
25
+ * description: Number of messages currently stored.
26
+ * example: 142
27
+ * max:
28
+ * type: integer
29
+ * nullable: true
30
+ * description: |
31
+ * Maximum number of messages allowed. `null` means unlimited.
32
+ * example: 10000
33
+ * size:
34
+ * type: object
35
+ * description: Storage-size quota (bytes).
36
+ * properties:
37
+ * used:
38
+ * type: integer
39
+ * description: Bytes currently used.
40
+ * example: 524288000
41
+ * max:
42
+ * type: integer
43
+ * nullable: true
44
+ * description: |
45
+ * Maximum bytes allowed. `null` means unlimited.
46
+ * example: 2147483648
47
+ * example:
48
+ * count:
49
+ * used: 142
50
+ * max: 10000
51
+ * size:
52
+ * used: 524288000
53
+ * max: 2147483648
54
+ */
8
55
  export default class James extends TwakePlugin {
9
56
  name: string;
10
57
  roles: Role[];
@@ -13,6 +13,7 @@ This guide will help you create powerful, production-ready plugins for LDAP-Rest
13
13
  - [Configuration Best Practices](#configuration-best-practices)
14
14
  - [Complete Plugin Example](#complete-plugin-example)
15
15
  - [Testing Your Plugin](#testing-your-plugin)
16
+ - [Documenting REST endpoints (OpenAPI)](./openapi.md)
16
17
 
17
18
  ---
18
19
 
@@ -1282,7 +1283,7 @@ source ~/.test-env && npm run test:dev
1282
1283
  ## Additional Resources
1283
1284
 
1284
1285
  - [Contributing Guide](../../CONTRIBUTING.md) - Git workflow and PR guidelines
1285
- - [Hooks Reference](../../HOOKS.md) - Complete list of available hooks
1286
+ - [Hooks Reference](./hooks.md) - Complete list of available hooks
1286
1287
  - [Core Plugin Examples](../../src/plugins/) - Browse existing plugins
1287
1288
  - [Test Examples](../../test/plugins/) - See how core plugins are tested
1288
1289