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
@@ -7,7 +7,7 @@ class SuperAuth::Permission < Sequel::Model(:super_auth_permissions)
7
7
  end
8
8
 
9
9
  def with_roles
10
- with_edges.join(Role.from(Role.trees).as(:roles), id: :role_id).select(
10
+ with_edges.join(SuperAuth::Role.from(SuperAuth::Role.trees).as(:roles), id: :role_id).select(
11
11
  Sequel[:super_auth_permissions][:id].as(:id),
12
12
  Sequel[:super_auth_permissions][:id].as(:permission_id),
13
13
  Sequel[:roles][:id].as(:role_id),
@@ -1,9 +1,39 @@
1
1
  module SuperAuth
2
+ if defined? Rails::Engine
3
+ class Engine < Rails::Engine
4
+ isolate_namespace SuperAuth
5
+
6
+ config.paths.add 'app/controllers', eager_load: true
7
+
8
+ # Use ActiveRecord migrations when in a Rails environment
9
+ if defined?(ActiveRecord)
10
+ config.paths['db/migrate'] = 'db/migrate_activerecord'
11
+ end
12
+ end
13
+ end
14
+
2
15
  if defined? Rails::Railtie
3
16
  class Railtie < Rails::Railtie
4
17
  rake_tasks do
5
18
  load "tasks/super_auth_tasks.rake"
6
19
  end
20
+
21
+ initializer "super_auth.initialize" do
22
+ if defined?(ActiveRecord) && defined?(ActiveRecord::Base)
23
+ SuperAuth.db
24
+ begin
25
+ SuperAuth.load
26
+ rescue Sequel::DatabaseError => e
27
+ # Tables don't exist yet (e.g., before migrations are run)
28
+ # This is OK - models will be loaded when needed
29
+ Rails.logger.debug "SuperAuth Sequel models not loaded: #{e.message}" if defined?(Rails.logger)
30
+ end
31
+ require "super_auth/active_record"
32
+ elsif defined?(Sequel) && Sequel.const_defined?("Model")
33
+ SuperAuth.db
34
+ SuperAuth.load
35
+ end
36
+ end
7
37
  end
8
38
  else
9
39
  class Railtie
@@ -1,3 +1,4 @@
1
1
  class SuperAuth::Role < Sequel::Model(:super_auth_roles)
2
+ unrestrict_primary_key # For ActiveRecord
2
3
  include SuperAuth::Nestable
3
- end
4
+ end
@@ -1,5 +1,9 @@
1
1
  class SuperAuth::User < Sequel::Model(:super_auth_users)
2
2
  one_to_many :edges
3
+ one_to_many :resources
4
+
5
+ def system? = self.class.system == self
6
+ def self.system = find_or_create(name: "system")
3
7
 
4
8
  dataset_module do
5
9
  def with_edges
@@ -7,7 +11,7 @@ class SuperAuth::User < Sequel::Model(:super_auth_users)
7
11
  end
8
12
 
9
13
  def with_groups
10
- with_edges.join(Group.from(Group.trees).as(:groups), id: :group_id).select(
14
+ with_edges.join(SuperAuth::Group.from(SuperAuth::Group.trees).as(:groups), id: :group_id).select(
11
15
  Sequel[:super_auth_users][:id].as(:id),
12
16
  Sequel[:super_auth_users][:id].as(:user_id),
13
17
  Sequel[:groups][:id].as(:group_id),
@@ -25,25 +29,21 @@ class SuperAuth::User < Sequel::Model(:super_auth_users)
25
29
  end
26
30
 
27
31
  def with_roles
28
- with_edges.join(Role.from(Role.trees).as(:roles), id: :role_id).select(
29
- Sequel[:users][:id].as(:id),
30
- Sequel[:users][:id].as(:user_id),
32
+ with_edges.join(SuperAuth::Role.from(SuperAuth::Role.trees).as(:roles), id: :role_id).select(
33
+ Sequel[:super_auth_users][:id].as(:id),
34
+ Sequel[:super_auth_users][:id].as(:user_id),
31
35
  Sequel[:roles][:id].as(:role_id),
32
- Sequel[:users][:name].as(:user_name),
36
+ Sequel[:super_auth_users][:name].as(:user_name),
33
37
  Sequel[:roles][:name].as(:role_name),
34
- Sequel[:edges][:id].as(:edge_id),
35
- Sequel[:edges][:permission_id].as(:edge_permission_id),
36
- Sequel[:edges][:group_id].as(:edge_group_id),
37
- Sequel[:edges][:user_id].as(:edge_user_id),
38
- Sequel[:edges][:role_id].as(:edge_role_id),
38
+ Sequel[:super_auth_edges][:id].as(:edge_id),
39
+ Sequel[:super_auth_edges][:permission_id].as(:edge_permission_id),
40
+ Sequel[:super_auth_edges][:group_id].as(:edge_group_id),
41
+ Sequel[:super_auth_edges][:user_id].as(:edge_user_id),
42
+ Sequel[:super_auth_edges][:role_id].as(:edge_role_id),
39
43
  Sequel[:roles][:role_path],
40
44
  Sequel[:roles][:role_name_path],
41
45
  Sequel[:roles][:parent_id]
42
46
  )
43
47
  end
44
-
45
- def with_roles_with_groups
46
- with_groups_with_roles
47
- end
48
48
  end
49
49
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SuperAuth
4
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
5
3
  end
data/lib/super_auth.rb CHANGED
@@ -1,41 +1,115 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative "super_auth/version"
2
+ require "sequel"
3
+
4
+ module SuperAuth
5
+ class Error < StandardError; end
4
6
 
5
- if defined? SuperAuth::AUTOLOADERS
6
- require 'zeitwerk'
7
- SuperAuth::AUTOLOADERS << Zeitwerk::Loader.for_gem.tap do |loader|
8
- loader.ignore("#{__dir__}/basic_loader.rb")
9
- loader.setup
7
+ def self.setup
8
+ yield self if block_given?
10
9
  end
11
- end
12
10
 
13
- require 'sequel'
11
+ def self.load
12
+ require "super_auth/authorization"
13
+ require "super_auth/edge"
14
+ require "super_auth/nestable"
15
+ require "super_auth/group"
16
+ require "super_auth/permission"
17
+ require "super_auth/railtie"
18
+ require "super_auth/resource"
19
+ require "super_auth/role"
20
+ require "super_auth/user"
21
+ require "super_auth/active_record" if defined?(ActiveRecord::Base)
22
+ end
14
23
 
15
- ENV["SUPER_AUTH_LOG_LEVEL"] = 'debug'
16
- require 'logger'
17
- logger = Logger.new(STDOUT)
24
+ def self.install_migrations
25
+ Sequel.extension :migration
26
+ require "pathname"
27
+ path = Pathname.new(__FILE__).parent.parent.join("db", "migrate")
28
+ Sequel::Migrator.run(SuperAuth.db, path)
29
+ end
18
30
 
19
- Sequel::Model.plugin :timestamps, update_on_create: true
20
- if !ENV['SUPER_AUTH_DATABASE_URL'].nil? && !ENV['SUPER_AUTH_DATABASE_URL'].empty?
21
- Sequel::Model.db = Sequel.connect(ENV['SUPER_AUTH_DATABASE_URL'], logger: logger)
22
- else
23
- logger.warn "SUPER_AUTH_DATABASE_URL not set, using sqlite in memory database."
24
- Sequel::Model.db = Sequel.sqlite(logger: logger)
25
- end
26
- Sequel::Model.default_association_options = {:class_namespace=>'SuperAuth'}
31
+ def self.uninstall_migrations
32
+ require "sequel"
33
+ Sequel.extension :migration
34
+ require "pathname"
27
35
 
28
- # I don't love this, but I don't know how to do it better
29
- unless Sequel::Model.db.table_exists?(:super_auth_edges)
30
- Sequel.extension :migration
31
- path = Pathname.new(__FILE__).parent.parent.join("db", "migrate")
32
- Sequel::Migrator.run(Sequel::Model.db, path)
33
- end
34
- require 'basic_loader' unless defined?(SuperAuth::AUTOLOADERS)
36
+ path = Pathname.new(__FILE__).parent.parent.join("db", "migrate")
37
+ db = SuperAuth.db
35
38
 
39
+ Sequel::Migrator.run(db, path, target: 0)
40
+ rescue => e
41
+ raise Error, "Failed to uninstall migrations: #{e.message}"
42
+ end
36
43
 
37
- module SuperAuth
38
- class Error < StandardError; end
44
+ def self.current_user=(user)
45
+ Thread.current[:super_auth_current_user] = user
46
+ end
47
+
48
+ def self.current_user
49
+ Thread.current[:super_auth_current_user]
50
+ end
51
+
52
+ def self.db
53
+ return @db if !@db.nil?
54
+
55
+ if !Gem::Specification.find_all_by_name("activerecord").empty?
56
+ require "active_record"
57
+ extensions = Gem::Specification.find_all_by_name("sequel-activerecord_connection").any? ? { extensions: :activerecord_connection } : {}
58
+
59
+ if extensions.empty?
60
+ warn "[SuperAuth] WARNING: Found ActiveRecord but could not find the gem 'sequel-activerecord_connection' installed. SuperAuth may not always work as expected."
61
+ end
62
+
63
+ begin
64
+ ::ActiveRecord::Base.establish_connection
65
+ rescue ActiveRecord::AdapterNotSpecified
66
+ if defined?(Rails) && !Rails.env.local?
67
+ raise Error, "SuperAuth could not find a database configuration. " \
68
+ "Please configure ActiveRecord or set SUPER_AUTH_DATABASE_URL."
69
+ end
70
+ warn "[SuperAuth] WARNING: No database configured. Falling back to in-memory SQLite. " \
71
+ "All authorization data will be lost on restart."
72
+ ::ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
73
+ end
74
+
75
+ case ::ActiveRecord::Base.adapter_class.to_s
76
+ when "ActiveRecord::ConnectionAdapters::SQLite3Adapter"
77
+ SuperAuth.db = Sequel.sqlite(**extensions)
78
+ when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
79
+ SuperAuth.db = Sequel.postgres(**extensions)
80
+ when "ActiveRecord::ConnectionAdapters::Mysql2Adapter"
81
+ SuperAuth.db = Sequel.mysql2(**extensions)
82
+ else
83
+ warn "[SuperAuth] WARNING: Unknown adapter: #{::ActiveRecord::Base.adapter_class}"
84
+ end
85
+ else
86
+ logger =
87
+ if defined?(Rails) && ENV["SUPER_AUTH_LOG_LEVEL"] == "debug"
88
+ { logger: Rails.logger }
89
+ elsif ENV["SUPER_AUTH_LOG_LEVEL"] == "debug"
90
+ require "logger"
91
+ { logger: Logger.new(STDOUT) }
92
+ else
93
+ {} # no logger
94
+ end
95
+
96
+ if !ENV['SUPER_AUTH_DATABASE_URL'].nil? && !ENV['SUPER_AUTH_DATABASE_URL'].empty?
97
+ SuperAuth.db = Sequel.connect(ENV['SUPER_AUTH_DATABASE_URL'], **logger)
98
+ else
99
+ if defined?(Rails) && !Rails.env.local?
100
+ raise Error, "SuperAuth could not find a database configuration. " \
101
+ "Please set SUPER_AUTH_DATABASE_URL or configure ActiveRecord."
102
+ end
103
+ warn "[SuperAuth] WARNING: SUPER_AUTH_DATABASE_URL not set. Falling back to in-memory SQLite. " \
104
+ "All authorization data will be lost on restart."
105
+ SuperAuth.db = Sequel.sqlite(**logger)
106
+ end
107
+ end
108
+ end
109
+
110
+ def self.db=(db)
111
+ @db = db
112
+ end
39
113
  end
40
114
 
41
115
  require "super_auth/railtie" if defined?(Rails::Railtie)
@@ -1,13 +1,14 @@
1
1
  namespace :super_auth do
2
2
  desc "Run the super_auth database migrations"
3
3
  task migrate: :environment do
4
- # TODO: Make this work properly without auto applying migrations, which is silly
5
- #
6
- # raise "ENV variable SUPER_AUTH_DATABASE_URL is not set" if ENV['SUPER_AUTH_DATABASE_URL'].nil? || ENV['SUPER_AUTH_DATABASE_URL'].empty?
7
- # Sequel::Model.db = Sequel.connect(ENV['SUPER_AUTH_DATABASE_URL'])
8
- # Sequel.extension :migration
9
- # binding.irb
10
- # path = Pathname.new(__FILE__).parent.parent.join("db", "migrate")
11
- # Sequel::Migrator.run(Sequel::Model.db, path)
4
+ raise "You must define SUPER_AUTH_DATABASE_URL in your environment for this to work" if ENV['SUPER_AUTH_DATABASE_URL'].nil? || ENV['SUPER_AUTH_DATABASE_URL'].empty?
5
+ SuperAuth.install_migrations
6
+ puts "Done"
7
+ end
8
+
9
+ task :rollback => :environment do
10
+ raise "You must define SUPER_AUTH_DATABASE_URL in your environment for this to work" if ENV['SUPER_AUTH_DATABASE_URL'].nil? || ENV['SUPER_AUTH_DATABASE_URL'].empty?
11
+ SuperAuth.uninstall_migrations
12
+ puts "Done"
12
13
  end
13
14
  end