securial 0.6.1 → 0.8.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -5
  3. data/app/controllers/concerns/securial/identity.rb +17 -16
  4. data/app/controllers/securial/accounts_controller.rb +2 -2
  5. data/app/controllers/securial/passwords_controller.rb +2 -2
  6. data/app/controllers/securial/role_assignments_controller.rb +5 -7
  7. data/app/controllers/securial/roles_controller.rb +1 -0
  8. data/app/controllers/securial/sessions_controller.rb +6 -8
  9. data/app/controllers/securial/status_controller.rb +1 -1
  10. data/app/controllers/securial/users_controller.rb +8 -7
  11. data/app/mailers/securial/securial_mailer.rb +17 -6
  12. data/app/models/concerns/securial/password_resettable.rb +1 -1
  13. data/app/models/securial/application_record.rb +1 -1
  14. data/app/models/securial/role.rb +1 -1
  15. data/app/models/securial/session.rb +16 -5
  16. data/app/models/securial/user.rb +4 -6
  17. data/app/views/securial/securial_mailer/forgot_password.html.erb +20 -0
  18. data/app/views/securial/securial_mailer/forgot_password.text.erb +14 -0
  19. data/app/views/securial/securial_mailer/sign_in.html.erb +31 -0
  20. data/app/views/securial/securial_mailer/sign_in.text.erb +17 -0
  21. data/app/views/securial/securial_mailer/update_account.html.erb +15 -0
  22. data/app/views/securial/securial_mailer/update_account.text.erb +11 -0
  23. data/app/views/securial/securial_mailer/welcome.html.erb +11 -0
  24. data/app/views/securial/securial_mailer/welcome.text.erb +8 -0
  25. data/app/views/securial/users/_securial_user.json.jbuilder +9 -3
  26. data/config/routes.rb +5 -1
  27. data/db/migrate/{20250515104930_create_securial_roles.rb → 20250603130214_create_securial_roles.rb} +0 -2
  28. data/db/migrate/{20250517155521_create_securial_users.rb → 20250604110520_create_securial_users.rb} +8 -3
  29. data/db/migrate/{20250519075407_create_securial_sessions.rb → 20250604184841_create_securial_sessions.rb} +4 -0
  30. data/lib/generators/securial/install/templates/securial_initializer.erb +2 -2
  31. data/lib/generators/securial/scaffold/templates/controller.erb +1 -0
  32. data/lib/generators/securial/scaffold/templates/routes.erb +1 -1
  33. data/lib/securial/auth/auth_encoder.rb +42 -43
  34. data/lib/securial/auth/session_creator.rb +15 -12
  35. data/lib/securial/auth/token_generator.rb +26 -0
  36. data/lib/securial/auth.rb +12 -0
  37. data/lib/securial/config/configuration.rb +33 -15
  38. data/lib/securial/config/validation/logger_validation.rb +29 -0
  39. data/lib/securial/config/validation/mailer_validation.rb +24 -0
  40. data/lib/securial/config/validation/password_validation.rb +91 -0
  41. data/lib/securial/config/validation/response_validation.rb +37 -0
  42. data/lib/securial/config/validation/roles_validation.rb +32 -0
  43. data/lib/securial/config/validation/security_validation.rb +56 -0
  44. data/lib/securial/config/validation/session_validation.rb +87 -0
  45. data/lib/securial/config/validation.rb +16 -239
  46. data/lib/securial/config.rb +26 -0
  47. data/lib/securial/engine.rb +7 -70
  48. data/lib/securial/engine_initializers.rb +33 -0
  49. data/lib/securial/error/auth.rb +21 -0
  50. data/lib/securial/error/base_securial_error.rb +16 -0
  51. data/lib/securial/error/config.rb +37 -0
  52. data/lib/securial/error.rb +9 -0
  53. data/lib/securial/helpers/normalizing_helper.rb +11 -9
  54. data/lib/securial/helpers/regex_helper.rb +12 -10
  55. data/lib/securial/helpers/roles_helper.rb +17 -0
  56. data/lib/securial/helpers.rb +10 -0
  57. data/lib/securial/logger/broadcaster.rb +60 -0
  58. data/lib/securial/logger/builder.rb +45 -0
  59. data/lib/securial/logger/formatter.rb +35 -0
  60. data/lib/securial/logger.rb +7 -63
  61. data/lib/securial/middleware/request_tag_logger.rb +39 -0
  62. data/lib/securial/middleware.rb +13 -0
  63. data/lib/securial/version.rb +1 -1
  64. data/lib/securial.rb +2 -20
  65. metadata +45 -150
  66. data/app/views/securial/securial_mailer/reset_password.html.erb +0 -5
  67. data/app/views/securial/securial_mailer/reset_password.text.erb +0 -4
  68. data/lib/securial/auth/_index.rb +0 -3
  69. data/lib/securial/auth/errors.rb +0 -15
  70. data/lib/securial/config/_index.rb +0 -3
  71. data/lib/securial/config/errors.rb +0 -20
  72. data/lib/securial/helpers/_index.rb +0 -2
  73. data/lib/securial/inspectors/_index.rb +0 -1
  74. data/lib/securial/inspectors/route_inspector.rb +0 -52
  75. data/lib/securial/key_transformer.rb +0 -32
  76. data/lib/securial/middleware/_index.rb +0 -3
  77. data/lib/securial/middleware/request_logger_tag.rb +0 -18
  78. data/lib/securial/middleware/transform_request_keys.rb +0 -33
  79. data/lib/securial/middleware/transform_response_keys.rb +0 -45
  80. data/lib/securial/rack_attack.rb +0 -48
  81. /data/db/migrate/{20250518122749_create_securial_role_assignments.rb → 20250604123805_create_securial_role_assignments.rb} +0 -0
  82. /data/lib/generators/securial/install/{views_generastor.rb → views_generator.rb} +0 -0
