super_auth 0.1.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +3 -7
  4. data/Gemfile.lock +25 -14
  5. data/LICENSE.txt +125 -21
  6. data/README.md +32 -1
  7. data/Rakefile +0 -2
  8. data/USAGE.md +619 -0
  9. data/VISUALIZATION.md +58 -0
  10. data/app/controllers/super_auth/graph_controller.rb +661 -0
  11. data/app/views/super_auth/graph/index.html.erb +1408 -0
  12. data/config/routes.rb +73 -0
  13. data/db/migrate/1_users.rb +1 -0
  14. data/db/migrate/2_groups.rb +8 -1
  15. data/db/migrate/4_roles.rb +8 -1
  16. data/db/migrate/5_resources.rb +1 -0
  17. data/db/migrate/7_authorization.rb +2 -0
  18. data/db/migrate/8_add_indexes_to_edges.rb +17 -0
  19. data/db/migrate/9_add_by_current_user_index.rb +12 -0
  20. data/db/migrate_activerecord/20250101000001_create_super_auth_users.rb +10 -0
  21. data/db/migrate_activerecord/20250101000002_create_super_auth_groups.rb +11 -0
  22. data/db/migrate_activerecord/20250101000003_create_super_auth_permissions.rb +8 -0
  23. data/db/migrate_activerecord/20250101000004_create_super_auth_roles.rb +11 -0
  24. data/db/migrate_activerecord/20250101000005_create_super_auth_resources.rb +10 -0
  25. data/db/migrate_activerecord/20250101000006_create_super_auth_edges.rb +12 -0
  26. data/db/migrate_activerecord/20250101000007_create_super_auth_authorizations.rb +41 -0
  27. data/db/migrate_activerecord/20250101000009_add_by_current_user_index_to_super_auth_authorizations.rb +7 -0
  28. data/db/seeds/sample_data.rb +193 -0
  29. data/lib/basic_loader.rb +0 -2
  30. data/lib/generators/super_auth/install/install_generator.rb +19 -0
  31. data/lib/generators/super_auth/install/templates/README +29 -0
  32. data/lib/generators/super_auth/install/templates/super_auth.rb +7 -0
  33. data/lib/super_auth/active_record/by_current_user.rb +26 -11
  34. data/lib/super_auth/active_record/edge.rb +45 -0
  35. data/lib/super_auth/active_record/group.rb +7 -0
  36. data/lib/super_auth/active_record/permission.rb +4 -0
  37. data/lib/super_auth/active_record/resource.rb +1 -0
  38. data/lib/super_auth/active_record/role.rb +7 -0
  39. data/lib/super_auth/active_record/user.rb +6 -0
  40. data/lib/super_auth/active_record.rb +17 -0
  41. data/lib/super_auth/edge.rb +190 -131
  42. data/lib/super_auth/group.rb +1 -0
  43. data/lib/super_auth/nestable.rb +17 -10
  44. data/lib/super_auth/permission.rb +1 -1
  45. data/lib/super_auth/railtie.rb +26 -25
  46. data/lib/super_auth/role.rb +2 -1
  47. data/lib/super_auth/user.rb +8 -8
  48. data/lib/super_auth/version.rb +1 -3
  49. data/lib/super_auth.rb +72 -40
  50. data/super_auth.gemspec +35 -0
  51. data/visualization.html +747 -0
  52. metadata +24 -6
