ldap-rest 0.9.0 → 0.11.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.
Files changed (47) hide show
  1. package/bin/index.mjs +1 -1
  2. package/dist/abstract/ldapFlat.js +1 -1
  3. package/dist/config/args.js +1 -1
  4. package/dist/lib/authz/base.js +1 -1
  5. package/dist/lib/changeContext.js +1 -0
  6. package/dist/lib/errors.js +1 -1
  7. package/dist/lib/ldapActions.js +1 -1
  8. package/dist/plugins/auth/authzLinid1.js +1 -1
  9. package/dist/plugins/auth/authzPerBranch.js +1 -1
  10. package/dist/plugins/auth/fake.js +1 -0
  11. package/dist/plugins/ldap/departmentSync.js +1 -1
  12. package/dist/plugins/ldap/groups.js +1 -1
  13. package/dist/plugins/ldap/onChange.js +1 -1
  14. package/dist/plugins/ldap/organizations.js +1 -1
  15. package/dist/plugins/ldap/trash.js +1 -1
  16. package/dist/plugins/priority.json.js +1 -1
  17. package/dist/plugins/scim/scim.js +1 -1
  18. package/dist/plugins/twake/appAccountsApi.js +1 -1
  19. package/dist/plugins/twake/appAccountsConsistency.js +1 -1
  20. package/dist/plugins/twake/clouderyProvision.js +1 -1
  21. package/dist/plugins/twake/cozyProvision.js +1 -1
  22. package/dist/src/abstract/plugin.d.ts +6 -0
  23. package/dist/src/config/args.d.ts +5 -3
  24. package/dist/src/hooks.d.ts +15 -6
  25. package/dist/src/lib/authz/base.d.ts +32 -0
  26. package/dist/src/lib/changeContext.d.ts +24 -0
  27. package/dist/src/lib/errors.d.ts +15 -0
  28. package/dist/src/lib/ldapActions.d.ts +2 -1
  29. package/dist/src/plugins/auth/authzLinid1.d.ts +14 -0
  30. package/dist/src/plugins/auth/authzPerBranch.d.ts +35 -0
  31. package/dist/src/plugins/auth/fake.d.ts +21 -0
  32. package/dist/src/plugins/ldap/groups.d.ts +13 -8
  33. package/dist/src/plugins/ldap/onChange.d.ts +17 -8
  34. package/dist/src/plugins/ldap/organizations.d.ts +2 -2
  35. package/docs/plugin-development/README.md +1 -18
  36. package/docs/plugin-development/hooks.md +6 -0
  37. package/docs/plugin-development/testing.md +1 -17
  38. package/docs/usage/configuration.md +15 -6
  39. package/docs/usage/plugins/auth/README.md +2 -0
  40. package/docs/usage/plugins/auth/authz-linid1.md +29 -34
  41. package/docs/usage/plugins/auth/authz-per-branch.md +33 -11
  42. package/docs/usage/plugins/auth/authz-scope.md +5 -0
  43. package/docs/usage/plugins/auth/fake.md +28 -0
  44. package/docs/usage/plugins/ldap/on-change.md +51 -284
  45. package/docs/usage/troubleshooting.md +1 -9
  46. package/docs/usage/upgrading.md +93 -0
  47. package/package.json +5 -1
@@ -1,33 +1,42 @@
1
1
  /**
2
2
  * @module core/onLdapChange
3
- * Check for ldap modify events and generate hooks:
4
- * - onLdapChange
5
- * - onLdapMailChange
3
+ * Publish the state of an entry before and after each write
4
+ * (onLdapEntryChange), and the hooks derived from it: onLdapChange,
5
+ * onLdapMailChange…
6
6
  * @author Xavier Guimard <xguimard@linagora.com>
7
7
  */
8
8
  import { Entry } from 'ldapts';
9
9
  import DmPlugin, { type Role } from '../../abstract/plugin';
10
10
  import type { Hooks } from '../../hooks';
11
11
  import type { AttributeValue } from '../../lib/ldapActions';
12
+ import type { ChangeContext } from '../../lib/changeContext';
12
13
  export type ChangesToNotify = Record<string, [
13
14
  AttributeValue | null,
14
15
  AttributeValue | null
15
16
  ]>;