@@ -1,247 +1,24 @@
1
1
  require "securial/logger"
2
+ require "securial/config/validation/logger_validation"
3
+ require "securial/config/validation/roles_validation"
4
+ require "securial/config/validation/session_validation"
5
+ require "securial/config/validation/mailer_validation"
6
+ require "securial/config/validation/password_validation"
7
+ require "securial/config/validation/response_validation"
8
+ require "securial/config/validation/security_validation"
2
9
 
3
10
  module Securial
4
11
  module Config
5
12
  module Validation
6
- class << self # rubocop:disable Metrics/ClassLength
7
- def validate_all!(config)
8
- validate_admin_role!(config)
9
- validate_session_config!(config)
10
- validate_mailer_sender!(config)
11
- validate_password_config!(config)
12
- validate_response_config!(config)
13
- validate_security_config!(config)
14
- end
15
-
16
- private
17
-
18
- def validate_admin_role!(config)
19
- if config.admin_role.nil? || config.admin_role.to_s.strip.empty?
20
- error_message = "Admin role is not set."
21
- Securial::ENGINE_LOGGER.fatal(error_message)
22
- raise Securial::Config::Errors::ConfigAdminRoleError, error_message
23
- end
24
-
25
- unless config.admin_role.is_a?(Symbol) || config.admin_role.is_a?(String)
26
- error_message = "Admin role must be a Symbol or String."
27
- Securial::ENGINE_LOGGER.fatal(error_message)
28
- raise Securial::Config::Errors::ConfigAdminRoleError, error_message
29
- end
30
-
31
- if config.admin_role.to_s.pluralize.downcase == "accounts"
32
- error_message = "The admin role cannot be 'account' or 'accounts' as it conflicts with the default routes."
33
- Securial::ENGINE_LOGGER.fatal(error_message)
34
- raise Securial::Config::Errors::ConfigAdminRoleError, error_message
35
- end
36
- end
37
-
38
- def validate_session_config!(config)
39
- validate_session_expiry_duration!(config)
40
- validate_session_algorithm!(config)
41
- validate_session_secret!(config)
42
- end
43
-
44
- def validate_session_expiry_duration!(config)
45
- if config.session_expiration_duration.nil?
46
- error_message = "Session expiration duration is not set."
47
- Securial::ENGINE_LOGGER.fatal(error_message)
48
- raise Securial::Config::Errors::ConfigSessionExpirationDurationError, error_message
49
- end
50
- if config.session_expiration_duration.class != ActiveSupport::Duration
51
- error_message = "Session expiration duration must be an ActiveSupport::Duration."
52
- Securial::ENGINE_LOGGER.fatal(error_message)
53
- raise Securial::Config::Errors::ConfigSessionExpirationDurationError, error_message
54
- end
55
- if config.session_expiration_duration <= 0
56
- Securial::ENGINE_LOGGER.fatal("Session expiration duration must be greater than 0.")
57
- raise Securial::Config::Errors::ConfigSessionExpirationDurationError, "Session expiration duration must be greater than 0."
58
- end
59
- end
60
-
61
- def validate_session_algorithm!(config)
62
- if config.session_algorithm.blank?
63
- error_message = "Session algorithm is not set."
64
- Securial::ENGINE_LOGGER.fatal(error_message)
65
- raise Securial::Config::Errors::ConfigSessionAlgorithmError, error_message
66
- end
67
- unless config.session_algorithm.is_a?(Symbol)
68
- error_message = "Session algorithm must be a Symbol."
69
- Securial::ENGINE_LOGGER.fatal(error_message)
70
- raise Securial::Config::Errors::ConfigSessionAlgorithmError, error_message
71
- end
72
- valid_algorithms = Securial::Config::VALID_SESSION_ENCRYPTION_ALGORITHMS
73
- unless valid_algorithms.include?(config.session_algorithm)
74
- error_message = "Invalid session algorithm. Valid options are: #{valid_algorithms.map(&:inspect).join(', ')}."
75
- Securial::ENGINE_LOGGER.fatal(error_message)
76
- raise Securial::Config::Errors::ConfigSessionAlgorithmError, error_message
77
- end
78
- end
79
-
80
- def validate_session_secret!(config)
81
- if config.session_secret.blank?
82
- error_message = "Session secret is not set."
83
- Securial::ENGINE_LOGGER.fatal(error_message)
84
- raise Securial::Config::Errors::ConfigSessionSecretError, error_message
85
- end
86
- unless config.session_secret.is_a?(String)
87
- error_message = "Session secret must be a String."
88
- Securial::ENGINE_LOGGER.fatal(error_message)
89
- raise Securial::Config::Errors::ConfigSessionSecretError, error_message
90
- end
91
- end
92
-
93
- def validate_mailer_sender!(config)
94
- if config.mailer_sender.blank?
95
- error_message = "Mailer sender is not set."
96
- Securial::ENGINE_LOGGER.fatal(error_message)
97
- raise Securial::Config::Errors::ConfigMailerSenderError, error_message
98
- end
99
- if config.mailer_sender !~ URI::MailTo::EMAIL_REGEXP
100
- error_message = "Mailer sender is not a valid email address."
101
- Securial::ENGINE_LOGGER.fatal(error_message)
102
- raise Securial::Config::Errors::ConfigMailerSenderError, error_message
103
- end
104
- end
105
-
106
- def validate_password_config!(config)
107
- validate_password_reset_subject!(config)
108
- validate_password_min_max_length!(config)
109
- validate_password_complexity!(config)
110
- validate_password_expiration!(config)
111
- validate_password_reset_token!(config)
112
- end
113
-
114
- def validate_password_reset_token!(config)
115
- if config.reset_password_token_secret.blank?
116
- error_message = "Reset password token secret is not set."
117
- Securial::ENGINE_LOGGER.fatal(error_message)
118
- raise Securial::Config::Errors::ConfigPasswordError, error_message
119
- end
120
- unless config.reset_password_token_secret.is_a?(String)
121
- error_message = "Reset password token secret must be a String."
122
- Securial::ENGINE_LOGGER.fatal(error_message)
123
- raise Securial::Config::Errors::ConfigPasswordError, error_message
124
- end
125
- end
126
-
127
- def validate_password_reset_subject!(config)
128
- if config.password_reset_email_subject.blank?
129
- error_message = "Password reset email subject is not set."
130
- Securial::ENGINE_LOGGER.fatal(error_message)
131
- raise Securial::Config::Errors::ConfigPasswordError, error_message
132
- end
133
- unless config.password_reset_email_subject.is_a?(String)
134
- error_message = "Password reset email subject must be a String."
135
- Securial::ENGINE_LOGGER.fatal(error_message)
136
- raise Securial::Config::Errors::ConfigPasswordError, error_message
137
- end
138
- end
139
-
140
- def validate_password_min_max_length!(config)
141
- unless config.password_min_length.is_a?(Integer) && config.password_min_length > 0
142
- error_message = "Password minimum length must be a positive integer."
143
- Securial::ENGINE_LOGGER.fatal(error_message)
144
- raise Securial::Config::Errors::ConfigPasswordError, error_message
145
- end
146
- unless config.password_max_length.is_a?(Integer) && config.password_max_length >= config.password_min_length
147
- error_message = "Password maximum length must be an integer greater than or equal to the minimum length."
148
- Securial::ENGINE_LOGGER.fatal(error_message)
149
- raise Securial::Config::Errors::ConfigPasswordError, error_message
150
- end
151
- end
152
-
153
- def validate_password_complexity!(config)
154
- if config.password_complexity.nil? || !config.password_complexity.is_a?(Regexp)
155
- error_message = "Password complexity regex is not set or is not a valid Regexp."
156
- Securial::ENGINE_LOGGER.fatal(error_message)
157
- raise Securial::Config::Errors::ConfigPasswordError, error_message
158
- end
159
- end
160
-
161
- def validate_password_expiration!(config)
162
- unless config.password_expires.is_a?(TrueClass) || config.password_expires.is_a?(FalseClass)
163
- error_message = "Password expiration must be a boolean value."
164
- Securial::ENGINE_LOGGER.fatal(error_message)
165
- raise Securial::Config::Errors::ConfigPasswordError, error_message
166
- end
167
-
168
- if config.password_expires == true && (
169
- config.password_expires_in.nil? ||
170
- !config.password_expires_in.is_a?(ActiveSupport::Duration) ||
171
- config.password_expires_in <= 0
172
- )
173
- error_message = "Password expiration duration is not set or is not a valid ActiveSupport::Duration."
174
- Securial::ENGINE_LOGGER.fatal(error_message)
175
- raise Securial::Config::Errors::ConfigPasswordError, error_message
176
- end
177
- end
178
-
179
- def validate_response_config!(config)
180
- validate_response_keys_format!(config)
181
- validate_timestamps_in_response!(config)
182
- end
183
-
184
- def validate_response_keys_format!(config)
185
- valid_formats = Securial::Config::VALID_RESPONSE_KEYS_FORMATS
186
- unless valid_formats.include?(config.response_keys_format)
187
- error_message = "Invalid response_keys_format option. Valid options are: #{valid_formats.map(&:inspect).join(', ')}."
188
- Securial::ENGINE_LOGGER.fatal(error_message)
189
- raise Securial::Config::Errors::ConfigResponseError, error_message
190
- end
191
- end
192
-
193
- def validate_timestamps_in_response!(config)
194
- valid_options = Securial::Config::VALID_TIMESTAMP_OPTIONS
195
- unless valid_options.include?(config.timestamps_in_response)
196
- error_message = "Invalid timestamps_in_response option. Valid options are: #{valid_options.map(&:inspect).join(', ')}."
197
- Securial::ENGINE_LOGGER.fatal(error_message)
198
- raise Securial::Config::Errors::ConfigResponseError, error_message
199
- end
200
- end
201
-
202
- def validate_security_config!(config)
203
- validate_security_headers!(config)
204
- validate_rate_limiting!(config)
205
- end
206
-
207
- def validate_security_headers!(config)
208
- valid_options = Securial::Config::VALID_SECURITY_HEADERS
209
- unless valid_options.include?(config.security_headers)
210
- error_message = "Invalid security_headers option. Valid options are: #{valid_options.map(&:inspect).join(', ')}."
211
- Securial::ENGINE_LOGGER.fatal(error_message)
212
- raise Securial::Config::Errors::ConfigSecurityError, error_message
213
- end
214
- end
215
-
216
- def validate_rate_limiting!(config) # rubocop:disable Metrics/MethodLength
217
- unless config.rate_limiting_enabled.is_a?(TrueClass) || config.rate_limiting_enabled.is_a?(FalseClass)
218
- error_message = "rate_limiting_enabled must be a boolean value."
219
- Securial::ENGINE_LOGGER.fatal(error_message)
220
- raise Securial::Config::Errors::ConfigSecurityError, error_message
221
- end
222
-
223
- return unless config.rate_limiting_enabled
224
-
225
- unless
226
- config.rate_limit_requests_per_minute.is_a?(Integer) &&
227
- config.rate_limit_requests_per_minute > 0
228
-
229
- error_message = "rate_limit_requests_per_minute must be a positive integer when rate limiting is enabled."
230
- Securial::ENGINE_LOGGER.fatal(error_message)
231
- raise Securial::Config::Errors::ConfigSecurityError, error_message
232
- end
233
-
234
- unless config.rate_limit_response_status.is_a?(Integer) && config.rate_limit_response_status.between?(400, 599)
235
- error_message = "rate_limit_response_status must be an HTTP status code between 4xx and 5xx."
236
- Securial::ENGINE_LOGGER.fatal(error_message)
237
- raise Securial::Config::Errors::ConfigSecurityError, error_message
238
- end
239
-
240
- unless config.rate_limit_response_message.is_a?(String) && !config.rate_limit_response_message.strip.empty?
241
- error_message = "rate_limit_response_message must be a non-empty String."
242
- Securial::ENGINE_LOGGER.fatal(error_message)
243
- raise Securial::Config::Errors::ConfigSecurityError, error_message
244
- end
13
+ class << self
14
+ def validate_all!(securial_config)
15
+ Securial::Config::Validation::LoggerValidation.validate!(securial_config)
16
+ Securial::Config::Validation::RolesValidation.validate!(securial_config)
17
+ Securial::Config::Validation::SessionValidation.validate!(securial_config)
18
+ Securial::Config::Validation::MailerValidation.validate!(securial_config)
19
+ Securial::Config::Validation::PasswordValidation.validate!(securial_config)
20
+ Securial::Config::Validation::ResponseValidation.validate!(securial_config)
21
+ Securial::Config::Validation::SecurityValidation.validate!(securial_config)
245
22
  end
