0
# Access Controllers
1
2
Access controllers provide a pluggable system for managing database permissions in OrbitDB. They determine who can write to databases, read from them, and perform administrative operations.
3
4
## Capabilities
5
6
### Access Controller System
7
8
Register and manage access controllers for different permission models.
9
10
```javascript { .api }
11
/**
12
* Registers a custom access controller
13
* @param accessController Access controller implementation with type property
14
* @throws "AccessController does not contain required field 'type'"
15
* @throws "AccessController '${type}' already added"
16
*/
17
function useAccessController(accessController: AccessController): void;
18
19
interface AccessController {
20
/** Unique type identifier for this access controller */
21
type: string;
22
/** Checks if an entry can be appended to the log */
23
canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
24
/** Optional: grants capabilities to identities */
25
grant?(capability: string, identity: string): Promise<void>;
26
/** Optional: revokes capabilities from identities */
27
revoke?(capability: string, identity: string): Promise<void>;
28
}
29
```
30
31
**Usage Examples:**
32
33
```javascript
34
import { useAccessController } from '@orbitdb/core';
35
36
// Register a custom access controller
37
const MyAccessController = {
38
type: 'my-custom-ac',
39
async canAppend(entry, identityProvider) {
40
// Custom permission logic
41
return entry.identity === 'allowed-user-id';
42
}
43
};
44
45
useAccessController(MyAccessController);
46
47
// Use the custom access controller
48
const db = await orbitdb.open('protected-db', {
49
AccessController: MyAccessController
50
});
51
```
52
53
### IPFS Access Controller
54
55
The default access controller that uses IPFS for permission management.
56
57
```javascript { .api }
58
interface IPFSAccessController extends AccessController {
59
type: 'ipfs';
60
61
/**
62
* Checks if an identity can append entries to the database
63
* @param entry The entry being added
64
* @param identityProvider Identity provider for verification
65
* @returns Promise resolving to true if allowed
66
*/
67
canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
68
69
/**
70
* Grants write permission to an identity
71
* @param capability The capability to grant (typically 'write')
72
* @param identity The identity to grant permission to
73
*/
74
grant(capability: string, identity: string): Promise<void>;
75
76
/**
77
* Revokes permission from an identity
78
* @param capability The capability to revoke
79
* @param identity The identity to revoke permission from
80
*/
81
revoke(capability: string, identity: string): Promise<void>;
82
}
83
```
84
85
**Usage Examples:**
86
87
```javascript
88
import { createOrbitDB, IPFSAccessController } from '@orbitdb/core';
89
90
const ipfs = await createHelia();
91
const orbitdb = await createOrbitDB({ ipfs });
92
93
// Create database with IPFS access controller (default)
94
const db = await orbitdb.open('my-db', {
95
AccessController: IPFSAccessController
96
});
97
98
// Grant write access to another identity
99
await db.access.grant('write', 'another-peer-id');
100
101
// Check if current identity can write
102
const canWrite = await db.access.canAppend(someEntry, identityProvider);
103
console.log('Can write:', canWrite);
104
```
105
106
### OrbitDB Access Controller
107
108
Enhanced access controller with OrbitDB-specific features.
109
110
```javascript { .api }
111
interface OrbitDBAccessController extends AccessController {
112
type: 'orbitdb';
113
114
/**
115
* Checks if an identity can append entries with OrbitDB-specific rules
116
* @param entry The entry being added
117
* @param identityProvider Identity provider for verification
118
* @returns Promise resolving to true if allowed
119
*/
120
canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
121
}
122
```
123
124
**Usage Examples:**
125
126
```javascript
127
import { createOrbitDB, OrbitDBAccessController } from '@orbitdb/core';
128
129
const ipfs = await createHelia();
130
const orbitdb = await createOrbitDB({ ipfs });
131
132
// Create database with OrbitDB access controller
133
const db = await orbitdb.open('enhanced-db', {
134
AccessController: OrbitDBAccessController
135
});
136
137
// OrbitDB access controller provides enhanced permission checking
138
const entry = await createSomeEntry();
139
const canAppend = await db.access.canAppend(entry, orbitdb.identities);
140
```
141
142
### Custom Access Controllers
143
144
Create custom access controllers for specific permission models.
145
146
```javascript { .api }
147
/**
148
* Template for custom access controller implementation
149
*/
150
interface CustomAccessController extends AccessController {
151
type: string;
152
canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
153
grant?(capability: string, identity: string): Promise<void>;
154
revoke?(capability: string, identity: string): Promise<void>;
155
/** Optional: validate access controller configuration */
156
validate?(config: any): boolean;
157
/** Optional: serialize access controller state */
158
serialize?(): any;
159
/** Optional: deserialize access controller state */
160
deserialize?(data: any): void;
161
}
162
```
163
164
**Usage Examples:**
165
166
```javascript
167
import { useAccessController } from '@orbitdb/core';
168
169
// Role-based access controller
170
const RoleBasedAccessController = {
171
type: 'role-based',
172
roles: new Map(), // identity -> role mapping
173
permissions: new Map(), // role -> permissions mapping
174
175
async canAppend(entry, identityProvider) {
176
const identity = entry.identity;
177
const role = this.roles.get(identity);
178
const permissions = this.permissions.get(role);
179
180
return permissions && permissions.includes('write');
181
},
182
183
async grant(capability, identity) {
184
// Grant role to identity
185
this.roles.set(identity, capability);
186
},
187
188
async revoke(capability, identity) {
189
// Remove role from identity
190
this.roles.delete(identity);
191
},
192
193
// Custom methods
194
setRole(identity, role) {
195
this.roles.set(identity, role);
196
},
197
198
defineRole(role, permissions) {
199
this.permissions.set(role, permissions);
200
}
201
};
202
203
// Register the custom access controller
204
useAccessController(RoleBasedAccessController);
205
206
// Use in database creation
207
const db = await orbitdb.open('role-based-db', {
208
AccessController: RoleBasedAccessController
209
});
210
211
// Configure roles and permissions
212
db.access.defineRole('admin', ['read', 'write', 'delete']);
213
db.access.defineRole('user', ['read', 'write']);
214
db.access.setRole('user123', 'user');
215
db.access.setRole('admin456', 'admin');
216
```
217
218
### Multi-Signature Access Controller
219
220
Example of a more complex access controller requiring multiple signatures.
221
222
```javascript
223
const MultiSigAccessController = {
224
type: 'multisig',
225
requiredSignatures: 2,
226
authorizedSigners: new Set(),
227
pendingOperations: new Map(),
228
229
async canAppend(entry, identityProvider) {
230
const identity = entry.identity;
231
232
// Check if signer is authorized
233
if (!this.authorizedSigners.has(identity)) {
234
return false;
235
}
236
237
const operationId = this.getOperationId(entry);
238
const signatures = this.pendingOperations.get(operationId) || new Set();
239
signatures.add(identity);
240
241
if (signatures.size >= this.requiredSignatures) {
242
// Enough signatures, allow operation
243
this.pendingOperations.delete(operationId);
244
return true;
245
} else {
246
// Store signature and wait for more
247
this.pendingOperations.set(operationId, signatures);
248
return false;
249
}
250
},
251
252
getOperationId(entry) {
253
// Create deterministic ID for the operation
254
return `${entry.payload.op}-${entry.payload.key}-${JSON.stringify(entry.payload.value)}`;
255
},
256
257
addSigner(identity) {
258
this.authorizedSigners.add(identity);
259
},
260
261
removeSigner(identity) {
262
this.authorizedSigners.delete(identity);
263
}
264
};
265
266
useAccessController(MultiSigAccessController);
267
```
268
269
### Time-Based Access Controller
270
271
Access controller with time-based permissions.
272
273
```javascript
274
const TimeBasedAccessController = {
275
type: 'time-based',
276
permissions: new Map(), // identity -> { start, end, capabilities }
277
278
async canAppend(entry, identityProvider) {
279
const identity = entry.identity;
280
const permission = this.permissions.get(identity);
281
282
if (!permission) {
283
return false;
284
}
285
286
const now = Date.now();
287
const isTimeValid = now >= permission.start && now <= permission.end;
288
const hasCapability = permission.capabilities.includes('write');
289
290
return isTimeValid && hasCapability;
291
},
292
293
async grant(capability, identity) {
294
const existing = this.permissions.get(identity) || { capabilities: [] };
295
if (!existing.capabilities.includes(capability)) {
296
existing.capabilities.push(capability);
297
this.permissions.set(identity, existing);
298
}
299
},
300
301
// Custom methods
302
grantTimeLimited(identity, capabilities, startTime, endTime) {
303
this.permissions.set(identity, {
304
start: startTime,
305
end: endTime,
306
capabilities: Array.isArray(capabilities) ? capabilities : [capabilities]
307
});
308
},
309
310
extendPermission(identity, newEndTime) {
311
const permission = this.permissions.get(identity);
312
if (permission) {
313
permission.end = newEndTime;
314
this.permissions.set(identity, permission);
315
}
316
}
317
};
318
319
useAccessController(TimeBasedAccessController);
320
321
// Usage
322
const db = await orbitdb.open('time-limited-db', {
323
AccessController: TimeBasedAccessController
324
});
325
326
// Grant write access for 1 hour
327
const oneHour = 60 * 60 * 1000;
328
db.access.grantTimeLimited(
329
'temp-user-id',
330
['read', 'write'],
331
Date.now(),
332
Date.now() + oneHour
333
);
334
```
335
336
### Access Controller Configuration
337
338
Configure access controllers when creating databases.
339
340
```javascript
341
import { createOrbitDB, IPFSAccessController } from '@orbitdb/core';
342
343
const ipfs = await createHelia();
344
const orbitdb = await createOrbitDB({ ipfs });
345
346
// Configure access controller with initial permissions
347
const adminDb = await orbitdb.open('admin-db', {
348
AccessController: IPFSAccessController({
349
write: ['admin-identity-1', 'admin-identity-2'],
350
admin: ['admin-identity-1']
351
})
352
});
353
354
// Public read-only database
355
const publicDb = await orbitdb.open('public-db', {
356
AccessController: IPFSAccessController({
357
write: [orbitdb.identity.id], // Only creator can write
358
read: ['*'] // Anyone can read
359
})
360
});
361
```
362
363
### Error Handling
364
365
Handle access control errors appropriately.
366
367
```javascript
368
import { createOrbitDB } from '@orbitdb/core';
369
370
const ipfs = await createHelia();
371
const orbitdb = await createOrbitDB({ ipfs });
372
373
try {
374
const db = await orbitdb.open('protected-db');
375
await db.add('Some data');
376
} catch (error) {
377
if (error.message.includes('Access denied')) {
378
console.error('Permission denied: Cannot write to this database');
379
console.log('Current identity:', orbitdb.identity.id);
380
} else if (error.message.includes('AccessController')) {
381
console.error('Access controller error:', error.message);
382
} else {
383
console.error('Unexpected error:', error.message);
384
}
385
}
386
387
// Check permissions before attempting operations
388
const db = await orbitdb.open('some-db');
389
const testEntry = { /* mock entry */ };
390
const canWrite = await db.access.canAppend(testEntry, orbitdb.identities);
391
392
if (canWrite) {
393
await db.add('Data');
394
} else {
395
console.log('No write permission for current identity');
396
}
397
```
398
399
## Integration with Database Operations
400
401
Access controllers are automatically invoked during database operations:
402
403
```javascript
404
// When adding data, access controller checks permissions
405
const db = await orbitdb.open('my-db');
406
407
// This triggers access controller's canAppend method
408
await db.add('New data'); // May throw if access denied
409
410
// Same for other database operations
411
await db.put('key', 'value'); // For KeyValue databases
412
await db.del('key'); // For deletion operations
413
```
414
415
Access controllers provide fine-grained control over database permissions while maintaining the distributed and decentralized nature of OrbitDB.