17
+ /**
18
+ * The attributes whose values differ between two states of an entry. Values
19
+ * are compared as sets, so a single value and a one-element array are equal,
20
+ * and so are two orderings of the same values.
21
+ */
22
+ export declare function diffEntries(before: Entry | null, after: Entry | null): ChangesToNotify;
16
23
  declare class OnLdapChange extends DmPlugin {
17
24
  name: string;
18
25
  roles: Role[];
19
26
  stack: Record<number, Entry>;
20
27
  pendingDeletions: Map<string, Entry>;
28
+ pendingRenames: Map<string, Entry>;
21
29
  hooks: Hooks;
22
- notify(dn: string, changes: ChangesToNotify): void;
30
+ read(dn: string): Promise<Entry | undefined>;
31
+ publish(dn: string, before: Entry | null, after: Entry | null, context?: ChangeContext): void;
32
+ notify(dn: string, changes: ChangesToNotify, before: Entry | null, after: Entry | null): void;
23
33
  notifyAttributeChange(attribute: string, hookName: keyof Hooks, dn: string, changes: ChangesToNotify, stringOnly?: boolean): void;
24
- notifyAttributeChangeWithMail(attribute: string, hookName: keyof Hooks, dn: string, changes: ChangesToNotify): Promise<void>;
34
+ notifyAttributeChangeWithMail(attribute: string, hookName: keyof Hooks, dn: string, changes: ChangesToNotify, after: Entry | null): void;
25
35
  /**
26
36
  * Reconstruct display name from cn, givenName, and sn attributes
27
- * @param changes - The changes object
28
- * @param index - 0 for old value, 1 for new value
37
+ * @param entry - The entry, before or after the change
29
38
  * @returns The reconstructed display name or null
30
39
  */
31
- reconstructDisplayName(changes: ChangesToNotify, index: 0 | 1): string | null;
40
+ reconstructDisplayName(entry: Entry | null): string | null;
32
41
  }
33
42
  export default OnLdapChange;