246
23
  end
247
24
  end
@@ -0,0 +1,26 @@
1
+ require "securial/config/configuration"
2
+ require "securial/config/validation"
3
+
4
+ module Securial
5
+ class << self
6
+ attr_accessor :configuration
7
+
8
+ def configuration
9
+ @configuration ||= Config::Configuration.new
10
+ end
11
+
12
+ def configuration=(config)
13
+ if config.is_a?(Config::Configuration)
14
+ @configuration = config
15
+ Securial::Config::Validation.validate_all!(configuration)
16
+ else
17
+ raise ArgumentError, "Expected an instance of Securial::Config::Configuration"
18
+ end
19
+ end
20
+
21
+ def configure
22
+ yield(configuration) if block_given?
23
+ Securial::Config::Validation.validate_all!(configuration)
24
+ end
25
+ end
26
+ end
@@ -1,73 +1,14 @@
1
- require_relative "./logger"
2
- require_relative "./key_transformer"
3
- require_relative "./config/_index"
4
- require_relative "./helpers/_index"
5
- require_relative "./auth/_index"
6
- require_relative "./inspectors/_index"
7
-
8
- require_relative "./middleware/_index"
9
- require "jwt"
1
+ require "securial/auth"
2
+ require "securial/config"
3
+ require "securial/helpers"
4
+ require "securial/logger"
5
+ require "securial/middleware"
10
6
 
