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/config/routes.rb ADDED
@@ -0,0 +1,73 @@
1
+ SuperAuth::Engine.routes.draw do
2
+ # Main graph visualization interface
3
+ get '/', to: 'graph#index', as: :root
4
+ get '/graph', to: 'graph#index'
5
+
6
+ # Graph data API
7
+ get '/graph/data', to: 'graph#data'
8
+ get '/graph/orphaned', to: 'graph#orphaned'
9
+ post '/graph/compile_authorizations', to: 'graph#compile_authorizations'
10
+
11
+ # Authorization check
12
+ get '/graph/authorize', to: 'graph#authorize'
13
+
14
+ # Legacy visualization endpoint
15
+ get '/visualization', to: 'graph#visualization'
16
+
17
+ # CRUD operations for graph entities
18
+ scope :graph do
19
+ resources :users, only: [:create, :destroy], controller: 'graph' do
20
+ collection do
21
+ post '/', action: :create_user
22
+ end
23
+ member do
24
+ delete '/', action: :delete_user
25
+ end
26
+ end
27
+
28
+ resources :groups, only: [:create, :destroy], controller: 'graph' do
29
+ collection do
30
+ post '/', action: :create_group
31
+ end
32
+ member do
33
+ delete '/', action: :delete_group
34
+ end
35
+ end
36
+
37
+ resources :roles, only: [:create, :destroy], controller: 'graph' do
38
+ collection do
39
+ post '/', action: :create_role
40
+ end
41
+ member do
42
+ delete '/', action: :delete_role
43
+ end
44
+ end
45
+
46
+ resources :permissions, only: [:create, :destroy], controller: 'graph' do
47
+ collection do
48
+ post '/', action: :create_permission
49
+ end
50
+ member do
51
+ delete '/', action: :delete_permission
52
+ end
53
+ end
54
+
55
+ resources :graph_resources, only: [:create, :destroy], controller: 'graph', path: 'resources' do
56
+ collection do
57
+ post '/', action: :create_resource
58
+ end
59
+ member do
60
+ delete '/', action: :delete_resource
61
+ end
62
+ end
63
+
64
+ resources :edges, only: [:create, :destroy], controller: 'graph' do
65
+ collection do
66
+ post '/', action: :create_edge
67
+ end
68
+ member do
69
+ delete '/', action: :delete_edge
70
+ end
71
+ end
72
+ end
73
+ end
@@ -3,6 +3,7 @@ Sequel.migration do
3
3
  create_table(:super_auth_users) do
4
4
  primary_key :id
5
5
  String :external_id # , null: false
6
+ String :external_type # , null: false
6
7
  String :name
7
8
  DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
9
  DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
@@ -1,9 +1,16 @@
1
1
  Sequel.migration do
2
2
  up do
3
+ is_postgres = self.database_type == :postgres
4
+
3
5
  create_table(:super_auth_groups) do
4
6
  primary_key :id
5
7
  String :name, null: false
6
- foreign_key :parent_id, :super_auth_groups, deferrable: true, type: :integer
8
+ # deferrable constraints only supported in PostgreSQL
9
+ if is_postgres
10
+ foreign_key :parent_id, :super_auth_groups, deferrable: true, type: :integer
11
+ else
12
+ foreign_key :parent_id, :super_auth_groups, type: :integer
13
+ end
7
14
  DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
15
  DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
9
16
  end
@@ -1,9 +1,16 @@
1
1
  Sequel.migration do
2
2
  up do
3
+ is_postgres = self.database_type == :postgres
4
+
3
5
  create_table(:super_auth_roles) do
4
6
  primary_key :id
5
7
  String :name, null: false
6
- foreign_key :parent_id, :super_auth_roles, deferrable: true, type: :integer
8
+ # deferrable constraints only supported in PostgreSQL
9
+ if is_postgres
10
+ foreign_key :parent_id, :super_auth_roles, deferrable: true, type: :integer
11
+ else
12
+ foreign_key :parent_id, :super_auth_roles, type: :integer
13
+ end
7
14
  DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
15
  DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
9
16
  end
@@ -4,6 +4,7 @@ Sequel.migration do
4
4
  primary_key :id
5
5
  String :name
6
6
  String :external_id # , null: false
7
+ String :external_type # , null: false
7
8
  DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
9
  DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
9
10
  end
@@ -4,6 +4,7 @@ Sequel.migration do
4
4
  Integer :user_id, null: true
