super_auth 0.1.4 → 0.2.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +7 -10
  4. data/Gemfile.lock +53 -5
  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 +7 -4
  14. data/db/migrate/2_groups.rb +14 -3
  15. data/db/migrate/3_permissions.rb +6 -2
  16. data/db/migrate/4_roles.rb +14 -3
  17. data/db/migrate/5_resources.rb +7 -4
  18. data/db/migrate/6_edge.rb +6 -4
  19. data/db/migrate/7_authorization.rb +41 -0
  20. data/db/migrate/8_add_indexes_to_edges.rb +17 -0
  21. data/db/migrate_activerecord/20250101000001_create_super_auth_users.rb +10 -0
  22. data/db/migrate_activerecord/20250101000002_create_super_auth_groups.rb +11 -0
  23. data/db/migrate_activerecord/20250101000003_create_super_auth_permissions.rb +8 -0
  24. data/db/migrate_activerecord/20250101000004_create_super_auth_roles.rb +11 -0
  25. data/db/migrate_activerecord/20250101000005_create_super_auth_resources.rb +10 -0
  26. data/db/migrate_activerecord/20250101000006_create_super_auth_edges.rb +12 -0
  27. data/db/migrate_activerecord/20250101000007_create_super_auth_authorizations.rb +41 -0
  28. data/db/seeds/sample_data.rb +193 -0
  29. data/lib/basic_loader.rb +10 -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/authorization.rb +3 -0
  34. data/lib/super_auth/active_record/by_current_user.rb +39 -0
  35. data/lib/super_auth/active_record/edge.rb +48 -0
  36. data/lib/super_auth/active_record/group.rb +10 -0
  37. data/lib/super_auth/active_record/permission.rb +7 -0
  38. data/lib/super_auth/active_record/resource.rb +4 -0
  39. data/lib/super_auth/active_record/role.rb +10 -0
  40. data/lib/super_auth/active_record/user.rb +14 -0
  41. data/lib/super_auth/active_record.rb +20 -0
  42. data/lib/super_auth/authorization.rb +2 -0
  43. data/lib/super_auth/edge.rb +205 -92
  44. data/lib/super_auth/group.rb +1 -0
  45. data/lib/super_auth/nestable.rb +17 -10
  46. data/lib/super_auth/permission.rb +1 -1
  47. data/lib/super_auth/railtie.rb +30 -0
  48. data/lib/super_auth/role.rb +2 -1
  49. data/lib/super_auth/user.rb +14 -14
  50. data/lib/super_auth/version.rb +1 -3
  51. data/lib/super_auth.rb +103 -29
  52. data/lib/tasks/super_auth_tasks.rake +9 -8
  53. data/visualization.html +747 -0
  54. metadata +33 -6
