rails_iam 0.1.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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/CONTRIBUTING.md +145 -0
  4. data/LICENSE +20 -0
  5. data/README.md +192 -0
  6. data/Rakefile +14 -0
  7. data/app/controllers/concerns/rails_iam/auth_concern.rb +60 -0
  8. data/app/controllers/concerns/rails_iam/authentication/authenticatable.rb +44 -0
  9. data/app/controllers/concerns/rails_iam/authorization/authorizable.rb +170 -0
  10. data/app/controllers/rails_iam/application_controller.rb +7 -0
  11. data/app/controllers/rails_iam/auth_controller.rb +113 -0
  12. data/app/helpers/rails_iam/config_loader.rb +71 -0
  13. data/app/models/concerns/rails_iam/auditable.rb +57 -0
  14. data/app/models/concerns/rails_iam/user_concern.rb +59 -0
  15. data/app/models/rails_iam/application_record.rb +8 -0
  16. data/app/models/rails_iam/permission.rb +14 -0
  17. data/app/models/rails_iam/refresh_token.rb +7 -0
  18. data/app/models/rails_iam/role.rb +14 -0
  19. data/app/models/rails_iam/role_permission.rb +20 -0
  20. data/app/models/rails_iam/user.rb +16 -0
  21. data/app/models/rails_iam/user_denied_permission.rb +12 -0
  22. data/app/models/rails_iam/user_permission.rb +12 -0
  23. data/app/models/rails_iam/user_role.rb +12 -0
  24. data/app/services/application_service.rb +4 -0
  25. data/app/services/rails_iam/authentication/authenticated_user.rb +13 -0
  26. data/app/services/rails_iam/authentication/authenticator.rb +39 -0
  27. data/app/services/rails_iam/authentication/current.rb +9 -0
  28. data/app/services/rails_iam/authentication/exceptions/authentication_error.rb +10 -0
  29. data/app/services/rails_iam/authentication/jwt_decoder.rb +38 -0
  30. data/app/services/rails_iam/authentication/jwt_encoder.rb +45 -0
  31. data/app/services/rails_iam/authentication/login.rb +67 -0
  32. data/app/services/rails_iam/authentication/logout.rb +35 -0
  33. data/app/services/rails_iam/authentication/refresh_token_encoder.rb +11 -0
  34. data/app/services/rails_iam/authentication/skip_authentication_rule.rb +20 -0
  35. data/app/services/rails_iam/authentication/token_extractor.rb +64 -0
  36. data/app/services/rails_iam/authorization/authorization_context.rb +41 -0
  37. data/app/services/rails_iam/authorization/authorization_rule.rb +38 -0
  38. data/app/services/rails_iam/authorization/authorizer.rb +35 -0
  39. data/app/services/rails_iam/authorization/cache.rb +39 -0
  40. data/app/services/rails_iam/authorization/denied_permission_checker.rb +33 -0
  41. data/app/services/rails_iam/authorization/exceptions/authorization_error.rb +10 -0
  42. data/app/services/rails_iam/authorization/exceptions/missing_permission_error.rb +35 -0
  43. data/app/services/rails_iam/authorization/exceptions/missing_role_error.rb +35 -0
  44. data/app/services/rails_iam/authorization/permission_checker.rb +42 -0
  45. data/app/services/rails_iam/authorization/permission_resolver.rb +70 -0
  46. data/app/services/rails_iam/authorization/permission_sources/role_permission_source.rb +37 -0
  47. data/app/services/rails_iam/authorization/permission_sources/user_denied_permission_source.rb +29 -0
  48. data/app/services/rails_iam/authorization/permission_sources/user_permission_source.rb +28 -0
  49. data/app/services/rails_iam/authorization/resolved_permissions.rb +63 -0
  50. data/app/services/rails_iam/authorization/role_checker.rb +35 -0
  51. data/app/services/rails_iam/authorization/role_resolver.rb +30 -0
  52. data/app/services/rails_iam/authorization/skip_authorization_rule.rb +19 -0
  53. data/app/services/rails_iam/rails_iam_service.rb +11 -0
  54. data/config/routes.rb +25 -0
  55. data/lib/generators/rails_iam/install_generator.rb +49 -0
  56. data/lib/generators/rails_iam/templates/create_rails_iam_tables.rb.erb +119 -0
  57. data/lib/generators/rails_iam/templates/initializer.rb +162 -0
  58. data/lib/rails_iam/config/authentication.rb +65 -0
  59. data/lib/rails_iam/config/authorization.rb +36 -0
  60. data/lib/rails_iam/config/jwt.rb +27 -0
  61. data/lib/rails_iam/configuration.rb +30 -0
  62. data/lib/rails_iam/controller_dsl.rb +41 -0
  63. data/lib/rails_iam/engine.rb +34 -0
  64. data/lib/rails_iam/model_dsl.rb +13 -0
  65. data/lib/rails_iam/version.rb +5 -0
  66. data/lib/rails_iam.rb +27 -0
  67. data/lib/tasks/rails_iam_tasks.rake +6 -0
  68. data/rails_iam.gemspec +46 -0
  69. metadata +161 -0
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Authorization
5
+ class RoleChecker < RailsIam::RailsIamService
6
+ def initialize(context)
7
+ super()
8
+ @context = context
9
+ end
10
+
11
+ def call
12
+ return true if @context.required_roles.empty?
13
+
14
+ success =
15
+ case @context.role_match
16
+ when :any
17
+ (@context.roles.to_a & @context.required_roles).any?
18
+
19
+ when :all
20
+ (@context.required_roles - @context.roles.to_a).empty?
21
+
22
+ else
23
+ raise ArgumentError, "Unknown role_match #{@context.role_match}"
24
+ end
25
+
26
+ return true if success
27
+
28
+ raise RailsIam::Authorization::Exceptions::MissingRoleError.new(
29
+ required: @context.required_roles,
30
+ actual: @context.roles,
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Authorization
5
+ class RoleResolver < RailsIam::RailsIamService
6
+ def initialize(user)
7
+ super()
8
+ @user = user
9
+ end
10
+
11
+ def call
12
+ Cache.read_or_write(Cache::ROLES, user.id) do
13
+ resolve
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :user
20
+
21
+ def resolve
22
+ user.roles
23
+ .pluck(:name)
24
+ .map!(&:to_s)
25
+ .to_set
26
+ .freeze
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Authorization
5
+ SkipAuthorizationRule = Data.define(
6
+ :only,
7
+ :except,
8
+ ) do
9
+ def matches?(action)
10
+ action = action.to_sym
11
+
12
+ return Array(only).map(&:to_sym).include?(action) if only.present?
13
+ return Array(except).map(&:to_sym).exclude?(action) if except.present?
14
+
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ class RailsIamService < ApplicationService
5
+ include RailsIam::ConfigLoader
6
+
7
+ def self.call(...)
8
+ new(...).call
9
+ end
10
+ end
11
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ sign_in_path = RailsIam.configuration.authentication.sign_in_path
5
+ sign_out_path = RailsIam.configuration.authentication.sign_out_path
6
+ refresh_token_path = RailsIam.configuration.authentication.refresh_token_path
7
+
8
+ if sign_in_path.present?
9
+ post sign_in_path,
10
+ to: "rails_iam/auth#sign_in",
11
+ as: :rails_iam_sign_in
12
+ end
13
+
14
+ if sign_out_path.present?
15
+ post sign_out_path,
16
+ to: "rails_iam/auth#sign_out",
17
+ as: :rails_iam_sign_out
18
+ end
19
+
20
+ if refresh_token_path.present?
21
+ post refresh_token_path,
22
+ to: "rails_iam/auth#refresh",
23
+ as: :rails_iam_refresh
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module RailsIam
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include ActiveRecord::Generators::Migration
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ def copy_migrations
13
+ say "Copying migration file", :yellow
14
+ migration_template "create_rails_iam_tables.rb.erb", "db/migrate/create_rails_iam_tables.rb"
15
+ end
16
+
17
+ def create_initializer
18
+ say "Copying initializer file", :yellow
19
+ template "initializer.rb", "config/initializers/rails_iam.rb"
20
+ end
21
+
22
+ def show_readme
23
+ say "\n=======================================================", :yellow
24
+ say " RailsIam installed successfully!", :green
25
+ say "=======================================================", :yellow
26
+ say "Next steps:", :yellow
27
+ say ""
28
+ say "1. Open: config/initializers/rails_iam.rb", :green
29
+ say ""
30
+ say "2. Configure your user model if you have an existing authentication model:", :green
31
+ say " • If you have existing authentication (Devise, Auth0, custom, etc.)", :green
32
+ say " - config.user_class = 'User' (or Account, Member, Admin...)", :green
33
+ say " - config.current_user_method = :current_user (or :current_account)", :green
34
+ say " - Add `acts_as_rails_iam_user` to your user (or Account, Member, Admin...) authentication model.", :green
35
+ say ""
36
+ say " • If you use RailsIam authentication", :green
37
+ say " - Keep the default configuration.", :green
38
+ say ""
39
+ say "3. Run: bin/rails db:migrate", :green
40
+ say ""
41
+ say "You're ready to start using RailsIam 🚀", :green
42
+ say "=======================================================\n", :yellow
43
+ end
44
+
45
+ def self.next_migration_number(dirname)
46
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,119 @@
1
+ migration_version = "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
2
+
3
+
4
+ class CreateRailsIamTables < ActiveRecord::Migration[migration_version.to_f]
5
+
6
+ def change
7
+
8
+ primary_key_type = Rails.application.config.generators.options.dig(:active_record, :primary_key_type) || :bigint
9
+ user_class_name = RailsIam.configuration&.user_class || "RailsIam::User"
10
+ user_table_name = user_class_name.underscore.pluralize.tr('/', '_')
11
+
12
+ create_table :rails_iam_users, id: primary_key_type do |t|
13
+ t.string :email, index: { unique: true }
14
+ t.string :password_digest
15
+
16
+ # Custom Audit Tracking Columns
17
+ t.string :created_by
18
+ t.string :updated_by
19
+ t.timestamp :deleted_at
20
+ t.string :deleted_by
21
+
22
+ t.timestamps
23
+ end
24
+
25
+ create_table :rails_iam_roles, id: primary_key_type do |t|
26
+ t.string :name, index: { unique: true }
27
+ t.text :description
28
+
29
+ # Custom Audit Tracking Columns
30
+ t.string :created_by
31
+ t.string :updated_by
32
+ t.timestamp :deleted_at
33
+ t.string :deleted_by
34
+
35
+ t.timestamps
36
+ end
37
+
38
+ create_table :rails_iam_permissions, id: primary_key_type do |t|
39
+ t.string :code, index: { unique: true }
40
+ t.text :description
41
+
42
+ # Custom Audit Tracking Columns
43
+ t.string :created_by
44
+ t.string :updated_by
45
+ t.timestamp :deleted_at
46
+ t.string :deleted_by
47
+
48
+ t.timestamps
49
+ end
50
+
51
+ create_table :rails_iam_user_roles, id: primary_key_type do |t|
52
+ t.references :user, null: false, type: primary_key_type, foreign_key: { to_table: user_table_name }
53
+ t.references :role, null: false, type: primary_key_type, foreign_key: { to_table: :rails_iam_roles }
54
+
55
+ # Custom Audit Tracking Columns
56
+ t.string :created_by
57
+ t.string :updated_by
58
+ t.timestamp :deleted_at
59
+ t.string :deleted_by
60
+
61
+ t.timestamps
62
+ end
63
+
64
+
65
+ create_table :rails_iam_user_permissions, id: primary_key_type do |t|
66
+ t.references :permission, null: false, type: primary_key_type, foreign_key: { to_table: :rails_iam_permissions }
67
+ t.references :user, null: false, type: primary_key_type, foreign_key: { to_table: user_table_name }
68
+
69
+ # Custom Audit Tracking Columns
70
+ t.string :created_by
71
+ t.string :updated_by
72
+ t.timestamp :deleted_at
73
+ t.string :deleted_by
74
+
75
+ t.timestamps
76
+ end
77
+
78
+ create_table :rails_iam_role_permissions, id: primary_key_type do |t|
79
+ t.references :permission, null: false, type: primary_key_type, foreign_key: { to_table: :rails_iam_permissions }
80
+ t.references :role, null: false, type: primary_key_type, foreign_key: { to_table: :rails_iam_roles }
81
+
82
+ # Custom Audit Tracking Columns
83
+ t.string :created_by
84
+ t.string :updated_by
85
+ t.timestamp :deleted_at
86
+ t.string :deleted_by
87
+
88
+ t.timestamps
89
+ end
90
+
91
+ create_table :rails_iam_user_denied_permissions, id: primary_key_type do |t|
92
+ t.references :permission, null: false, type: primary_key_type, foreign_key: { to_table: :rails_iam_permissions }
93
+ t.references :user, null: false, type: primary_key_type, foreign_key: { to_table: user_table_name }
94
+
95
+ # Custom Audit Tracking Columns
96
+ t.string :created_by
97
+ t.string :updated_by
98
+ t.timestamp :deleted_at
99
+ t.string :deleted_by
100
+
101
+ t.timestamps
102
+ end
103
+
104
+ create_table :rails_iam_refresh_tokens, id: primary_key_type do |t|
105
+ t.references :user, null: false, type: primary_key_type, foreign_key: { to_table: user_table_name }
106
+ t.string :token_digest, index: { unique: true }
107
+ t.datetime :expires_at, null: false
108
+ # to invalidate the token
109
+ t.datetime :revoked_at
110
+ t.datetime :last_used_at
111
+ # Jti from access token
112
+ t.string :jti, index: { unique: true }
113
+ t.string :ip_address
114
+ t.string :user_agent
115
+
116
+ t.timestamps
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # RailsIam Configuration
5
+ #
6
+ # Use this file to configure how your application handles authentication,
7
+ # authorization scopes, JWT token properties, caching, and error handling.
8
+ #
9
+ RailsIam.configure do |config|
10
+ # =========================================================================
11
+ # Host Application Integration
12
+ # =========================================================================
13
+
14
+ # - The class name of your main User/Account model which will be used to login
15
+ # - It's been used throughout RailsIam for authentication, authorization, associations and user lookup.
16
+ # - Configure this only if you use a different model
17
+ # or you have your own authentication mechanism (devise or jwt/bcrypt)
18
+ # - Default: 'RailsIam::User'
19
+ # config.user_class = 'RailsIam::User'
20
+
21
+ # - The method name your controllers use to look up the currently active session user.
22
+ # - Default: :current_user
23
+ # config.current_user_method = :current_user
24
+
25
+ # - The performer method that rails_iam use to track who creates and assign role/permission
26
+ # - Support symbol and proc
27
+ # - Default: :email
28
+ # config.audit_performer_method = :email
29
+
30
+ # =========================================================================
31
+ # Authentication Settings (config.authentication)
32
+ # =========================================================================
33
+
34
+ # - The key used when storing or reading the access token from cookies or parameters.
35
+ # - Default: 'access_token'
36
+ # config.authentication.access_token_key = 'access_token'
37
+
38
+ # - The key used when storing or reading the long-lived refresh token.
39
+ # - Default: 'refresh_token'
40
+ # config.authentication.refresh_token_key = 'refresh_token'
41
+
42
+ # - Supported authentication delivery strategies.
43
+ # - Options: [:cookie], [:bearer], or combined [:cookie, :bearer]
44
+ # - For :bearer, the gem expects the token in authorization header with `Bearer your_token`
45
+ # - Default: [:cookie]
46
+ # config.authentication.authentication_strategies = [:cookie] # httponly
47
+
48
+ # - Cookie configuration (Web / Cookie-based Strategy)
49
+ # - Configure the security and behavior properties for session cookies issued by
50
+ # - the RailsIam engine. These settings strictly apply when using the :cookie authentication strategy.
51
+ # - Default: { httponly: true, secure: Rails.env.production?, same_site: :lax, }
52
+ # config.authentication.cookie_options = { httponly: true, secure: Rails.env.production?, same_site: :lax, }
53
+
54
+ # - Refresh Token Transport, only needed if authentication_strategies uses :bearer
55
+ # - Determines how RailsIam receives the refresh token for
56
+ # refresh and sign-out requests when using bearer authentication.
57
+ # - Will use the same @refresh_token_key to get the header
58
+ # i,e: request.get_header(refresh_token_key.upcase) -> HTTP_X_REFRESH_TOKEN
59
+ # - Options: :header, :request_body
60
+ # - Default: :header
61
+ # config.authentication.refresh_token_transport = :header
62
+
63
+ # - Session persistence limit strategy.
64
+ # [:single] - Forces one active device/session per user at any time (invalidates old tokens).
65
+ # [:multi] - Allows concurrent login sessions across multiple devices or browsers.
66
+ # - Default: :single
67
+ # config.authentication.session_strategy = :single
68
+
69
+ #
70
+ # Built-in Authentication Routes
71
+ #
72
+ # - Routes exposed by RailsIam when using its built-in authentication
73
+ # controllers. Put `nil` if you don't want RailsIam to provide
74
+ # the HTTP sign-in, sign-out and refresh token endpoints. You don't need to add
75
+ # anything to your host application. RailsIam will map this routes
76
+ # to your host application i,e: example.com/auth/sign_in
77
+ #
78
+ # You can also:
79
+ # - Use Devise (or another authentication library) and leave these default nil.
80
+ # - Build your own authentication controllers and use RailsIam's
81
+ # JWT encoder/decoder, refresh token services, or login/logout
82
+ # services without exposing the built-in routes.
83
+ # - Mix both approaches (e.g. use RailsIam's sign-in route but your
84
+ # own sign-out route).
85
+ # - Use nil if your application already have authentication routes
86
+ # config.authentication.sign_in_path = '/auth/sign_in'
87
+ # config.authentication.sign_out_path = '/auth/sign_out'
88
+ # config.authentication.refresh_token_path = '/auth/refresh
89
+
90
+ # - Lifespan for the Access Token used to set httponly cookie expiration
91
+ # - Keep the value sync jwt expiry time
92
+ # - Default: 15.minutes
93
+ # config.authentication.access_token_expires_in = 15.minutes
94
+
95
+ # - Lifespan for the Refresh Token used to request replacement access tokens.
96
+ # - Default: 30.days
97
+ # config.authentication.refresh_token_expires_in = 30.days
98
+
99
+ # - Custom system error messages emitted during authentication edge cases.
100
+ # - Configure the error messages if you need different messages
101
+ # config.authentication.token_error = 'The provided authentication token is invalid or malformed.'
102
+ # config.authentication.token_expired_error = 'The authentication token has expired. Please re-authenticate.'
103
+ # config.authentication.authentication_error = 'Invalid email or password.'
104
+ # config.authentication.authentication_strategy_error = 'Unknown authentication strategy. Please configure it from rails_iam initializer'
105
+ # config.authentication.session_strategy_error = 'Unknown session strategy. Please configure it from rails_iam initializer'
106
+ # config.authentication.refresh_token_transport_error = 'Unknown refresh token transport. Please configure it from rails_iam initializer'
107
+
108
+ # =========================================================================
109
+ # Authorization & Cache Settings (config.authorization)
110
+ # =========================================================================
111
+
112
+ # - Custom formatters for policy denial messaging.
113
+ # - Accepts plain Strings, dynamic blocks, or Lambdas with positional keywords.
114
+ #
115
+ # config.authorization.missing_role_error = lambda do |required:, actual:|
116
+ # "Missing roles: #{required}. You only have: #{actual}"
117
+ # end
118
+ #
119
+ # config.authorization.missing_permission_error = lambda do |required:|
120
+ # "Missing permission(s): #{required.join(', ')}"
121
+ # end
122
+
123
+ # - Error message if permission is not applied on endpoint
124
+ # - Example: No authorization rule for users#show
125
+ # config.authorization.authorization_rule_error = "No authorization rule for "
126
+
127
+ # - The store engine to cache resolved roles and permissions queries for low DB footprints.
128
+ # - Default: Rails.cache
129
+ # config.authorization.cache_store = Rails.cache
130
+
131
+ # - Lifespan duration for records cached within the authorization engine layer.
132
+ # - Default: 15.minutes
133
+ # config.authorization.cache_ttl = 15.minutes
134
+
135
+ # - Controller namespaces excluded from automatic authorization.
136
+ # - Use this when a third-party library or Rails engine (such as Devise, or another mounted engine) provides controllers
137
+ # - that inherit from your ApplicationController but should not be subject to RailsIam authorization.
138
+ # - Example: [/\ADevise::/, /\ARailsIam::/]
139
+ # - Default []
140
+ # config.authorization.excluded_controller_patterns = []
141
+
142
+ # =========================================================================
143
+ # JWT Cryptography Settings (config.jwt)
144
+ # =========================================================================
145
+
146
+ # - Secret verification string used to sign security tokens.
147
+ # - Default: ENV['JWT_SECRET'] or Rails.application.secret_key_base
148
+ # config.jwt.secret = ENV['JWT_SECRET']
149
+
150
+ # - Digital signature algorithm category mapping.
151
+ # - Default: 'HS256'
152
+ # config.jwt.algorithm = 'HS256'
153
+
154
+ # - Jwt expiry, default to 15 minutes, same as access token in cookie expiry
155
+ # config.jwt.jwt_expires_in = 15.minutes
156
+
157
+ # - Optional security claim validations (RFC 7519 standard targets).
158
+ # - Leave nil to disable strict validation checks.
159
+ # - You can also dynamically set them in `extra_claims` at `JwtEncoder`
160
+ # config.jwt.issuer = 'https://yourdomain.com'
161
+ # config.jwt.audience = 'your_application_client_id'
162
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Config
5
+ class Authentication
6
+ attr_accessor :access_token_key,
7
+ :refresh_token_key,
8
+ :authentication_strategies,
9
+ :session_strategy,
10
+ :sign_in_path,
11
+ :sign_out_path,
12
+ :refresh_token_path,
13
+ :refresh_token_transport,
14
+ :access_token_expires_in,
15
+ :refresh_token_expires_in,
16
+ :token_error,
17
+ :token_expired_error,
18
+ :authentication_error,
19
+ :authentication_strategy_error,
20
+ :session_strategy_error,
21
+ :refresh_token_transport_error,
22
+ :cookie_options
23
+
24
+ def initialize
25
+ # The key used when storing or reading the access token from cookies or parameters.
26
+ @access_token_key = "access_token"
27
+
28
+ # The key used when storing or reading the long-lived refresh token.
29
+ @refresh_token_key = "refresh_token"
30
+
31
+ # Authentication Strategy
32
+ # Supported: [:cookie], [:bearer], or combined [:cookie, :bearer]
33
+ @authentication_strategies = %i[cookie]
34
+
35
+ # Session Strategy, allowing user to login from multiple device at the same time
36
+ # Supported: :single || :multi
37
+ @session_strategy = :single
38
+
39
+ # Determines how RailsIam receives the refresh token for :bearer strategy
40
+ # Supported: :header || :request_body
41
+ @refresh_token_transport = :header
42
+
43
+ # Authentication Endpoints
44
+ @sign_in_path = "/auth/sign_in"
45
+ @sign_out_path = "/auth/sign_out"
46
+ @refresh_token_path = "/auth/refresh"
47
+
48
+ # expiry for access token
49
+ @access_token_expires_in = 15.minutes
50
+ @refresh_token_expires_in = 30.days
51
+
52
+ # cookie options
53
+ @cookie_options = { httponly: true, secure: Rails.env.production?, same_site: :lax }.freeze
54
+
55
+ # Authentication Messages
56
+ @token_error = "The provided authentication token is invalid or malformed."
57
+ @token_expired_error = "The authentication token has expired. Please re-authenticate."
58
+ @authentication_error = "Invalid email or password."
59
+ @authentication_strategy_error = "Unknown authentication strategy. Please configure it from rails_iam initializer"
60
+ @session_strategy_error = "Unknown session strategy. Please configure it from rails_iam initializer"
61
+ @refresh_token_transport_error = "Unknown refresh token transport. Please configure it from rails_iam initializer"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Config
5
+ class Authorization
6
+ attr_accessor :missing_role_error,
7
+ :missing_permission_error,
8
+ :authorization_rule_error,
9
+ :cache_store,
10
+ :cache_ttl,
11
+ :excluded_controller_patterns
12
+
13
+ def initialize
14
+ # Accepts plain Strings, dynamic blocks, or Lambdas with positional keywords.
15
+ @missing_role_error = lambda do |required:, actual:|
16
+ "Missing roles: #{required.join(', ')}. You only have: #{actual.join(', ')}"
17
+ end
18
+
19
+ @missing_permission_error = lambda do |required:|
20
+ "Missing permission(s): #{required.join(', ')}"
21
+ end
22
+
23
+ @authorization_rule_error = "No authorization rule applied for"
24
+
25
+ # The store engine to cache resolved roles and permissions queries for low DB footprints.
26
+ @cache_store = Rails.cache
27
+ @cache_ttl = 15.minutes
28
+
29
+ # Useful for third-party gems and mounted engines (e.g. Devise)
30
+ # whose controllers inherit from ApplicationController but
31
+ # should bypass RailsIam authorization.
32
+ @excluded_controller_patterns = []
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module Config
5
+ class Jwt
6
+ attr_accessor :secret,
7
+ :algorithm,
8
+ :issuer,
9
+ :audience,
10
+ :jwt_expires_in
11
+
12
+ def initialize
13
+ # Secret verification string used to sign security tokens.
14
+ @secret = ENV["JWT_SECRET"].presence || Rails.application&.secret_key_base
15
+
16
+ # Digital signature algorithm category mapping.
17
+ @algorithm = "HS256"
18
+ @jwt_expires_in = 15.minutes
19
+
20
+ # Optional security claim validations (RFC 7519 standard targets).
21
+ # Leave nil to disable strict validation checks.
22
+ @issuer = nil
23
+ @audience = nil
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ class Configuration
5
+ attr_reader :authorization, :jwt, :authentication
6
+
7
+ attr_accessor :user_class, :current_user_method, :audit_performer_method
8
+
9
+ def initialize
10
+ # Each concern has its own configuration object to keep the
11
+ @authorization = RailsIam::Config::Authorization.new
12
+ @jwt = RailsIam::Config::Jwt.new
13
+ @authentication = RailsIam::Config::Authentication.new
14
+
15
+ # Host Application User Model, defaults to RailsIam::User
16
+ # Used throughout RailsIam for authentication, authorization associations, and user lookup.
17
+ @user_class = "RailsIam::User"
18
+
19
+ # Current User Resolver, defaults to current_user
20
+ # Method used by RailsIam to obtain the authenticated user when
21
+ # the host application already manages authentication (e.g. Devise,)
22
+ @current_user_method = :current_user
23
+
24
+ # Audit performer method
25
+ # Used to audit who created/assigned what permission
26
+ # support symbol both symbol and proc i,e ->(user) { "#{user.id}:#{user.username}" }
27
+ @audit_performer_method = :email
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsIam
4
+ module ControllerDsl
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def rails_iam(*features)
9
+ features.each do |feature|
10
+ case feature.to_sym
11
+ when :authentication
12
+ include RailsIam::Authentication::Authenticatable
13
+
14
+ install_authentication_rescue_handler
15
+
16
+ when :authorization
17
+ include RailsIam::Authorization::Authorizable
18
+
19
+ install_authorization_rescue_handler
20
+ else
21
+ raise ArgumentError, "Unknown RailsIam feature: #{feature.inspect}, only support :authentication and :authorization"
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def install_authentication_rescue_handler
29
+ rescue_from RailsIam::Authentication::Exceptions::AuthenticationError do |exception|
30
+ render_rails_iam_authentication_error(exception)
31
+ end
32
+ end
33
+
34
+ def install_authorization_rescue_handler
35
+ rescue_from RailsIam::Authorization::Exceptions::AuthorizationError do |exception|
36
+ render_rails_iam_authorization_error(exception)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end