securial 0.8.1 → 1.0.1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -16
  3. data/app/controllers/concerns/securial/identity.rb +18 -9
  4. data/app/controllers/securial/status_controller.rb +2 -0
  5. data/app/controllers/securial/users_controller.rb +1 -1
  6. data/app/views/securial/status/show.json.jbuilder +1 -1
  7. data/bin/securial +5 -54
  8. data/db/migrate/20250606182648_seed_roles_and_users.rb +69 -0
  9. data/lib/generators/securial/install/install_generator.rb +2 -2
  10. data/lib/generators/securial/install/templates/securial_initializer.erb +115 -18
  11. data/lib/generators/securial/install/views_generator.rb +2 -1
  12. data/lib/generators/securial/jbuilder/jbuilder_generator.rb +2 -0
  13. data/lib/generators/securial/scaffold/scaffold_generator.rb +2 -0
  14. data/lib/securial/auth/auth_encoder.rb +3 -3
  15. data/lib/securial/auth/session_creator.rb +1 -1
  16. data/lib/securial/auth/token_generator.rb +13 -13
  17. data/lib/securial/cli.rb +158 -0
  18. data/lib/securial/config/configuration.rb +3 -53
  19. data/lib/securial/config/signature.rb +107 -0
  20. data/lib/securial/config/validation.rb +59 -16
  21. data/lib/securial/config.rb +17 -16
  22. data/lib/securial/engine.rb +2 -0
  23. data/lib/securial/engine_initializers.rb +21 -2
  24. data/lib/securial/error/base_securial_error.rb +5 -3
  25. data/lib/securial/error/config.rb +0 -28
  26. data/lib/securial/helpers/key_transformer.rb +33 -0
  27. data/lib/securial/helpers/normalizing_helper.rb +1 -1
  28. data/lib/securial/helpers/regex_helper.rb +6 -7
  29. data/lib/securial/helpers/roles_helper.rb +6 -7
  30. data/lib/securial/helpers.rb +1 -0
  31. data/lib/securial/logger.rb +7 -8
  32. data/lib/securial/middleware/response_headers.rb +19 -0
  33. data/lib/securial/middleware/transform_request_keys.rb +35 -0
  34. data/lib/securial/middleware/transform_response_keys.rb +47 -0
  35. data/lib/securial/middleware.rb +3 -0
  36. data/lib/securial/security/request_rate_limiter.rb +45 -0
  37. data/lib/securial/security.rb +8 -0
  38. data/lib/securial/version.rb +1 -1
  39. data/lib/securial.rb +4 -4
  40. data/lib/tasks/securial_routes.rake +26 -0
  41. metadata +47 -19
  42. data/lib/securial/config/validation/logger_validation.rb +0 -29
  43. data/lib/securial/config/validation/mailer_validation.rb +0 -24
  44. data/lib/securial/config/validation/password_validation.rb +0 -91
  45. data/lib/securial/config/validation/response_validation.rb +0 -37
  46. data/lib/securial/config/validation/roles_validation.rb +0 -32
  47. data/lib/securial/config/validation/security_validation.rb +0 -56
  48. data/lib/securial/config/validation/session_validation.rb +0 -87
@@ -1,15 +1,14 @@
1
1
  require_relative "logger/builder"
2
2
 
3
3
  module Securial
4
- class << self
5
- attr_accessor :logger
4
+ extend self
5
+ attr_accessor :logger
6
6
 
7
- def logger
8
- @logger ||= Logger::Builder.build
9
- end
7
+ def logger
8
+ @logger ||= Logger::Builder.build
9
+ end
10
10
 
11
- def logger=(logger)
12
- @logger = logger
13
- end
11
+ def logger=(logger)
12
+ @logger = logger
14
13
  end
15
14
  end
