securial 0.7.0 → 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 (77) 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/users_controller.rb +8 -7
  10. data/app/mailers/securial/securial_mailer.rb +17 -6
  11. data/app/models/concerns/securial/password_resettable.rb +1 -1
  12. data/app/models/securial/application_record.rb +1 -1
  13. data/app/models/securial/session.rb +16 -5
  14. data/app/models/securial/user.rb +1 -3
  15. data/app/views/securial/securial_mailer/forgot_password.html.erb +20 -0
  16. data/app/views/securial/securial_mailer/forgot_password.text.erb +14 -0
  17. data/app/views/securial/securial_mailer/sign_in.html.erb +31 -0
  18. data/app/views/securial/securial_mailer/sign_in.text.erb +17 -0
  19. data/app/views/securial/securial_mailer/update_account.html.erb +15 -0
  20. data/app/views/securial/securial_mailer/update_account.text.erb +11 -0
  21. data/app/views/securial/securial_mailer/welcome.html.erb +11 -0
  22. data/app/views/securial/securial_mailer/welcome.text.erb +8 -0
  23. data/app/views/securial/users/_securial_user.json.jbuilder +9 -3
  24. data/config/routes.rb +5 -1
  25. data/db/migrate/{20250515104930_create_securial_roles.rb → 20250603130214_create_securial_roles.rb} +0 -2
  26. data/db/migrate/{20250517155521_create_securial_users.rb → 20250604110520_create_securial_users.rb} +8 -3
  27. data/db/migrate/{20250519075407_create_securial_sessions.rb → 20250604184841_create_securial_sessions.rb} +4 -0
  28. data/lib/generators/securial/install/templates/securial_initializer.erb +1 -1
  29. data/lib/generators/securial/scaffold/templates/controller.erb +1 -0
  30. data/lib/generators/securial/scaffold/templates/routes.erb +1 -1
  31. data/lib/securial/auth/auth_encoder.rb +8 -6
  32. data/lib/securial/auth/session_creator.rb +6 -3
  33. data/lib/securial/auth/token_generator.rb +26 -0
  34. data/lib/securial/auth.rb +7 -5
  35. data/lib/securial/config/configuration.rb +31 -13
  36. data/lib/securial/config/validation/logger_validation.rb +29 -0
  37. data/lib/securial/config/validation/mailer_validation.rb +24 -0
  38. data/lib/securial/config/validation/password_validation.rb +91 -0
  39. data/lib/securial/config/validation/response_validation.rb +37 -0
  40. data/lib/securial/config/validation/roles_validation.rb +32 -0
  41. data/lib/securial/config/validation/security_validation.rb +56 -0
  42. data/lib/securial/config/validation/session_validation.rb +87 -0
  43. data/lib/securial/config/validation.rb +16 -239
  44. data/lib/securial/config.rb +20 -4
  45. data/lib/securial/engine.rb +5 -79
  46. data/lib/securial/engine_initializers.rb +33 -0
  47. data/lib/securial/error/auth.rb +21 -0
  48. data/lib/securial/error/base_securial_error.rb +16 -0
  49. data/lib/securial/error/config.rb +37 -0
  50. data/lib/securial/error.rb +9 -0
  51. data/lib/securial/helpers/roles_helper.rb +17 -0
  52. data/lib/securial/helpers.rb +1 -0
  53. data/lib/securial/logger/broadcaster.rb +36 -24
  54. data/lib/securial/logger/builder.rb +29 -44
  55. data/lib/securial/logger/formatter.rb +35 -0
  56. data/lib/securial/logger.rb +10 -3
  57. data/lib/securial/middleware/request_tag_logger.rb +39 -0
  58. data/lib/securial/middleware.rb +13 -0
  59. data/lib/securial/version.rb +1 -1
  60. data/lib/securial.rb +2 -26
  61. metadata +39 -149
  62. data/app/views/securial/securial_mailer/reset_password.html.erb +0 -5
  63. data/app/views/securial/securial_mailer/reset_password.text.erb +0 -4
  64. data/lib/securial/auth/errors.rb +0 -15
  65. data/lib/securial/config/errors.rb +0 -20
  66. data/lib/securial/inspectors/route_inspector.rb +0 -52
  67. data/lib/securial/inspectors.rb +0 -8
  68. data/lib/securial/key_transformer.rb +0 -32
  69. data/lib/securial/logger/colors.rb +0 -14
  70. data/lib/securial/middlewares/request_logger_tag.rb +0 -19
  71. data/lib/securial/middlewares/transform_request_keys.rb +0 -33
  72. data/lib/securial/middlewares/transform_response_keys.rb +0 -52
  73. data/lib/securial/middlewares.rb +0 -10
  74. data/lib/securial/security/request_rate_limiter.rb +0 -68
  75. data/lib/securial/security.rb +0 -8
  76. data/lib/securial/version_checker.rb +0 -31
  77. /data/db/migrate/{20250518122749_create_securial_role_assignments.rb → 20250604123805_create_securial_role_assignments.rb} +0 -0
