@icazemier/gibbons-mongodb 1.0.0-beta.1

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +554 -0
  3. package/build/cjs/bin/cli.d.ts +2 -0
  4. package/build/cjs/bin/cli.js +52 -0
  5. package/build/cjs/bin/init.d.ts +17 -0
  6. package/build/cjs/bin/init.js +29 -0
  7. package/build/cjs/config.d.ts +22 -0
  8. package/build/cjs/config.js +35 -0
  9. package/build/cjs/gibbons-mongo-db.d.ts +593 -0
  10. package/build/cjs/gibbons-mongo-db.js +699 -0
  11. package/build/cjs/index.d.ts +6 -0
  12. package/build/cjs/index.js +22 -0
  13. package/build/cjs/interfaces/config.d.ts +42 -0
  14. package/build/cjs/interfaces/config.js +2 -0
  15. package/build/cjs/interfaces/gibbon-group.d.ts +21 -0
  16. package/build/cjs/interfaces/gibbon-group.js +2 -0
  17. package/build/cjs/interfaces/gibbon-like.d.ts +8 -0
  18. package/build/cjs/interfaces/gibbon-like.js +2 -0
  19. package/build/cjs/interfaces/gibbon-permission.d.ts +11 -0
  20. package/build/cjs/interfaces/gibbon-permission.js +2 -0
  21. package/build/cjs/interfaces/gibbon-user.d.ts +12 -0
  22. package/build/cjs/interfaces/gibbon-user.js +2 -0
  23. package/build/cjs/interfaces/index.d.ts +6 -0
  24. package/build/cjs/interfaces/index.js +22 -0
  25. package/build/cjs/interfaces/permissions-resource.d.ts +15 -0
  26. package/build/cjs/interfaces/permissions-resource.js +2 -0
  27. package/build/cjs/models/gibbon-group.d.ts +133 -0
  28. package/build/cjs/models/gibbon-group.js +389 -0
  29. package/build/cjs/models/gibbon-model.d.ts +69 -0
  30. package/build/cjs/models/gibbon-model.js +78 -0
  31. package/build/cjs/models/gibbon-permission.d.ts +71 -0
  32. package/build/cjs/models/gibbon-permission.js +154 -0
  33. package/build/cjs/models/gibbon-user.d.ts +122 -0
  34. package/build/cjs/models/gibbon-user.js +380 -0
  35. package/build/cjs/models/index.d.ts +4 -0
  36. package/build/cjs/models/index.js +20 -0
  37. package/build/cjs/package.json +3 -0
  38. package/build/cjs/seeder.d.ts +56 -0
  39. package/build/cjs/seeder.js +118 -0
  40. package/build/cjs/utils.d.ts +31 -0
  41. package/build/cjs/utils.js +38 -0
  42. package/build/esm/bin/cli.d.ts +2 -0
  43. package/build/esm/bin/cli.js +47 -0
  44. package/build/esm/bin/init.d.ts +17 -0
  45. package/build/esm/bin/init.js +25 -0
  46. package/build/esm/config.d.ts +22 -0
  47. package/build/esm/config.js +31 -0
  48. package/build/esm/gibbons-mongo-db.d.ts +593 -0
  49. package/build/esm/gibbons-mongo-db.js +700 -0
  50. package/build/esm/index.d.ts +6 -0
  51. package/build/esm/index.js +6 -0
  52. package/build/esm/interfaces/config.d.ts +42 -0
  53. package/build/esm/interfaces/config.js +1 -0
  54. package/build/esm/interfaces/gibbon-group.d.ts +21 -0
  55. package/build/esm/interfaces/gibbon-group.js +1 -0
  56. package/build/esm/interfaces/gibbon-like.d.ts +8 -0
  57. package/build/esm/interfaces/gibbon-like.js +1 -0
  58. package/build/esm/interfaces/gibbon-permission.d.ts +11 -0
  59. package/build/esm/interfaces/gibbon-permission.js +1 -0
  60. package/build/esm/interfaces/gibbon-user.d.ts +12 -0
  61. package/build/esm/interfaces/gibbon-user.js +1 -0
  62. package/build/esm/interfaces/index.d.ts +6 -0
  63. package/build/esm/interfaces/index.js +6 -0
  64. package/build/esm/interfaces/permissions-resource.d.ts +15 -0
  65. package/build/esm/interfaces/permissions-resource.js +1 -0
  66. package/build/esm/models/gibbon-group.d.ts +133 -0
  67. package/build/esm/models/gibbon-group.js +321 -0
  68. package/build/esm/models/gibbon-model.d.ts +69 -0
  69. package/build/esm/models/gibbon-model.js +76 -0
  70. package/build/esm/models/gibbon-permission.d.ts +71 -0
  71. package/build/esm/models/gibbon-permission.js +134 -0
  72. package/build/esm/models/gibbon-user.d.ts +122 -0
  73. package/build/esm/models/gibbon-user.js +302 -0
  74. package/build/esm/models/index.d.ts +4 -0
  75. package/build/esm/models/index.js +4 -0
  76. package/build/esm/package.json +3 -0
  77. package/build/esm/seeder.d.ts +56 -0
  78. package/build/esm/seeder.js +116 -0
  79. package/build/esm/utils.d.ts +31 -0
  80. package/build/esm/utils.js +34 -0
  81. package/package.json +115 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Ivo Cazemier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,554 @@