@@ -0,0 +1,39 @@
1
+ module SuperAuth::ActiveRecord::ByCurrentUser
2
+ def self.included(base)
3
+ base.has_many :super_auth_authorizations
4
+
5
+ base.send(:default_scope, **{all_queries: true}) do
6
+ raise "SuperAuth.current_user not set" if SuperAuth.current_user.blank?
7
+
8
+ if SuperAuth.current_user.respond_to?(:system?) && SuperAuth.current_user.system?
9
+ self
10
+ else
11
+ user_where =
12
+ if SuperAuth.current_user.is_a?(SuperAuth::ActiveRecord::User)
13
+ { user_id: SuperAuth.current_user.id }
14
+ else
15
+ { user_external_id: SuperAuth.current_user.id, user_external_type: SuperAuth.current_user.class.name }
16
+ end
17
+
18
+ resource_where =
19
+ if try(:id)
20
+ { resource_external_id: self.id, resource_external_type: self.class.name }
21
+ else
22
+ { resource_external_type: self.model.name }
23
+ end
24
+
25
+ # Important:
26
+ # We use a subquery here instead of a inner join because we don't want
27
+ # to potentially affect break on queries issue count queries in their app.
28
+ where(
29
+ id: SuperAuth::ActiveRecord::Authorization
30
+ .where(**user_where, **resource_where)
31
+ .select(:resource_id))
32
+ end
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+ end
38
+
39
+ end
@@ -0,0 +1,48 @@
1
+ class SuperAuth::ActiveRecord::Edge < ActiveRecord::Base
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
+
48
+ end
@@ -0,0 +1,10 @@
1
+ class SuperAuth::ActiveRecord::Group < ActiveRecord::Base
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
10
+ end
@@ -0,0 +1,7 @@
1
+ class SuperAuth::ActiveRecord::Permission < ActiveRecord::Base
2
+ self.table_name = 'super_auth_permissions'
3
+
4
+ has_many :edges, class_name: 'SuperAuth::ActiveRecord::Edge'
5
+ scope :with_edges, -> { joins(:edges) }
6
+ scope :with_roles, -> { from(%Q[(#{SuperAuth::Permission.with_roles.sql}) as super_auth_permissions]) }
7
+ end
@@ -0,0 +1,4 @@
1
+ class SuperAuth::ActiveRecord::Resource < ActiveRecord::Base
2
+ self.table_name = 'super_auth_resources'
3
+ belongs_to :external, polymorphic: true, optional: true
4
+ end
@@ -0,0 +1,10 @@
1
+ class SuperAuth::ActiveRecord::Role < ActiveRecord::Base
2
+ self.table_name = 'super_auth_roles'
3
+
4
+ belongs_to :parent, class_name: 'SuperAuth::ActiveRecord::Role', optional: true
5
+
6
+ def descendants_dataset
7
+ sql = SuperAuth::Role.new(id: self.id, parent_id: self.parent_id).descendants_dataset.sql
8
+ self.class.from(%Q[(#{sql}) as super_auth_roles])
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class SuperAuth::ActiveRecord::User < ActiveRecord::Base
2
+ self.table_name = 'super_auth_users'
3
+
4
+ belongs_to :external, polymorphic: true, optional: true
5
+
6
+ def model_name = ActiveModel::Name.new(:user)
7
+
8
+ def system? = self.class.system == self
9
+ def self.system = find_or_create_by(name: "system")
10
+
11
+ has_many :edges, class_name: 'SuperAuth::ActiveRecord::Edge'
12
+ scope :with_edges, -> { joins(:edges) }
13
+ scope :with_groups, -> { from(%Q[(#{SuperAuth::User.with_groups.sql}) as super_auth_users]) }
14
+ end
@@ -0,0 +1,20 @@
1
+ require "active_record"
2
+ module SuperAuth::ActiveRecord
3
+ end
4
+
5
+ class ActiveRecord::Base
6
+ class << self
7
+ def super_auth
8
+ include SuperAuth::ActiveRecord::ByCurrentUser
9
+ end
10
+ end
11
+ end
12
+
13
+ require "super_auth/active_record/authorization"
14
+ require "super_auth/active_record/by_current_user"
15
+ require "super_auth/active_record/edge"
16
+ require "super_auth/active_record/group"
17
+ require "super_auth/active_record/permission"
18
+ require "super_auth/active_record/resource"
19
+ require "super_auth/active_record/role"
20
+ require "super_auth/active_record/user"
@@ -0,0 +1,2 @@
1
+ class SuperAuth::Authorization < Sequel::Model(:super_auth_authorizations)
2
+ end
@@ -1,4 +1,6 @@
1
1
  class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
2
+ plugin :dirty
3
+
2
4
  many_to_one :user
3
5
  many_to_one :group
4
6
  many_to_one :permission
@@ -6,19 +8,42 @@ class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
6
8
  many_to_one :resource
7
9
 
8
10
  class << self
11
+ def string_cast_type
12
+ case SuperAuth.db.database_type
13
+ when :mysql, :mysql2
14
+ :char
15
+ else
16
+ :text
17
+ end
18
+ end
19
+
20
+ def integer_cast_type
21
+ case SuperAuth.db.database_type
22
+ when :mysql, :mysql2
23
+ :signed
24
+ else
25
+ :bigint
26
+ end
27
+ end
9
28
 
10
29
  def authorizations
11
30
  users_groups_roles_permissions_resources
12
31
  .union(users_roles_permissions_resources)
13
32
  .union(users_groups_permissions_resources)
14
33
  .union(users_permissions_resources)
34
+ .union(users_resources)
15
35
  end
16
36
 
17
37
  def users_groups_roles_permissions_resources
18
- users_groups_roles_ds = SuperAuth::User.join(:super_auth_edges, user_id: :id).select_all(:super_auth_users).join(SuperAuth::Group.from(SuperAuth::Group.trees).as(:groups), id: :group_id).select(
38
+ cast_type = string_cast_type
39
+ users_groups_roles_ds = SuperAuth::User.join(:super_auth_edges, user_id: :id).
40
+ select_all(:super_auth_users).
41
+ join(SuperAuth::Group.from(SuperAuth::Group.trees).as(:groups), Sequel.function(:concat, ',', Sequel[:groups][:group_path], ',').like(Sequel.function(:concat, '%,', Sequel[:groups][:id], ',%'))).
42
+ select(
19
43
  Sequel[:super_auth_users][:id].as(:user_id),
20
44
  Sequel[:super_auth_users][:name].as(:user_name),
21
45
  Sequel[:super_auth_users][:external_id].as(:user_external_id),
46
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
22
47
  Sequel[:super_auth_users][:created_at].as(:user_created_at),
23
48
  Sequel[:super_auth_users][:updated_at].as(:user_updated_at),
24
49
  Sequel[:groups][:id].as(:group_id),
@@ -31,8 +56,8 @@ class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
31
56
  Sequel[:groups][:group_path],
32
57
  Sequel[:groups][:group_name_path],
33
58
  Sequel[:groups][:parent_id],
34
- Sequel[:groups][:created_at].as(:group_created_at),
35
- Sequel[:groups][:updated_at].as(:group_updated_at),
59
+ Sequel[:groups][:created_at].cast(cast_type).as(:group_created_at),
60
+ Sequel[:groups][:updated_at].cast(cast_type).as(:group_updated_at),
36
61
  ).join(Sequel[:super_auth_edges].as(:group_role_edges), Sequel[:group_role_edges][:group_id] => Sequel[:groups][:id]).select_append(
37
62
  Sequel[:group_role_edges][:id].as(:group_role_edge_id),
38
63
  Sequel[:group_role_edges][:permission_id].as(:group_role_edge_permission_id),
@@ -43,7 +68,7 @@ class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
43
68
 
44
69
  SuperAuth::Edge.from(
45
70
  SuperAuth::Edge.from(
46
- SuperAuth::Group.cte(SuperAuth::Group.where(id: users_groups_roles_ds.select(Sequel[:groups][:id])).select(:id)).select { [id.as(:group_id), name.as(:group_name), parent_id.as(:group_parent_id), group_path, group_name_path, created_at.as(:group_created_at), updated_at.as(:group_updated_at)] },
71
+ SuperAuth::Group.cte(SuperAuth::Group.where(id: users_groups_roles_ds.select(Sequel[:groups][:id])).select(:id)).select { [id.as(:group_id), name.as(:group_name), parent_id.as(:group_parent_id), group_path, group_name_path, created_at.cast(cast_type).as(:group_created_at), updated_at.as(:group_updated_at)] },
47
72
  SuperAuth::Role.cte(users_groups_roles_ds.select(Sequel[:group_role_edges][:role_id])).select { [id.as(:role_id), name.as(:role_name), parent_id.as(:role_parent_id), role_path, role_name_path, created_at.as(:role_created_at), updated_at.as(:role_updated_at) ] }
48
73
  ).as(:users_groups_roles_permissions_resources)
49
74
  ).join(Sequel[:super_auth_edges].as(:user_edges), Sequel[:user_edges][:group_id] => Sequel[:users_groups_roles_permissions_resources][:group_id])
@@ -52,33 +77,35 @@ class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
52
77
  Sequel[:super_auth_users][:id].as(:user_id),
53
78
  Sequel[:super_auth_users][:name].as(:user_name),
54
79
  Sequel[:super_auth_users][:external_id].as(:user_external_id),
55
- Sequel[:super_auth_users][:created_at].cast(:text).as(:user_created_at),
56
- Sequel[:super_auth_users][:updated_at].cast(:text).as(:user_updated_at),
80
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
81
+ Sequel[:super_auth_users][:created_at].cast(cast_type).as(:user_created_at),
82
+ Sequel[:super_auth_users][:updated_at].cast(cast_type).as(:user_updated_at),
57
83
 
58
84
  Sequel[:users_groups_roles_permissions_resources][:group_id],
59
85
  Sequel[:users_groups_roles_permissions_resources][:group_name],
60
86
  Sequel[:users_groups_roles_permissions_resources][:group_path],
61
87
  Sequel[:users_groups_roles_permissions_resources][:group_name_path],
62
88
  Sequel[:users_groups_roles_permissions_resources][:group_parent_id],
63
- Sequel[:users_groups_roles_permissions_resources][:group_created_at].cast(:text),
64
- Sequel[:users_groups_roles_permissions_resources][:group_updated_at].cast(:text),
89
+ Sequel[:users_groups_roles_permissions_resources][:group_created_at].cast(cast_type).as(:group_created_at),
90
+ Sequel[:users_groups_roles_permissions_resources][:group_updated_at].cast(cast_type).as(:group_updated_at),
65
91
 
66
92
  Sequel[:users_groups_roles_permissions_resources][:role_id],
67
93
  Sequel[:users_groups_roles_permissions_resources][:role_name],
68
94
  Sequel[:users_groups_roles_permissions_resources][:role_path],
69
95
  Sequel[:users_groups_roles_permissions_resources][:role_name_path],
70
96
  Sequel[:users_groups_roles_permissions_resources][:role_parent_id],
71
- Sequel[:users_groups_roles_permissions_resources][:role_created_at].cast(:text),
72
- Sequel[:users_groups_roles_permissions_resources][:role_updated_at].cast(:text),
97
+ Sequel[:users_groups_roles_permissions_resources][:role_created_at].cast(cast_type).as(:role_created_at),
98
+ Sequel[:users_groups_roles_permissions_resources][:role_updated_at].cast(cast_type).as(:role_updated_at),
73
99
 
74
100
  Sequel[:super_auth_permissions][:id].as(:permission_id),
75
101
  Sequel[:super_auth_permissions][:name].as(:permission_name),
76
- Sequel[:super_auth_permissions][:created_at].cast(:text).as(:permission_created_at),
77
- Sequel[:super_auth_permissions][:updated_at].cast(:text).as(:permission_updated_at),
102
+ Sequel[:super_auth_permissions][:created_at].cast(cast_type).as(:permission_created_at),
103
+ Sequel[:super_auth_permissions][:updated_at].cast(cast_type).as(:permission_updated_at),
78
104
 
79
105
  Sequel[:super_auth_resources][:id].as(:resource_id),
80
106
  Sequel[:super_auth_resources][:name].as(:resource_name),
81
- Sequel[:super_auth_resources][:external_id].as(:resource_external_id)
107
+ Sequel[:super_auth_resources][:external_id].as(:resource_external_id),
108
+ Sequel[:super_auth_resources][:external_type].as(:resource_external_type)
82
109
  )
83
110
  .join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:role_id] => Sequel[:users_groups_roles_permissions_resources][:role_id])
84
111
  .join(Sequel[:super_auth_permissions], id: Sequel[:permission_edges][:permission_id])
@@ -88,132 +115,218 @@ class SuperAuth::Edge < Sequel::Model(:super_auth_edges)
88
115
  end
89
116
 
90
117
  def users_groups_permissions_resources
91
- SuperAuth::User.
118
+ cast_type = string_cast_type
119
+ # Join users to their group via edges, then to the group CTE to get the user's group_path.
120
+ # Use group_path to find all ancestor groups (any group whose id appears in the user's group_path).
121
+ # Then join permission edges on those ancestor groups.
122
+ SuperAuth::User.db[:super_auth_users].
92
123
  join(Sequel[:super_auth_edges].as(:user_edges), user_id: :id).
93
- join(SuperAuth::Group.from(SuperAuth::Group.trees).as(:groups), id: :group_id).
124
+ join(SuperAuth::Group.from(SuperAuth::Group.trees).as(:user_groups), Sequel[:user_groups][:id] => Sequel[:user_edges][:group_id]).
125
+ join(Sequel[:super_auth_edges].as(:group_edges),
126
+ Sequel.function(:concat, ',', Sequel[:user_groups][:group_path], ',').like(
127
+ Sequel.function(:concat, '%,', Sequel[:group_edges][:group_id].cast(cast_type), ',%')
128
+ )
129
+ ).
130
+ join(Sequel[:super_auth_permissions], id: Sequel[:group_edges][:permission_id]).
131
+ join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:permission_id] => Sequel[:super_auth_permissions][:id]).
132
+ join(Sequel[:super_auth_resources], id: Sequel[:permission_edges][:resource_id]).
94
133
  select(
95
134
  Sequel[:super_auth_users][:id].as(:user_id),
96
135
  Sequel[:super_auth_users][:name].as(:user_name),
97
136
  Sequel[:super_auth_users][:external_id].as(:user_external_id),
98
- Sequel[:super_auth_users][:created_at].cast(:text).as(:user_created_at),
99
- Sequel[:super_auth_users][:updated_at].cast(:text).as(:user_updated_at),
100
-
101
- Sequel[:groups][:id].as(:group_id),
102
- Sequel[:groups][:name].as(:group_name),
103
- Sequel[:groups][:group_path],
104
- Sequel[:groups][:group_name_path],
105
- Sequel[:groups][:parent_id].as(:group_parent_id),
106
- Sequel[:groups][:created_at].cast(:text).as(:group_created_at),
107
- Sequel[:groups][:updated_at].cast(:text).as(:group_updated_at),
108
-
109
- Sequel.lit(%[0 as "role_id"]), # Sequel[:roles][:id].as(:role_id),
110
- Sequel::NULL.as(:role_name), # Sequel[:roles][:name].as(:role_name),
111
- Sequel::NULL.as(:role_path), # Sequel[:roles][:role_path],
112
- Sequel::NULL.as(:role_name_path), # Sequel[:roles][:role_name_path].as(:role_name_path),
113
- Sequel::lit(%Q[0 as "role_parent_id"]), # Sequel[:roles][:parent_id].as(:role_parent_id),
114
- Sequel::NULL.as(:role_created_at), # Sequel[:roles][:created_at].as(:role_created_at),
115
- Sequel::NULL.as(:role_updated_at), # Sequel[:roles][:updated_at].as(:role_updated_at),
137
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
138
+ Sequel[:super_auth_users][:created_at].cast(cast_type).as(:user_created_at),
139
+ Sequel[:super_auth_users][:updated_at].cast(cast_type).as(:user_updated_at),
140
+
141
+ Sequel[:user_groups][:id].as(:group_id),
142
+ Sequel[:user_groups][:name].as(:group_name),
143
+ Sequel[:user_groups][:group_path],
144
+ Sequel[:user_groups][:group_name_path],
145
+ Sequel[:user_groups][:parent_id].as(:group_parent_id),
146
+ Sequel[:user_groups][:created_at].cast(cast_type).as(:group_created_at),
147
+ Sequel[:user_groups][:updated_at].cast(cast_type).as(:group_updated_at),
148
+
149
+ Sequel.cast(nil, integer_cast_type).as(:role_id),
150
+ Sequel.cast(nil, string_cast_type).as(:role_name),
151
+ Sequel.cast(nil, string_cast_type).as(:role_path),
152
+ Sequel.cast(nil, string_cast_type).as(:role_name_path),
153
+ Sequel.cast(nil, integer_cast_type).as(:role_parent_id),
154
+ Sequel.cast(nil, string_cast_type).as(:role_created_at),
155
+ Sequel.cast(nil, string_cast_type).as(:role_updated_at),
116
156
 
117
157
  Sequel[:super_auth_permissions][:id].as(:permission_id),
118
158
  Sequel[:super_auth_permissions][:name].as(:permission_name),
119
- Sequel[:super_auth_permissions][:created_at].cast(:text).as(:permission_created_at),
120
- Sequel[:super_auth_permissions][:updated_at].cast(:text).as(:permission_updated_at),
159
+ Sequel[:super_auth_permissions][:created_at].cast(cast_type).as(:permission_created_at),
160
+ Sequel[:super_auth_permissions][:updated_at].cast(cast_type).as(:permission_updated_at),
121
161
 
122
162
  Sequel[:super_auth_resources][:id].as(:resource_id),
123
163
  Sequel[:super_auth_resources][:name].as(:resource_name),
124
164
  Sequel[:super_auth_resources][:external_id].as(:resource_external_id),
165
+ Sequel[:super_auth_resources][:external_type].as(:resource_external_type),
125
166
  ).
126
- join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:group_id] => Sequel[:groups][:id]).
127
- join(Sequel[:super_auth_permissions], id: Sequel[:permission_edges][:permission_id]).
128
- join(Sequel[:super_auth_edges].as(:resource_edges), Sequel[:resource_edges][:permission_id] => Sequel[:super_auth_permissions][:id]).
129
- join(Sequel[:super_auth_resources], id: Sequel[:resource_edges][:resource_id]).
130
167
  distinct
131
168
  end
132
169
 
133
170
  def users_roles_permissions_resources
171
+ cast_type = string_cast_type
172
+
173
+ # Step 1: Find which roles users are directly linked to via edges
174
+ user_role_ids_ds = SuperAuth::Edge.where(Sequel.~(user_id: nil) & Sequel.~(role_id: nil)).select(:role_id)
175
+
176
+ # Step 2: Expand those roles to all descendants via CTE
177
+ role_cte = SuperAuth::Role.cte(user_role_ids_ds).select {
178
+ [id.as(:role_id), name.as(:role_name), parent_id.as(:role_parent_id), role_path, role_name_path, created_at.as(:role_created_at), updated_at.as(:role_updated_at)]
179
+ }
180
+
181
+ # Step 3: Build the query from the expanded role tree
182
+ SuperAuth::Edge.from(role_cte.as(:users_roles_permissions_resources)).
183
+ # Join user_edges — match users who link to any role in the expanded CTE
184
+ # The user's edge links to an ancestor role, but the CTE path contains that ancestor
185
+ # We use the role_path to check: the role_path of the CTE row starts with the user's linked role
186
+ join(Sequel[:super_auth_edges].as(:user_edges),
187
+ Sequel.function(:concat, ',', Sequel[:users_roles_permissions_resources][:role_path], ',').like(
188
+ Sequel.function(:concat, '%,', Sequel[:user_edges][:role_id].cast(cast_type), ',%')
189
+ )
190
+ ).
191
+ where(Sequel.~(Sequel[:user_edges][:user_id] => nil) & Sequel.~(Sequel[:user_edges][:role_id] => nil)).
192
+ join(Sequel[:super_auth_users], id: Sequel[:user_edges][:user_id]).
193
+ select(
194
+ Sequel[:super_auth_users][:id].as(:user_id),
195
+ Sequel[:super_auth_users][:name].as(:user_name),
196
+ Sequel[:super_auth_users][:external_id].as(:user_external_id),
197
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
198
+ Sequel[:super_auth_users][:created_at].cast(cast_type).as(:user_created_at),
199
+ Sequel[:super_auth_users][:updated_at].cast(cast_type).as(:user_updated_at),
200
+
201
+ Sequel.cast(nil, integer_cast_type).as(:group_id),
202
+ Sequel.cast(nil, string_cast_type).as(:group_name),
203
+ Sequel.cast(nil, string_cast_type).as(:group_path),
204
+ Sequel.cast(nil, string_cast_type).as(:group_name_path),
205
+ Sequel.cast(nil, integer_cast_type).as(:group_parent_id),
206
+ Sequel.cast(nil, string_cast_type).as(:group_created_at),
207
+ Sequel.cast(nil, string_cast_type).as(:group_updated_at),
208
+
209
+ Sequel[:users_roles_permissions_resources][:role_id],
210
+ Sequel[:users_roles_permissions_resources][:role_name],
211
+ Sequel[:users_roles_permissions_resources][:role_path],
212
+ Sequel[:users_roles_permissions_resources][:role_name_path],
213
+ Sequel[:users_roles_permissions_resources][:role_parent_id],
214
+ Sequel[:users_roles_permissions_resources][:role_created_at].cast(cast_type).as(:role_created_at),
215
+ Sequel[:users_roles_permissions_resources][:role_updated_at].cast(cast_type).as(:role_updated_at),
216
+
217
+ Sequel[:super_auth_permissions][:id].as(:permission_id),
218
+ Sequel[:super_auth_permissions][:name].as(:permission_name),
219
+ Sequel[:super_auth_permissions][:created_at].cast(cast_type).as(:permission_created_at),
220
+ Sequel[:super_auth_permissions][:updated_at].cast(cast_type).as(:permission_updated_at),
221
+
222
+ Sequel[:super_auth_resources][:id].as(:resource_id),
223
+ Sequel[:super_auth_resources][:name].as(:resource_name),
224
+ Sequel[:super_auth_resources][:external_id].as(:resource_external_id),
225
+ Sequel[:super_auth_resources][:external_type].as(:resource_external_type),
226
+ ).
227
+ # Join permission and resource edges on the expanded role
228
+ join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:role_id] => Sequel[:users_roles_permissions_resources][:role_id]).
229
+ join(Sequel[:super_auth_permissions], id: Sequel[:permission_edges][:permission_id]).
230
+ join(Sequel[:super_auth_edges].as(:resource_edges), Sequel[:resource_edges][:permission_id] => Sequel[:super_auth_permissions][:id]).
231
+ join(Sequel[:super_auth_resources], id: Sequel[:resource_edges][:resource_id]).
232
+ distinct
233
+ end
234
+
235
+ def users_permissions_resources
236
+ cast_type = string_cast_type
134
237
  SuperAuth::User.
135
238
  join(Sequel[:super_auth_edges].as(:user_edges), user_id: :id).
136
- join(SuperAuth::Role.from(SuperAuth::Role.trees).as(:roles), id: :role_id).
137
239
  select(
138
240
  Sequel[:super_auth_users][:id].as(:user_id),
139
241
  Sequel[:super_auth_users][:name].as(:user_name),
140
242
  Sequel[:super_auth_users][:external_id].as(:user_external_id),
141
- Sequel[:super_auth_users][:created_at].cast(:text).as(:user_created_at),
142
- Sequel[:super_auth_users][:updated_at].cast(:text).as(:user_updated_at),
143
-
144
- Sequel.lit(%Q[0 as "group_id"]), # Sequel[:super_auth_groups][:group_id],
145
- Sequel::NULL.as(:group_name), # Sequel[:super_auth_groups][:group_name],
146
- Sequel::NULL.as(:group_path), # Sequel[:super_auth_groups][:group_path],
147
- Sequel::NULL.as(:group_name_path), # Sequel[:super_auth_groups][:group_name_path],
148
- Sequel.lit(%Q[0 as "group_parent_id"]), # Sequel[:super_auth_groups][:group_parent_id],
149
- Sequel.lit(%Q['1970-01-01 00:00:00.000000-00' as "group_created_at"]), # Sequel[:super_auth_groups][:group_created_at],
150
- Sequel.lit(%Q['1970-01-01 00:00:00.000000-00' as "group_updated_at"]), # Sequel[:super_auth_groups][:group_updated_at],
151
-
152
- Sequel[:roles][:id].as(:role_id),
153
- Sequel[:roles][:name].as(:role_name),
154
- Sequel[:roles][:role_path],
155
- Sequel[:roles][:role_name_path].as(:role_name_path),
156
- Sequel[:roles][:parent_id].as(:role_parent_id),
157
- Sequel[:roles][:created_at].cast(:text).as(:role_created_at),
158
- Sequel[:roles][:updated_at].cast(:text).as(:role_updated_at),
243
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
244
+ Sequel[:super_auth_users][:created_at].cast(cast_type).as(:user_created_at),
245
+ Sequel[:super_auth_users][:updated_at].cast(cast_type).as(:user_updated_at),
246
+
247
+ Sequel.cast(nil, integer_cast_type).as(:group_id),
248
+ Sequel.cast(nil, string_cast_type).as(:group_name),
249
+ Sequel.cast(nil, string_cast_type).as(:group_path),
250
+ Sequel.cast(nil, string_cast_type).as(:group_name_path),
251
+ Sequel.cast(nil, integer_cast_type).as(:group_parent_id),
252
+ Sequel.cast(nil, string_cast_type).as(:group_created_at),
253
+ Sequel.cast(nil, string_cast_type).as(:group_updated_at),
254
+
255
+ Sequel.cast(nil, integer_cast_type).as(:role_id),
256
+ Sequel.cast(nil, string_cast_type).as(:role_name),
257
+ Sequel.cast(nil, string_cast_type).as(:role_path),
258
+ Sequel.cast(nil, string_cast_type).as(:role_name_path),
259
+ Sequel.cast(nil, integer_cast_type).as(:role_parent_id),
260
+ Sequel.cast(nil, string_cast_type).as(:role_created_at),
261
+ Sequel.cast(nil, string_cast_type).as(:role_updated_at),
159
262
 
160
263
  Sequel[:super_auth_permissions][:id].as(:permission_id),
161
264
  Sequel[:super_auth_permissions][:name].as(:permission_name),
162
- Sequel[:super_auth_permissions][:created_at].cast(:text).as(:permission_created_at),
163
- Sequel[:super_auth_permissions][:updated_at].cast(:text).as(:permission_updated_at),
265
+ Sequel[:super_auth_permissions][:created_at].cast(cast_type).as(:permission_created_at),
266
+ Sequel[:super_auth_permissions][:updated_at].cast(cast_type).as(:permission_updated_at),
164
267
 
165
268
  Sequel[:super_auth_resources][:id].as(:resource_id),
166
269
  Sequel[:super_auth_resources][:name].as(:resource_name),
167
270
  Sequel[:super_auth_resources][:external_id].as(:resource_external_id),
168
- ).
169
- join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:role_id] => Sequel[:roles][:id]).
271
+ Sequel[:super_auth_resources][:external_type].as(:resource_external_type)
272
+ ).
273
+ join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:user_id] => Sequel[:super_auth_users][:id]).
170
274
  join(Sequel[:super_auth_permissions], id: Sequel[:permission_edges][:permission_id]).