@@ -1,3 +1,5 @@
1
+ require "jwt"
2
+
1
3
  module Securial
2
4
  module Auth
3
5
  module AuthEncoder
@@ -20,17 +22,17 @@ module Securial
20
22
 
21
23
  payload = base_payload.merge(session_payload)
22
24
  begin
23
- JWT.encode(payload, secret, algorithm, { kid: "hmac" })
24
- rescue JWT::EncodeError => e
25
- raise Errors::AuthEncodeError, "Failed to encode session: #{e.message}"
25
+ ::JWT.encode(payload, secret, algorithm, { kid: "hmac" })
26
+ rescue JWT::EncodeError
27
+ raise Securial::Error::Auth::TokenEncodeError
26
28
  end
27
29
  end
28
30
 
29
31
  def decode(token)
30
32
  begin
31
- decoded = JWT.decode(token, secret, true, { algorithm: algorithm, verify_jti: true, iss: "securial" })
32
- rescue JWT::DecodeError => e
33
- raise Securial::Auth::Errors::AuthDecodeError, "Failed to decode session token: #{e.message}"
33
+ decoded = ::JWT.decode(token, secret, true, { algorithm: algorithm, verify_jti: true, iss: "securial" })
34
+ rescue JWT::DecodeError
35
+ raise Securial::Error::Auth::TokenDecodeError
34
36
  end
35
37
  decoded.first
36
38
  end
@@ -3,13 +3,16 @@ module Securial
3
3
  module SessionCreator
4
4
  module_function
5
5
 
6
- def create_session(user, request)
7
- return nil unless user && user.persisted? && request.is_a?(ActionDispatch::Request)
6
+ def create_session!(user, request)
7
+ valid_user = user && user.is_a?(Securial::User) && user.persisted?
8
+ valid_request = request.is_a?(ActionDispatch::Request)
9
+
10
+ return nil unless valid_user && valid_request
8
11
 
9
12
  user.sessions.create!(
10
13
  user_agent: request.user_agent,
11
14
  ip_address: request.remote_ip,
12
- refresh_token: SecureRandom.hex(64),
15
+ refresh_token: Securial::Auth::TokenGenerator.generate_refresh_token,
13
16
  last_refreshed_at: Time.current,
14
17
  refresh_token_expires_at: 1.week.from_now,
15
18
  ).tap do |session|
@@ -0,0 +1,26 @@
1
+ require "openssl"
2
+ require "securerandom"
3
+
4
+ module Securial
5
+ module Auth
6
+ module TokenGenerator
7
+ class << self
8
+ def generate_refresh_token
9
+ secret = Securial.configuration.session_secret
10
+ algo = "SHA256"
11
+
12
+ random_data = SecureRandom.hex(32)
13
+ digest = OpenSSL::Digest.new(algo)
14
+ hmac = OpenSSL::HMAC.hexdigest(digest, secret, random_data)
15
+
16
+ "#{hmac}#{random_data}"
17
+ end
18
+
19
+ def generate_password_reset_token
20
+ token = SecureRandom.alphanumeric(12)
21
+ "#{token[0, 6]}-#{token[6, 6]}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/securial/auth.rb CHANGED
@@ -1,10 +1,12 @@
1
- require "securial/auth/errors"
2
- require_relative "auth/auth_encoder"
3
- require_relative "auth/session_creator"
1
+ require "securial/auth/auth_encoder"
2
+ require "securial/auth/session_creator"
3
+ require "securial/auth/token_generator"
4
4
 