5
5
  String :user_name, null: true
6
6
  String :user_external_id, null: true
7
+ String :user_external_type, null: true
7
8
  DateTime :user_created_at, null: true
8
9
  DateTime :user_updated_at, null: true
9
10
  Integer :group_id, null: true
@@ -28,6 +29,7 @@ Sequel.migration do
28
29
  Integer :resource_id, null: true
29
30
  String :resource_name, null: true
30
31
  String :resource_external_id, null: true
32
+ String :resource_external_type, null: true
31
33
  DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
32
34
  DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
33
35
  end
@@ -0,0 +1,17 @@
1
+ Sequel.migration do
2
+ up do
3
+ add_index :super_auth_edges, :user_id
4
+ add_index :super_auth_edges, :group_id
5
+ add_index :super_auth_edges, :role_id
6
+ add_index :super_auth_edges, :permission_id
7
+ add_index :super_auth_edges, :resource_id
8
+ end
9
+
10
+ down do
11
+ drop_index :super_auth_edges, :user_id
12
+ drop_index :super_auth_edges, :group_id
13
+ drop_index :super_auth_edges, :role_id
14
+ drop_index :super_auth_edges, :permission_id
15
+ drop_index :super_auth_edges, :resource_id
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ up do
3
+ add_index :super_auth_authorizations,
4
+ [:user_external_id, :resource_external_type, :resource_external_id],
5
+ name: :idx_sa_auth_by_current_user
6
+ end
7
+ down do
8
+ drop_index :super_auth_authorizations,
9
+ [:user_external_id, :resource_external_type, :resource_external_id],
10
+ name: :idx_sa_auth_by_current_user
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSuperAuthUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_users do |t|
4
+ t.string :external_id
5
+ t.string :external_type
6
+ t.string :name
7
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ class CreateSuperAuthGroups < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_groups do |t|
4
+ t.string :name, null: false
5
+ t.bigint :parent_id
6
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
7
+ end
8
+
9
+ add_foreign_key :super_auth_groups, :super_auth_groups, column: :parent_id
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class CreateSuperAuthPermissions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_permissions do |t|
4
+ t.string :name, null: false
5
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ class CreateSuperAuthRoles < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_roles do |t|
4
+ t.string :name, null: false
5
+ t.bigint :parent_id
6
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
7
+ end
8
+
9
+ add_foreign_key :super_auth_roles, :super_auth_roles, column: :parent_id
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSuperAuthResources < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_resources do |t|
4
+ t.string :name
5
+ t.string :external_id
6
+ t.string :external_type
7
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class CreateSuperAuthEdges < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_edges do |t|
4
+ t.references :user, foreign_key: { to_table: :super_auth_users }, null: true
5
+ t.references :group, foreign_key: { to_table: :super_auth_groups }, null: true
6
+ t.references :permission, foreign_key: { to_table: :super_auth_permissions }, null: true
7
+ t.references :role, foreign_key: { to_table: :super_auth_roles }, null: true
8
+ t.references :resource, foreign_key: { to_table: :super_auth_resources }, null: true
9
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ class CreateSuperAuthAuthorizations < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :super_auth_authorizations do |t|
4
+ t.integer :user_id
5
+ t.string :user_name
6
+ t.string :user_external_id
7
+ t.string :user_external_type
8
+ t.datetime :user_created_at
9
+ t.datetime :user_updated_at
10
+
11
+ t.integer :group_id
12
+ t.string :group_name
13
+ t.string :group_path
14
+ t.string :group_name_path
15
+ t.string :group_parent_name
16
+ t.string :group_parent_id
17
+ t.datetime :group_created_at
18
+ t.datetime :group_updated_at
19
+
20
+ t.integer :role_id
21
+ t.string :role_name
22
+ t.string :role_path
23
+ t.string :role_name_path
24
+ t.string :role_parent_id
25
+ t.datetime :role_created_at
26
+ t.datetime :role_updated_at
27
+
28
+ t.integer :permission_id
29
+ t.string :permission_name
30
+ t.datetime :permission_created_at
31
+ t.datetime :permission_updated_at
32
+
33
+ t.integer :resource_id
34
+ t.string :resource_name
35
+ t.string :resource_external_id
36
+ t.string :resource_external_type
37
+
38
+ t.timestamps default: -> { "CURRENT_TIMESTAMP" }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ class AddByCurrentUserIndexToSuperAuthAuthorizations < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_index :super_auth_authorizations,
4
+ [:user_external_id, :resource_external_type, :resource_external_id],
5
+ name: :idx_sa_auth_by_current_user
6
+ end
7
+ end
@@ -0,0 +1,193 @@
1
+ # Sample data based on the README examples
2
+ # This seed file creates a complete authorization graph for demonstration
3
+
4
+ puts "Creating sample data for SuperAuth..."
5
+
6
+ # Initialize SuperAuth
7
+ require 'super_auth'
8
+ SuperAuth.load
9
+
10
+ # Detect which ORM we're using
11
+ if defined?(SuperAuth::ActiveRecord::User)
12
+ User = SuperAuth::ActiveRecord::User
13
+ Group = SuperAuth::ActiveRecord::Group
14
+ Role = SuperAuth::ActiveRecord::Role
15
+ Permission = SuperAuth::ActiveRecord::Permission
16
+ Resource = SuperAuth::ActiveRecord::Resource
17
+ Edge = SuperAuth::ActiveRecord::Edge
18
+ else
19
+ User = SuperAuth::User
20
+ Group = SuperAuth::Group
21
+ Role = SuperAuth::Role
22
+ Permission = SuperAuth::Permission
23
+ Resource = SuperAuth::Resource
24
+ Edge = SuperAuth::Edge
25
+ end
26
+
27
+ # Clear existing data (optional - comment out if you don't want this)
28
+ puts "Clearing existing data..."
29
+ Edge.delete_all
30
+ Group.update_all(parent_id: nil)
31
+ Role.update_all(parent_id: nil)
32
+ User.delete_all
33
+ Group.delete_all
34
+ Role.delete_all
35
+ Permission.delete_all
36
+ Resource.delete_all
37
+
38
+ # Create users
39
+ puts "Creating users..."
40
+ peter = User.create!(name: 'Peter')
41
+ michael = User.create!(name: 'Michael')
42
+ bethany = User.create!(name: 'Bethany')
43
+ eloise = User.create!(name: 'Eloise')
44
+ anna = User.create!(name: 'Anna')
45
+ dillon = User.create!(name: 'Dillon')
46
+ guest = User.create!(name: 'Guest')
47
+
48
+ # Create group hierarchy
49
+ puts "Creating groups..."
50
+ company = Group.create!(name: 'Company')
51
+ engineering_dept = Group.create!(name: 'Engineering_dept', parent: company)
52
+ backend = Group.create!(name: 'Backend', parent: engineering_dept)
53
+ frontend = Group.create!(name: 'Frontend', parent: engineering_dept)
54
+ sales_department = Group.create!(name: 'Sales Department', parent: company)
55
+ marketing_department = Group.create!(name: 'Marketing Department', parent: company)
56
+ customers = Group.create!(name: 'Customers')
57
+ customer_a = Group.create!(name: 'CustomerA', parent: customers)
58
+ customer_b = Group.create!(name: 'CustomerB', parent: customers)
59
+ vendors = Group.create!(name: 'Vendors')
60
+ vendor_a = Group.create!(name: 'VendorA', parent: vendors)
61
+ vendor_b = Group.create!(name: 'VendorB', parent: vendors)
62
+
63
+ # Create role hierarchy
64
+ puts "Creating roles..."
65
+ employee = Role.create!(name: 'Employee')
66
+ engineering = Role.create!(name: 'Engineering', parent: employee)
67
+ senor_software_dev = Role.create!(name: 'Señor Software Developer', parent: engineering)
68
+ senor_designer = Role.create!(name: 'Señor Designer', parent: engineering)
69
+ software_developer = Role.create!(name: 'Software Developer', parent: engineering)
70
+ production_support = Role.create!(name: 'Production Support', parent: engineering)
71
+ sales_and_marketing = Role.create!(name: 'Sales and Marketing', parent: employee)
72
+ marketing_manager = Role.create!(name: 'Marketing Manager', parent: sales_and_marketing)
73
+ marketing_associate = Role.create!(name: 'Marketing Associate', parent: sales_and_marketing)
74
+ customer_role = Role.create!(name: 'CustomerRole')
75
+
76
+ # Create permissions
77
+ puts "Creating permissions..."
78
+ create_perm = Permission.create!(name: 'create')
79
+ read_perm = Permission.create!(name: 'read')
80
+ update_perm = Permission.create!(name: 'update')
81
+ delete_perm = Permission.create!(name: 'delete')
82
+ invoice_perm = Permission.create!(name: 'invoice')
83
+ login_perm = Permission.create!(name: 'login')
84
+ reboot_perm = Permission.create!(name: 'reboot')
85
+ deploy_perm = Permission.create!(name: 'deploy')
86
+ sign_contract_perm = Permission.create!(name: 'sign_contract')
87
+ subscribe_perm = Permission.create!(name: 'subscribe')
88
+ unsubscribe_perm = Permission.create!(name: 'unsubscribe')
89
+ publish_design_perm = Permission.create!(name: 'publish_design')
90
+
91
+ # Create resources
92
+ puts "Creating resources..."
93
+ app1 = Resource.create!(name: 'app1')
94
+ app2 = Resource.create!(name: 'app2')
95
+ staging = Resource.create!(name: 'staging')
96
+ db1 = Resource.create!(name: 'db1')
97
+ db2 = Resource.create!(name: 'db2')
98
+ core_design_template = Resource.create!(name: 'core_design_template')
99
+ customer_profile = Resource.create!(name: 'customer_profile')
100
+ marketing_website = Resource.create!(name: 'marketing_website')
101
+ customer_post1 = Resource.create!(name: 'customer_post1')
102
+ customer_post2 = Resource.create!(name: 'customer_post2')
103
+ customer_post3 = Resource.create!(name: 'customer_post3')
104
+
105
+ # Create edges (authorization relationships)
106
+ puts "Creating authorization edges..."
107
+
108
+ # Example 1: Peter can access core_design_template through Frontend -> Engineering -> CRUD
109
+ Edge.create!(user: peter, group: frontend)
110
+ Edge.create!(group: frontend, role: engineering)
111
+ Edge.create!(role: engineering, permission: create_perm)
112
+ Edge.create!(role: engineering, permission: read_perm)
113
+ Edge.create!(role: engineering, permission: update_perm)
114
+ Edge.create!(role: engineering, permission: delete_perm)
115
+ Edge.create!(resource: core_design_template, permission: create_perm)
116
+ Edge.create!(resource: core_design_template, permission: read_perm)
117
+ Edge.create!(resource: core_design_template, permission: update_perm)
118
+ Edge.create!(resource: core_design_template, permission: delete_perm)
119
+
120
+ # Example 2: Michael can deploy to app1 through Backend -> Production Support
121
+ Edge.create!(user: michael, group: backend)
122
+ Edge.create!(group: backend, role: production_support)
123
+ Edge.create!(role: production_support, permission: deploy_perm)
124
+ Edge.create!(permission: deploy_perm, resource: app1)
125
+
126
+ # Example 3: Anna can read customer posts through CustomerA -> CustomerRole
127
+ Edge.create!(user: anna, group: customer_a)
128
+ Edge.create!(group: customer_a, role: customer_role)
129
+ Edge.create!(role: customer_role, permission: read_perm)
130
+ Edge.create!(permission: read_perm, resource: customer_post1)
131
+ Edge.create!(permission: read_perm, resource: customer_post2)
132
+
133
+ # Additional scenarios for demonstration
134
+
135
+ # Bethany has multiple authorization paths
136
+ Edge.create!(user: bethany, group: engineering_dept)
137
+ Edge.create!(group: engineering_dept, role: engineering)
138
+ Edge.create!(permission: read_perm, resource: staging)
139
+
140
+ # Bethany also has direct role assignment
141
+ Edge.create!(user: bethany, role: production_support)
142
+ Edge.create!(permission: deploy_perm, resource: staging)
143
+
144
+ # Dillon has direct resource access (simplest path)
145
+ Edge.create!(user: dillon, resource: db1)
146
+
147
+ # Eloise has user -> permission -> resource path
148
+ Edge.create!(user: eloise, permission: reboot_perm)
149
+ Edge.create!(permission: reboot_perm, resource: db2)
150
+
151
+ # Marketing department scenario
152
+ Edge.create!(user: anna, group: marketing_department)
153
+ Edge.create!(group: marketing_department, role: marketing_associate)
154
+ Edge.create!(role: marketing_associate, permission: subscribe_perm)
155
+ Edge.create!(role: marketing_associate, permission: unsubscribe_perm)
156
+ Edge.create!(permission: subscribe_perm, resource: customer_profile)
157
+ Edge.create!(permission: unsubscribe_perm, resource: customer_profile)
158
+
159
+ # Vendor access
160
+ Edge.create!(user: michael, group: vendor_a)
161
+ Edge.create!(group: vendor_a, permission: invoice_perm)
162
+ Edge.create!(permission: invoice_perm, resource: app1)
163
+
164
+ # Company-wide access
165
+ Edge.create!(user: bethany, group: company)
166
+ Edge.create!(group: company, role: employee)
167
+ Edge.create!(role: employee, permission: login_perm)
168
+ Edge.create!(permission: login_perm, resource: app2)
169
+
170
+ # Senior Designer role
171
+ Edge.create!(user: peter, group: frontend) # Already created above, but illustrating
172
+ Edge.create!(group: frontend, role: senor_designer)
173
+ Edge.create!(role: senor_designer, permission: publish_design_perm)
174
+ Edge.create!(permission: publish_design_perm, resource: marketing_website)
175
+
176
+ # Backend developers sharing permissions
177
+ Edge.create!(user: dillon, group: backend)
178
+ Edge.create!(group: backend, role: software_developer)
179
+ Edge.create!(role: software_developer, permission: update_perm)
180
+ Edge.create!(permission: update_perm, resource: staging)
181
+
182
+ # Senior software developer with multiple permissions
183
+ # (Michael already has vendor_a group)
184
+ Edge.create!(group: backend, role: senor_software_dev)
185
+ Edge.create!(role: senor_software_dev, permission: read_perm)
186
+ Edge.create!(role: senor_software_dev, permission: update_perm)
187
+ Edge.create!(role: senor_software_dev, permission: deploy_perm)
188
+ # Resources already connected above
189
+
190
+ puts "Sample data created successfully!"
191
+ puts "#{User.count} users, #{Group.count} groups, #{Role.count} roles"
192
+ puts "#{Permission.count} permissions, #{Resource.count} resources"
193
+ puts "#{Edge.count} authorization edges"
data/lib/basic_loader.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  # File generated automatically, do not edit
4
2
  # See https://blog.pawelpokrywka.com/p/gem-with-zeitwerk-as-development-only-dependency