171
275
  join(Sequel[:super_auth_edges].as(:resource_edges), Sequel[:resource_edges][:permission_id] => Sequel[:super_auth_permissions][:id]).
172
276
  join(Sequel[:super_auth_resources], id: Sequel[:resource_edges][:resource_id]).
173
277
  distinct
174
278
  end
175
279
 
176
- def users_permissions_resources
280
+ def users_resources
281
+ cast_type = string_cast_type
177
282
  SuperAuth::User.
178
283
  join(Sequel[:super_auth_edges].as(:user_edges), user_id: :id).
179
284
  select(
180
285
  Sequel[:super_auth_users][:id].as(:user_id),
181
286
  Sequel[:super_auth_users][:name].as(:user_name),
182
287
  Sequel[:super_auth_users][:external_id].as(:user_external_id),
183
- Sequel[:super_auth_users][:created_at].cast(:text).as(:user_created_at),
184
- Sequel[:super_auth_users][:updated_at].cast(:text).as(:user_updated_at),
185
-
186
- Sequel.lit(%Q[0 as "group_id"]), # Sequel[:groups][:group_id],
187
- Sequel::NULL.as(:group_name), # Sequel[:groups][:group_name],
188
- Sequel::NULL.as(:group_path), # Sequel[:groups][:group_path],
189
- Sequel::NULL.as(:group_name_path), # Sequel[:groups][:group_name_path],
190
- Sequel.lit(%Q[0 as "group_parent_id"]), # Sequel[:groups][:group_id],
191
- Sequel.lit(%Q['1970-01-01 00:00:00.000000-00' as "group_created_at"]), # Sequel[:groups][:group_created_at],
192
- Sequel.lit(%Q['1970-01-01 00:00:00.000000-00' as "group_updated_at"]), # Sequel[:groups][:group_updated_at],
193
-
194
-
195
- Sequel.lit(%Q[0 as "role_id"]), # Sequel[:roles][:role_id],
196
- Sequel::NULL.as(:role_name), # Sequel[:roles][:role_name],
197
- Sequel::NULL.as(:role_path), # Sequel[:roles][:role_path],
198
- Sequel::NULL.as(:role_name_path), # Sequel[:roles][:role_name_path],
199
- Sequel.lit(%Q[0 as "role_parent_id"]), # Sequel[:roles][:role_parent_id],
200
- Sequel::NULL.as(:role_created_at), # Sequel[:roles][:role_created_at],
201
- Sequel::NULL.as(:role_updated_at), # Sequel[:roles][:role_updated_at],
288
+ Sequel[:super_auth_users][:external_type].as(:user_external_type),
289
+ Sequel[:super_auth_users][:created_at].cast(cast_type).as(:user_created_at),
290
+ Sequel[:super_auth_users][:updated_at].cast(cast_type).as(:user_updated_at),
202
291
 
203
- Sequel[:super_auth_permissions][:id].as(:permission_id),
204
- Sequel[:super_auth_permissions][:name].as(:permission_name),
205
- Sequel[:super_auth_permissions][:created_at].cast(:text).as(:permission_created_at),
206
- Sequel[:super_auth_permissions][:updated_at].cast(:text).as(:permission_updated_at),
292
+ Sequel.cast(nil, integer_cast_type).as(:group_id),
293
+ Sequel.cast(nil, string_cast_type).as(:group_name),
294
+ Sequel.cast(nil, string_cast_type).as(:group_path),
295
+ Sequel.cast(nil, string_cast_type).as(:group_name_path),
296
+ Sequel.cast(nil, integer_cast_type).as(:group_parent_id),
297
+ Sequel.cast(nil, string_cast_type).as(:group_created_at),
298
+ Sequel.cast(nil, string_cast_type).as(:group_updated_at),
299
+
300
+ Sequel.cast(nil, integer_cast_type).as(:role_id),
301
+ Sequel.cast(nil, string_cast_type).as(:role_name),
302
+ Sequel.cast(nil, string_cast_type).as(:role_path),
303
+ Sequel.cast(nil, string_cast_type).as(:role_name_path),
304
+ Sequel.cast(nil, integer_cast_type).as(:role_parent_id),
305
+ Sequel.cast(nil, string_cast_type).as(:role_created_at),
306
+ Sequel.cast(nil, string_cast_type).as(:role_updated_at),
307
+
308
+ Sequel.cast(nil, integer_cast_type).as(:permission_id),
309
+ Sequel.cast(nil, string_cast_type).as(:permission_name),
310
+ Sequel.cast(nil, string_cast_type).as(:permission_created_at),
311
+ Sequel.cast(nil, string_cast_type).as(:permission_updated_at),
207
312
 
208
313
  Sequel[:super_auth_resources][:id].as(:resource_id),
209
314
  Sequel[:super_auth_resources][:name].as(:resource_name),
210
- Sequel[:super_auth_resources][:external_id].as(:resource_external_id)
315
+ Sequel[:super_auth_resources][:external_id].as(:resource_external_id),
316
+ Sequel[:super_auth_resources][:external_type].as(:resource_external_type)
211
317
  ).
