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.
- checksums.yaml +4 -4
- data/README.md +14 -16
- data/app/controllers/concerns/securial/identity.rb +18 -9
- data/app/controllers/securial/status_controller.rb +2 -0
- data/app/controllers/securial/users_controller.rb +1 -1
- data/app/views/securial/status/show.json.jbuilder +1 -1
- data/bin/securial +5 -54
- data/db/migrate/20250606182648_seed_roles_and_users.rb +69 -0
- data/lib/generators/securial/install/install_generator.rb +2 -2
- data/lib/generators/securial/install/templates/securial_initializer.erb +115 -18
- data/lib/generators/securial/install/views_generator.rb +2 -1
- data/lib/generators/securial/jbuilder/jbuilder_generator.rb +2 -0
- data/lib/generators/securial/scaffold/scaffold_generator.rb +2 -0
- data/lib/securial/auth/auth_encoder.rb +3 -3
- data/lib/securial/auth/session_creator.rb +1 -1
- data/lib/securial/auth/token_generator.rb +13 -13
- data/lib/securial/cli.rb +158 -0
- data/lib/securial/config/configuration.rb +3 -53
- data/lib/securial/config/signature.rb +107 -0
- data/lib/securial/config/validation.rb +59 -16
- data/lib/securial/config.rb +17 -16
- data/lib/securial/engine.rb +2 -0
- data/lib/securial/engine_initializers.rb +21 -2
- data/lib/securial/error/base_securial_error.rb +5 -3
- data/lib/securial/error/config.rb +0 -28
- data/lib/securial/helpers/key_transformer.rb +33 -0
- data/lib/securial/helpers/normalizing_helper.rb +1 -1
- data/lib/securial/helpers/regex_helper.rb +6 -7
- data/lib/securial/helpers/roles_helper.rb +6 -7
- data/lib/securial/helpers.rb +1 -0
- data/lib/securial/logger.rb +7 -8
- data/lib/securial/middleware/response_headers.rb +19 -0
- data/lib/securial/middleware/transform_request_keys.rb +35 -0
- data/lib/securial/middleware/transform_response_keys.rb +47 -0
- data/lib/securial/middleware.rb +3 -0
- data/lib/securial/security/request_rate_limiter.rb +45 -0
- data/lib/securial/security.rb +8 -0
- data/lib/securial/version.rb +1 -1
- data/lib/securial.rb +4 -4
- data/lib/tasks/securial_routes.rake +26 -0
- metadata +47 -19
- data/lib/securial/config/validation/logger_validation.rb +0 -29
- data/lib/securial/config/validation/mailer_validation.rb +0 -24
- data/lib/securial/config/validation/password_validation.rb +0 -91
- data/lib/securial/config/validation/response_validation.rb +0 -37
- data/lib/securial/config/validation/roles_validation.rb +0 -32
- data/lib/securial/config/validation/security_validation.rb +0 -56
- data/lib/securial/config/validation/session_validation.rb +0 -87
data/lib/securial/logger.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
require_relative "logger/builder"
|
2
2
|
|
3
3
|
module Securial
|
4
|
-
|
5
|
-
|
4
|
+
extend self
|
5
|
+
attr_accessor :logger
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def logger
|
8
|
+
@logger ||= Logger::Builder.build
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
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
|
data/lib/securial/middleware.rb
CHANGED
@@ -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
|
data/lib/securial/version.rb
CHANGED
data/lib/securial.rb
CHANGED
@@ -4,8 +4,8 @@ require "securial/engine"
|
|
4
4
|
require "jbuilder"
|
5
5
|
|
6
6
|
module Securial
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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.
|
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-
|
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.
|
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.
|
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: '
|
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: '
|
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-
|
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
|
192
|
-
and access control for Rails applications
|
193
|
-
and
|
194
|
-
\
|
195
|
-
|
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
|