5
3
 
@@ -0,0 +1,19 @@
1
+ require 'rails/generators'
2
+
3
+ module SuperAuth
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ desc "Creates a SuperAuth initializer"
9
+
10
+ def copy_initializer
11
+ template 'super_auth.rb', 'config/initializers/super_auth.rb'
12
+ end
13
+
14
+ def show_readme
15
+ readme 'README' if behavior == :invoke
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ ===============================================================================
2
+
3
+ SuperAuth initializer created at config/initializers/super_auth.rb
4
+
5
+ Next steps:
6
+
7
+ 1. Run migrations to create the database tables:
8
+
9
+ SuperAuth.install_migrations
10
+
11
+ You can run this in the Rails console or add it to a rake task.
12
+
13
+ 2. Mount the engine in config/routes.rb:
14
+
15
+ mount SuperAuth::Engine => '/super_auth'
16
+
17
+ 3. (Optional) Load sample data to see the visualization in action:
18
+
19
+ rails runner "load File.join(SuperAuth::Engine.root, 'db/seeds/sample_data.rb')"
20
+
21
+ 4. Start your Rails server and visit:
22
+
23
+ http://localhost:3000/super_auth/visualization
24
+
25
+ For more information, see:
26
+ - VISUALIZATION.md for full documentation
27
+ - README.md for usage examples
28
+
29
+ ===============================================================================
@@ -0,0 +1,7 @@
1
+ # SuperAuth Initializer
2
+ # The SuperAuth Railtie automatically connects to your database and loads all
3
+ # models on boot. Use this file for any additional configuration.
4
+ #
5
+ # SuperAuth.setup do |config|
6
+ # # configuration options here
7
+ # end
@@ -1,24 +1,39 @@
1
1
  module SuperAuth::ActiveRecord::ByCurrentUser