212
- join(Sequel[:super_auth_edges].as(:permission_edges), Sequel[:permission_edges][:user_id] => Sequel[:super_auth_users][:id]).
213
- join(Sequel[:super_auth_permissions], id: Sequel[:permission_edges][:permission_id]).
214
- join(Sequel[:super_auth_edges].as(:resource_edges), Sequel[:resource_edges][:permission_id] => Sequel[:super_auth_permissions][:id]).
215
- join(Sequel[:super_auth_resources], id: Sequel[:resource_edges][:resource_id]).
318
+ join(Sequel[:super_auth_resources], Sequel[:user_edges][:resource_id] => Sequel[:super_auth_resources][:id]).
216
319
  distinct
217
320
  end
218
321
  end
219
- end
322
+
323
+ def to_h
324
+ {
325
+ user: self&.user&.name,
326
+ group: self&.group&.name,
327
+ role: self&.role&.name,
328
+ resource: self&.resource&.name,
329
+ permission: self&.permission&.name,
330
+ }
331
+ end
332
+ end
@@ -1,3 +1,4 @@
1
1
  class SuperAuth::Group < Sequel::Model(:super_auth_groups)
2
+ unrestrict_primary_key # For ActiveRecord
2
3
  include SuperAuth::Nestable
3
4
  end