1
+ <img src="https://raw.githubusercontent.com/icazemier/gibbons/master/gibbons.png" width="200" />
2
+
3
+ # Gibbons for MongoDB
4
+
5
+ A high-performance library to manage user groups and user permissions in [MongoDB](https://www.mongodb.com/) using bitwise operations with [Gibbons](https://github.com/icazemier/gibbons).
6
+
7
+ ## Features
8
+
9
+ - ⚡ **Bitwise efficiency** - Store and query thousands of permissions/groups using minimal space
10
+ - 🔍 **Fast queries** - Leverage MongoDB's bitwise operators for lightning-fast permission checks
11
+ - 🎯 **Type-safe** - Full TypeScript support with comprehensive type definitions
12
+ - 📦 **Easy setup** - CLI tool for database initialization
13
+ - 🔄 **Flexible** - Works with existing user collections
14
+
15
+ ## What this is NOT
16
+
17
+ - An ORM (Object-Relational Mapper)
18
+ - A complete auth/authentication solution
19
+ - A replacement for your existing user management system
20
+
21
+ ## Quick Example
22
+
23
+ ```typescript
24
+ import { GibbonsMongoDb, ConfigLoader } from "@icazemier/gibbons-mongodb";
25
+
26
+ // Initialize
27
+ const config = await ConfigLoader.load();
28
+ const gibbonsDb = new GibbonsMongoDb("mongodb://localhost:27017", config);
29
+ await gibbonsDb.initialize();
30
+
31
+ // Allocate permissions
32
+ const editPerm = await gibbonsDb.allocatePermission({ name: "posts.edit" });
33
+ const deletePerm = await gibbonsDb.allocatePermission({ name: "posts.delete" });
34
+
35
+ // Create a group with permissions
36
+ const adminGroup = await gibbonsDb.allocateGroup({ name: "Admins" });
37
+ await gibbonsDb.subscribePermissionsToGroups(
38
+ [adminGroup.gibbonGroupPosition],
39
+ [editPerm.gibbonPermissionPosition, deletePerm.gibbonPermissionPosition]
40
+ );
41
+
42
+ // Create user and assign to group
43
+ const user = await gibbonsDb.createUser({
44
+ name: "John",
45
+ email: "john@example.com"
46
+ });
47
+ await gibbonsDb.subscribeUsersToGroups(
48
+ { _id: user._id },
49
+ [adminGroup.gibbonGroupPosition]
50
+ );
51
+
52
+ // Validate permissions
53
+ const hasEdit = gibbonsDb.validateUserPermissionsForAnyPermissions(
54
+ user.permissionsGibbon,
55
+ [editPerm.gibbonPermissionPosition]
56
+ );
57
+ console.log("User can edit:", hasEdit); // true
58
+ ```
59
+
60
+ ## How It Works
61
+
62
+ Gibbons uses MongoDB's `Binary` data type to store bitwise masks. Each bit represents a group or permission position, allowing you to:
63
+
64
+ - Store thousands of permissions in a few bytes
65
+ - Use MongoDB's `$bitsAnySet`, `$bitsAllSet` operators for fast queries
66
+ - Aggregate permissions from multiple groups automatically
67
+
68
+ Example MongoDB query:
69
+
70
+ ```typescript
71
+ import { Gibbon } from "@icazemier/gibbons";
72
+
73
+ const gibbon = Gibbon.create(256).setPosition(1).setPosition(3);
74
+ const cursor = mongoClient
75
+ .db("mydb")
76
+ .collection("users")
77
+ .find({
78
+ groupsGibbon: {
79
+ $bitsAnySet: gibbon.toBuffer(),
80
+ },
81
+ });
82
+ ```
83
+
84
+ # Installation
85
+
86
+ ```bash
87
+ npm install @icazemier/gibbons-mongodb
88
+ ```
89
+
90
+ # Setup
91
+
92
+ ## 1. Configuration File
93
+
94
+ Create a configuration file that Gibbons can discover. We use [cosmiconfig](https://github.com/davidtheclark/cosmiconfig#readme), so you can use any of these formats:
95
+
96
+ - `.gibbons-mongodbrc.json` (recommended)
97
+ - `.gibbons-mongodbrc.yaml`
98
+ - `gibbons-mongodb` property in `package.json`
99
+ - And more (see cosmiconfig docs)
100
+
101
+ ### Configuration Structure
102
+
103
+ **⚠️ IMPORTANT:** Config settings affect how data is stored. Changing these on a live system can break existing data!
104
+
105
+ Example `.gibbons-mongodbrc.json`:
106
+
107
+ ```json
108
+ {
109
+ "permissionByteLength": 256,
110
+ "groupByteLength": 256,
111
+ "mongoDbMutationConcurrency": 10,
112
+ "dbStructure": {
113
+ "user": {
114
+ "dbName": "myapp",
115
+ "collectionName": "users"
116
+ },
117
+ "group": {
118
+ "dbName": "myapp",
119
+ "collectionName": "groups"
120
+ },
121
+ "permission": {
122
+ "dbName": "myapp",
123
+ "collectionName": "permissions"
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Configuration Options
130
+
131
+ | Option | Type | Description |
132
+ | ----------------------------- | ------ | --------------------------------------------------------------------------- |
133
+ | `permissionByteLength` | number | Bytes for permission Gibbon (e.g., 256 = 2,048 possible permissions) |
134
+ | `groupByteLength` | number | Bytes for group Gibbon (e.g., 256 = 2,048 possible groups) |
135
+ | `mongoDbMutationConcurrency` | number | Concurrency limit for bulk operations |
136
+ | `dbStructure.user` | object | User collection config - can point to existing collection |
137
+ | `dbStructure.group` | object | Group collection config - will be created/managed by Gibbons |
138
+ | `dbStructure.permission` | object | Permission collection config - will be created/managed by Gibbons |
139
+
140
+ **Note:** The user collection can be an existing one. Gibbons adds `groupsGibbon` and `permissionsGibbon` fields without affecting other fields.
141
+
142
+ ## 2. Initialize Database
143
+
144
+ Run the CLI tool to populate groups and permissions collections:
145
+
146
+ ```bash
147
+ # Using default config
148
+ npx gibbons-mongodb init --uri mongodb://localhost:27017
149
+
150
+ # Using custom config file
151
+ npx gibbons-mongodb init --uri mongodb://localhost:27017 --config ./my-config.json
152
+
153
+ # Or use the alias
154
+ npx @icazemier/gibbons-mongodb init -u mongodb://localhost:27017
155
+ ```
156
+
157
+ This creates:
158
+ - `permissionByteLength * 8` non-allocated permission slots
159
+ - `groupByteLength * 8` non-allocated group slots
160
+
161
+ For 256 bytes each, that's **2,048 groups** and **2,048 permissions** ready to allocate!
162
+
163
+ # Usage
164
+
165
+ ## Initialize the Library
166
+
167
+ ```typescript
168
+ import { GibbonsMongoDb, ConfigLoader } from "@icazemier/gibbons-mongodb";
169
+
170
+ // Load config (searches for .gibbons-mongodbrc files)
171
+ const config = await ConfigLoader.load();
172
+
173
+ // Or load from specific file
174
+ const config = await ConfigLoader.load("gibbons-mongodb", "./custom-config.json");
175
+
176
+ // Create instance
177
+ const gibbonsDb = new GibbonsMongoDb("mongodb://localhost:27017", config);
178
+
179
+ // Initialize (connects to MongoDB and sets up collections)
180
+ await gibbonsDb.initialize();
181
+ ```
182
+
183
+ ## Managing Permissions
184
+
185
+ ```typescript
186
+ // Allocate new permissions
187
+ const createPost = await gibbonsDb.allocatePermission({
188
+ name: "posts.create",
189
+ description: "Create new blog posts",
190
+ });
191
+
192
+ const editPost = await gibbonsDb.allocatePermission({
193
+ name: "posts.edit",
194
+ description: "Edit any blog post",
195
+ });
196
+
197
+ const deletePost = await gibbonsDb.allocatePermission({
198
+ name: "posts.delete",
199
+ description: "Delete any blog post",
200
+ });
201
+
202
+ console.log(createPost.gibbonPermissionPosition); // e.g., 1
203
+ console.log(createPost.gibbonIsAllocated); // true
204
+
205
+ // Update permission metadata
206
+ await gibbonsDb.updatePermissionMetadata(createPost.gibbonPermissionPosition, {
207
+ description: "Create and publish blog posts",
208
+ module: "blog",
209
+ });
210
+
211
+ // List all allocated permissions
212
+ const permissionsCursor = gibbonsDb.findAllAllocatedPermissions();
213
+ for await (const perm of permissionsCursor) {
214
+ console.log(perm.name, perm.gibbonPermissionPosition);
215
+ }
216
+
217
+ // Deallocate permissions (removes from groups and users)
218
+ await gibbonsDb.deallocatePermissions([deletePost.gibbonPermissionPosition]);
219
+ ```
220
+
221
+ ## Managing Groups
222
+
223
+ ```typescript
224
+ // Allocate new groups
225
+ const admins = await gibbonsDb.allocateGroup({
226
+ name: "Admins",
227
+ description: "Full system access",
228
+ });
229
+
230
+ const editors = await gibbonsDb.allocateGroup({
231
+ name: "Editors",
232
+ description: "Content editors",
233
+ });
234
+
235
+ // Assign permissions to groups
236
+ await gibbonsDb.subscribePermissionsToGroups(
237
+ [admins.gibbonGroupPosition],
238
+ [createPost.gibbonPermissionPosition, editPost.gibbonPermissionPosition, deletePost.gibbonPermissionPosition]
239
+ );
240
+
241
+ await gibbonsDb.subscribePermissionsToGroups(
242
+ [editors.gibbonGroupPosition],
243
+ [createPost.gibbonPermissionPosition, editPost.gibbonPermissionPosition]
244
+ );
245
+
246
+ // Update group metadata
247
+ await gibbonsDb.updateGroupMetadata(admins.gibbonGroupPosition, {
248
+ color: "#FF0000",
249
+ priority: 1,
250
+ });
251
+
252
+ // Find groups by permissions
253
+ const groupsWithDelete = gibbonsDb.findGroupsByPermissions([deletePost.gibbonPermissionPosition]);
254
+ for await (const group of groupsWithDelete) {
255
+ console.log(`${group.name} can delete posts`);
256
+ }
257
+
258
+ // Remove permissions from groups
259
+ await gibbonsDb.unsubscribePermissionsFromGroups(
260
+ [editors.gibbonGroupPosition],
261
+ [createPost.gibbonPermissionPosition]
262
+ );
263
+
264
+ // Deallocate groups (removes from users)
265
+ await gibbonsDb.deallocateGroups([editors.gibbonGroupPosition]);
266
+ ```
267
+
268
+ ## Managing Users
269
+
270
+ ```typescript
271
+ // Create users
272
+ const user1 = await gibbonsDb.createUser({
273
+ name: "Alice",
274
+ email: "alice@example.com",
275
+ username: "alice",
276
+ });
277
+
278
+ const user2 = await gibbonsDb.createUser({
279
+ name: "Bob",
280
+ email: "bob@example.com",
281
+ username: "bob",
282
+ });
283
+
284
+ // Assign users to groups
285
+ await gibbonsDb.subscribeUsersToGroups(
286
+ { email: "alice@example.com" },
287
+ [admins.gibbonGroupPosition]
288
+ );
289
+
290
+ await gibbonsDb.subscribeUsersToGroups(
291
+ { _id: user2._id },
292
+ [editors.gibbonGroupPosition]
293
+ );
294
+
295
+ // Find users by groups
296
+ const adminUsers = gibbonsDb.findUsersByGroups([admins.gibbonGroupPosition]);
297
+ for await (const user of adminUsers) {
298
+ console.log(`${user.name} is an admin`);
299
+ }
300
+
301
+ // Find users by permissions
302
+ const usersWhoCanEdit = gibbonsDb.findUsersByPermissions([editPost.gibbonPermissionPosition]);
303
+
304
+ // Find users with custom filter
305
+ const activeUsers = gibbonsDb.findUsers({
306
+ status: "active",
307
+ createdAt: { $gte: new Date("2024-01-01") },
308
+ });
309
+
310
+ // Remove users from groups
311
+ await gibbonsDb.unsubscribeUsersFromGroups(
312
+ { email: "bob@example.com" },
313
+ [editors.gibbonGroupPosition]
314
+ );
315
+
316
+ // Delete users
317
+ const deletedCount = await gibbonsDb.removeUser({ email: "bob@example.com" });
318
+ console.log(`Deleted ${deletedCount} user(s)`);
319
+ ```
320
+
321
+ ## Permission Validation
322
+
323
+ ```typescript
324
+ // Fetch user with populated gibbons
325
+ const user = await gibbonsDb.findUsers({ email: "alice@example.com" }).next();
326
+
327
+ if (user) {
328
+ // Check if user has ALL specified permissions
329
+ const canEditAndDelete = gibbonsDb.validateUserPermissionsForAllPermissions(
330
+ user.permissionsGibbon,
331
+ [editPost.gibbonPermissionPosition, deletePost.gibbonPermissionPosition]
332
+ );
333
+
334
+ // Check if user has ANY of the specified permissions
335
+ const canModify = gibbonsDb.validateUserPermissionsForAnyPermissions(
336
+ user.permissionsGibbon,
337
+ [editPost.gibbonPermissionPosition, deletePost.gibbonPermissionPosition]
338
+ );
339
+
340
+ // Check if user has ALL specified groups
341
+ const isAdmin = gibbonsDb.validateUserGroupsForAllGroups(
342
+ user.groupsGibbon,
343
+ [admins.gibbonGroupPosition]
344
+ );
345
+
346
+ // Check if user has ANY of the specified groups
347
+ const isStaff = gibbonsDb.validateUserGroupsForAnyGroups(
348
+ user.groupsGibbon,
349
+ [admins.gibbonGroupPosition, editors.gibbonGroupPosition]
350
+ );
351
+
352
+ console.log({
353
+ canEditAndDelete,
354
+ canModify,
355
+ isAdmin,
356
+ isStaff,
357
+ });
358
+ }
359
+ ```
360
+
361
+ ## Database Validation
362
+
363
+ ```typescript
364
+ // Verify groups are allocated before using them
365
+ const groupsValid = await gibbonsDb.validateAllocatedGroups([1, 2, 3]);
366
+ if (!groupsValid) {
367
+ throw new Error("Some groups are not allocated");
368
+ }
369
+
370
+ // Verify permissions are allocated
371
+ const permsValid = await gibbonsDb.validateAllocatedPermissions([5, 6, 7]);
372
+ ```
373
+
374
+ ## Working with Gibbons Directly
375
+
376
+ ```typescript
377
+ import { Gibbon } from "@icazemier/gibbons";
378
+
379
+ // Get aggregated permissions from groups
380
+ const permissionsGibbon = await gibbonsDb.getPermissionsGibbonForGroups([
381
+ admins.gibbonGroupPosition,
382
+ editors.gibbonGroupPosition,
383
+ ]);
384
+
385
+ // Get positions as array
386
+ const positions = permissionsGibbon.getPositionsArray();
387
+ console.log("Permission positions:", positions); // e.g., [1, 2, 3, 5]
388
+
389
+ // Manual permission checks
390
+ const hasPermission = permissionsGibbon.isPositionSet(editPost.gibbonPermissionPosition);
391
+ ```
392
+
393
+ # API Reference
394
+
395
+ For complete API documentation with examples, see the [TypeScript definitions](./src/index.ts) or check the TSDoc comments in your IDE.
396
+
397
+ ## Main Classes
398
+
399
+ - **`GibbonsMongoDb`** - Main class for managing users, groups, and permissions
400
+ - **`MongoDbSeeder`** - Seeds database with pre-allocated groups and permissions
401
+ - **`ConfigLoader`** - Loads configuration from filesystem
402
+
403
+ ## Key Methods
404
+
405
+ ### GibbonsMongoDb
406
+
407
+ #### Permissions
408
+ - `allocatePermission<T>(data: T)` - Allocate new permission
409
+ - `deallocatePermissions(permissions)` - Deallocate permissions
410
+ - `updatePermissionMetadata(position, data)` - Update permission metadata
411
+ - `findAllAllocatedPermissions()` - List all allocated permissions
412
+ - `validateAllocatedPermissions(permissions)` - Validate permissions exist
413
+
414
+ #### Groups
415
+ - `allocateGroup<T>(data: T)` - Allocate new group
416
+ - `deallocateGroups(groups)` - Deallocate groups
417
+ - `updateGroupMetadata(position, data)` - Update group metadata
418
+ - `findGroups(groups)` - Find specific groups
419
+ - `findGroupsByPermissions(permissions)` - Find groups with permissions
420
+ - `findAllAllocatedGroups()` - List all allocated groups
421
+ - `validateAllocatedGroups(groups)` - Validate groups exist
422
+
423
+ #### Users
424
+ - `createUser<T>(data: T)` - Create new user
425
+ - `removeUser(filter)` - Delete users
426
+ - `findUsers(filter)` - Query users
427
+ - `findUsersByGroups(groups)` - Find users by groups
428
+ - `findUsersByPermissions(permissions)` - Find users by permissions
429
+
430
+ #### Subscriptions
431
+ - `subscribeUsersToGroups(filter, groups)` - Add users to groups
432
+ - `subscribePermissionsToGroups(groups, permissions)` - Add permissions to groups
433
+ - `unsubscribeUsersFromGroups(filter, groups)` - Remove users from groups
434
+ - `unsubscribePermissionsFromGroups(groups, permissions)` - Remove permissions from groups
435
+
436
+ #### Validation
437
+ - `validateUserGroupsForAllGroups(userGroups, groups)` - Check if user has all groups
438
+ - `validateUserGroupsForAnyGroups(userGroups, groups)` - Check if user has any group
439
+ - `validateUserPermissionsForAllPermissions(userPerms, perms)` - Check if user has all permissions
440
+ - `validateUserPermissionsForAnyPermissions(userPerms, perms)` - Check if user has any permission
441
+
442
+ # Advanced Topics
443
+
444
+ ## Data Structure
445
+
446
+ ### User Document
447
+ ```typescript
448
+ {
449
+ _id: ObjectId,
450
+ // Your custom fields
451
+ name: "Alice",
452
+ email: "alice@example.com",
453
+ // Gibbons-managed fields
454
+ groupsGibbon: Binary, // Bitwise mask of group memberships
455
+ permissionsGibbon: Binary, // Aggregated permissions from groups
456
+ }
457
+ ```
458
+
459
+ ### Group Document
460
+ ```typescript
461
+ {
462
+ _id: ObjectId,
463
+ gibbonGroupPosition: 1, // Unique position (1-based)
464
+ gibbonIsAllocated: true, // Allocation status
465
+ permissionsGibbon: Binary, // Bitwise mask of permissions
466
+ // Your custom fields
467
+ name: "Admins",
468
+ description: "Full access",
469
+ }
470
+ ```
471
+
472
+ ### Permission Document
473
+ ```typescript
474
+ {
475
+ _id: ObjectId,
476
+ gibbonPermissionPosition: 5, // Unique position (1-based)
477
+ gibbonIsAllocated: true, // Allocation status
478
+ // Your custom fields
479
+ name: "posts.edit",
480
+ description: "Edit posts",
481
+ }
482
+ ```
483
+
484
+ ## MongoDB Queries
485
+
486
+ You can query directly using MongoDB's bitwise operators:
487
+
488
+ ```typescript
489
+ import { Binary } from "mongodb";
490
+ import { Gibbon } from "@icazemier/gibbons";
491
+
492
+ // Find users with specific permissions
493
+ const gibbon = Gibbon.create(256)
494
+ .setPosition(editPost.gibbonPermissionPosition)
495
+ .setPosition(deletePost.gibbonPermissionPosition);
496
+
497
+ const users = await db.collection("users").find({
498
+ permissionsGibbon: {
499
+ $bitsAllSet: new Binary(gibbon.toBuffer()),
500
+ },
501
+ }).toArray();
502
+ ```
503
+
504
+ ## Environment Variables
505
+
506
+ - `GIBBONS_ENCODE_FROM_TO_STRING` - Controls whether Gibbons encodes to UTF-16 string or Buffer (see [Gibbons docs](https://github.com/icazemier/gibbons))
507
+
508
+ # Best Practices
509
+
510
+ 1. **Choose appropriate byte lengths** - Calculate based on your maximum expected permissions/groups:
511
+ - 256 bytes = 2,048 items
512
+ - 512 bytes = 4,096 items
513
+ - 1024 bytes = 8,192 items
514
+
515
+ 2. **Don't change byte lengths on live systems** - This will corrupt existing data
516
+
517
+ 3. **Use meaningful names** - Store descriptive names/descriptions with permissions and groups for easier management
518
+
519
+ 4. **Aggregate permissions through groups** - Don't assign permissions directly to users; use groups for better maintainability
520
+
521
+ 5. **Validate before operations** - Always check if groups/permissions are allocated before using them
522
+
523
+ 6. **Monitor allocation usage** - Keep track of how many slots you've used vs. available
524
+
525
+ 7. **Backup before migrations** - Config changes can affect data structure
526
+
527
+ # Troubleshooting
528
+
529
+ ## "Could not load config"
530
+ Make sure you have a `.gibbons-mongodbrc.json` (or equivalent) in your project root or run `npx gibbons-mongodb init` with `--config` flag.
531
+
532
+ ## "Not able to allocate permission/group"
533
+ All slots are used. Increase `permissionByteLength` or `groupByteLength` in config, reinitialize database.
534
+
535
+ ## "Called populateGroupsAndPermissions, but permissions and groups seem to be populated already"
536
+ Database is already initialized. This is expected on subsequent runs.
537
+
538
+ # License
539
+
540
+ MIT
541
+
542
+ # Contributing
543
+
544
+ Issues and pull requests welcome! See [repository](https://github.com/icazemier/gibbons-mongodb) for details.
545
+
546
+ ## For Contributors
547
+
548
+ This project uses automated semantic versioning. Please use conventional commits:
549
+
550
+ ```bash
551
+ npm run commit # Interactive commit tool
552
+ ```
553
+
554
+ See [SEMANTIC-VERSIONING-QUICKSTART.md](SEMANTIC-VERSIONING-QUICKSTART.md) for details.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const yargs_1 = __importDefault(require("yargs"));
8
+ const helpers_1 = require("yargs/helpers");
9
+ const init_js_1 = require("./init.js");
10
+ /**
11
+ * Main CLI entry point using yargs for command parsing and routing.
12
+ */
13
+ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
14
+ .scriptName('gibbons-mongodb')
15
+ .command('init', 'Populate new groups and permissions collections in your existing MongoDB instance', (yargs) => {
16
+ return yargs
17
+ .options({
18
+ uri: {
19
+ demandOption: true,
20
+ alias: 'u',
21
+ type: 'string',
22
+ description: 'MongoDB URI (Note: Database name and collections are configured through config)',
23
+ nargs: 1,
24
+ },
25
+ config: {
26
+ demandOption: false,
27
+ alias: 'c',
28
+ type: 'string',
29
+ description: 'Point to custom/own config file',
30
+ nargs: 1,
31
+ },
32
+ })
33
+ .example('$0 init --uri=mongodb://localhost:27017 --config=./someconfig.json', 'Populates groups and permissions in MongoDB for given URI specified by a custom config file');
34
+ }, async (argv) => {
35
+ try {
36
+ await (0, init_js_1.init)(argv);
37
+ console.log('✓ Database initialization completed successfully');
38
+ process.exit(0);
39
+ }
40
+ catch (error) {
41
+ console.error('✗ Initialization failed:', error instanceof Error ? error.message : error);
42
+ process.exit(1);
43
+ }
44
+ })
45
+ .usage('Usage: $0 <command> [options]')
46
+ .demandCommand(1, 'Expected a command, e.g. `init`')
47
+ .strict()
48
+ .help()
49
+ .alias('help', 'h')
50
+ .version()
51
+ .alias('version', 'v')
52
+ .parse();
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Command arguments for the init command.
3
+ */
4
+ export interface InitCommandArgs {
5
+ /** MongoDB connection URI */
6
+ uri: string;
7
+ /** Optional path to custom configuration file */
8
+ config?: string;
9
+ }
10
+ /**
11
+ * Initializes a MongoDB instance with pre-populated groups and permissions.
12
+ * Connects to the database, loads configuration, and runs the seeding process.
13
+ *
14
+ * @param argv - Command-line arguments containing URI and optional config path
15
+ * @throws Error when configuration cannot be loaded or seeding fails
16
+ */
17
+ export declare const init: (argv: InitCommandArgs) => Promise<void>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.init = void 0;
4
+ const mongodb_1 = require("mongodb");
5
+ const config_js_1 = require("../config.js");
6
+ const seeder_js_1 = require("../seeder.js");
7
+ /**
8
+ * Initializes a MongoDB instance with pre-populated groups and permissions.
9
+ * Connects to the database, loads configuration, and runs the seeding process.
10
+ *
11
+ * @param argv - Command-line arguments containing URI and optional config path
12
+ * @throws Error when configuration cannot be loaded or seeding fails
13
+ */
14
+ const init = async (argv) => {
15
+ const { uri, config: configFile } = argv;
16
+ let mongoClient = null;
17
+ try {
18
+ mongoClient = await new mongodb_1.MongoClient(uri).connect();
19
+ const config = await config_js_1.ConfigLoader.load('gibbons-mongodb', configFile);
20
+ const mongoDbSeeder = new seeder_js_1.MongoDbSeeder(mongoClient, config);
21
+ await mongoDbSeeder.initialize();
22
+ }
23
+ finally {
24
+ if (mongoClient) {
25
+ await mongoClient.close();
26
+ }
27
+ }
28
+ };
29
+ exports.init = init;
@@ -0,0 +1,22 @@
1
+ export declare class ConfigLoader {
2
+ /**
3
+ * Load config from disk, looks for `.gibbons-mongodbrc` file by default
4
+ * @see For Usage {@link https://github.com/davidtheclark/cosmiconfig}
5
+ *
6
+ * @param {string} [module="gibbons-mongodb"]
7
+ * @param {string} [filepath]
8
+ * @throws {Error} When no config file could be resolved
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Load config from default locations (.gibbons-mongodbrc, package.json, etc.)
13
+ * const config = await ConfigLoader.load();
14
+ *
15
+ * // Load config from specific file
16
+ * const config = await ConfigLoader.load('gibbons-mongodb', './my-config.json');
17
+ * ```
18
+ *
19
+ * @public
20
+ */
21
+ static load(module?: string, filepath?: string): Promise<any>;
22
+ }