2
2
  def self.included(base)
3
- base.has_many :super_auth_authorizations
4
-
5
3
  base.send(:default_scope, **{all_queries: true}) do
6
- raise "SuperAuth.current_user not set" if SuperAuth.current_user.blank?
4
+ next none if SuperAuth.current_user.blank?
7
5
 
8
- if SuperAuth.current_user.system?
6
+ if SuperAuth.current_user.respond_to?(:system?) && SuperAuth.current_user.system?
9
7
  self
10
8
  else
11
- # Important:
12
- # We use a subquery here instead of a inner join because we don't want
13
- # to potentially affect break on queries issue count queries in their app.
14
- where(id: SuperAuth::ActiveRecord::Authorization.where(super_auth_user_id: SuperAuth.current_user.id).select(:resource_id))
9
+ user_where =
10
+ if SuperAuth.current_user.is_a?(SuperAuth::ActiveRecord::User)
11
+ { user_id: SuperAuth.current_user.id }
12
+ else
13
+ { user_external_id: SuperAuth.current_user.id, user_external_type: SuperAuth.current_user.class.name }
14
+ end
15
+
16
+ resource_type = self.model.name
17
+
18
+ # Type-level authorization (resource_external_id IS NULL) acts as wildcard:
19
+ # user has access to ALL records of this type (e.g., admin with ADMIN_ACCESS).
20
+ type_level = SuperAuth::ActiveRecord::Authorization
21
+ .where(**user_where, resource_external_type: resource_type, resource_external_id: nil)
22
+
23
+ if type_level.exists?
24
+ self
25
+ else
26
+ # Per-record authorization: filter to specific records the user can access.
27
+ where(
28
+ id: SuperAuth::ActiveRecord::Authorization
29
+ .where(**user_where, resource_external_type: resource_type)
30
+ .where.not(resource_external_id: nil)
31
+ .select(:resource_external_id))
32
+ end
15
33
  end