@@ -25,6 +25,16 @@ module SuperAuth::Nestable
25
25
  end
26
26
 
27
27
  module ClassMethods
28
+ # Helper method to get the appropriate string cast type for the database
29
+ def string_cast_type
30
+ case SuperAuth.db.database_type
31
+ when :mysql, :mysql2
32
+ :char
33
+ else
34
+ :text
35
+ end
36
+ end
37
+
28
38
  def cte(id = nil, direction = :desc)
29
39
  model = self
30
40
  cte_name = model.cte_name
@@ -59,17 +69,14 @@ module SuperAuth::Nestable
59
69
  def with_descending_paths(base_ds, recursive_ds, cte_name)
60
70
  [
61
71
  base_ds.select_append(
62
- Sequel.function(
63
- :cast,
64
- Sequel[table_name][:id].as(:text)
65
- ).as(base_path)
72
+ Sequel[table_name][:id].cast(string_cast_type).as(base_path)
66
73
  ).select_append(Sequel[table_name][:name].as(base_name_path)),
67
74
 
68
75
  recursive_ds.select_append(
69
76
  Sequel.function(:concat,
70
- Sequel.function(:cast, Sequel[cte_name][base_path].as(:text)),
77
+ Sequel[cte_name][base_path].cast(string_cast_type),
71
78
  Sequel.lit("','"),
72
- Sequel.function(:cast, Sequel[pluralize][:id].as(:text)),
79
+ Sequel[pluralize][:id].cast(string_cast_type),
73
80
  ).as(base_path)
74
81
  ).select_append(
75
82
  Sequel.function(:concat,
@@ -83,12 +90,12 @@ module SuperAuth::Nestable
83
90
 
84
91
  def with_ascending_paths(base_ds, recursive_ds, cte_name)
85
92
  [
86
- base_ds.select_append(Sequel.function(:cast, Sequel[table_name][:id].as(:text)).as(base_path)).select_append(Sequel[table_name][:name].as(:base_name_path)),
93
+ base_ds.select_append(Sequel[table_name][:id].cast(string_cast_type).as(base_path)).select_append(Sequel[table_name][:name].as(base_name_path)),
87
94
  recursive_ds.select_append(
88
95
  Sequel.function(:concat,
89
- Sequel.function(:cast, Sequel[table_name][:id].as(:text)),
96
+ Sequel[table_name][:id].cast(string_cast_type),
90
97
  Sequel.lit("','"),
91
- Sequel.function(:cast, Sequel[cte_name][base_path].as(:text)),
98
+ Sequel[cte_name][base_path].cast(string_cast_type),
92
99
  ).as(base_path)
93
100
  ).select_append(
94
101
  Sequel.function(:concat,
@@ -129,4 +136,4 @@ module SuperAuth::Nestable
129
136
  "#{singularize(base)}_name_path".to_sym
130
137
  end
131
138
  end
132
- end
139
+ end