securial 0.7.0 → 0.8.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 (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 +2 -2
  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,19 +0,0 @@
1
- module Securial
2
- module Middlewares
3
- class RequestLoggerTag
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- request_id = env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
10
- tags = ["Securial"]
11
- tags << request_id if request_id
12
-
13
- logger = Securial.logger || Rails.logger || ::Logger.new(IO::NULL)
14
- tagged_logger = logger.is_a?(ActiveSupport::TaggedLogging) ? logger : ActiveSupport::TaggedLogging.new(logger)
15
- tagged_logger.tagged(*tags) { @app.call(env) }
16
- end
17
- end
18
- end
19
- end
@@ -1,33 +0,0 @@
1
- # lib/securial/middlewares/transform_request_keys.rb
2
- require "json"
3
-
4
- module Securial
5
- module Middlewares
6
- class TransformRequestKeys
7
- def initialize(app)
8
- @app = app
9
- end
10
-
11
- def call(env)
12
- if env["CONTENT_TYPE"]&.include?("application/json") && Securial.configuration.response_keys_format != :snake_case
13
- req = Rack::Request.new(env)
14
- if (req.body&.size || 0) > 0
15
- raw = req.body.read
16
- req.body.rewind
17
- begin
18
- parsed = JSON.parse(raw)
19
- transformed = Securial::KeyTransformer.deep_transform_keys(parsed) do |key|
20
- Securial::KeyTransformer.underscore(key)
21
- end
22
- env["rack.input"] = StringIO.new(JSON.dump(transformed))
23
- env["rack.input"].rewind
24
- rescue JSON::ParserError
25
- # noop
26
- end
27
- end
28
- end
29
- @app.call(env)
30
- end
31
- end
32
- end
33
- end
@@ -1,52 +0,0 @@
1
- require "json"
2
-
3
- module Securial
4
- module Middlewares
5
- class TransformResponseKeys
6
- def initialize(app)
7
- @app = app
8
- end
9
-
10
- def call(env)
11
- status, headers, response = @app.call(env)
12
-
13
- if json_response?(headers)
14
- body = extract_body(response)
15
-
16
- if body.present?
17
- format = Securial.configuration.response_keys_format
18
-
19
- # Only transform if not snake_case
20
- if format != :snake_case
21
- begin
22
- transformed = Securial::KeyTransformer.deep_transform_keys(JSON.parse(body)) do |key|
23
- Securial::KeyTransformer.camelize(key, format)
24
- end
25
-
26
- new_body = [JSON.generate(transformed)]
27
- headers["Content-Length"] = new_body.first.bytesize.to_s
28
- return [status, headers, new_body]
29
- rescue JSON::ParserError
30
- # If not valid JSON, fall through and return original response
31
- end
32
- end
33
- end
34
- end
35
-
36
- [status, headers, response]
37
- end
38
-
39
- private
40
-
41
- def json_response?(headers)
42
- headers["Content-Type"]&.include?("application/json")
43
- end
44
-
45
- def extract_body(response)
46
- response_body = ""
47
- response.each { |part| response_body << part }
48
- response_body
49
- end
50
- end
51
- end
52
- end
@@ -1,10 +0,0 @@
1
- require_relative "middlewares/transform_request_keys"
2
- require_relative "middlewares/transform_response_keys"
3
- require_relative "middlewares/request_logger_tag"
4
-
5
- module Securial
6
- module Middleware
7
- # This module serves as a namespace for middlewares.
8
- # It requires the necessary middleware files to provide functionality.
9
- end
10
- end
@@ -1,68 +0,0 @@
1
- require "rack/attack"
2
- require "securial/config"
3
- require "securial/logger"
4
-
5
- module Securial
6
- module Security
7
- module RequestRateLimiter
8
- module_function
9
-
10
- def apply! # rubocop:disable Metrics/MethodLength
11
- resp_status = Securial.configuration.rate_limit_response_status
12
- resp_message = Securial.configuration.rate_limit_response_message
13
- # Throttle login attempts by IP
14
- Rack::Attack.throttle("securial/logins/ip",
15
- limit: ->(_req) { Securial.configuration.rate_limit_requests_per_minute },
16
- period: 1.minute
17
- ) do |req|
18
- if req.path.include?("sessions/login") && req.post?
19
- req.ip
20
- end
21
- end
22
-
23
- # Throttle login attempts by username/email
24
- Rack::Attack.throttle("securial/logins/email",
25
- limit: ->(_req) { Securial.configuration.rate_limit_requests_per_minute },
26
- period: 1.minute
27
- ) do |req|
28
- if req.path.include?("sessions/login") && req.post?
29
- req.params["email_address"].to_s.downcase.strip
30
- end
31
- end
32
-
33
- # Throttle password reset requests by IP
34
- Rack::Attack.throttle("securial/password_resets/ip",
35
- limit: ->(_req) { Securial.configuration.rate_limit_requests_per_minute },
36
- period: 1.minute
37
- ) do |req|
38
- if req.path.include?("password/forgot") && req.post?
39
- req.ip
40
- end
41
- end
42
-
43
- # Throttle password reset requests by email
44
- Rack::Attack.throttle("securial/password_resets/email",
45
- limit: ->(_req) { Securial.configuration.rate_limit_requests_per_minute },
46
- period: 1.minute
47
- ) do |req|
48
- if req.path.include?("password/forgot") && req.post?
49
- req.params["email_address"].to_s.downcase.strip
50
- end
51
- end
52
-
53
- # Custom response for throttled requests
54
- Rack::Attack.throttled_responder = lambda do |request|
55
- retry_after = (request.env["rack.attack.match_data"] || {})[:period]
56
- [
57
- resp_status,
58
- {
59
- "Content-Type" => "application/json",
60
- "Retry-After" => retry_after.to_s,
61
- },
62
- [{ error: resp_message }.to_json]
63
- ]
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,8 +0,0 @@
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,31 +0,0 @@
1
- require "net/http"
2
- require "json"
3
-
4
- module Securial
5
- module VersionChecker
6
- module_function
7
-
8
- def check_latest_version
9
- begin
10
- rubygems_api_url = "https://rubygems.org/api/v1/versions/securial/latest.json"
11
- uri = URI(rubygems_api_url)
12
- http = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: 5, read_timeout: 5)
13
- response = http.request(Net::HTTP::Get.new(uri))
14
- latest = JSON.parse(response.body)["version"]
15
-
16
- current = Securial::VERSION
17
- if Gem::Version.new(latest) > Gem::Version.new(current)
18
- Securial.logger.info "A newer version (#{latest}) of Securial is available. You are using #{current}."
19
- Securial.logger.info "Please consider updating!"
20
- Securial.logger.debug "You can update Securial by running the following command in your terminal:"
21
- Securial.logger.debug "`bundle update securial`"
22
- else
23
- Securial.logger.info "You are using the latest version of Securial (#{current})."
24
- Securial.logger.debug "No updates available at this time."
25
- end
26
- rescue => e
27
- Securial.logger.debug("Version check failed: #{e.message}")
28
- end
29
- end
30
- end
31
- end