11
7
  module Securial
12
8
  class Engine < ::Rails::Engine
13
9
  isolate_namespace Securial
14
10
 
15
- # Set API-only mode and autoload custom generator paths
16
- config.api_only = true
17
11
  config.generators.api_only = true
18
- config.autoload_paths += Dir["#{config.root}/lib/generators"]
19
-
20
- initializer "securial.filter_parameters" do |app|
21
- app.config.filter_parameters += [
22
- :password,
23
- :password_confirmation,
24
- :password_reset_token,
25
- :reset_password_token
26
- ]
27
- end
28
-
29
- initializer "securial.logger" do
30
- Securial.const_set(:ENGINE_LOGGER, Securial::Logger.build)
31
- end
32
-
33
- initializer "securial.log_initialization" do |app|
34
- log "[Initializing Engine] Host app: #{app.class.name}"
35
- end
36
-
37
- initializer "securial.validate_config" do
38
- log "[Validating configuration] from `config/initializers/securial.rb`..."
39
- Securial::Config::Validation.validate_all!(Securial.configuration)
40
- end
41
-
42
- initializer "securial.extend_application_controller" do
43
- ActiveSupport.on_load(:action_controller_base) { include Securial::Identity }
44
- ActiveSupport.on_load(:action_controller_api) { include Securial::Identity }
45
- end
46
-
47
- initializer "securial.factory_bot", after: "factory_bot.set_factory_paths" do
48
- FactoryBot.definition_file_paths << Engine.root.join("lib", "securial", "factories") if defined?(FactoryBot)
49
- end
50
-
51
- initializer "securial.factory_bot_generator" do
52
- require_relative "../generators/factory_bot/model/model_generator"
53
- end
54
-
55
- initializer "securial.middleware" do |app|
56
- middleware.use Securial::Middleware::RequestLoggerTag
57
- middleware.use Securial::Middleware::TransformRequestKeys
58
- middleware.use Securial::Middleware::TransformResponseKeys
59
- if Securial.configuration.rate_limiting_enabled
60
- require "rack/attack"
61
- require_relative "./rack_attack"
62
- middleware.use Rack::Attack
63
- end
64
- end
65
-
66
- initializer "securial.log_ready", after: :load_config_initializers do
67
- Rails.application.config.after_initialize do
68
- log "[Engine fully initialized] Environment: #{Rails.env}"
69
- end
70
- end
71
12
 
