ldap-rest 0.6.2 → 0.8.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/dist/abstract/ldapFlat.js +1 -1
- package/dist/abstract/plugin.js +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/lib/expressFormatedResponses.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/auth/authzScope.js +1 -0
- package/dist/plugins/auth/llng.js +1 -1
- package/dist/plugins/ldap/accountLifecycle.js +1 -0
- package/dist/plugins/ldap/bulkImport.js +1 -1
- package/dist/plugins/ldap/departmentSync.js +1 -1
- package/dist/plugins/ldap/enterpriseRules.js +1 -0
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
- package/dist/plugins/ldap/flatGeneric.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/organizations.js +1 -1
- package/dist/plugins/scim/bulk.js +1 -1
- package/dist/plugins/scim/errors.js +1 -1
- package/dist/plugins/scim/filter.js +1 -1
- package/dist/plugins/scim/groups.js +1 -1
- package/dist/plugins/scim/list.js +1 -1
- package/dist/plugins/scim/mapping.js +1 -1
- package/dist/plugins/scim/patch.js +1 -1
- package/dist/plugins/scim/projection.js +1 -0
- package/dist/plugins/scim/scim.js +1 -1
- package/dist/plugins/scim/users.js +1 -1
- package/dist/plugins/twake/calendar.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -1
- package/dist/plugins/twake/james.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +159 -1
- package/dist/src/abstract/plugin.d.ts +13 -0
- package/dist/src/config/args.d.ts +5 -0
- package/dist/src/config/schema.d.ts +382 -12
- package/dist/src/lib/expressFormatedResponses.d.ts +14 -0
- package/dist/src/lib/ldapActions.d.ts +59 -6
- package/dist/src/lib/utils.d.ts +65 -0
- package/dist/src/plugins/auth/authzScope.d.ts +53 -0
- package/dist/src/plugins/auth/llng.d.ts +30 -1
- package/dist/src/plugins/ldap/accountLifecycle.d.ts +56 -0
- package/dist/src/plugins/ldap/departmentSync.d.ts +43 -0
- package/dist/src/plugins/ldap/enterpriseRules.d.ts +436 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +29 -0
- package/dist/src/plugins/ldap/groups.d.ts +22 -0
- package/dist/src/plugins/ldap/organizations.d.ts +25 -4
- package/dist/src/plugins/scim/errors.d.ts +17 -4
- package/dist/src/plugins/scim/filter.d.ts +11 -1
- package/dist/src/plugins/scim/groups.d.ts +2 -0
- package/dist/src/plugins/scim/list.d.ts +3 -5
- package/dist/src/plugins/scim/mapping.d.ts +90 -9
- package/dist/src/plugins/scim/patch.d.ts +16 -1
- package/dist/src/plugins/scim/projection.d.ts +15 -0
- package/dist/src/plugins/scim/scim.d.ts +5 -0
- package/dist/src/plugins/scim/users.d.ts +21 -0
- package/dist/src/plugins/twake/calendar.d.ts +113 -0
- package/dist/src/plugins/twake/calendarResources.d.ts +9 -58
- package/docs/README.md +2 -0
- package/docs/client-development/api/openapi.md +1 -1
- package/docs/client-development/browser/libraries.md +1 -1
- package/docs/plugin-development/README.md +22 -0
- package/docs/plugin-development/dependencies.md +10 -7
- package/docs/presentations/en.md +1 -1
- package/docs/presentations/fr.md +1 -1
- package/docs/usage/README.md +2 -0
- package/docs/usage/configuration.md +35 -15
- package/docs/usage/directory-audit.md +105 -0
- package/docs/usage/plugins/README.md +6 -6
- package/docs/usage/plugins/auth/README.md +6 -5
- package/docs/usage/plugins/auth/authz-scope.md +103 -0
- package/docs/usage/plugins/auth/llng.md +17 -0
- package/docs/usage/plugins/integrations/README.md +5 -5
- package/docs/usage/plugins/integrations/{calendar-resources.md → calendar.md} +70 -13
- package/docs/usage/plugins/integrations/scim.md +147 -38
- package/docs/usage/plugins/ldap/README.md +13 -11
- package/docs/usage/plugins/ldap/account-lifecycle.md +129 -0
- package/docs/usage/plugins/ldap/bulk-import.md +8 -1
- package/docs/usage/plugins/ldap/enterprise-rules.md +167 -0
- package/docs/usage/plugins/ldap/flat-generic.md +172 -0
- package/docs/usage/plugins/ldap/groups.md +10 -4
- package/docs/usage/plugins/ldap/organizations.md +59 -33
- package/docs/usage/upgrading.md +444 -0
- package/package.json +54 -5
- package/static/schemas/example/README.md +36 -0
- package/static/schemas/example/groups.json +94 -0
- package/static/schemas/example/nomenclature/domains.json +49 -0
- package/static/schemas/example/organizations.json +106 -0
- package/static/schemas/example/positions.json +35 -0
- package/static/schemas/example/users.json +246 -0
- package/static/schemas/twake/groups.json +81 -15
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +26 -0
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +22 -0
- package/static/schemas/twake/nomenclature/twakeDomain.json +60 -0
- package/static/schemas/twake/nomenclature/twakeListType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeTitle.json +42 -0
- package/static/schemas/twake/organizations.json +92 -12
- package/static/schemas/twake/positions.json +22 -4
- package/static/schemas/twake/users.json +269 -32
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LDAP low-level library
|
|
3
|
-
* @author Xavier Guimard <xguimard@linagora.com>
|
|
4
|
-
*/
|
|
5
1
|
import type { Request } from 'express';
|
|
6
2
|
import type { ClientOptions, SearchResult, SearchOptions } from 'ldapts';
|
|
7
3
|
import type winston from 'winston';
|
|
@@ -12,6 +8,27 @@ export type AttributeValue = Buffer | Buffer[] | string[] | string;
|
|
|
12
8
|
export type AttributesList = Record<string, AttributeValue>;
|
|
13
9
|
export type LdapList = Record<string, AttributesList>;
|
|
14
10
|
export type { SearchOptions, SearchResult };
|
|
11
|
+
/**
|
|
12
|
+
* The directory as seen by one HTTP request.
|
|
13
|
+
*
|
|
14
|
+
* `ldapActions`'s own methods take the request as a trailing optional
|
|
15
|
+
* argument, and the authorization plugins hook `ldap*request` and skip every
|
|
16
|
+
* check when it is missing — so omitting it is an authorization bypass that
|
|
17
|
+
* fails silently rather than loudly. Three shipped in the SCIM plugin alone.
|
|
18
|
+
*
|
|
19
|
+
* These methods take no request parameter: it is captured once by
|
|
20
|
+
* `ldapActions.forRequest()`, and forgetting it stops being expressible.
|
|
21
|
+
* Request handlers should bind once and use this; the unbound methods remain
|
|
22
|
+
* for work that genuinely belongs to no request, such as startup and cron
|
|
23
|
+
* tasks, where passing nothing is the deliberate answer.
|
|
24
|
+
*/
|
|
25
|
+
export interface RequestBoundLdap {
|
|
26
|
+
search(options: SearchOptions, base?: string): Promise<SearchResult | AsyncGenerator<SearchResult>>;
|
|
27
|
+
add(dn: string, entry: AttributesList): Promise<boolean>;
|
|
28
|
+
modify(dn: string, changes: ModifyRequest): Promise<boolean>;
|
|
29
|
+
rename(dn: string, newRdn: string): Promise<boolean>;
|
|
30
|
+
delete(dn: string | string[]): Promise<boolean>;
|
|
31
|
+
}
|
|
15
32
|
export interface ModifyRequest {
|
|
16
33
|
add?: AttributesList;
|
|
17
34
|
replace?: AttributesList;
|
|
@@ -26,6 +43,22 @@ declare class ldapActions {
|
|
|
26
43
|
parent: DM;
|
|
27
44
|
logger: winston.Logger;
|
|
28
45
|
private searchCache;
|
|
46
|
+
private cacheEnabled;
|
|
47
|
+
/**
|
|
48
|
+
* How many writes this instance has made since it was built.
|
|
49
|
+
*
|
|
50
|
+
* A read can be overtaken by a write: the write lands and `invalidateCache`
|
|
51
|
+
* finds nothing to drop, because the read has not stored its answer yet —
|
|
52
|
+
* and the read then stores what it read *before* the write, an entry
|
|
53
|
+
* nothing will drop until its TTL runs out. `search()` remembers the count
|
|
54
|
+
* it started at and stores only if no write happened in between, so a read
|
|
55
|
+
* a write overtook costs a cache entry, never a stale answer.
|
|
56
|
+
*
|
|
57
|
+
* The count is per instance and per write, not per DN: a write to another
|
|
58
|
+
* branch keeps a concurrent read out of the cache too. That is hit rate
|
|
59
|
+
* given up, not freshness.
|
|
60
|
+
*/
|
|
61
|
+
private cacheGeneration;
|
|
29
62
|
queryLimit: ReturnType<typeof pLimit>;
|
|
30
63
|
private connectionPool;
|
|
31
64
|
private poolSize;
|
|
@@ -62,13 +95,33 @@ declare class ldapActions {
|
|
|
62
95
|
private getAttributeSignature;
|
|
63
96
|
/**
|
|
64
97
|
* Generate cache key for LDAP search
|
|
98
|
+
*
|
|
99
|
+
* Every option that changes the answer belongs in here, or a search that
|
|
100
|
+
* asked for more is served what a search that asked for less got: the
|
|
101
|
+
* requested attributes above all, but also the ones that change the shape
|
|
102
|
+
* of what comes back. `timeLimit` is deliberately absent — it bounds how
|
|
103
|
+
* long the server may spend, not what it returns.
|
|
65
104
|
*/
|
|
66
105
|
private getCacheKey;
|
|
67
106
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
107
|
+
* Drop every cached read of `dn`, and of everything below it.
|
|
108
|
+
*
|
|
109
|
+
* Called after each write, so the next read of what just changed goes to
|
|
110
|
+
* the directory. Two things this does that a `key.startsWith(dn)` test did
|
|
111
|
+
* not: it compares DNs the way the directory does (see {@link foldDn}), so
|
|
112
|
+
* a write spelling a DN differently from the read that cached it still
|
|
113
|
+
* drops it; and it drops the subtree, because `rename()` and `move()`
|
|
114
|
+
* re-parent a whole branch in one operation — renaming an organization
|
|
115
|
+
* changes the DN of every entry under it — leaving the cached reads of
|
|
116
|
+
* those children filed under DNs that no longer exist. A leaf has nothing
|
|
117
|
+
* below it and the extra test costs nothing.
|
|
70
118
|
*/
|
|
71
119
|
invalidateCache(dn: string): void;
|
|
120
|
+
/**
|
|
121
|
+
* Bind every directory operation to one request, so the authorization
|
|
122
|
+
* hooks always see it. See {@link RequestBoundLdap}.
|
|
123
|
+
*/
|
|
124
|
+
forRequest(req: Request): RequestBoundLdap;
|
|
72
125
|
search(options: SearchOptions, base?: string, req?: Request): Promise<SearchResult | AsyncGenerator<SearchResult>>;
|
|
73
126
|
/**
|
|
74
127
|
* Wrap paginated search to ensure connection is released when done
|
package/dist/src/lib/utils.d.ts
CHANGED
|
@@ -41,6 +41,30 @@ export declare const transformSchemas: (schemas: string | Buffer, config: Config
|
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
export declare function escapeLdapFilter(value: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Build the LDAP filter of a list search: a substring match on one attribute,
|
|
46
|
+
* or on any of several.
|
|
47
|
+
*
|
|
48
|
+
* The value is escaped, so nothing a client types can reach the filter as
|
|
49
|
+
* syntax; the attribute names cannot be escaped — they *are* syntax — so they
|
|
50
|
+
* are checked against what an LDAP attribute name may contain and refused
|
|
51
|
+
* otherwise.
|
|
52
|
+
*
|
|
53
|
+
* Every list route builds the same filter, and a client asking for a mail
|
|
54
|
+
* address expects the same answer whichever entity it is asking about.
|
|
55
|
+
*
|
|
56
|
+
* @param match - The substring to look for
|
|
57
|
+
* @param attribute - One attribute name, or several separated by commas
|
|
58
|
+
* @returns The LDAP filter
|
|
59
|
+
* @throws BadRequestError when an attribute name is not one
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* substringSearchFilter('ali', 'cn,mail')
|
|
64
|
+
* // => '(|(cn=*ali*)(mail=*ali*))'
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function substringSearchFilter(match: string, attribute: string): string;
|
|
44
68
|
/**
|
|
45
69
|
* Escape special characters in LDAP DN attribute values according to RFC 4514
|
|
46
70
|
* Prevents LDAP injection attacks by escaping characters that have special meaning in DNs
|
|
@@ -145,6 +169,22 @@ export declare function getParentDn(dn: string): string;
|
|
|
145
169
|
* ```
|
|
146
170
|
*/
|
|
147
171
|
export declare function getRdn(dn: string): string;
|
|
172
|
+
/**
|
|
173
|
+
* Extract the *value* of the first RDN of a DN, with its escapes removed
|
|
174
|
+
*
|
|
175
|
+
* Where {@link getRdn} returns `ou=Test Org`, this returns `Test Org`: the
|
|
176
|
+
* name the entry is known by, which is what a path or a label is built from.
|
|
177
|
+
*
|
|
178
|
+
* @param dn - The DN to read
|
|
179
|
+
* @returns The value, or an empty string when the DN has no RDN
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* rdnValue('ou=Test\\, Org,ou=organization,dc=example,dc=com')
|
|
184
|
+
* // => 'Test, Org'
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function rdnValue(dn: string): string;
|
|
148
188
|
/**
|
|
149
189
|
* Check if a DN is a child of another DN
|
|
150
190
|
*
|
|
@@ -184,6 +224,31 @@ export declare function isChildOf(dn: string, parentDn: string): boolean;
|
|
|
184
224
|
* ```
|
|
185
225
|
*/
|
|
186
226
|
export declare function normalizeDn(dn: string): string;
|
|
227
|
+
/**
|
|
228
|
+
* Whether a member DN is the placeholder a `groupOfNames` holds so it stays
|
|
229
|
+
* valid with no real member (`--group-dummy-user`).
|
|
230
|
+
*
|
|
231
|
+
* Compared as DNs rather than as text: the directory answers with its own
|
|
232
|
+
* spelling, so the configured `uid=fakeUser,ou=users,…` has to match the
|
|
233
|
+
* `uid=fakeuser, ou=users,…` a group may actually hold. A textual comparison
|
|
234
|
+
* counted such a member as a real one — enough to make an empty group refuse
|
|
235
|
+
* deletion, and to show the placeholder to SCIM clients.
|
|
236
|
+
*
|
|
237
|
+
* Every reader of the member list asks here, so the four of them cannot drift
|
|
238
|
+
* apart again.
|
|
239
|
+
*
|
|
240
|
+
* @param member - The member DN to test
|
|
241
|
+
* @param dummy - The configured placeholder DN, if any
|
|
242
|
+
* @returns true when `member` is that placeholder; false when none is
|
|
243
|
+
* configured, as there is then no placeholder to hide
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* isDummyMemberDn('uid=fakeuser, ou=users,dc=e,dc=c', 'uid=fakeUser,ou=users,dc=e,dc=c')
|
|
248
|
+
* // => true
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
export declare function isDummyMemberDn(member: unknown, dummy: string | undefined): boolean;
|
|
187
252
|
/**
|
|
188
253
|
* Check whether a DN is a branch base itself or sits anywhere below it.
|
|
189
254
|
*
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/auth/authzScope
|
|
3
|
+
* @author Xavier Guimard <xguimard@linagora.com>
|
|
4
|
+
*
|
|
5
|
+
* Tells a client what the signed-in administrator may actually do, before it
|
|
6
|
+
* offers them a button that will fail.
|
|
7
|
+
*
|
|
8
|
+
* In a local-administration model the scope *is* the interface: a manager
|
|
9
|
+
* administers a few branches of the tree and nothing else, and an application
|
|
10
|
+
* that cannot name those branches leaves them guessing. This endpoint answers
|
|
11
|
+
* both halves — which branches, and which entities can be created in them —
|
|
12
|
+
* by asking the authorization plugin in force, whichever one that is.
|
|
13
|
+
* @group Plugins
|
|
14
|
+
*/
|
|
15
|
+
import type { Express } from 'express';
|
|
16
|
+
import DmPlugin, { type Role } from '../../abstract/plugin';
|
|
17
|
+
export default class AuthzScope extends DmPlugin {
|
|
18
|
+
name: string;
|
|
19
|
+
roles: Role[];
|
|
20
|
+
/**
|
|
21
|
+
* Register `GET {api_prefix}/v1/authz/scope`.
|
|
22
|
+
*
|
|
23
|
+
* @param app Express application
|
|
24
|
+
*/
|
|
25
|
+
api(app: Express): void;
|
|
26
|
+
/**
|
|
27
|
+
* The authorization plugin in force, if any.
|
|
28
|
+
*
|
|
29
|
+
* @returns the plugin, or undefined when the server is unrestricted
|
|
30
|
+
*/
|
|
31
|
+
private authz;
|
|
32
|
+
/**
|
|
33
|
+
* Entities a client can create, as declared by the loaded plugins.
|
|
34
|
+
*
|
|
35
|
+
* @returns entity name and branch, one per creatable entity
|
|
36
|
+
*/
|
|
37
|
+
private entities;
|
|
38
|
+
/**
|
|
39
|
+
* Answer the scope request.
|
|
40
|
+
*
|
|
41
|
+
* @param req Express request, carrying the authenticated user
|
|
42
|
+
* @param res Express response
|
|
43
|
+
*/
|
|
44
|
+
private scope;
|
|
45
|
+
/**
|
|
46
|
+
* Read the display name and path of a branch, so a client can show the
|
|
47
|
+
* scope in the words the directory uses rather than as a raw DN.
|
|
48
|
+
*
|
|
49
|
+
* @param dn branch to describe
|
|
50
|
+
* @returns name and path when the entry carries them
|
|
51
|
+
*/
|
|
52
|
+
private branchLabel;
|
|
53
|
+
}
|
|
@@ -1,8 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/auth/llng
|
|
3
|
+
* @group Plugins
|
|
4
|
+
* @author Xavier Guimard <xguimard@linagora.com>
|
|
5
|
+
*
|
|
6
|
+
* Lemonldap::NG authentication plugin
|
|
7
|
+
* This plugin enables authentication and authorization using Lemonldap::NG.
|
|
8
|
+
*/
|
|
9
|
+
import type { Express, Response } from 'express';
|
|
2
10
|
import AuthBase, { DmRequest } from '../../lib/auth/base';
|
|
3
11
|
import type { Role } from '../../abstract/plugin';
|
|
12
|
+
type LlngHandler = typeof import('lemonldap-ng-handler');
|
|
4
13
|
export default class AuthLLNG extends AuthBase {
|
|
5
14
|
name: string;
|
|
6
15
|
roles: Role[];
|
|
16
|
+
private handler?;
|
|
17
|
+
/**
|
|
18
|
+
* Load the optional `lemonldap-ng-handler` dependency before the server
|
|
19
|
+
* starts serving requests, rather than importing it statically.
|
|
20
|
+
*
|
|
21
|
+
* A static import would throw the moment this file is loaded — as soon as
|
|
22
|
+
* this plugin is configured — with Node's own opaque "Cannot find
|
|
23
|
+
* package" error. Loading it here instead means a server that never
|
|
24
|
+
* configures this plugin never touches the package at all, and one that
|
|
25
|
+
* does configure it gets a clear failure naming both the plugin and the
|
|
26
|
+
* missing dependency, at startup, instead of on the first request an auth
|
|
27
|
+
* plugin that cannot run must not pretend to succeed.
|
|
28
|
+
*/
|
|
29
|
+
api(app: Express): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Load the handler. Split out so tests can override it to simulate the
|
|
32
|
+
* dependency being absent, without actually uninstalling it.
|
|
33
|
+
*/
|
|
34
|
+
protected loadHandler(): Promise<LlngHandler>;
|
|
7
35
|
authMethod(req: DmRequest, res: Response, next: () => void): void;
|
|
8
36
|
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
import DmPlugin, { type Role } from '../../abstract/plugin';
|
|
3
|
+
/**
|
|
4
|
+
* Draw a password from {@link PASSWORD_ALPHABET} with a uniform distribution.
|
|
5
|
+
*
|
|
6
|
+
* Bytes whose value would wrap the alphabet are discarded rather than folded
|
|
7
|
+
* with a modulo, which would make the first characters of the alphabet more
|
|
8
|
+
* likely than the last.
|
|
9
|
+
*
|
|
10
|
+
* @param length number of characters
|
|
11
|
+
* @returns the generated password
|
|
12
|
+
*/
|
|
13
|
+
export declare function generatePassword(length?: number): string;
|
|
14
|
+
export default class LdapAccountLifecycle extends DmPlugin {
|
|
15
|
+
name: string;
|
|
16
|
+
roles: Role[];
|
|
17
|
+
dependencies: {
|
|
18
|
+
ldapFlatGeneric: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Register the lifecycle endpoints of every flat entity whose schema asks
|
|
22
|
+
* for them.
|
|
23
|
+
*
|
|
24
|
+
* @param app Express application
|
|
25
|
+
*/
|
|
26
|
+
api(app: Express): void;
|
|
27
|
+
/**
|
|
28
|
+
* Apply a named state to an account.
|
|
29
|
+
*
|
|
30
|
+
* @param entity flat entity holding the account
|
|
31
|
+
* @param attribute attribute carrying the state
|
|
32
|
+
* @param states state names declared by the schema
|
|
33
|
+
* @param req Express request
|
|
34
|
+
* @param res Express response
|
|
35
|
+
*/
|
|
36
|
+
private setStatus;
|
|
37
|
+
/**
|
|
38
|
+
* Replace an account's credential, and flag it for a change at next login
|
|
39
|
+
* when the schema knows how to.
|
|
40
|
+
*
|
|
41
|
+
* @param entity flat entity holding the account
|
|
42
|
+
* @param attribute attribute carrying the credential
|
|
43
|
+
* @param req Express request
|
|
44
|
+
* @param res Express response
|
|
45
|
+
*/
|
|
46
|
+
private resetPassword;
|
|
47
|
+
/**
|
|
48
|
+
* Apply a change through the entity's own modify path, turning a missing
|
|
49
|
+
* entry into a 404 rather than a directory error.
|
|
50
|
+
*
|
|
51
|
+
* @param entity flat entity holding the account
|
|
52
|
+
* @param id identifier or DN of the entry
|
|
53
|
+
* @param changes modification to apply
|
|
54
|
+
*/
|
|
55
|
+
private modify;
|
|
56
|
+
}
|
|
@@ -15,7 +15,50 @@ export default class LdapDepartmentSync extends DmPlugin {
|
|
|
15
15
|
private linkAttr;
|
|
16
16
|
private pathAttr;
|
|
17
17
|
constructor(server: DM);
|
|
18
|
+
/**
|
|
19
|
+
* The attribute holding an organization's path: the `organizationPath` role
|
|
20
|
+
* of the organization schema, then the configured name.
|
|
21
|
+
*
|
|
22
|
+
* Role first, as the enterprise rules read it: a deployment naming the
|
|
23
|
+
* attribute only through the role otherwise saw nothing recomputed, and
|
|
24
|
+
* nothing said so. Resolved on each call, the organization schema being
|
|
25
|
+
* read asynchronously.
|
|
26
|
+
*
|
|
27
|
+
* @returns the attribute name
|
|
28
|
+
*/
|
|
29
|
+
private organizationPathAttribute;
|
|
30
|
+
/**
|
|
31
|
+
* The link and path attributes of the entries attached to an organization:
|
|
32
|
+
* those every loaded entity schema declares through the `organizationLink`
|
|
33
|
+
* and `organizationPath` roles, then the configured names.
|
|
34
|
+
*
|
|
35
|
+
* @returns distinct pairs, the ones declared by roles first
|
|
36
|
+
*/
|
|
37
|
+
private linkedAttributes;
|
|
18
38
|
hooks: Hooks;
|
|
39
|
+
/**
|
|
40
|
+
* Recompute the path of a renamed or moved organization and of every
|
|
41
|
+
* organization below it.
|
|
42
|
+
*
|
|
43
|
+
* A path names an organization's ancestors, root first, its own name last,
|
|
44
|
+
* the top organization left out. Moving or renaming one therefore changes
|
|
45
|
+
* the path of its whole subtree. Only the entries *linked* to the tree used
|
|
46
|
+
* to be updated, from paths the tree itself still held: the organizations
|
|
47
|
+
* kept the path of their former parent, the linked users were rewritten to
|
|
48
|
+
* it, and nothing could correct it since the path is computed by the server.
|
|
49
|
+
*
|
|
50
|
+
* Only organizations already holding a path are written.
|
|
51
|
+
*
|
|
52
|
+
* @param orgDn DN of the organization after the rename
|
|
53
|
+
*/
|
|
54
|
+
private updateOrganizationPaths;
|
|
55
|
+
/**
|
|
56
|
+
* Read the path an organization holds.
|
|
57
|
+
*
|
|
58
|
+
* @param dn organization DN
|
|
59
|
+
* @returns its path, or null when it holds none or cannot be read
|
|
60
|
+
*/
|
|
61
|
+
private readPath;
|
|
19
62
|
/**
|
|
20
63
|
* Update resources that are directly linked to the renamed organization
|
|
21
64
|
*/
|