5
5
  module Securial
6
6
  module Auth
7
- # This module serves as a namespace for authentication-related components.
8
- # It requires all key auth files: Errors, AuthEncoder, SessionCreator.
7
+ # This is a placeholder for the Auth module.
8
+ # It can be used to include authentication-related methods or constants
9
+ # that are not specific to encoding or session creation.
10
+ # Currently, it serves as a namespace for the AuthEncoder and SessionCreator modules.
9
11
  end
10
12
  end
@@ -1,24 +1,39 @@
1
- require "securial/helpers"
1
+ require "securial/config/validation"
2
+ require "securial/helpers/regex_helper"
3
+
2
4
  module Securial
3
5
  module Config
4
- VALID_SESSION_ENCRYPTION_ALGORITHMS = [:hs256, :hs384, :hs512].freeze
5
- VALID_TIMESTAMP_OPTIONS = [:all, :admins_only, :none].freeze
6
- VALID_RESPONSE_KEYS_FORMATS = [:snake_case, :lowerCamelCase, :UpperCamelCase].freeze
7
- VALID_SECURITY_HEADERS = [:strict, :default, :none].freeze
8
-
9
6
  class Configuration
10
- def self.config_attributes # rubocop:disable Metrics/MethodLength
7
+ def self.default_config_attributes # rubocop:disable Metrics/MethodLength
11
8
  {
9
+ # General configuration
10
+ app_name: "Securial",
11
+
12
+ # Logger configuration
12
13
  log_to_file: !Rails.env.test?,
13
14
  log_to_stdout: !Rails.env.test?,
14
15
  log_file_level: :debug,
15
16
  log_stdout_level: :debug,
17
+
18
+ # Roles configuration
16
19
  admin_role: :admin,
20
+
17
21
  session_expiration_duration: 3.minutes,
18
22
  session_secret: "secret",
19
23
  session_algorithm: :hs256,
24
+ session_refresh_token_expires_in: 1.week,
25
+
26
+ # Mailer configuration
20
27
  mailer_sender: "no-reply@example.com",
21
- password_reset_email_subject: "SECURIAL: Password Reset Instructions",
28
+ mailer_sign_up_enabled: true,
29
+ mailer_sign_up_subject: "SECURIAL: Welcome to Our Service",
30
+ mailer_sign_in_enabled: true,
31
+ mailer_sign_in_subject: "SECURIAL: Sign In Notification",
32
+ mailer_update_account_enabled: true,
33
+ mailer_update_account_subject: "SECURIAL: Account Update Notification",
34
+ mailer_forgot_password_subject: "SECURIAL: Password Reset Instructions",
35
+
36
+ # Password configuration
22
37
  password_min_length: 8,
23
38
  password_max_length: 128,
24
39
  password_complexity: Securial::Helpers::RegexHelper::PASSWORD_REGEX,
@@ -26,8 +41,12 @@ module Securial
26
41
  password_expires_in: 90.days,
27
42
  reset_password_token_expires_in: 2.hours,
28
43
  reset_password_token_secret: "reset_secret",
29
- timestamps_in_response: :all,
44
+
45
+ # Response configuration
30
46
  response_keys_format: :snake_case,
47
+ timestamps_in_response: :all,
48
+
49
+ # Security configuration
31
50
  security_headers: :strict,
32
51
  rate_limiting_enabled: true,
33
52
  rate_limit_requests_per_minute: 60,
@@ -37,13 +56,12 @@ module Securial
37
56
  end
38
57
 
39
58
  def initialize
40
- self.class.config_attributes.each do |attr, default|
41
- instance_variable_set("@#{attr}", default)
59
+ self.class.default_config_attributes.each do |attr, default_value|
60
+ instance_variable_set("@#{attr}", default_value)
42
61
  end
43
- validate!
44
62
  end
45
63
 
46
- config_attributes.each_key do |attr|
64
+ default_config_attributes.each_key do |attr|
47
65
  define_method(attr) { instance_variable_get("@#{attr}") }
48
66
 
49
67
  define_method("#{attr}=") do |value|
@@ -0,0 +1,29 @@
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
@@ -0,0 +1,24 @@
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
@@ -0,0 +1,91 @@
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
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,56 @@
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
@@ -0,0 +1,87 @@
1
+ require "securial/error"
2
+
3
+ module Securial
4
+ module Config
5
+ module Validation
6
+ module SessionValidation
7
+ class << self
8
+ VALID_SESSION_ENCRYPTION_ALGORITHMS = %i[hs256 hs384 hs512].freeze
9
+
10
+ def validate!(securial_config)
11
+ validate_session_expiry_duration!(securial_config)
12
+ validate_session_algorithm!(securial_config)
13
+ validate_session_secret!(securial_config)
14
+ validate_session_refresh_token!(securial_config)
15
+ end
16
+
17
+ private
18
+
19
+ def validate_session_expiry_duration!(securial_config)
20
+ if securial_config.session_expiration_duration.nil?
21
+ error_message = "Session expiration duration is not set."
22
+ Securial.logger.fatal(error_message)
23
+ raise Securial::Error::Config::SessionValidationError, error_message
24
+ end
25
+ if securial_config.session_expiration_duration.class != ActiveSupport::Duration
26
+ error_message = "Session expiration duration must be an ActiveSupport::Duration."
27
+ Securial.logger.fatal(error_message)
28
+ raise Securial::Error::Config::SessionValidationError, error_message
29
+ end
30
+ if securial_config.session_expiration_duration <= 0
31
+ Securial.logger.fatal("Session expiration duration must be greater than 0.")
32
+ raise Securial::Error::Config::SessionValidationError, "Session expiration duration must be greater than 0."
33
+ end
34
+ end
35
+
36
+ def validate_session_algorithm!(securial_config)
37
+ if securial_config.session_algorithm.blank?
38
+ error_message = "Session algorithm is not set."
39
+ Securial.logger.fatal(error_message)
40
+ raise Securial::Error::Config::SessionValidationError, error_message
41
+ end
42
+ unless securial_config.session_algorithm.is_a?(Symbol)
43
+ error_message = "Session algorithm must be a Symbol."
44
+ Securial.logger.fatal(error_message)
45
+ raise Securial::Error::Config::SessionValidationError, error_message
46
+ end
47
+ unless VALID_SESSION_ENCRYPTION_ALGORITHMS.include?(securial_config.session_algorithm)
48
+ error_message = "Invalid session algorithm. Valid options are: #{VALID_SESSION_ENCRYPTION_ALGORITHMS.map(&:inspect).join(', ')}."
49
+ Securial.logger.fatal(error_message)
50
+ raise Securial::Error::Config::SessionValidationError, error_message
51
+ end
52
+ end
53
+
54
+ def validate_session_secret!(securial_config)
55
+ if securial_config.session_secret.blank?
56
+ error_message = "Session secret is not set."
57
+ Securial.logger.fatal(error_message)
58
+ raise Securial::Error::Config::SessionValidationError, error_message
59
+ end
60
+ unless securial_config.session_secret.is_a?(String)
61
+ error_message = "Session secret must be a String."
62
+ Securial.logger.fatal(error_message)
63
+ raise Securial::Error::Config::SessionValidationError, error_message
64
+ end
65
+ end
66
+
67
+ def validate_session_refresh_token!(securial_config)
68
+ if securial_config.session_refresh_token_expires_in.nil?
69
+ error_message = "Session refresh token expiration duration is not set."
70
+ Securial.logger.fatal(error_message)
71
+ raise Securial::Error::Config::SessionValidationError, error_message
72
+ end
73
+ if securial_config.session_refresh_token_expires_in.class != ActiveSupport::Duration
74
+ error_message = "Session refresh token expiration duration must be an ActiveSupport::Duration."
75
+ Securial.logger.fatal(error_message)
76
+ raise Securial::Error::Config::SessionValidationError, error_message
77
+ end
78
+ if securial_config.session_refresh_token_expires_in <= 0
79
+ Securial.logger.fatal("Session refresh token expiration duration must be greater than 0.")
80
+ raise Securial::Error::Config::SessionValidationError, "Session refresh token expiration duration must be greater than 0."
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end