72
13
  config.generators do |g|
73
14
  g.orm :active_record, primary_key_type: :string
@@ -82,11 +23,7 @@ module Securial
82
23
  g.fixture_replacement :factory_bot, dir: "lib/securial/factories"
83
24
  g.template_engine :jbuilder
84
25
  end
85
-
86
- private
87
-
88
- def log(message)
89
- Securial::ENGINE_LOGGER.info("[Securial] #{message}")
90
- end
91
26
  end
92
27
  end
28
+
29
+ require_relative "engine_initializers"
@@ -0,0 +1,33 @@
1
+ module Securial
2
+ class Engine < ::Rails::Engine
3
+ initializer "securial.filter_parameters" do |app|
4
+ app.config.filter_parameters += [
5
+ :password,
6
+ :password_confirmation,
7
+ :password_reset_token,
8
+ :reset_password_token
9
+ ]
10
+ end
11
+
12
+ initializer "securial.factory_bot", after: "factory_bot.set_factory_paths" do
13
+ if defined?(FactoryBot)
14
+ FactoryBot.definition_file_paths << Engine.root.join("lib", "securial", "factories")
15
+ require_relative "../generators/factory_bot/model/model_generator"
16
+ end
17
+ end
18
+
19
+ initializer "securial.logger_middleware" do |app|
20
+ app.middleware.use Securial::Middleware::RequestTagLogger
21
+ end
22
+
23
+ initializer "securial.extend_application_controller" do
24
+ ActiveSupport.on_load(:action_controller_base) { include Securial::Identity }
25
+ ActiveSupport.on_load(:action_controller_api) { include Securial::Identity }
26
+ end
27
+
28
+ initializer "securial.action_mailer.preview_path", after: "action_mailer.set_configs" do |app|
29
+ app.config.action_mailer.preview_paths ||= []
30
+ app.config.action_mailer.preview_paths |= [root.join("spec/mailers/previews").to_s]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module Securial
2
+ module Error
3
+ module Auth
4
+ class TokenEncodeError < BaseError
5
+ default_message "Error while encoding session token"
6
+ end
7
+
8
+ class TokenDecodeError < BaseError
9
+ default_message "Error while decoding session token"
10
+ end
11
+
12
+ class TokenRevokedError < BaseError
13
+ default_message "Session token is revoked"
14
+ end
15
+
16
+ class TokenExpiredError < BaseError
17
+ default_message "Session token is expired"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Securial
2
+ module Error
3
+ class BaseError < StandardError
4
+ def self.default_message(message = nil)
5
+ @default_message = message if message
6
+ @default_message
7
+ end
8
+
9
+ def initialize(message = nil)
10
+ super(message || self.class.default_message || "An error occurred in Securial")
11
+ end
12
+
13
+ def backtrace; []; end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module Securial
2
+ module Error
3
+ module Config
4
+ class InvalidConfigurationError < Securial::Error::BaseError
5
+ default_message "Invalid configuration for Securial"
6
+ end
7
+
8
+ class LoggerValidationError < InvalidConfigurationError
9
+ default_message "Logger configuration validation failed"
10
+ end
11
+
12
+ class RolesValidationError < InvalidConfigurationError
13
+ default_message "Roles configuration validation failed"
14
+ end
15
+
16
+ class SessionValidationError < InvalidConfigurationError
17
+ default_message "Session configuration validation failed"
18
+ end
19
+
20
+ class MailerValidationError < InvalidConfigurationError
21
+ default_message "Mailer configuration validation failed"
22
+ end
23
+
24
+ class PasswordValidationError < InvalidConfigurationError
25
+ default_message "Password configuration validation failed"
26
+ end
27
+
28
+ class ResponseValidationError < InvalidConfigurationError
29
+ default_message "Response configuration validation failed"
30
+ end
31
+
32
+ class SecurityValidationError < InvalidConfigurationError
33
+ default_message "Security configuration validation failed"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ require "securial/error/base_securial_error"
2
+ require "securial/error/config"
3
+ require "securial/error/auth"
4
+
5
+ module Securial
6
+ module Error
7
+ # This serves as a namespace for all Securial errors.
8
+ end
9
+ end
@@ -1,17 +1,19 @@
1
1
  module Securial