@@ -0,0 +1,19 @@
1
+ module Securial
2
+ module Middleware
3
+ # This middleware removes sensitive headers from the request environment.
4
+ # It is designed to enhance security by ensuring that sensitive information
5
+ # is not inadvertently logged or processed.
6
+ class ResponseHeaders
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ status, headers, response = @app.call(env)
13
+ headers["X-Securial-Mounted"] = "true"
14
+ headers["X-Securial-Developer"] = "Aly Badawy - https://alybadawy.com | @alybadawy"
15
+ [status, headers, response]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Securial
2
+ module Middleware
3
+ # This middleware transforms request keys to a specified format.
4
+ # It uses the KeyTransformer helper to apply the transformation.
5
+ #
6
+ # It reads the request body if the content type is JSON and transforms
7
+ # the keys to underscore format. If the body is not valid JSON, it does nothing.
8
+ class TransformRequestKeys
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ if env["CONTENT_TYPE"]&.include?("application/json")
15
+ req = Rack::Request.new(env)
16
+ if (req.body&.size || 0) > 0
17
+ raw = req.body.read
18
+ req.body.rewind
19
+ begin
20
+ parsed = JSON.parse(raw)
21
+ transformed = Securial::Helpers::KeyTransformer.deep_transform_keys(parsed) do |key|
22
+ Securial::Helpers::KeyTransformer.underscore(key)
23
+ end
24
+ env["rack.input"] = StringIO.new(JSON.dump(transformed))
25
+ env["rack.input"].rewind
26
+ rescue JSON::ParserError
27
+ # no-op
28
+ end
29
+ end
30
+ end
31
+ @app.call(env)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ module Securial
2
+ module Middleware
3
+ # This middleware transforms response keys to a specified format.
4
+ # It uses the KeyTransformer helper to apply the transformation.
5
+ #
6
+ # It reads the response body if the content type is JSON and transforms
7
+ # the keys to the specified format (default is lowerCamelCase).
8
+ class TransformResponseKeys
9
+ def initialize(app, format: :lowerCamelCase)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ status, headers, response = @app.call(env)
15
+
16
+ if json_response?(headers)
17
+ body = extract_body(response)
18
+
19
+ if body.present?
20
+ format = Securial.configuration.response_keys_format
21
+ transformed = Securial::Helpers::KeyTransformer.deep_transform_keys(JSON.parse(body)) do |key|
22
+ Securial::Helpers::KeyTransformer.camelize(key, format)
23
+ end
24
+
25
+ new_body = [JSON.generate(transformed)]
26
+ headers["Content-Length"] = new_body.first.bytesize.to_s
27
+ return [status, headers, new_body]
28
+ end
29
+ end
30
+
31
+ [status, headers, response]
32
+ end
33
+
34
+ private
35
+
36
+ def json_response?(headers)
37
+ headers["Content-Type"]&.include?("application/json")
38
+ end
39
+
40
+ def extract_body(response)
41
+ response_body = ""
42
+ response.each { |part| response_body << part }
43
+ response_body
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,4 +1,7 @@
1
1
  require "securial/middleware/request_tag_logger"
2
+ require "securial/middleware/transform_request_keys"
3
+ require "securial/middleware/transform_response_keys"
4
+ require "securial/middleware/response_headers"
2
5
 
3
6
  module Securial
4
7
  module Middleware
