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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +3 -7
- data/Gemfile.lock +25 -14
- data/LICENSE.txt +125 -21
- data/README.md +32 -1
- data/Rakefile +0 -2
- data/USAGE.md +619 -0
- data/VISUALIZATION.md +58 -0
- data/app/controllers/super_auth/graph_controller.rb +661 -0
- data/app/views/super_auth/graph/index.html.erb +1408 -0
- data/config/routes.rb +73 -0
- data/db/migrate/1_users.rb +1 -0
- data/db/migrate/2_groups.rb +8 -1
- data/db/migrate/4_roles.rb +8 -1
- data/db/migrate/5_resources.rb +1 -0
- data/db/migrate/7_authorization.rb +2 -0
- data/db/migrate/8_add_indexes_to_edges.rb +17 -0
- data/db/migrate/9_add_by_current_user_index.rb +12 -0
- data/db/migrate_activerecord/20250101000001_create_super_auth_users.rb +10 -0
- data/db/migrate_activerecord/20250101000002_create_super_auth_groups.rb +11 -0
- data/db/migrate_activerecord/20250101000003_create_super_auth_permissions.rb +8 -0
- data/db/migrate_activerecord/20250101000004_create_super_auth_roles.rb +11 -0
- data/db/migrate_activerecord/20250101000005_create_super_auth_resources.rb +10 -0
- data/db/migrate_activerecord/20250101000006_create_super_auth_edges.rb +12 -0
- data/db/migrate_activerecord/20250101000007_create_super_auth_authorizations.rb +41 -0
- data/db/migrate_activerecord/20250101000009_add_by_current_user_index_to_super_auth_authorizations.rb +7 -0
- data/db/seeds/sample_data.rb +193 -0
- data/lib/basic_loader.rb +0 -2
- data/lib/generators/super_auth/install/install_generator.rb +19 -0
- data/lib/generators/super_auth/install/templates/README +29 -0
- data/lib/generators/super_auth/install/templates/super_auth.rb +7 -0
- data/lib/super_auth/active_record/by_current_user.rb +26 -11
- data/lib/super_auth/active_record/edge.rb +45 -0
- data/lib/super_auth/active_record/group.rb +7 -0
- data/lib/super_auth/active_record/permission.rb +4 -0
- data/lib/super_auth/active_record/resource.rb +1 -0
- data/lib/super_auth/active_record/role.rb +7 -0
- data/lib/super_auth/active_record/user.rb +6 -0
- data/lib/super_auth/active_record.rb +17 -0
- data/lib/super_auth/edge.rb +190 -131
- data/lib/super_auth/group.rb +1 -0
- data/lib/super_auth/nestable.rb +17 -10
- data/lib/super_auth/permission.rb +1 -1
- data/lib/super_auth/railtie.rb +26 -25
- data/lib/super_auth/role.rb +2 -1
- data/lib/super_auth/user.rb +8 -8
- data/lib/super_auth/version.rb +1 -3
- data/lib/super_auth.rb +72 -40
- data/super_auth.gemspec +35 -0
- data/visualization.html +747 -0
- 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
|
data/db/migrate/1_users.rb
CHANGED
|
@@ -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
|
data/db/migrate/2_groups.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/db/migrate/4_roles.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/db/migrate/5_resources.rb
CHANGED
|
@@ -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,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,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,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,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
|
@@ -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
|
+
===============================================================================
|
|
@@ -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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|