2
- module NormalizingHelper
3
- module_function
2
+ module Helpers
3
+ module NormalizingHelper
4
+ module_function
4
5
 
5
- def normalize_email_address(email)
6
- return "" if email.empty?
6
+ def normalize_email_address(email)
7
+ return "" if email.empty?
7
8
 
8
- email.strip.downcase
9
- end
9
+ email.strip.downcase
10
+ end
10
11
 
11
- def normalize_role_name(role_name)
12
- return "" if role_name.empty?
12
+ def normalize_role_name(role_name)
13
+ return "" if role_name.empty?
13
14
 
14
- role_name.strip.downcase.titleize
15
+ role_name.strip.downcase.titleize
16
+ end
15
17
  end
16
18
  end
17
19
  end
@@ -1,16 +1,18 @@
1
1
  module Securial
2
- module RegexHelper
3
- EMAIL_REGEX = URI::MailTo::EMAIL_REGEXP
4
- USERNAME_REGEX = /\A(?![0-9])[a-zA-Z](?:[a-zA-Z0-9]|[._](?![._]))*[a-zA-Z0-9]\z/
5
- PASSWORD_REGEX = %r{\A(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])[a-zA-Z].*\z}
2
+ module Helpers
3
+ module RegexHelper
4
+ EMAIL_REGEX = URI::MailTo::EMAIL_REGEXP
5
+ USERNAME_REGEX = /\A(?![0-9])[a-zA-Z](?:[a-zA-Z0-9]|[._](?![._]))*[a-zA-Z0-9]\z/
6
+ PASSWORD_REGEX = %r{\A(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])[a-zA-Z].*\z}
6
7
 
