ldap-rest 0.1.8 → 0.2.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/abstract/twakePlugin.js +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/ldap/bulkImport.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/onChange.js +1 -1
- package/dist/plugins/ldap/organization.js +1 -1
- package/dist/plugins/twake/appAccountsApi.js +1 -1
- package/dist/plugins/twake/appAccountsConsistency.js +1 -1
- package/dist/plugins/twake/drive.js +1 -0
- package/dist/src/abstract/plugin.d.ts +1 -1
- package/dist/src/config/args.d.ts +6 -0
- package/dist/src/hooks.d.ts +1 -0
- package/dist/src/lib/utils.d.ts +35 -0
- package/dist/src/plugins/twake/drive.d.ts +76 -0
- package/docs/usage/plugins/integrations/drive.md +646 -0
- package/package.json +9 -2
- package/static/browser/shared/utils/form.js +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import TwakePlugin from '../../abstract/twakePlugin';
|
|
2
|
+
import { type Role } from '../../abstract/plugin';
|
|
3
|
+
import type { DM } from '../../bin';
|
|
4
|
+
import { Hooks } from '../../hooks';
|
|
5
|
+
export default class Drive extends TwakePlugin {
|
|
6
|
+
name: string;
|
|
7
|
+
roles: Role[];
|
|
8
|
+
dependencies: {
|
|
9
|
+
onLdapChange: string;
|
|
10
|
+
};
|
|
11
|
+
private cozyDomainAttr;
|
|
12
|
+
private defaultDomainTemplate;
|
|
13
|
+
private templateAttrs;
|
|
14
|
+
private driveQuotaAttr;
|
|
15
|
+
constructor(server: DM);
|
|
16
|
+
/**
|
|
17
|
+
* Validate Cozy domain format to prevent URL injection attacks
|
|
18
|
+
* @param domain Cozy domain to validate
|
|
19
|
+
* @returns true if domain format is valid
|
|
20
|
+
*/
|
|
21
|
+
private isValidCozyDomain;
|
|
22
|
+
/**
|
|
23
|
+
* Get Cozy domain for a user from LDAP
|
|
24
|
+
* Falls back to template-based domain if attribute not found
|
|
25
|
+
* @param dn User's LDAP DN
|
|
26
|
+
* @returns Cozy domain or null if not found
|
|
27
|
+
*/
|
|
28
|
+
getCozyDomain(dn: string): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Update Cozy instance via Admin API
|
|
31
|
+
* @param hookname Hook name for logging
|
|
32
|
+
* @param cozyDomain Cozy instance domain
|
|
33
|
+
* @param dn User's LDAP DN
|
|
34
|
+
* @param params Query parameters to update
|
|
35
|
+
*/
|
|
36
|
+
private updateCozyInstance;
|
|
37
|
+
hooks: Hooks;
|
|
38
|
+
/**
|
|
39
|
+
* Extract display name from LDAP attributes
|
|
40
|
+
* Fallback logic: displayName → cn → givenName+sn
|
|
41
|
+
*/
|
|
42
|
+
private getDisplayNameFromAttributes;
|
|
43
|
+
/**
|
|
44
|
+
* Public method to get display name from DN
|
|
45
|
+
* Useful for external integrations
|
|
46
|
+
*/
|
|
47
|
+
getDisplayNameFromDN(dn: string): Promise<string | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Public method to get mail from DN
|
|
50
|
+
* Useful for external integrations
|
|
51
|
+
*/
|
|
52
|
+
getMailFromDN(dn: string): Promise<string | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Public method to get drive quota from DN (in bytes)
|
|
55
|
+
* Useful for external integrations
|
|
56
|
+
*/
|
|
57
|
+
getDriveQuotaFromDN(dn: string): Promise<number | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Public method to manually sync a user to Twake Drive
|
|
60
|
+
* Useful for initial sync or manual refresh
|
|
61
|
+
*/
|
|
62
|
+
syncUserToCozy(dn: string): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Block a Cozy instance
|
|
65
|
+
* @param dn User's LDAP DN
|
|
66
|
+
* @param reason Optional blocking reason (e.g., 'PAYMENT_FAILED', 'LOGIN_FAILED', 'SUSPENDED')
|
|
67
|
+
* @returns true if successful, false otherwise
|
|
68
|
+
*/
|
|
69
|
+
blockInstance(dn: string, reason?: string): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Unblock a Cozy instance
|
|
72
|
+
* @param dn User's LDAP DN
|
|
73
|
+
* @returns true if successful, false otherwise
|
|
74
|
+
*/
|
|
75
|
+
unblockInstance(dn: string): Promise<boolean>;
|
|
76
|
+
}
|
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
# Twake Drive Plugin
|
|
2
|
+
|
|
3
|
+
Synchronize LDAP user changes with Twake Drive via Admin API.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `twake/drive` plugin automatically synchronizes email address, display name, and disk quota changes from LDAP to [Twake Drive](https://twake.app/).
|
|
8
|
+
It listens to `onChange` hooks and updates Twake Drive instances via the Admin API.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
1. **Twake Drive / Cozy** instance with Admin API enabled
|
|
13
|
+
2. **onChange plugin** loaded to detect LDAP changes
|
|
14
|
+
3. **Cozy domain** - either:
|
|
15
|
+
- Per-user attribute in LDAP schema (e.g., `twakeCozyDomain`), or
|
|
16
|
+
- Default domain template configured (e.g., `{uid}.mycompany.cloud`)
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx ldap-rest \
|
|
22
|
+
--plugin core/ldap/onChange \
|
|
23
|
+
--plugin core/ldap/flatGeneric \
|
|
24
|
+
--plugin twake/drive \
|
|
25
|
+
--ldap-flat-schema ./static/schemas/twake/users.json \
|
|
26
|
+
--mail-attribute mail \
|
|
27
|
+
--twake-drive-domain-attribute twakeCozyDomain \
|
|
28
|
+
--twake-drive-webadmin-url http://cozy-admin:6060 \
|
|
29
|
+
--twake-drive-webadmin-token "your-admin-token" \
|
|
30
|
+
...
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Environment Variables:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
DM_TWAKE_DRIVE_WEBADMIN_URL="http://cozy-admin:6060"
|
|
37
|
+
DM_TWAKE_DRIVE_WEBADMIN_TOKEN="your-admin-token"
|
|
38
|
+
DM_TWAKE_DRIVE_DOMAIN_ATTRIBUTE="twakeCozyDomain"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Parameters
|
|
42
|
+
|
|
43
|
+
| Parameter | Description | Default |
|
|
44
|
+
| --------------------------------------- | ------------------------------------------------------------- | ----------------- |
|
|
45
|
+
| `--twake-drive-webadmin-url` | Twake Drive Admin API base URL (required) | - |
|
|
46
|
+
| `--twake-drive-webadmin-token` | Bearer token for Admin authentication | - |
|
|
47
|
+
| `--mail-attribute` | LDAP attribute for email | `mail` |
|
|
48
|
+
| `--display-name-attribute` | LDAP attribute for display name | `displayName` |
|
|
49
|
+
| `--drive-quota-attribute` | LDAP attribute for disk quota (in bytes) | `twakeDriveQuota` |
|
|
50
|
+
| `--twake-drive-domain-attribute` | LDAP attribute storing user's Twake Drive domain | `twakeCozyDomain` |
|
|
51
|
+
| `--twake-drive-default-domain-template` | Template for Twake Drive domain (e.g., `{uid}.company.cloud`) | - |
|
|
52
|
+
| `--twake-drive-concurrency` | Maximum concurrent API requests | `10` |
|
|
53
|
+
|
|
54
|
+
## Cozy Domain Resolution
|
|
55
|
+
|
|
56
|
+
The plugin determines each user's Cozy domain using the following logic:
|
|
57
|
+
|
|
58
|
+
1. **Per-user attribute** (if present): Use the `twakeCozyDomain` LDAP attribute value
|
|
59
|
+
2. **Default template** (fallback): Build domain from template with LDAP attribute placeholders
|
|
60
|
+
|
|
61
|
+
### Per-user Cozy domain attribute
|
|
62
|
+
|
|
63
|
+
Users can have an explicit `twakeCozyDomain` attribute for custom domains:
|
|
64
|
+
|
|
65
|
+
```ldif
|
|
66
|
+
dn: uid=jdoe,ou=users,dc=example,dc=com
|
|
67
|
+
uid: jdoe
|
|
68
|
+
mail: jdoe@example.com
|
|
69
|
+
twakeCozyDomain: john-doe.partner.cloud
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Default domain template
|
|
73
|
+
|
|
74
|
+
Configure a template for users without an explicit attribute. Use `{attribute}` placeholders:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
--twake-drive-default-domain-template "{uid}.mycompany.cloud"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Users without `twakeCozyDomain` will have their domain built from the template:
|
|
81
|
+
|
|
82
|
+
- User `uid=jdoe` → Cozy domain `jdoe.mycompany.cloud`
|
|
83
|
+
|
|
84
|
+
Other template examples:
|
|
85
|
+
|
|
86
|
+
- `"{mail}"` → use mail attribute directly as domain
|
|
87
|
+
- `"{cn}.cloud.example.com"` → use common name
|
|
88
|
+
|
|
89
|
+
### Combined usage
|
|
90
|
+
|
|
91
|
+
Both can be used together. The per-user attribute takes precedence:
|
|
92
|
+
|
|
93
|
+
```ldif
|
|
94
|
+
# User with custom domain (uses twakeCozyDomain)
|
|
95
|
+
dn: uid=partner1,ou=users,dc=example,dc=com
|
|
96
|
+
uid: partner1
|
|
97
|
+
twakeCozyDomain: partner1.external.cloud
|
|
98
|
+
|
|
99
|
+
# Regular user (uses default suffix → jdoe.mycompany.cloud)
|
|
100
|
+
dn: uid=jdoe,ou=users,dc=example,dc=com
|
|
101
|
+
uid: jdoe
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## How It Works
|
|
105
|
+
|
|
106
|
+
### Mail Address Changes
|
|
107
|
+
|
|
108
|
+
When a user's mail attribute changes:
|
|
109
|
+
|
|
110
|
+
1. **LDAP modify operation**
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
PUT /api/v1/ldap/users/jdoe
|
|
114
|
+
{
|
|
115
|
+
"replace": {
|
|
116
|
+
"mail": "john.doe@company.com"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
2. **onChange plugin** detects mail change and triggers `onLdapMailChange` hook
|
|
122
|
+
|
|
123
|
+
3. **Drive plugin** receives hook and calls Cozy Admin API:
|
|
124
|
+
|
|
125
|
+
```http
|
|
126
|
+
PATCH /instances/jdoe.mycompany.cloud?Email=john.doe@company.com&FromCloudery=true
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
4. **Cozy** updates the instance owner's email address
|
|
130
|
+
|
|
131
|
+
### Display Name Changes
|
|
132
|
+
|
|
133
|
+
When a user's display name changes:
|
|
134
|
+
|
|
135
|
+
1. **LDAP modify operation**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
PUT /api/v1/ldap/users/jdoe
|
|
139
|
+
{
|
|
140
|
+
"replace": {
|
|
141
|
+
"displayName": "John M. Doe"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
2. **onChange plugin** triggers `onLdapDisplayNameChange` hook
|
|
147
|
+
|
|
148
|
+
3. **Drive plugin** updates Cozy instance:
|
|
149
|
+
|
|
150
|
+
```http
|
|
151
|
+
PATCH /instances/jdoe.mycompany.cloud?PublicName=John%20M.%20Doe&FromCloudery=true
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Display Name Fallback Logic
|
|
155
|
+
|
|
156
|
+
If `displayName` is not set, the plugin uses fallback logic:
|
|
157
|
+
|
|
158
|
+
1. `displayName` attribute (first choice)
|
|
159
|
+
2. `cn` (Common Name)
|
|
160
|
+
3. `givenName` + `sn` (First Name + Last Name)
|
|
161
|
+
|
|
162
|
+
### Disk Quota Changes
|
|
163
|
+
|
|
164
|
+
When a user's drive quota changes:
|
|
165
|
+
|
|
166
|
+
1. **LDAP modify operation**
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
PUT /api/v1/ldap/users/jdoe
|
|
170
|
+
{
|
|
171
|
+
"replace": {
|
|
172
|
+
"twakeDriveQuota": "5368709120"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
2. **onChange plugin** triggers `onLdapDriveQuotaChange` hook
|
|
178
|
+
|
|
179
|
+
3. **Drive plugin** updates Twake Drive instance:
|
|
180
|
+
|
|
181
|
+
```http
|
|
182
|
+
PATCH /instances/jdoe.company.cloud?DiskQuota=5368709120&FromCloudery=true
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
4. **Twake Drive** updates the instance disk quota
|
|
186
|
+
|
|
187
|
+
**Note:** The quota value is in bytes. Common values:
|
|
188
|
+
|
|
189
|
+
- 1 GB = `1073741824`
|
|
190
|
+
- 5 GB = `5368709120`
|
|
191
|
+
- 10 GB = `10737418240`
|
|
192
|
+
|
|
193
|
+
## Public Methods
|
|
194
|
+
|
|
195
|
+
Public methods available on the Drive plugin instance:
|
|
196
|
+
|
|
197
|
+
### Usage
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import type { DM } from 'ldap-rest';
|
|
201
|
+
import type Drive from 'ldap-rest/plugin-twake-drive';
|
|
202
|
+
|
|
203
|
+
// Get the Drive plugin instance
|
|
204
|
+
const drive = server.getPlugin('drive') as Drive;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### getCozyDomain(dn)
|
|
208
|
+
|
|
209
|
+
Retrieve the Cozy domain for a user given their LDAP DN.
|
|
210
|
+
|
|
211
|
+
**Signature:**
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
async getCozyDomain(dn: string): Promise<string | null>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Example:**
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const domain = await drive.getCozyDomain('uid=jdoe,ou=users,dc=example,dc=com');
|
|
221
|
+
// Returns: "jdoe.mycompany.cloud"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### getDisplayNameFromDN(dn)
|
|
227
|
+
|
|
228
|
+
Retrieve the display name for a user using fallback logic.
|
|
229
|
+
|
|
230
|
+
**Signature:**
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
async getDisplayNameFromDN(dn: string): Promise<string | null>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Example:**
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const name = await drive.getDisplayNameFromDN(
|
|
240
|
+
'uid=jdoe,ou=users,dc=example,dc=com'
|
|
241
|
+
);
|
|
242
|
+
// Returns: "John Doe"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### getMailFromDN(dn)
|
|
248
|
+
|
|
249
|
+
Retrieve the email address for a user.
|
|
250
|
+
|
|
251
|
+
**Signature:**
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
async getMailFromDN(dn: string): Promise<string | null>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Example:**
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const mail = await drive.getMailFromDN('uid=jdoe,ou=users,dc=example,dc=com');
|
|
261
|
+
// Returns: "jdoe@example.com"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### getDriveQuotaFromDN(dn)
|
|
267
|
+
|
|
268
|
+
Retrieve the drive quota for a user (in bytes).
|
|
269
|
+
|
|
270
|
+
**Signature:**
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
async getDriveQuotaFromDN(dn: string): Promise<number | null>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Example:**
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const quota = await drive.getDriveQuotaFromDN(
|
|
280
|
+
'uid=jdoe,ou=users,dc=example,dc=com'
|
|
281
|
+
);
|
|
282
|
+
// Returns: 5368709120 (5 GB)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
### syncUserToCozy(dn)
|
|
288
|
+
|
|
289
|
+
Manually synchronize a user's attributes (email, display name, disk quota) to their Twake Drive instance. Useful for initial sync or manual refresh.
|
|
290
|
+
|
|
291
|
+
**Signature:**
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
async syncUserToCozy(dn: string): Promise<boolean>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Example:**
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const success = await drive.syncUserToCozy(
|
|
301
|
+
'uid=jdoe,ou=users,dc=example,dc=com'
|
|
302
|
+
);
|
|
303
|
+
if (success) {
|
|
304
|
+
console.log('User synced successfully');
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### blockInstance(dn, reason?)
|
|
311
|
+
|
|
312
|
+
Block a user's Cozy instance with an optional reason.
|
|
313
|
+
|
|
314
|
+
**Signature:**
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
async blockInstance(dn: string, reason?: string): Promise<boolean>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Parameters:**
|
|
321
|
+
|
|
322
|
+
- `dn` - User's LDAP DN
|
|
323
|
+
- `reason` - Optional blocking reason. Common values:
|
|
324
|
+
- `PAYMENT_FAILED` - Payment issue
|
|
325
|
+
- `LOGIN_FAILED` - Too many failed login attempts
|
|
326
|
+
- `SUSPENDED` - Account suspended by admin
|
|
327
|
+
- Any custom string
|
|
328
|
+
|
|
329
|
+
**Example:**
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// Block with reason
|
|
333
|
+
await drive.blockInstance(
|
|
334
|
+
'uid=jdoe,ou=users,dc=example,dc=com',
|
|
335
|
+
'PAYMENT_FAILED'
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Block without reason
|
|
339
|
+
await drive.blockInstance('uid=jdoe,ou=users,dc=example,dc=com');
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
### unblockInstance(dn)
|
|
345
|
+
|
|
346
|
+
Unblock a user's Cozy instance.
|
|
347
|
+
|
|
348
|
+
**Signature:**
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
async unblockInstance(dn: string): Promise<boolean>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Example:**
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
await drive.unblockInstance('uid=jdoe,ou=users,dc=example,dc=com');
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Cozy Admin API
|
|
361
|
+
|
|
362
|
+
### Endpoints Used
|
|
363
|
+
|
|
364
|
+
| Operation | Endpoint | Method |
|
|
365
|
+
| --------------- | ----------------------------------------------------------------------------------------- | ------ |
|
|
366
|
+
| Update email | `/instances/{domain}?Email={email}&FromCloudery=true` | PATCH |
|
|
367
|
+
| Update name | `/instances/{domain}?PublicName={name}&FromCloudery=true` | PATCH |
|
|
368
|
+
| Update quota | `/instances/{domain}?DiskQuota={bytes}&FromCloudery=true` | PATCH |
|
|
369
|
+
| Combined update | `/instances/{domain}?Email={email}&PublicName={name}&DiskQuota={bytes}&FromCloudery=true` | PATCH |
|
|
370
|
+
|
|
371
|
+
### Available Cozy Parameters
|
|
372
|
+
|
|
373
|
+
The Cozy Admin API supports the following parameters (query string):
|
|
374
|
+
|
|
375
|
+
| Parameter | Type | Description |
|
|
376
|
+
| ---------------- | ------- | ---------------------------------------------------- |
|
|
377
|
+
| `Email` | string | Owner email address (supported by this plugin) |
|
|
378
|
+
| `PublicName` | string | Display name (supported by this plugin) |
|
|
379
|
+
| `Locale` | string | Locale (e.g. `fr`, `en`) |
|
|
380
|
+
| `Timezone` | string | Timezone (e.g. `Europe/Paris`) |
|
|
381
|
+
| `Phone` | string | Phone number |
|
|
382
|
+
| `DiskQuota` | integer | Disk quota in bytes |
|
|
383
|
+
| `Blocked` | bool | Block or unblock the instance |
|
|
384
|
+
| `BlockingReason` | string | Reason code (e.g. `PAYMENT_FAILED`, `LOGIN_FAILED`) |
|
|
385
|
+
| `FromCloudery` | bool | Skip Cloudery callback (always true for this plugin) |
|
|
386
|
+
|
|
387
|
+
See the [Cozy Admin Documentation](https://docs.cozy.io/en/cozy-stack/admin/#patch-instancesdomain) for the complete list.
|
|
388
|
+
|
|
389
|
+
### FromCloudery Parameter
|
|
390
|
+
|
|
391
|
+
The `FromCloudery=true` query parameter is always included to:
|
|
392
|
+
|
|
393
|
+
- Prevent the Cozy stack from sending callback notifications
|
|
394
|
+
- Avoid notification loops when changes originate from the LDAP directory
|
|
395
|
+
- Indicate the change is administrative (from the cloudery/provisioning system)
|
|
396
|
+
|
|
397
|
+
### Authentication
|
|
398
|
+
|
|
399
|
+
The Cozy Admin API uses bearer token authentication:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
--twake-drive-webadmin-token "your-admin-token"
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
The plugin automatically adds the `Authorization: Bearer {token}` header to all requests.
|
|
406
|
+
|
|
407
|
+
**Note:** The Cozy Admin API typically runs on port 6060 and should be protected at the network level.
|
|
408
|
+
|
|
409
|
+
## Examples
|
|
410
|
+
|
|
411
|
+
### Example 1: Basic Setup
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
--plugin core/ldap/onChange \
|
|
415
|
+
--plugin core/ldap/flatGeneric \
|
|
416
|
+
--plugin twake/drive \
|
|
417
|
+
--ldap-flat-schema ./schemas/twake/users.json \
|
|
418
|
+
--mail-attribute mail \
|
|
419
|
+
--twake-drive-webadmin-url http://cozy-admin:6060
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Example 2: With Authentication
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
--plugin core/ldap/onChange \
|
|
426
|
+
--plugin core/ldap/flatGeneric \
|
|
427
|
+
--plugin twake/drive \
|
|
428
|
+
--ldap-flat-schema ./schemas/twake/users.json \
|
|
429
|
+
--mail-attribute mail \
|
|
430
|
+
--twake-drive-webadmin-url http://cozy-admin:6060 \
|
|
431
|
+
--twake-drive-webadmin-token "cozy-admin-secret"
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Example 3: Complete Twake Setup (James + Drive)
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
--plugin core/auth/token \
|
|
438
|
+
--plugin core/ldap/onChange \
|
|
439
|
+
--plugin core/ldap/flatGeneric \
|
|
440
|
+
--plugin core/ldap/groups \
|
|
441
|
+
--plugin twake/james \
|
|
442
|
+
--plugin twake/drive \
|
|
443
|
+
--ldap-flat-schema ./schemas/twake/users.json \
|
|
444
|
+
--mail-attribute mail \
|
|
445
|
+
--quota-attribute mailQuotaSize \
|
|
446
|
+
--james-webadmin-url http://james:8000 \
|
|
447
|
+
--james-webadmin-token "james-admin-token" \
|
|
448
|
+
--twake-drive-webadmin-url http://cozy-admin:6060 \
|
|
449
|
+
--twake-drive-webadmin-token "cozy-admin-token" \
|
|
450
|
+
--twake-drive-domain-attribute twakeCozyDomain \
|
|
451
|
+
--auth-token "api-admin-token"
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Logging
|
|
455
|
+
|
|
456
|
+
The plugin logs all operations:
|
|
457
|
+
|
|
458
|
+
### Successful Operations
|
|
459
|
+
|
|
460
|
+
```json
|
|
461
|
+
{
|
|
462
|
+
"level": "info",
|
|
463
|
+
"plugin": "drive",
|
|
464
|
+
"event": "onLdapMailChange",
|
|
465
|
+
"result": "success",
|
|
466
|
+
"http_status": 200,
|
|
467
|
+
"dn": "uid=jdoe,ou=users,dc=example,dc=com",
|
|
468
|
+
"cozyDomain": "jdoe.mycompany.cloud",
|
|
469
|
+
"Email": "john.doe@company.com"
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Skipped Operations
|
|
474
|
+
|
|
475
|
+
```json
|
|
476
|
+
{
|
|
477
|
+
"level": "debug",
|
|
478
|
+
"message": "Skipping mail change for uid=jdoe,...: no Cozy domain attribute (twakeCozyDomain)"
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Failed Operations
|
|
483
|
+
|
|
484
|
+
```json
|
|
485
|
+
{
|
|
486
|
+
"level": "error",
|
|
487
|
+
"plugin": "drive",
|
|
488
|
+
"event": "onLdapMailChange",
|
|
489
|
+
"result": "error",
|
|
490
|
+
"http_status": 500,
|
|
491
|
+
"http_status_text": "Internal Server Error",
|
|
492
|
+
"dn": "uid=jdoe,ou=users,dc=example,dc=com",
|
|
493
|
+
"cozyDomain": "jdoe.mycompany.cloud"
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Instance Not Found
|
|
498
|
+
|
|
499
|
+
```json
|
|
500
|
+
{
|
|
501
|
+
"level": "debug",
|
|
502
|
+
"plugin": "drive",
|
|
503
|
+
"event": "onLdapMailChange",
|
|
504
|
+
"result": "ignored",
|
|
505
|
+
"http_status": 404,
|
|
506
|
+
"message": "Cozy instance not found (may not be provisioned yet)"
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Error Handling
|
|
511
|
+
|
|
512
|
+
### Non-Existent Instance (404)
|
|
513
|
+
|
|
514
|
+
If the Cozy instance doesn't exist yet:
|
|
515
|
+
|
|
516
|
+
- Error is logged at DEBUG level (not ERROR)
|
|
517
|
+
- LDAP operation succeeds
|
|
518
|
+
- This is normal during user provisioning flow
|
|
519
|
+
|
|
520
|
+
### Cozy Server Down
|
|
521
|
+
|
|
522
|
+
If the Cozy Admin API is unreachable:
|
|
523
|
+
|
|
524
|
+
- Error is logged
|
|
525
|
+
- LDAP operation succeeds
|
|
526
|
+
- Manual sync may be required later using `syncUserToCozy()`
|
|
527
|
+
|
|
528
|
+
### Invalid Token
|
|
529
|
+
|
|
530
|
+
If the admin token is invalid:
|
|
531
|
+
|
|
532
|
+
- 401 or 403 error is logged
|
|
533
|
+
- LDAP operation succeeds
|
|
534
|
+
- Check token configuration
|
|
535
|
+
|
|
536
|
+
## Synchronization Scenarios
|
|
537
|
+
|
|
538
|
+
### Scenario 1: User Creation
|
|
539
|
+
|
|
540
|
+
1. Create user in LDAP with `twakeCozyDomain` attribute
|
|
541
|
+
2. Provision Cozy instance separately (via cloudery or API)
|
|
542
|
+
3. Future mail/name changes are synced automatically
|
|
543
|
+
4. Use `syncUserToCozy()` for initial sync if needed
|
|
544
|
+
|
|
545
|
+
### Scenario 2: Email Change
|
|
546
|
+
|
|
547
|
+
1. User changes email in LDAP: `old@company.com` → `new@company.com`
|
|
548
|
+
2. Drive plugin updates Cozy instance email
|
|
549
|
+
3. Cozy updates instance owner's email
|
|
550
|
+
4. User receives notifications at new email
|
|
551
|
+
|
|
552
|
+
### Scenario 3: Name Change
|
|
553
|
+
|
|
554
|
+
1. Admin updates user's display name in LDAP
|
|
555
|
+
2. Drive plugin updates Cozy instance PublicName
|
|
556
|
+
3. User's name is updated across Cozy apps
|
|
557
|
+
|
|
558
|
+
### Scenario 4: Manual Sync
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// Sync all attributes for a user
|
|
562
|
+
const drive = server.getPlugin('drive') as Drive;
|
|
563
|
+
await drive.syncUserToCozy('uid=jdoe,ou=users,dc=example,dc=com');
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Limitations
|
|
567
|
+
|
|
568
|
+
1. **One-way sync**: LDAP → Cozy only. Cozy changes don't sync back to LDAP.
|
|
569
|
+
2. **No instance creation**: Plugin doesn't create Cozy instances, only updates existing ones.
|
|
570
|
+
3. **No user deletion**: Plugin doesn't delete Cozy instances when LDAP users are deleted.
|
|
571
|
+
4. **Domain required**: Users must have the `twakeCozyDomain` attribute to be synced.
|
|
572
|
+
|
|
573
|
+
## Troubleshooting
|
|
574
|
+
|
|
575
|
+
### Problem: Changes Not Syncing
|
|
576
|
+
|
|
577
|
+
**Solutions:**
|
|
578
|
+
|
|
579
|
+
1. Verify onChange plugin is loaded:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
--plugin core/ldap/onChange
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
2. Check Cozy domain attribute is set for the user:
|
|
586
|
+
|
|
587
|
+
```bash
|
|
588
|
+
ldapsearch -b "uid=jdoe,ou=users,dc=example,dc=com" twakeCozyDomain
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
3. Enable debug logging:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
--log-level debug
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
4. Verify Cozy Admin API is reachable:
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
curl http://cozy-admin:6060/instances
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Problem: 404 Errors
|
|
604
|
+
|
|
605
|
+
**Symptoms:**
|
|
606
|
+
|
|
607
|
+
```json
|
|
608
|
+
{ "level": "debug", "message": "Cozy instance not found" }
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
**Solutions:**
|
|
612
|
+
|
|
613
|
+
1. Provision Cozy instance for the user first
|
|
614
|
+
2. Verify `twakeCozyDomain` attribute matches the actual Cozy domain
|
|
615
|
+
3. Check Cozy logs for instance existence
|
|
616
|
+
|
|
617
|
+
### Problem: Authentication Errors
|
|
618
|
+
|
|
619
|
+
**Symptoms:**
|
|
620
|
+
|
|
621
|
+
```json
|
|
622
|
+
{ "http_status": 401, "http_status_text": "Unauthorized" }
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**Solutions:**
|
|
626
|
+
|
|
627
|
+
1. Verify admin token is correct
|
|
628
|
+
2. Check token hasn't expired
|
|
629
|
+
3. Ensure Admin API is configured to accept bearer tokens
|
|
630
|
+
|
|
631
|
+
## Integration with James
|
|
632
|
+
|
|
633
|
+
When using both James and Drive plugins, email changes are propagated to both services:
|
|
634
|
+
|
|
635
|
+
```bash
|
|
636
|
+
--plugin twake/james \
|
|
637
|
+
--plugin twake/drive
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Both plugins listen to `onLdapMailChange` and update their respective services in parallel.
|
|
641
|
+
|
|
642
|
+
## See Also
|
|
643
|
+
|
|
644
|
+
- [onChange.md](onChange.md) - LDAP change detection
|
|
645
|
+
- [james-mail.md](james-mail.md) - James mail server integration
|
|
646
|
+
- [Cozy Admin Documentation](https://docs.cozy.io/en/cozy-stack/admin/) - Admin API reference
|