@@ -0,0 +1,45 @@
1
+ require "rack/attack"
2
+ require "securial/config"
3
+
4
+ module Securial
5
+ module Security
6
+ module RequestRateLimiter
7
+ extend self
8
+
9
+ def apply! # rubocop:disable Metrics/MethodLength
10
+ resp_status = Securial.configuration.rate_limit_response_status
11
+ resp_message = Securial.configuration.rate_limit_response_message
12
+ throttle_configs = [
13
+ { name: "securial/logins/ip", path: "sessions/login", key: ->(req) { req.ip } },
14
+ { name: "securial/logins/email", path: "sessions/login", key: ->(req) { req.params["email_address"].to_s.downcase.strip } },
15
+ { name: "securial/password_resets/ip", path: "password/forgot", key: ->(req) { req.ip } },
16
+ { name: "securial/password_resets/email", path: "password/forgot", key: ->(req) { req.params["email_address"].to_s.downcase.strip } },
17
+ ]
18
+
19
+ throttle_configs.each do |config|
20
+ Rack::Attack.throttle(config[:name],
21
+ limit: ->(_req) { Securial.configuration.rate_limit_requests_per_minute },
22
+ period: 1.minute
23
+ ) do |req|
24
+ if req.path.include?(config[:path]) && req.post?
25
+ config[:key].call(req)
26
+ end
27
+ end
28
+ end
29
+
30
+ # Custom response for throttled requests
31
+ Rack::Attack.throttled_responder = lambda do |request|
32
+ retry_after = (request.env["rack.attack.match_data"] || {})[:period]
33
+ [
34
+ resp_status,
35
+ {
36
+ "Content-Type" => "application/json",
37
+ "Retry-After" => retry_after.to_s,
38
+ },
39
+ [{ error: resp_message }.to_json],
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ require "securial/security/request_rate_limiter"
2
+
3
+ module Securial
4
+ module Security
5
+ # This module serves as a namespace for security-related functionality.
6
+ # It can be extended with additional security features in the future.
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Securial
2
- VERSION = "0.8.1".freeze
2
+ VERSION = "1.0.1".freeze
3
3
  end
data/lib/securial.rb CHANGED
@@ -4,8 +4,8 @@ require "securial/engine"
4
4
  require "jbuilder"
5
5
 
6
6
  module Securial
7
- class << self
8
- delegate :protected_namespace, to: Securial::Helpers::RolesHelper
9
- delegate :titleized_admin_role, to: Securial::Helpers::RolesHelper
10
- end
7
+ extend self
8
+
9
+ delegate :protected_namespace, to: Securial::Helpers::RolesHelper
10
+ delegate :titleized_admin_role, to: Securial::Helpers::RolesHelper
11
11
  end
@@ -0,0 +1,26 @@
1
+ namespace :securial do
2
+ desc "Print all routes for the Securial engine"
3
+ task routes: :environment do
4
+ engine = Securial::Engine
5
+ routes = engine.routes
6
+
7
+ puts "Routes for Securial::Engine:"
8
+ all_routes = routes.routes.map do |route|
9
+ {
10
+ verb: route.verb,
11
+ path: route.path.spec.to_s.gsub("(.:format)", ""),
12
+ name: route.name,
13
+ controller: route.defaults[:controller],
14
+ action: route.defaults[:action],
15
+ }
16
+ end
17
+
18
+ # Print in a table format with adjusted column widths
19
+ puts "%-10s %-35s %-35s %-30s" % ["Verb", "Path", "Name", "Controller#Action"]
20
+ puts "-" * 110
21
+ all_routes.each do |r|
22
+ ctrl_action = [r[:controller], r[:action]].compact.join("#")
23
+ puts "%-10s %-35s %-35s %-30s" % [r[:verb], r[:path], r[:name], ctrl_action]
24
+ end
25
+ end
26
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: securial
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aly Badawy
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-06-06 00:00:00.000000000 Z
10
+ date: 2025-06-24 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -43,28 +43,48 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.11'
46
+ version: '2.13'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2.11'
53
+ version: '2.13'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: jwt
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ - - "<"
62
+ - !ruby/object:Gem::Version
63
+ version: '3.2'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '3.0'
71
+ - - "<"
72
+ - !ruby/object:Gem::Version
73
+ version: '3.2'
74
+ - !ruby/object:Gem::Dependency
75
+ name: rack-attack
56
76
  requirement: !ruby/object:Gem::Requirement
57
77
  requirements:
58
78
  - - "~>"
59
79
  - !ruby/object:Gem::Version
60
- version: '2.10'
80
+ version: '6.7'
61
81
  type: :runtime
62
82
  prerelease: false
63
83
  version_requirements: !ruby/object:Gem::Requirement
64
84
  requirements:
65
85
  - - "~>"
66
86
  - !ruby/object:Gem::Version
67
- version: '2.10'
87
+ version: '6.7'
68
88
  description: Securial is a mountable Rails engine that provides robust, extensible
69
89
  authentication and access control for Rails applications. It supports JWT, API tokens,
70
90
  session-based auth, and is designed for easy integration with modern web and mobile
@@ -127,6 +147,7 @@ files:
127
147
  - db/migrate/20250604110520_create_securial_users.rb
128
148
  - db/migrate/20250604123805_create_securial_role_assignments.rb
129
149
  - db/migrate/20250604184841_create_securial_sessions.rb
150
+ - db/migrate/20250606182648_seed_roles_and_users.rb
130
151
  - lib/generators/factory_bot/model/model_generator.rb
131
152
  - lib/generators/factory_bot/templates/factory.erb
132
153
  - lib/generators/securial/install/install_generator.rb
@@ -146,16 +167,11 @@ files:
146
167
  - lib/securial/auth/auth_encoder.rb
147
168
  - lib/securial/auth/session_creator.rb
148
169
  - lib/securial/auth/token_generator.rb
170
+ - lib/securial/cli.rb
149
171
  - lib/securial/config.rb
150
172
  - lib/securial/config/configuration.rb
173
+ - lib/securial/config/signature.rb
151
174
  - lib/securial/config/validation.rb
152
- - lib/securial/config/validation/logger_validation.rb
153
- - lib/securial/config/validation/mailer_validation.rb
154
- - lib/securial/config/validation/password_validation.rb
155
- - lib/securial/config/validation/response_validation.rb
156
- - lib/securial/config/validation/roles_validation.rb
157
- - lib/securial/config/validation/security_validation.rb
158
- - lib/securial/config/validation/session_validation.rb
159
175
  - lib/securial/engine.rb
160
176
  - lib/securial/engine_initializers.rb
161
177
  - lib/securial/error.rb
@@ -167,6 +183,7 @@ files:
167
183
  - lib/securial/factories/securial/sessions.rb
168
184
  - lib/securial/factories/securial/users.rb
169
185
  - lib/securial/helpers.rb
186
+ - lib/securial/helpers/key_transformer.rb
170
187
  - lib/securial/helpers/normalizing_helper.rb
171
188
  - lib/securial/helpers/regex_helper.rb
172
189
  - lib/securial/helpers/roles_helper.rb
@@ -176,23 +193,34 @@ files:
176
193
  - lib/securial/logger/formatter.rb
177
194
  - lib/securial/middleware.rb
178
195
  - lib/securial/middleware/request_tag_logger.rb
196
+ - lib/securial/middleware/response_headers.rb
197
+ - lib/securial/middleware/transform_request_keys.rb
198
+ - lib/securial/middleware/transform_response_keys.rb
199
+ - lib/securial/security.rb
200
+ - lib/securial/security/request_rate_limiter.rb
179
201
  - lib/securial/version.rb
202
+ - lib/tasks/securial_routes.rake
180
203
  - lib/tasks/securial_tasks.rake
181
204
  homepage: https://github.com/AlyBadawy/Securial/wiki
182
205
  licenses:
183
206
  - MIT
184
207
  metadata:
185
- release_date: '2025-06-06'
208
+ release_date: '2025-06-24'
186
209
  allowed_push_host: https://rubygems.org
187
210
  homepage_uri: https://github.com/AlyBadawy/Securial/wiki
188
211
  source_code_uri: https://github.com/AlyBadawy/Securial
189
212
  changelog_uri: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md
190
213
  post_install_message: "\n ---\n [SECURIAL] Thank you for installing Securial!\n\n
191
- \ Securial is a mountable Rails engine that provides robust,\n extensible authentication
192
- and access control for Rails applications.\n Please review the [changelog]
193
- and [WIKI] for more info on the latest\n changes and how to use this gem/engine:\n
194
- \ [changelog]: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md\n
195
- \ [WIKI]: https://github.com/AlyBadawy/Securial/wiki\n ---\n "
214
+ \ Securial is a mountable Rails engine that provides robust, extensible\n authentication
215
+ and access control for Rails applications. It supports JWT,\n API tokens, session-based
216
+ auth, and is designed for easy integration with\n modern web and mobile apps.\n\n
217
+ \ Usage:\n securial new APP_NAME [rails_options...] # Create a new Rails
218
+ app with Securial pre-installed\n securial -v, --version #
219
+ Show the Securial gem version\n securial -h, --help #
220
+ Show this help message\n\n Example:\n securial new myapp --api --database=postgresql
221
+ -T\n\n More Info:\n review the [changelog] and [WIKI] for more info on the
222
+ latest\n changes and how to use this gem/engine:\n [Changelog]: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md\n
223
+ \ [WIKI]: https://github.com/AlyBadawy/Securial/wiki\n ---\n "
196
224
  rdoc_options: []
197
225
  require_paths:
198
226
  - lib
@@ -1,29 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module LoggerValidation
7
- class << self
8
- LEVELS = %i[debug info warn error fatal unknown].freeze
9
-
10
- def validate!(securial_config)
11
- allowed_options = {
12
- log_to_file: [securial_config.log_to_file, [true, false], "boolean"],
13
- log_to_stdout: [securial_config.log_to_stdout, [true, false], "boolean"],
14
- log_file_level: [securial_config.log_file_level, LEVELS, "symbol"],
15
- log_stdout_level: [securial_config.log_stdout_level, LEVELS, "symbol"],
16
- }
17
-
18
- allowed_options.each do |attr, (value, allowed, type)|
19
- unless allowed.include?(value)
20
- allowed_values = type == "symbol" ? allowed.map { |v| ":#{v}" }.join(", ") : allowed.join(", ")
21
- raise Securial::Error::Config::LoggerValidationError, "#{attr} must be a #{type}. Allowed values: #{allowed_values}. Received: #{value.inspect}"
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,24 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module MailerValidation
7
- class << self
8
- def validate!(securial_config)
9
- if securial_config.mailer_sender.blank?
10
- error_message = "Mailer sender is not set."
11
- Securial.logger.fatal(error_message)
12
- raise Securial::Error::Config::MailerValidationError, error_message
13
- end
14
- if securial_config.mailer_sender !~ URI::MailTo::EMAIL_REGEXP
15
- error_message = "Mailer sender is not a valid email address."
16
- Securial.logger.fatal(error_message)
17
- raise Securial::Error::Config::MailerValidationError, error_message
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,91 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module PasswordValidation
7
- class << self
8
- def validate!(securial_config)
9
- validate_password_reset_subject!(securial_config)
10
- validate_password_min_max_length!(securial_config)
11
- validate_password_complexity!(securial_config)
12
- validate_password_expiration!(securial_config)
13
- validate_password_reset_token!(securial_config)
14
- end
15
-
16
- private
17
-
18
- def validate_password_reset_token!(securial_config)
19
- if securial_config.reset_password_token_secret.blank?
20
- error_message = "Reset password token secret is not set."
21
- Securial.logger.fatal(error_message)
22
- raise Securial::Error::Config::PasswordValidationError, error_message
23
- end
24
- unless securial_config.reset_password_token_secret.is_a?(String)
25
- error_message = "Reset password token secret must be a String."
26
- Securial.logger.fatal(error_message)
27
- raise Securial::Error::Config::PasswordValidationError, error_message
28
- end
29
- unless securial_config.reset_password_token_expires_in.is_a?(ActiveSupport::Duration) && securial_config.reset_password_token_expires_in > 0
30
- error_message = "Reset password token expiration must be a valid ActiveSupport::Duration greater than 0."
31
- Securial.logger.fatal(error_message)
32
- raise Securial::Error::Config::PasswordValidationError, error_message
33
- end
34
- end
35
-
36
- def validate_password_reset_subject!(securial_config)
37
- if securial_config.mailer_forgot_password_subject.blank?
38
- error_message = "Password reset email subject is not set."
39
- Securial.logger.fatal(error_message)
40
- raise Securial::Error::Config::PasswordValidationError, error_message
41
- end
42
- unless securial_config.mailer_forgot_password_subject.is_a?(String)
43
- error_message = "Password reset email subject must be a String."
44
- Securial.logger.fatal(error_message)
45
- raise Securial::Error::Config::PasswordValidationError, error_message
46
- end
47
- end
48
-
49
- def validate_password_min_max_length!(securial_config)
50
- unless securial_config.password_min_length.is_a?(Integer) && securial_config.password_min_length > 0
51
- error_message = "Password minimum length must be a positive integer."
52
- Securial.logger.fatal(error_message)
53
- raise Securial::Error::Config::PasswordValidationError, error_message
54
- end
55
- unless securial_config.password_max_length.is_a?(Integer) && securial_config.password_max_length >= securial_config.password_min_length
56
- error_message = "Password maximum length must be an integer greater than or equal to the minimum length."
57
- Securial.logger.fatal(error_message)
58
- raise Securial::Error::Config::PasswordValidationError, error_message
59
- end
60
- end
61
-
62
- def validate_password_complexity!(securial_config)
63
- if securial_config.password_complexity.nil? || !securial_config.password_complexity.is_a?(Regexp)
64
- error_message = "Password complexity regex is not set or is not a valid Regexp."
65
- Securial.logger.fatal(error_message)
66
- raise Securial::Error::Config::PasswordValidationError, error_message
67
- end
68
- end
69
-
70
- def validate_password_expiration!(securial_config)
71
- unless securial_config.password_expires.is_a?(TrueClass) || securial_config.password_expires.is_a?(FalseClass)
72
- error_message = "Password expiration must be a boolean value."
73
- Securial.logger.fatal(error_message)
74
- raise Securial::Error::Config::PasswordValidationError, error_message
75
- end
76
-
77
- if securial_config.password_expires == true && (
78
- securial_config.password_expires_in.nil? ||
79
- !securial_config.password_expires_in.is_a?(ActiveSupport::Duration) ||
80
- securial_config.password_expires_in <= 0
81
- )
82
- error_message = "Password expiration duration is not set or is not a valid ActiveSupport::Duration."
83
- Securial.logger.fatal(error_message)
84
- raise Securial::Error::Config::PasswordValidationError, error_message
85
- end
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end
@@ -1,37 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module ResponseValidation
7
- class << self
8
- VALID_TIMESTAMP_OPTIONS = %i[all admins_only none].freeze
9
- VALID_RESPONSE_KEYS_FORMATS = %i[snake_case lowerCamelCase UpperCamelCase].freeze
10
-
11
- def validate!(securial_config)
12
- validate_response_keys_format!(securial_config)
13
- validate_timestamps_in_response!(securial_config)
14
- end
15
-
16
- private
17
-
18
- def validate_response_keys_format!(securial_config)
19
- unless VALID_RESPONSE_KEYS_FORMATS.include?(securial_config.response_keys_format)
20
- error_message = "Invalid response_keys_format option. Valid options are: #{VALID_RESPONSE_KEYS_FORMATS.map(&:inspect).join(', ')}."
21
- Securial.logger.fatal(error_message)
22
- raise Securial::Error::Config::ResponseValidationError, error_message
23
- end
24
- end
25
-
26
- def validate_timestamps_in_response!(securial_config)
27
- unless VALID_TIMESTAMP_OPTIONS.include?(securial_config.timestamps_in_response)
28
- error_message = "Invalid timestamps_in_response option. Valid options are: #{VALID_TIMESTAMP_OPTIONS.map(&:inspect).join(', ')}."
29
- Securial.logger.fatal(error_message)
30
- raise Securial::Error::Config::ResponseValidationError, error_message
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,32 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module RolesValidation
7
- class << self
8
- def validate!(securial_config)
9
- if securial_config.admin_role.nil? || securial_config.admin_role.to_s.strip.empty?
10
- error_message = "Admin role is not set."
11
- Securial.logger.fatal(error_message)
12
- raise Securial::Error::Config::RolesValidationError, error_message
13
- end
14
-
15
- unless securial_config.admin_role.is_a?(Symbol) || securial_config.admin_role.is_a?(String)
16
- error_message = "Admin role must be a Symbol or String."
17
- Securial.logger.fatal(error_message)
18
- raise Securial::Error::Config::RolesValidationError, error_message
19
- end
20
-
21
- admin_role_downcase = securial_config.admin_role.to_s.downcase
22
- if admin_role_downcase == "account" || admin_role_downcase == "accounts"
23
- error_message = "The admin role cannot be 'account' or 'accounts' as it conflicts with the default routes."
24
- Securial.logger.fatal(error_message)
25
- raise Securial::Error::Config::RolesValidationError, error_message
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,56 +0,0 @@
1
- require "securial/error"
2
-
3
- module Securial
4
- module Config
5
- module Validation
6
- module SecurityValidation
7
- class << self
8
- VALID_SECURITY_HEADERS = %i[strict default none].freeze
9
-
10
- def validate!(securial_config)
11
- validate_security_headers!(securial_config)
12
- validate_rate_limiting!(securial_config)
13
- end
14
-
15
- private
16
-
17
- def validate_security_headers!(securial_config)
18
- unless VALID_SECURITY_HEADERS.include?(securial_config.security_headers)
19
- error_message = "Invalid security_headers option. Valid options are: #{VALID_SECURITY_HEADERS.map(&:inspect).join(', ')}."
20
- Securial.logger.fatal(error_message)
21
- raise Securial::Error::Config::SecurityValidationError, error_message
22
- end
23
- end
24
-
25
- def validate_rate_limiting!(securial_config) # rubocop:disable Metrics/MethodLength
26
- unless securial_config.rate_limiting_enabled.is_a?(TrueClass) || securial_config.rate_limiting_enabled.is_a?(FalseClass)
27
- error_message = "rate_limiting_enabled must be a boolean value."
28
- Securial.logger.fatal(error_message)
29
- raise Securial::Error::Config::SecurityValidationError, error_message
30
- end
31
-
32
- return unless securial_config.rate_limiting_enabled
33
-
34
- unless securial_config.rate_limit_requests_per_minute.is_a?(Integer) && securial_config.rate_limit_requests_per_minute > 0
35
- error_message = "rate_limit_requests_per_minute must be a positive integer when rate limiting is enabled."
36
- Securial.logger.fatal(error_message)
37
- raise Securial::Error::Config::SecurityValidationError, error_message
38
- end
39
-
40
- unless securial_config.rate_limit_response_status.is_a?(Integer) && securial_config.rate_limit_response_status.between?(400, 599)
41
- error_message = "rate_limit_response_status must be an HTTP status code between 4xx and 5xx."
42
- Securial.logger.fatal(error_message)
43
- raise Securial::Error::Config::SecurityValidationError, error_message
44
- end
45
-
46
- unless securial_config.rate_limit_response_message.is_a?(String) && !securial_config.rate_limit_response_message.strip.empty?
47
- error_message = "rate_limit_response_message must be a non-empty String."
48
- Securial.logger.fatal(error_message)
49
- raise Securial::Error::Config::SecurityValidationError, error_message
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end