7
- class << self
8
- def valid_email?(email)
9
- email.match?(EMAIL_REGEX)
10
- end
8
+ class << self
9
+ def valid_email?(email)
10
+ email.match?(EMAIL_REGEX)
11
+ end
11
12
 
12
- def valid_username?(username)
13
- username.match?(USERNAME_REGEX)
13
+ def valid_username?(username)
14
+ username.match?(USERNAME_REGEX)
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -0,0 +1,17 @@
1
+ module Securial
2
+ module Helpers
3
+ module RolesHelper
4
+ # This module provides helper methods related to roles.
5
+ # It can be extended with additional role-related functionality as needed.
6
+ class << self
7
+ def protected_namespace
8
+ Securial.configuration.admin_role.to_s.strip.underscore.pluralize
9
+ end
10
+
11
+ def titleized_admin_role
12
+ Securial.configuration.admin_role.to_s.strip.titleize
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require "securial/helpers/normalizing_helper"
2
+ require "securial/helpers/regex_helper"
3
+ require "securial/helpers/roles_helper"
4
+
5
+ module Securial
6
+ module Helpers
7
+ # This module acts as a namespace for helper modules.
8
+ # It requires all helper modules to make them available for consumers.
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ module Securial
2
+ module Logger
3
+ class Broadcaster
4
+ def initialize(loggers)
5
+ @loggers = loggers
6
+ end
7
+
8
+ ::Logger::Severity.constants.each do |severity|
9
+ define_method(severity.downcase) do |*args, &block|
10
+ @loggers.each { |logger| logger.public_send(severity.downcase, *args, &block) }
11
+ end
12
+ end
13
+
14
+ def <<(msg)
15
+ @loggers.each { |logger| logger << msg }
16
+ end
17
+
18
+ def close
19
+ @loggers.each(&:close)
20
+ end
21
+
22
+ def formatter=(_formatter)
23
+ # Do nothing
24
+ end
25
+
26
+ def formatter
27
+ nil
28
+ end
29
+
30
+ def loggers
31
+ @loggers
32
+ end
33
+
34
+ def tagged(*tags, &block)
35
+ # If all loggers support tagged, nest the calls, otherwise just yield.
36
+ taggable_loggers = @loggers.select { |logger| logger.respond_to?(:tagged) }
37
+ if taggable_loggers.any?
38
+ # Nest tags for all taggable loggers
39
+ taggable_loggers.reverse.inject(block) do |blk, logger|
40
+ proc { logger.tagged(*tags, &blk) }
41
+ end.call
42
+ else
43
+ yield
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(method, include_private = false)
48
+ @loggers.any? { |logger| logger.respond_to?(method, include_private) } || super
49
+ end
50
+
51
+ def method_missing(method, *args, &block)
52
+ if @loggers.all? { |logger| logger.respond_to?(method) }
53
+ @loggers.map { |logger| logger.send(method, *args, &block) }
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end