@@ -214,7 +214,7 @@ export default class LdapOrganizations extends DmPlugin {
214
214
  */
215
215
  private linkedEntities;
216
216
  addOrganization(dn: string, entry: AttributesList, req?: Request): Promise<boolean>;
217
- modifyOrganization(dn: string, changes: ModifyRequest): Promise<boolean>;
217
+ modifyOrganization(dn: string, changes: ModifyRequest, req?: Request): Promise<boolean>;
218
218
  validateNewOrganization(dn: string, entry: AttributesList): Promise<boolean>;
219
219
  /**
220
220
  * Build the rejection message for a value that failed its schema `test`,
@@ -241,7 +241,7 @@ export default class LdapOrganizations extends DmPlugin {
241
241
  moveOrganization(dn: string, targetOrgDn: string, req?: Request): Promise<{
242
242
  newDn: string;
243
243
  }>;
244
- deleteOrganization(dn: string): Promise<boolean>;
244
+ deleteOrganization(dn: string, req?: Request): Promise<boolean>;
245
245
  /**
246
246
  * The same two searches as `getOrganisationSubnodes`, narrowed by a query.
247
247
  *
@@ -1283,24 +1283,7 @@ describe('NotificationPlugin', function () {
1283
1283
 
1284
1284
  ### Running Tests
1285
1285
 
1286
- ```bash
1287
- # Set up test environment
1288
- cat > ~/.test-env <<EOF
1289
- export DM_LDAP_URL="ldap://localhost:389"
1290
- export DM_LDAP_DN="cn=admin,dc=example,dc=com"
1291
- export DM_LDAP_PWD="admin"
1292
- export DM_LDAP_BASE="dc=example,dc=com"
1293
- EOF
1294
-
1295
- # Run all tests
1296
- source ~/.test-env && npm test
1297
-
1298
- # Run specific test file
1299
- source ~/.test-env && npm run test:one test/plugins/notification/webhook.test.ts
1300
-
1301
- # Run tests in watch mode
1302
- source ~/.test-env && npm run test:dev
1303
- ```
1286
+ See [Tests and checks](../../CONTRIBUTING.md#tests-and-checks).
1304
1287
 
1305
1288
  ### Test Best Practices
1306
1289
 
@@ -16,3 +16,9 @@ Typescript definitions into [hooks.ts](./src/hooks.ts)
16
16
  - **ldapmodifyrequest**: called before any ldapmodify
17
17
  - **ldapdeleterequest**: called before any ldapdelete
18
18
  - **ldaprenamerequest**: called before any ldap rename/modifyDN operation
19
+
20
+ ## [onChange](../usage/plugins/ldap/on-change.md) hooks
21
+
22
+ - **onLdapEntryChange**: called after any write, with the entry before and
23
+ after it, and who made it
24
+ - **onLdapChange**, **onLdapMailChange**…: derived from it
@@ -76,23 +76,7 @@ Loading initial LDIF from .../base-structure.ldif...
76
76
 
77
77
  ### Running Tests
78
78
 
79
- **Using embedded LDAP (no setup required):**
80
-
81
- ```bash
82
- npm test
83
- npm run test:one test/plugins/ldap/flatGeneric.test.ts
84
- ```
85
-
86
- **Using external LDAP:**
87
-
88
- ```bash
89
- export DM_LDAP_URL=ldap://localhost:389
90
- export DM_LDAP_DN=cn=admin,dc=example,dc=com
91
- export DM_LDAP_PWD=secret
92
- export DM_LDAP_BASE=dc=example,dc=com
93
-
94
- npm test
95
- ```
79
+ See [Tests and checks](../../CONTRIBUTING.md#tests-and-checks).
96
80
 
97
81
  ### In CI/CD
98
82
 
@@ -25,6 +25,7 @@ All LDAP-Rest configuration options.
25
25
  - [core/auth/hmac](#coreauthhmac)
26
26
  - [core/auth/llng](#coreauthllng)
27
27
  - [core/auth/openidconnect](#coreauthopenidconnect)
28
+ - [core/auth/fake](#coreauthfake)
28
29
  - [core/bcl](#corebcl)
29
30
  - [Authorization Plugins](#authorization-plugins)
30
31
  - [Common to every authorization plugin](#common-to-every-authorization-plugin)
@@ -101,7 +102,7 @@ export DM_PLUGINS="core/auth/token,core/ldap/flatGeneric,core/ldap/groups"
101
102
  | `--ldap-url` | `--ldap-urls` | `DM_LDAP_URL` | `ldap://localhost` | LDAP server URL(s) |
102
103
  | `--ldap-dn` | | `DM_LDAP_DN` | `cn=admin,dc=example,dc=com` | Bind DN |
103
104
  | `--ldap-pwd` | | `DM_LDAP_PWD` | `admin` | Password |
104
- | `--ldap-base` | | `DM_LDAP_BASE` | | Base DN for searches |
105
+ | `--ldap-base` | | `DM_LDAP_BASE` | | Base DN for searches (required) |
105
106
  | `--ldap-user-main-attribute` | | `DM_LDAP_USER_ATTRIBUTE` | `uid` | User identifier attribute |
106
107
  | `--ldap-cache-max` | | `DM_LDAP_CACHE_MAX` | `1000` | Max cache entries |
107
108
  | `--ldap-cache-ttl` | | `DM_LDAP_CACHE_TTL` | `0` | Search cache TTL (seconds), `0` disables it |
@@ -238,6 +239,14 @@ Empty means the plugin guards every path. Scoping several instances to different
238
239
  | -------------- | --------------- | ---------------- | ------- | --------------------- |
239
240
  | `--auth-token` | `--auth-tokens` | `DM_AUTH_TOKENS` | `[]` | Authentication tokens |
240
241
 
242
+ #### `core/auth/fake`
243
+
244
+ | CLI | Env | Default | Description |
245
+ | ------------------ | ------------------- | ------- | ---------------------------------------------- |
246
+ | `--auth-fake-user` | `DM_AUTH_FAKE_USER` | | Identity every request is served as (required) |
247
+
248
+ Development only, refused with `NODE_ENV=production` — see [Fake Authentication](plugins/auth/fake.md).
249
+
241
250
  #### `core/auth/totp`
242
251
 
243
252
  | CLI | Plural | Env | Default | Description |
@@ -324,11 +333,11 @@ whichever of the two names it selects. A value other than `req.user` or
324
333
 
325
334
  #### `core/auth/authzPerBranch`
326
335
 
327
- | CLI | Env | Default | Description |
328
- | --------------------------------- | ---------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
329
- | `--authz-per-branch-config` | `DM_AUTHZ_PER_BRANCH_CONFIG` | `{default:{read:true,write:false,delete:false}}` | Authorization config (JSON) |
330
- | `--authz-per-branch-cache-ttl` | `DM_AUTHZ_PER_BRANCH_CACHE_TTL` | `60` | Cache TTL (seconds) |
331
- | `--authz-filter-attached-entries` | `DM_AUTHZ_FILTER_ATTACHED_ENTRIES` | `false` | Judge an attached entry by the organization it hangs off, and filter listings accordingly. Honoured by `authzPerBranch` and `authzLinid1`; does nothing under `authzDynamic`. |
336
+ | CLI | Env | Default | Description |
337
+ | --------------------------------- | ---------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
338
+ | `--authz-per-branch-config` | `DM_AUTHZ_PER_BRANCH_CONFIG` | `{default:{read:true,write:false,delete:false}}` | Authorization config (JSON) |
339
+ | `--authz-per-branch-cache-ttl` | `DM_AUTHZ_PER_BRANCH_CACHE_TTL` | `60` | Cache TTL (seconds) |
340
+ | `--authz-filter-attached-entries` | `DM_AUTHZ_FILTER_ATTACHED_ENTRIES` | `false` | Judge an attached entry by the organization it hangs off, and filter listings accordingly. Honoured by `authzPerBranch`; always on under `authzLinid1`; does nothing under `authzDynamic`. |
332
341
 
333
342
  #### `core/auth/authzScope`
334
343
 
@@ -11,6 +11,7 @@ LDAP-Rest provides multiple authentication plugins to secure API access. These p
11
11
  | [HMAC](hmac.md) | `core/auth/hmac` | HMAC-SHA256 request signing for backend services |
12
12
  | [LemonLDAP::NG](llng.md) | `core/auth/llng` | Integration with LemonLDAP::NG SSO |
13
13
  | [OpenID Connect](oidc.md) | `core/auth/openidconnect` | OAuth 2.0 / OpenID Connect authentication |
14
+ | [Fake](fake.md) | `core/auth/fake` | Development only: a fixed identity, no login |
14
15
 
15
16
  ## What a rule is keyed on
16
17
 
@@ -25,6 +26,7 @@ of what a caller is called. Every one of them publishes the caller twice:
25
26
  | `core/auth/llng` | LLNG's `whatToTrace` | `--llng-username-header`, or the same |
26
27
  | `core/auth/openidconnect` | the OIDC `sub` | `--oidc-username-claim` (default: `sub`) |
27
28
  | `core/auth/authzDynamic` | the token's tenant | the same |
29
+ | `core/auth/fake` | `--auth-fake-user` | the same |
28
30
 
29
31
  `req.user` is this server's identifier for the caller; `req.userName` is the
30
32
  caller under a name a person would use. `--authz-identity` says which one is
@@ -48,6 +48,24 @@ twakeDepartmentLink: ou=HR,ou=organization,dc=example,dc=com
48
48
 
49
49
  The HR admin (listed in `ou=HR`'s `twakeLocalAdminLink`) can manage John even though he's in `ou=users` because his `twakeDepartmentLink` points to the HR organization.
50
50
 
51
+ ### What an administrator reads
52
+
53
+ `authzLinid1` always judges an attached entry this way, as
54
+ `--authz-filter-attached-entries` does for `authzPerBranch`, whatever that
55
+ option says: judged by its parent instead, an account in a flat `ou=users`
56
+ belongs to nobody's branch, and no administrator could list or change one.
57
+
58
+ - Listings of accounts and groups drop the entries attached to an
59
+ organization outside the caller's branches. A single read of one answers
60
+ `404`.
61
+ - Entries attached to nothing — the organizations, the nomenclature values, a
62
+ group or an account with no `twakeDepartmentLink` — are readable by every
63
+ administrator: the organization tree is reference data. Writes to them are
64
+ judged on their parent. The tree a console draws still starts at the
65
+ caller's own branches: see [getOrganisationTop Hook](#getorganisationtop-hook).
66
+ - A caller named in no `twakeLocalAdminLink` is refused every read (`403`)
67
+ but the top organization's own entry, which `organizations/top` answers.
68
+
51
69
  ## Configuration
52
70
 
53
71
  No configuration file needed - permissions are read from LDAP. Simply load the plugin:
@@ -75,19 +93,20 @@ works behind an authenticator that publishes what the directory calls a user:
75
93
  | `core/auth/openidconnect` | the OIDC `sub` | **no** — an opaque provider identifier |
76
94
 
77
95
  An identity that does not resolve is **refused** (403), and the refusal is
78
- logged with the plugin's name. Until 0.9.0 it was let through instead, which
79
- made this pairing an open door rather than a misconfiguration; see
80
- [the note](../../upgrading.md#an-identity-that-does-not-resolve-is-refused)
81
- and `--authz-unresolved-user` for the transition.
96
+ logged with the plugin's name; `--authz-unresolved-user` sets it otherwise
97
+ ([notes](../../upgrading.md#an-identity-that-does-not-resolve-is-refused)).
98
+ An identity naming **several** entries is refused (403) whatever that
99
+ option says, and a warning gives the number of entries. A lookup the
100
+ directory fails fails the request and is not cached.
82
101
 
83
102
  Both answers are cached for the plugin's TTL (5 minutes), negatives
84
103
  included, so a mismatch does not turn every request into a directory search.
85
104
  The TTL cuts both ways: an identity added to the directory after being
86
105
  refused waits it out, and an administrator whose **own entry is renamed or
87
106
  moved** keeps resolving to the former DN — where no organization names them,
88
- so their own operations are refused. A rename or a delete performed through
89
- this server drops what was resolved; a change made directly in the directory
90
- waits out the TTL.
107
+ so their own operations are refused. A rename, a move to the trash or a
108
+ delete performed through this server drops what was resolved; a change made
109
+ directly in the directory waits out the TTL.
91
110
 
92
111
  `GET /api/v1/authz/scope` resolves the identity outside that cache, so
93
112
  during such a window it can describe a scope the hooks no longer grant.
@@ -178,8 +197,9 @@ Intercepts all LDAP search operations:
178
197
  2. Resolves user's DN from username
179
198
  3. Allows base scope searches on top organization (for navigation)
180
199
  4. Allows searches for `twakeLocalAdminLink` (for permission refresh)
181
- 5. Verifies user has read permission for search base
182
- 6. Throws error if access denied
200
+ 5. Refuses a caller who administers no branch; lets the others search, and
201
+ drops from the answer the entries attached outside their branches
202
+ (`ldapsearchfilter`)
183
203
 
184
204
  **Special allowances:**
185
205
 
@@ -460,31 +480,6 @@ User jdoe not found in LDAP
460
480
 
461
481
  3. Verify authentication plugin sets `req.user` correctly
462
482
 
463
- ### Problem: Cannot Search Top Organization
464
-
465
- **Symptoms:**
466
-
467
- ```json
468
- {
469
- "error": "User admin does not have read permission for branch ou=organization,dc=example,dc=com"
470
- }
471
- ```
472
-
473
- **Solutions:**
474
-
475
- This is expected behavior - users can only see organizations they manage. To grant access to top organization:
476
-
477
- ```bash
478
- ldapsearch -x -b "ou=organization,dc=example,dc=com" -s base "(objectClass=*)"
479
- # Modify to add user:
480
- ldapmodify <<EOF
481
- dn: ou=organization,dc=example,dc=com
482
- changetype: modify
483
- add: twakeLocalAdminLink
484
- twakeLocalAdminLink: uid=admin,ou=users,dc=example,dc=com
485
- EOF
486
- ```
487
-
488
483
  ## Integration Examples
489
484
 
490
485
  ### With Token Authentication
@@ -174,10 +174,20 @@ Modifies the organization tree API:
174
174
 
175
175
  Groups are resolved dynamically:
176
176
 
177
- 1. Searches for all groups where user is a member
178
- 2. Uses wildcard DN pattern: `(member=uid={user},*)`
179
- 3. Caches results for configured TTL (default: 60 seconds)
180
- 4. Cache prevents repeated LDAP queries for same user
177
+ 1. Resolves the caller to its DN, searching
178
+ `(<ldap_user_main_attribute>=<uid>)` under the LDAP base (`--ldap-base`)
179
+ 2. Searches for all groups where that DN is a member
180
+ 3. Matches each group found against the `groups` keys **as DNs**: case and
181
+ the spaces around `,` and `=` do not matter, so
182
+ `CN=Editors, OU=Groups,dc=example,dc=com` names the same group as
183
+ `cn=editors,ou=groups,dc=example,dc=com`
184
+ 4. Caches results (see [Group Caching](#group-caching)); concurrent
185
+ requests for the same user share one lookup
186
+
187
+ A uid that names **more than one** entry gets **no group rule**, since its
188
+ groups cannot be told apart from its homonym's; a warning is logged, and the
189
+ uid's own `users` rule still applies. A lookup that fails answers `500` and
190
+ is not cached.
181
191
 
182
192
  ## Sub-branch Inheritance
183
193
 
@@ -379,6 +389,10 @@ Group memberships are cached to reduce LDAP queries:
379
389
  - **Configurable**: `--authz-per-branch-cache-ttl`
380
390
  - **Per-user cache**: Each user's groups cached separately
381
391
  - **Automatic expiry**: Cache entries expire after TTL
392
+ - **Dropped on writes**: any add, delete, rename or move made through
393
+ ldap-rest empties the cache, and so does a modify touching the group member
394
+ attribute or `--ldap-user-main-attribute`. A change made directly in the
395
+ directory waits for the TTL
382
396
 
383
397
  Cache hit/miss logging:
384
398
 
@@ -420,7 +434,6 @@ names, this plugin lets them read and refuses every write. Give it an
420
434
 
421
435
  - Group DNs must be fully qualified
422
436
  - Group resolution uses LDAP search - ensure proper indexing
423
- - Cache can delay permission revocation (up to TTL duration)
424
437
 
425
438
  ### Authentication Required
426
439
 
@@ -531,7 +544,8 @@ Changed permissions don't take effect immediately.
531
544
 
532
545
  **Solutions:**
533
546
 
534
- 1. Wait for group cache TTL to expire
547
+ 1. A membership changed directly in the directory waits for the
548
+ [group cache](#group-caching) TTL
535
549
  2. Restart server to clear all caches
536
550
  3. Reduce `--authz-per-branch-cache-ttl` for testing
537
551
 
@@ -542,17 +556,25 @@ User should inherit group permissions but doesn't.
542
556
 
543
557
  **Solutions:**
544
558
 
545
- 1. Verify user is member of group:
559
+ 1. Verify the user's DN is a member of the group:
546
560
 
547
561
  ```bash
548
- ldapsearch -x -b "ou=groups,dc=example,dc=com" "(member=uid=jdoe,*)"
562
+ ldapsearch -x -b "dc=example,dc=com" \
563
+ "(member=uid=jdoe,ou=users,dc=example,dc=com)"
549
564
  ```
550
565
 
551
- 2. Check group DN in config matches LDAP exactly
566
+ 2. Check the group DN in config names the group: it is compared as a DN
567
+ (case and spaces are ignored), but every RDN must be there
552
568
 
553
- 3. Ensure group membership uses correct attribute (default: `member`)
569
+ 3. Check the uid names a single entry:
554
570
 
555
- 4. Configure custom member attribute if needed:
571
+ ```bash
572
+ ldapsearch -x -b "dc=example,dc=com" "(uid=jdoe)" dn
573
+ ```
574
+
575
+ 4. Ensure group membership uses correct attribute (default: `member`)
576
+
577
+ 5. Configure custom member attribute if needed:
556
578
  ```bash
557
579
  --ldap-group-member-attribute uniqueMember
558
580
  ```
@@ -150,6 +150,11 @@ identified caller of the same server is told. A server loading no
150
150
  authorization plugin at all answers `unrestricted` to anyone, anonymous
151
151
  included, which is what it does.
152
152
 
153
+ An identity that names **several** entries — the same `uid` in two branches of
154
+ a directory without the `unique` overlay, which `authzLinid1` refuses with
155
+ `AmbiguousIdentityError` — is answered `403`, not `401`: the caller cannot be
156
+ told apart from the homonym, so no scope can be described for them.
157
+
153
158
  ## See also
154
159
 
155
160
  - [authz-linid1](authz-linid1.md) — the local-administrator model this serves
@@ -0,0 +1,28 @@
1
+ # Fake Authentication
2
+
3
+ For development only: every request is served as the identity given on the
4
+ command line, without asking for anything. Restarting with another
5
+ `--auth-fake-user` shows an interface as another administrator sees it.
6
+
7
+ ```bash
8
+ npx ldap-rest \
9
+ --plugin core/auth/fake --auth-fake-user alice \
10
+ --plugin core/auth/authzLinid1 \
11
+ ...
12
+ ```
13
+
14
+ **Environment Variable:** `DM_AUTH_FAKE_USER=alice`
15
+
16
+ The identity goes to `req.user` and `req.userName` alike, so the
17
+ authorization plugins judge it as they would the same login coming from a
18
+ real authenticator.
19
+
20
+ ## Safeguards
21
+
22
+ - The server refuses to start when `NODE_ENV` is `production`, as it is in
23
+ the Docker image.
24
+ - It refuses to start without `--auth-fake-user`.
25
+ - A warning naming the identity is logged at every start.
26
+
27
+ Loaded without an authorization plugin, it grants everything to anyone who
28
+ reaches the port. Listen on a local address only.