16
34
  end
17
35
  end
18
36
 
19
- def system? = false
20
-
21
37
  module ClassMethods
22
38
  end
23
-
24
39
  end
@@ -1,3 +1,48 @@
1
1
  class SuperAuth::ActiveRecord::Edge < ActiveRecord::Base
2
2
  self.table_name = 'super_auth_edges'
3
+ belongs_to :user, class_name: 'SuperAuth::ActiveRecord::User', optional: true
4
+ belongs_to :group, class_name: 'SuperAuth::ActiveRecord::Group', optional: true
5
+ belongs_to :permission, class_name: 'SuperAuth::ActiveRecord::Permission', optional: true
6
+ belongs_to :role, class_name: 'SuperAuth::ActiveRecord::Role', optional: true
7
+ belongs_to :resource, class_name: 'SuperAuth::ActiveRecord::Resource', optional: true
8
+
9
+ class << self
10
+ def authorizations
11
+ from("(#{
12
+ SuperAuth::Edge.authorizations.sql
13
+ }) as super_auth_edges".squish
14
+ )
15
+ end
16
+
17
+ def users_resources
18
+ SuperAuth::ActiveRecord::Edge.from(
19
+ %Q[(#{SuperAuth::Edge.users_resources.sql}) as super_auth_edges]
20
+ )
21
+ end
22
+
23
+ def users_groups_roles_permissions_resources
24
+ SuperAuth::ActiveRecord::Edge.from(
25
+ %Q[(#{SuperAuth::Edge.users_groups_roles_permissions_resources.sql}) as super_auth_edges]
26
+ )
27
+ end
28
+
29
+ def users_groups_permissions_resources
30
+ SuperAuth::ActiveRecord::Edge.from(
31
+ %Q[(#{SuperAuth::Edge.users_groups_permissions_resources.sql}) as super_auth_edges]
32
+ )
33
+ end
34
+
35
+ def users_roles_permissions_resources
36
+ SuperAuth::ActiveRecord::Edge.from(
37
+ %Q[(#{SuperAuth::Edge.users_roles_permissions_resources.sql}) as super_auth_edges]
38
+ )
39
+ end
40
+
41
+ def users_permissions_resources
42
+ SuperAuth::ActiveRecord::Edge.from(
43
+ %Q[(#{SuperAuth::Edge.users_permissions_resources.sql}) as super_auth_edges]
44
+ )
45
+ end
46
+ end
47
+
3
48
  end
@@ -1,3 +1,10 @@
1
1
  class SuperAuth::ActiveRecord::Group < ActiveRecord::Base
2
2
  self.table_name = 'super_auth_groups'
3
+
4
+ belongs_to :parent, class_name: 'SuperAuth::ActiveRecord::Group', optional: true
5
+
6
+ def descendants_dataset
7
+ sql = SuperAuth::Group.new(id: self.id, parent_id: self.parent_id).descendants_dataset.sql
8
+ self.class.from(%Q[(#{sql}) as super_auth_groups])
9
+ end
3
10
  end