data/USAGE.md ADDED
@@ -0,0 +1,619 @@
1
+ # SuperAuth Usage Guide
2
+
3
+ SuperAuth is a graph-based authorization engine that makes unauthorized access structurally impossible. Instead of scattering authorization checks throughout your codebase, SuperAuth centralizes all rules in a database-backed graph and computes every valid access path automatically.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Core Concepts](#core-concepts)
9
+ - [Quick Start](#quick-start)
10
+ - [Creating Entities](#creating-entities)
11
+ - [Drawing Edges](#drawing-edges)
12
+ - [Authorization Strategies](#authorization-strategies)
13
+ - [Querying Authorizations](#querying-authorizations)
14
+ - [Revoking Access](#revoking-access)
15
+ - [Rails Integration](#rails-integration)
16
+ - [Auditing](#auditing)
17
+ - [Visualization](#visualization)
18
+
19
+ ## Installation
20
+
21
+ Add to your Gemfile:
22
+
23
+ ```ruby
24
+ gem "super_auth"
25
+ ```
26
+
27
+ Then run:
28
+
29
+ ```bash
30
+ bundle install
31
+ ```
32
+
33
+ ### Rails Setup
34
+
35
+ **Step 1.** Add SuperAuth and the Sequel-ActiveRecord bridge to your Gemfile:
36
+
37
+ ```ruby
38
+ gem "super_auth"
39
+ gem "sequel-activerecord_connection"
40
+ ```
41
+
42
+ Then run:
43
+
44
+ ```bash
45
+ bundle install
46
+ ```
47
+
48
+ The `sequel-activerecord_connection` gem lets SuperAuth's Sequel engine share your existing ActiveRecord database connection, so there is nothing extra to configure.
49
+
50
+ **Step 2.** Run the install generator:
51
+
52
+ ```bash
53
+ rails generate super_auth:install
54
+ ```
55
+
56
+ This creates an initializer at `config/initializers/super_auth.rb`. The SuperAuth Railtie automatically connects to your ActiveRecord database and loads all models on boot -- no manual configuration is needed.
57
+
58
+ **Step 3.** Copy the SuperAuth migrations into your app and run them:
59
+
60
+ ```bash
61
+ rails railties:install:migrations
62
+ rails db:migrate
63
+ ```
64
+
65
+ This creates the `super_auth_*` tables (users, groups, roles, permissions, resources, edges, authorizations) alongside your application's tables.
66
+
67
+ **Step 4.** Set the current user in your controller:
68
+
69
+ ```ruby
70
+ class ApplicationController < ActionController::Base
71
+ before_action :set_super_auth_user
72
+
73
+ private
74
+
75
+ def set_super_auth_user
76
+ SuperAuth.current_user = current_user
77
+ end
78
+ end
79
+ ```
80
+
81
+ SuperAuth uses `SuperAuth.current_user` (a thread-local) to know who is making the request. You can pass your own application's user object -- SuperAuth matches it via `external_id` and `external_type`.
82
+
83
+ **Step 5.** Add `super_auth` to any model you want to protect:
84
+
85
+ ```ruby
86
+ class Post < ApplicationRecord
87
+ super_auth
88
+ end
89
+ ```
90
+
91
+ This adds a default scope that automatically filters records. Only records the current user is authorized to access will be returned from queries:
92
+
93
+ ```ruby
94
+ Post.all # only posts the current user can access
95
+ Post.where(published: true) # scoped AND filtered by authorization
96
+ ```
97
+
98
+ **Step 6 (optional).** Mount the visualization engine:
99
+
100
+ ```ruby
101
+ # config/routes.rb
102
+ mount SuperAuth::Engine => '/super_auth'
103
+ ```
104
+
105
+ Then visit `http://localhost:3000/super_auth/visualization` to see your authorization graph.
106
+
107
+ ### Standalone Setup (without Rails)
108
+
109
+ ```ruby
110
+ require "super_auth"
111
+
112
+ # Connect to a database
113
+ SuperAuth.db = Sequel.sqlite("super_auth.db")
114
+ # Or use an environment variable:
115
+ # ENV['SUPER_AUTH_DATABASE_URL'] = 'postgresql://user:pass@localhost/mydb'
116
+
117
+ SuperAuth.install_migrations
118
+ SuperAuth.load
119
+ ```
120
+
121
+ SuperAuth uses [Sequel](https://sequel.jeremyevans.net/) for all database operations and supports SQLite, PostgreSQL, and MySQL.
122
+
123
+ ## Core Concepts
124
+
125
+ SuperAuth models authorization as a graph with 5 entity types:
126
+
127
+ | Entity | Purpose | Hierarchical? |
128
+ |----------------|------------------------------------------------|---------------|
129
+ | **User** | Who is requesting access | No |
130
+ | **Group** | Organizational units (teams, departments, etc) | Yes (nested) |
131
+ | **Role** | Job titles or permission sets | Yes (nested) |
132
+ | **Permission** | Actions (read, write, deploy, etc) | No |
133
+ | **Resource** | Things being protected (files, APIs, records) | No |
134
+
135
+ **Edges** are connections drawn between any two entities. SuperAuth traverses the graph to find all valid paths from a User to a Resource. If a path exists, access is granted.
136
+
137
+ ```
138
+ +-------+ +------+
139
+ | Group |<----->| Role |
140
+ +-------+\ / +------+
141
+ ^ \ / ^
142
+ | \/ |
143
+ | /\ |
144
+ | / \ |
145
+ V / \ V
146
+ +--------+ +------+/ \+------------+ +----------+
147
+ | App |<-------->| User |<------>| Permission |<-->| Resource |
148
+ | Models | +------+ +------------+ +----------+
149
+ +--------+ ^ ^
150
+ | |
151
+ +----------------------------------+
152
+ ```
153
+
154
+ ## Quick Start
155
+
156
+ ```ruby
157
+ # Create a user, a permission, and a resource
158
+ alice = SuperAuth::User.create(name: "Alice")
159
+ read = SuperAuth::Permission.create(name: "read")
160
+ docs = SuperAuth::Resource.create(name: "documents")
161
+
162
+ # Draw edges to connect them
163
+ SuperAuth::Edge.create(user: alice, permission: read)
164
+ SuperAuth::Edge.create(permission: read, resource: docs)
165
+
166
+ # Query authorizations
167
+ auths = SuperAuth::Edge.authorizations.all
168
+ alice_auths = auths.select { |a| a[:user_id] == alice.id }
169
+
170
+ alice_auths.first[:permission_name] # => "read"
171
+ alice_auths.first[:resource_name] # => "documents"
172
+ ```
173
+
174
+ ## Creating Entities
175
+
176
+ ### Users
177
+
178
+ ```ruby
179
+ # Basic user
180
+ peter = SuperAuth::User.create(name: "Peter")
181
+
182
+ # User linked to your app's user model
183
+ alice = SuperAuth::User.create(
184
+ name: "Alice",
185
+ external_id: 42,
186
+ external_type: "User"
187
+ )
188
+
189
+ # System user (bypasses all authorization checks in ActiveRecord integration)
190
+ system = SuperAuth::User.system
191
+ system.system? # => true
192
+ ```
193
+
194
+ ### Groups (hierarchical)
195
+
196
+ Groups represent organizational structure. They can be nested to any depth.
197
+
198
+ ```ruby
199
+ company = SuperAuth::Group.create(name: "Company")
200
+ engineering = SuperAuth::Group.create(name: "Engineering", parent: company)
201
+ backend = SuperAuth::Group.create(name: "Backend", parent: engineering)
202
+ frontend = SuperAuth::Group.create(name: "Frontend", parent: engineering)
203
+ ```
204
+
205
+ This creates the hierarchy:
206
+
207
+ ```
208
+ Company
209
+ └── Engineering
210
+ ├── Backend
211
+ └── Frontend
212
+ ```
213
+
214
+ Navigate the hierarchy:
215
+
216
+ ```ruby
217
+ SuperAuth::Group.roots # Groups with no parent
218
+ SuperAuth::Group.trees # All groups with computed paths
219
+ backend.ancestors_dataset.all # => [Engineering, Company]
220
+ company.descendants_dataset.all # => [Engineering, Backend, Frontend]
221
+ ```
222
+
223
+ ### Roles (hierarchical)
224
+
225
+ Roles work exactly like Groups -- they support the same nesting.
226
+
227
+ ```ruby
228
+ employee = SuperAuth::Role.create(name: "Employee")
229
+ engineer = SuperAuth::Role.create(name: "Engineer", parent: employee)
230
+ senior_dev = SuperAuth::Role.create(name: "Senior Developer", parent: engineer)
231
+ jr_dev = SuperAuth::Role.create(name: "Junior Developer", parent: engineer)
232
+ ```
233
+
234
+ ### Permissions
235
+
236
+ Permissions are flat -- they represent actions.
237
+
238
+ ```ruby
239
+ read_perm = SuperAuth::Permission.create(name: "read")
240
+ write_perm = SuperAuth::Permission.create(name: "write")
241
+ deploy_perm = SuperAuth::Permission.create(name: "deploy")
242
+ ```
243
+
244
+ ### Resources
245
+
246
+ Resources represent what you are protecting. They can link to your app's models.
247
+
248
+ ```ruby
249
+ # Named resource
250
+ staging = SuperAuth::Resource.create(name: "staging")
251
+
252
+ # Linked to an ActiveRecord model
253
+ posts = SuperAuth::Resource.create(
254
+ name: "posts",
255
+ external_id: nil,
256
+ external_type: "Post"
257
+ )
258
+ ```
259
+
260
+ ## Drawing Edges
261
+
262
+ Edges are the core of SuperAuth. Each edge connects exactly two entities.
263
+
264
+ ```ruby
265
+ # User belongs to a group
266
+ SuperAuth::Edge.create(user: peter, group: backend)
267
+
268
+ # Group has a role
269
+ SuperAuth::Edge.create(group: backend, role: engineer)
270
+
271
+ # Role has a permission
272
+ SuperAuth::Edge.create(role: engineer, permission: read_perm)
273
+
274
+ # Permission applies to a resource
275
+ SuperAuth::Edge.create(permission: read_perm, resource: staging)
276
+ ```
277
+
278
+ You can also create shortcuts by skipping intermediate entities:
279
+
280
+ ```ruby
281
+ # User directly linked to a role (no group)
282
+ SuperAuth::Edge.create(user: alice, role: senior_dev)
283
+
284
+ # User directly linked to a permission (no group or role)
285
+ SuperAuth::Edge.create(user: alice, permission: deploy_perm)
286
+
287
+ # Group directly linked to a permission (no role)
288
+ SuperAuth::Edge.create(group: backend, permission: write_perm)
289
+
290
+ # User directly linked to a resource (no permission check)
291
+ SuperAuth::Edge.create(user: alice, resource: staging)
292
+ ```
293
+
294
+ ## Authorization Strategies
295
+
296
+ SuperAuth automatically evaluates 5 pathing strategies and unions the results. You don't need to choose one -- all valid paths are discovered.
297
+
298
+ | # | Path | Use Case |
299
+ |---|------------------------------------------------------------|-------------------------------------|
300
+ | 1 | User -> Group(s) -> Role(s) -> Permission -> Resource | Full organizational hierarchy |
301
+ | 2 | User -> Role(s) -> Permission -> Resource | Direct role assignment |
302
+ | 3 | User -> Group(s) -> Permission -> Resource | Group-level permissions (no roles) |
303
+ | 4 | User -> Permission -> Resource | Direct permission grant |
304
+ | 5 | User -> Resource | Direct resource access (no permissions) |
305
+
306
+ ### Hierarchy propagation
307
+
308
+ When groups or roles are nested, SuperAuth considers the full tree. If you assign a user to a parent group, they can access resources through roles attached to that group *and all its descendants*.
309
+
310
+ ```ruby
311
+ # Bethany is in Company (the root group)
312
+ SuperAuth::Edge.create(user: bethany, group: company)
313
+ SuperAuth::Edge.create(group: company, role: employee)
314
+ SuperAuth::Edge.create(role: employee, permission: login_perm)
315
+ SuperAuth::Edge.create(permission: login_perm, resource: app)
316
+
317
+ # Bethany can login to app -- the path flows through Company -> Employee -> login -> app
318
+ ```
319
+
320
+ ## Querying Authorizations
321
+
322
+ ### Get all authorizations
323
+
324
+ ```ruby
325
+ authorizations = SuperAuth::Edge.authorizations.all
326
+ ```
327
+
328
+ Each row contains the full path:
329
+
330
+ ```ruby
331
+ auth = authorizations.first
332
+ auth[:user_id] # Integer
333
+ auth[:user_name] # "Peter"
334
+ auth[:group_id] # Integer (0 if no group in path)
335
+ auth[:group_name] # "Backend" or nil
336
+ auth[:group_path] # "1,2,3" (comma-separated group IDs)
337
+ auth[:group_name_path] # "Company,Engineering,Backend"
338
+ auth[:role_id] # Integer (0 if no role in path)
339
+ auth[:role_name] # "Engineer" or nil
340
+ auth[:role_path] # "1,2" (comma-separated role IDs)
341
+ auth[:role_name_path] # "Employee,Engineer"
342
+ auth[:permission_id] # Integer (0 if no permission in path)
343
+ auth[:permission_name] # "read" or nil
344
+ auth[:resource_id] # Integer
345
+ auth[:resource_name] # "staging"
346
+ ```
347
+
348
+ ### Filter by user
349
+
350
+ ```ruby
351
+ peter_auths = SuperAuth::Edge.authorizations.all.select { |a| a[:user_id] == peter.id }
352
+ ```
353
+
354
+ ### Check specific access
355
+
356
+ ```ruby
357
+ auths = SuperAuth::Edge.authorizations.all
358
+
359
+ can_deploy = auths.any? { |a|
360
+ a[:user_id] == peter.id &&
361
+ a[:resource_name] == "staging" &&
362
+ a[:permission_name] == "deploy"
363
+ }
364
+ ```
365
+
366
+ ### Query individual strategies
367
+
368
+ ```ruby
369
+ SuperAuth::Edge.users_groups_roles_permissions_resources # Strategy 1
370
+ SuperAuth::Edge.users_roles_permissions_resources # Strategy 2
371
+ SuperAuth::Edge.users_groups_permissions_resources # Strategy 3
372
+ SuperAuth::Edge.users_permissions_resources # Strategy 4
373
+ SuperAuth::Edge.users_resources # Strategy 5
374
+ ```
375
+
376
+ ## Revoking Access
377
+
378
+ Delete the edge to revoke access. All authorization paths that flowed through that edge are immediately removed.
379
+
380
+ ```ruby
381
+ # Find and destroy the edge
382
+ edge = SuperAuth::Edge.where(user_id: guest.id, group_id: customers.id).first
383
+ edge.destroy
384
+
385
+ # Authorizations are recomputed -- guest no longer has access through that group
386
+ ```
387
+
388
+ ## Rails Integration
389
+
390
+ ### How it works
391
+
392
+ SuperAuth uses [Sequel](https://sequel.jeremyevans.net/) internally for its graph query engine and ships a set of ActiveRecord adapters so it integrates seamlessly with your Rails app. The `sequel-activerecord_connection` gem lets both ORMs share the same database connection, so there is nothing extra to configure.
393
+
394
+ On boot, the SuperAuth Railtie:
395
+
396
+ 1. Detects your ActiveRecord database configuration
397
+ 2. Creates a matching Sequel connection (shared via `sequel-activerecord_connection`)
398
+ 3. Loads all SuperAuth models (both Sequel and ActiveRecord)
399
+
400
+ ### ActiveRecord auto-filtering
401
+
402
+ Add `super_auth` to any model to automatically filter records based on the current user's authorizations:
403
+
404
+ ```ruby
405
+ class Post < ApplicationRecord
406
+ super_auth
407
+ end
408
+ ```
409
+
410
+ Set the current user in your controller (see [Rails Setup](#rails-setup) Step 4):
411
+
412
+ ```ruby
413
+ class ApplicationController < ActionController::Base
414
+ before_action :set_super_auth_user
415
+
416
+ private
417
+
418
+ def set_super_auth_user
419
+ SuperAuth.current_user = current_user
420
+ end
421
+ end
422
+ ```
423
+
424
+ Now queries are automatically scoped:
425
+
426
+ ```ruby
427
+ # Only returns posts the current user is authorized to access
428
+ Post.all
429
+ Post.where(published: true)
430
+ ```
431
+
432
+ The system user bypasses all filters:
433
+
434
+ ```ruby
435
+ SuperAuth.current_user = SuperAuth::User.system
436
+ Post.all # Returns all posts
437
+ ```
438
+
439
+ ### Linking to your app's models
440
+
441
+ Connect SuperAuth entities to your ActiveRecord models via `external_id` and `external_type`:
442
+
443
+ ```ruby
444
+ # Link a SuperAuth user to your app's User model
445
+ sa_user = SuperAuth::User.create(
446
+ name: user.name,
447
+ external_id: user.id,
448
+ external_type: "User"
449
+ )
450
+
451
+ # Link a SuperAuth resource to your app's Post model
452
+ sa_resource = SuperAuth::Resource.create(
453
+ name: "posts",
454
+ external_type: "Post"
455
+ )
456
+ ```
457
+
458
+ When `super_auth` is included in a model, the default scope matches the current user's `id` and class name against `external_id` / `external_type` in the authorizations table. This means your application user objects work directly -- no need to convert to SuperAuth users in the controller.
459
+
460
+ ### ActiveRecord models
461
+
462
+ SuperAuth provides ActiveRecord-compatible models under the `SuperAuth::ActiveRecord` namespace:
463
+
464
+ ```ruby
465
+ SuperAuth::ActiveRecord::User
466
+ SuperAuth::ActiveRecord::Group
467
+ SuperAuth::ActiveRecord::Role
468
+ SuperAuth::ActiveRecord::Permission
469
+ SuperAuth::ActiveRecord::Resource
470
+ SuperAuth::ActiveRecord::Edge
471
+ SuperAuth::ActiveRecord::Authorization
472
+ ```
473
+
474
+ These models point to the same `super_auth_*` tables and can be used alongside the Sequel models. The ActiveRecord Edge model delegates authorization queries to the Sequel engine:
475
+
476
+ ```ruby
477
+ # Works the same as SuperAuth::Edge.authorizations, but returns ActiveRecord objects
478
+ SuperAuth::ActiveRecord::Edge.authorizations
479
+ ```
480
+
481
+ ## Auditing
482
+
483
+ Every authorization path is stored with full context. This makes it straightforward to answer questions like:
484
+
485
+ **"Why does Peter have access to the design template?"**
486
+
487
+ ```ruby
488
+ auths = SuperAuth::Edge.authorizations.all
489
+ peter_design = auths.select { |a|
490
+ a[:user_id] == peter.id && a[:resource_name] == "core_design_template"
491
+ }
492
+
493
+ peter_design.each do |auth|
494
+ puts "#{auth[:user_name]} -> #{auth[:group_name]} -> #{auth[:role_name]} -> #{auth[:permission_name]} -> #{auth[:resource_name]}"
495
+ end
496
+ # Peter -> Frontend -> Engineering -> create -> core_design_template
497
+ # Peter -> Frontend -> Engineering -> read -> core_design_template
498
+ # Peter -> Frontend -> Engineering -> update -> core_design_template
499
+ # Peter -> Frontend -> Engineering -> delete -> core_design_template
500
+ ```
501
+
502
+ **"Who can deploy to staging?"**
503
+
504
+ ```ruby
505
+ deployers = SuperAuth::Edge.authorizations.all.select { |a|
506
+ a[:resource_name] == "staging" && a[:permission_name] == "deploy"
507
+ }
508
+ deployers.map { |a| a[:user_name] }.uniq
509
+ ```
510
+
511
+ ## Visualization
512
+
513
+ SuperAuth includes an interactive graph visualization UI. After mounting the engine:
514
+
515
+ ```ruby
516
+ # config/routes.rb
517
+ mount SuperAuth::Engine => '/super_auth'
518
+ ```
519
+
520
+ Load sample data (optional):
521
+
522
+ ```bash
523
+ rails runner "load File.join(SuperAuth::Engine.root, 'db/seeds/sample_data.rb')"
524
+ ```
525
+
526
+ Visit `http://localhost:3000/super_auth/visualization` to see:
527
+
528
+ - Color-coded nodes for each entity type
529
+ - Interactive path finding
530
+ - Real-time authorization queries
531
+
532
+ ## Full Example
533
+
534
+ Here's a complete example modeling a company with departments, roles, and resources:
535
+
536
+ ```ruby
537
+ # Organization
538
+ company = SuperAuth::Group.create(name: "Acme Corp")
539
+ engineering = SuperAuth::Group.create(name: "Engineering", parent: company)
540
+ backend = SuperAuth::Group.create(name: "Backend", parent: engineering)
541
+ frontend = SuperAuth::Group.create(name: "Frontend", parent: engineering)
542
+ sales = SuperAuth::Group.create(name: "Sales", parent: company)
543
+
544
+ # Roles
545
+ developer = SuperAuth::Role.create(name: "Developer")
546
+ senior_dev = SuperAuth::Role.create(name: "Senior Developer", parent: developer)
547
+ ops = SuperAuth::Role.create(name: "Operations", parent: developer)
548
+
549
+ # Permissions
550
+ read = SuperAuth::Permission.create(name: "read")
551
+ write = SuperAuth::Permission.create(name: "write")
552
+ deploy = SuperAuth::Permission.create(name: "deploy")
553
+
554
+ # Resources
555
+ api = SuperAuth::Resource.create(name: "api", external_type: "API")
556
+ dashboard = SuperAuth::Resource.create(name: "dashboard", external_type: "Dashboard")
557
+ prod_db = SuperAuth::Resource.create(name: "production_db")
558
+
559
+ # Users
560
+ alice = SuperAuth::User.create(name: "Alice") # Senior backend dev
561
+ bob = SuperAuth::User.create(name: "Bob") # Frontend dev
562
+ carol = SuperAuth::User.create(name: "Carol") # Ops
563
+
564
+ # Assign users to groups
565
+ SuperAuth::Edge.create(user: alice, group: backend)
566
+ SuperAuth::Edge.create(user: bob, group: frontend)
567
+ SuperAuth::Edge.create(user: carol, group: backend)
568
+
569
+ # Assign roles to groups
570
+ SuperAuth::Edge.create(group: backend, role: senior_dev)
571
+ SuperAuth::Edge.create(group: frontend, role: developer)
572
+
573
+ # Give Carol ops role directly
574
+ SuperAuth::Edge.create(user: carol, role: ops)
575
+
576
+ # Assign permissions to roles
577
+ SuperAuth::Edge.create(role: developer, permission: read)
578
+ SuperAuth::Edge.create(role: developer, permission: write)
579
+ SuperAuth::Edge.create(role: ops, permission: deploy)
580
+
581
+ # Assign permissions to resources
582
+ SuperAuth::Edge.create(permission: read, resource: api)
583
+ SuperAuth::Edge.create(permission: write, resource: api)
584
+ SuperAuth::Edge.create(permission: read, resource: dashboard)
585
+ SuperAuth::Edge.create(permission: deploy, resource: api)
586
+ SuperAuth::Edge.create(permission: deploy, resource: prod_db)
587
+
588
+ # Now query:
589
+ auths = SuperAuth::Edge.authorizations.all
590
+
591
+ # Alice can read and write the API (via Backend -> Senior Developer -> read/write -> api)
592
+ # Bob can read and write the API (via Frontend -> Developer -> read/write -> api)
593
+ # Bob can read the dashboard (via Frontend -> Developer -> read -> dashboard)
594
+ # Carol can deploy to the API and prod_db (via direct ops role -> deploy)
595
+ # Carol can also read/write the API (via Backend -> Senior Developer -> read/write -> api)
596
+ ```
597
+
598
+ ## Configuration Reference
599
+
600
+ | Method | Description |
601
+ |---------------------------------|------------------------------------------------------|
602
+ | `SuperAuth.load` | Load all SuperAuth models |
603
+ | `SuperAuth.db` | Access the Sequel database connection |
604
+ | `SuperAuth.db = connection` | Set a custom Sequel database connection |
605
+ | `SuperAuth.current_user = user` | Set the current user (required for AR auto-filtering)|
606
+ | `SuperAuth.current_user` | Get the current user |
607
+ | `SuperAuth.install_migrations` | Create all `super_auth_*` tables |
608
+ | `SuperAuth.uninstall_migrations`| Drop all `super_auth_*` tables |
609
+
610
+ ### Environment Variables
611
+
612
+ | Variable | Description |
613
+ |----------------------------|-------------------------------------------|
614
+ | `SUPER_AUTH_DATABASE_URL` | Database connection string (non-Rails) |
615
+ | `SUPER_AUTH_LOG_LEVEL` | Set to `"debug"` for SQL query logging |
616
+
617
+ ## License
618
+
619
+ SuperAuth is available as open source under the [GPL License](https://www.gnu.org/licenses/quick-guide-gplv3.html).
data/VISUALIZATION.md ADDED
@@ -0,0 +1,58 @@
1
+ # SuperAuth Graph Visualization
2
+
3
+ SuperAuth includes an interactive graph visualization tool that helps you understand and debug your authorization rules.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Run the Installer
8
+
9
+ Generate the initializer and install migrations:
10
+
11
+ ```bash
12
+ rails generate super_auth:install
13
+ ```
14
+
15
+ This will:
16
+ - Create `config/initializers/super_auth.rb`
17
+ - Install SuperAuth database migrations
18
+ - Show you the next steps
19
+
20
+ ### 2. Mount the Engine
21
+
22
+ Add the following to your `config/routes.rb`:
23
+
24
+ ```ruby
25
+ Rails.application.routes.draw do
26
+ mount SuperAuth::Engine => '/super_auth'
27
+
28
+ # Your other routes...
29
+ end
30
+ ```
31
+
32
+ ## Features
33
+
34
+ ### Interactive Graph
35
+
36
+ - **Nodes**: Color-coded by type (Users, Groups, Roles, Permissions, Resources)
37
+ - **Edges**: Solid lines for authorization relationships, dashed for hierarchy
38
+ - **Zoom & Pan**: Navigate large graphs easily
39
+ - **Click nodes**: View node details
40
+
41
+ ### Authorization Query
42
+
43
+ 1. Select a user from the dropdown
44
+ 2. Select a resource from the dropdown
45
+ 3. Click "Find Authorization Paths"
46
+ 4. View all paths that grant access
47
+ 5. See the first path highlighted on the graph
48
+
49
+ ### Statistics Panel
50
+
51
+ Real-time counts of:
52
+ - Users
53
+ - Groups (with hierarchical relationships)
54
+ - Roles (with hierarchical relationships)
55
+ - Permissions
56
+ - Resources
57
+ - Authorization edges
58
+