securial 0.5.0 → 0.6.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 +17 -26
- data/app/controllers/concerns/securial/identity.rb +4 -12
- data/app/controllers/securial/sessions_controller.rb +20 -8
- data/app/models/concerns/securial/password_resettable.rb +15 -0
- data/app/models/securial/session.rb +2 -2
- data/app/views/securial/users/_securial_user.json.jbuilder +2 -0
- data/config/routes.rb +0 -1
- data/db/migrate/20250517155521_create_securial_users.rb +10 -7
- data/lib/generators/securial/install/templates/securial_initializer.erb +43 -13
- data/lib/generators/securial/install/views_generastor.rb +25 -0
- data/lib/securial/auth/_index.rb +3 -0
- data/lib/securial/{sessions/session_encoder.rb → auth/auth_encoder.rb} +4 -4
- data/lib/securial/auth/errors.rb +15 -0
- data/lib/securial/auth/session_creator.rb +21 -0
- data/lib/securial/config/configuration.rb +50 -30
- data/lib/securial/config/errors.rb +2 -1
- data/lib/securial/config/validation.rb +96 -26
- data/lib/securial/engine.rb +35 -31
- data/lib/securial/key_transformer.rb +32 -0
- data/lib/securial/middleware/_index.rb +3 -0
- data/lib/securial/middleware/transform_request_keys.rb +33 -0
- data/lib/securial/middleware/transform_response_keys.rb +45 -0
- data/lib/securial/rack_attack.rb +48 -0
- data/lib/securial/version.rb +1 -1
- data/lib/securial.rb +6 -1
- metadata +29 -149
- data/db/migrate/20250524210207_add_password_reset_fields_to_securial_users.rb +0 -6
- data/lib/securial/sessions/_index.rb +0 -2
- data/lib/securial/sessions/errors.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1628d056fdb0a93bc954107766c8f5ed2975d4690c58dbdbe56a0d45e63eefa3
|
4
|
+
data.tar.gz: 166ce5d80dc63642239a0f6c26702e4bbca469c2f7d886d322aa07e119ef8928
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 997ba8dc253bf3772ef806e9c81cce2fb7a7b0dd263527f0896badf74e5ea09ab5560ca804162cde01441054ac5b5d0fca9c39dfc2607cf18086ec6bb4667af2
|
7
|
+
data.tar.gz: b7ff4072c37645e9dc6f1bd5551bb47ec829cbefd412221bcdf13e5a2e788450243c1e7853e918248a2b1faaea61aec40122ddea32f1ef80a1ae013367d9d349
|
data/README.md
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-

|
2
|
-
|
3
|
-
---
|
4
1
|
# Securial
|
5
2
|
|
6
3
|
[](https://rubygems.org/gems/securial)
|
@@ -10,6 +7,16 @@
|
|
10
7
|
[](https://github.com/alybadawy/securial/actions)
|
11
8
|
[](https://coveralls.io/github/AlyBadawy/Securial?branch=main)
|
12
9
|
|
10
|
+
> [!WARNING]
|
11
|
+
>
|
12
|
+
> **Securial is currently in active development (major version zero).**
|
13
|
+
>
|
14
|
+
> While the gem is functional and versioned, it is not yet considered stable. Until v1.0.0 is released, any updates may introduce breaking changes as the API and features continue to evolve. If you plan to use Securial in production, please do so with caution and pin a specific version.
|
15
|
+
>
|
16
|
+
> You can track the roadmap and remaining tasks for the v1.0.0 release in [this GitHub issue](https://github.com/AlyBadawy/Securial/issues/64).
|
17
|
+
|
18
|
+
---
|
19
|
+
|
13
20
|
**Securial** is a mountable Rails engine that provides robust, extensible authentication and access control for Rails applications. It supports:
|
14
21
|
|
15
22
|
- ✅ JWT-based authentication
|
@@ -20,33 +27,13 @@
|
|
20
27
|
- ✅ Database-agnostic support
|
21
28
|
|
22
29
|
|
23
|
-
### 🚀 Why
|
24
|
-
|
25
|
-
Securial isn't just another auth library — it's designed to give you control, flexibility, and peace of mind when building secure Rails APIs.
|
30
|
+
### 🚀 Why Securial?
|
26
31
|
|
27
|
-
|
28
|
-
- Easy to mount and extend using familiar Rails conventions.
|
29
|
-
- Fully customizable controllers, serializers, and logic — no more black-box auth.
|
32
|
+
Securial was built to offer a clean, modular, and API-first authentication system for Rails developers who want full control without the black-box complexity. Whether you're building for the web, mobile, or both, Securial gives you the flexibility to implement exactly what you need — from simple JWT authentication to more advanced setups involving sessions, API tokens, and role-based access.
|
30
33
|
|
31
|
-
|
32
|
-
- Use only the components you need: JWT, sessions, API tokens — or all of them together.
|
33
|
-
- Clean separation of concerns makes testing and debugging simpler.
|
34
|
+
It follows familiar Rails conventions, stays lightweight and database-agnostic, and keeps security at the core. With fully customizable controllers, serializers, and logic, Securial is designed to grow with your project — making it an ideal choice for everything from side projects to production-grade APIs.
|
34
35
|
|
35
|
-
**⚡ API-First Approach**
|
36
|
-
- JSON-only responses make Securial ideal for frontend frameworks and mobile apps.
|
37
|
-
- No HTML views or form helpers — just clean endpoints that work out of the box.
|
38
36
|
|
39
|
-
**🛡️ Secure by Default**
|
40
|
-
- Uses industry best practices for token management and access control.
|
41
|
-
- No reliance on client-side sessions or cookies.
|
42
|
-
|
43
|
-
**📦 Lightweight, Database-Agnostic**
|
44
|
-
- No assumptions about your schema or ORM — works with any relational database.
|
45
|
-
- Minimal dependencies, fast to integrate.
|
46
|
-
|
47
|
-
**🌱 Ready to Grow With You**
|
48
|
-
- Start small with basic JWT auth, scale to multi-token API clients, admin scopes, or full RBAC.
|
49
|
-
- Perfect for startups, side projects, and production APIs alike.
|
50
37
|
|
51
38
|
## 🚀 Installation
|
52
39
|
|
@@ -133,3 +120,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/alybad
|
|
133
120
|
## ⚖️ License
|
134
121
|
|
135
122
|
The gem is available as open source under the terms of the [MIT license](https://github.com/AlyBadawy/Securial?tab=MIT-1-ov-file#readme).
|
123
|
+
|
124
|
+
---
|
125
|
+
|
126
|
+

|
@@ -32,9 +32,9 @@ module Securial
|
|
32
32
|
if auth_header.present? && auth_header.start_with?("Bearer ")
|
33
33
|
token = auth_header.split(" ").last
|
34
34
|
begin
|
35
|
-
decoded_token = Securial::
|
35
|
+
decoded_token = Securial::Auth::AuthEncoder.decode(token)
|
36
36
|
Current.session = Session.find_by!(id: decoded_token["jti"], revoked: false)
|
37
|
-
rescue Securial::
|
37
|
+
rescue Securial::Auth::Errors::AuthDecodeError, ActiveRecord::RecordNotFound => e
|
38
38
|
render status: :unauthorized, json: { error: "Invalid token: #{e.message}" } and return
|
39
39
|
end
|
40
40
|
else
|
@@ -43,19 +43,11 @@ module Securial
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def start_new_session_for(user)
|
46
|
-
|
47
|
-
user_agent: request.user_agent,
|
48
|
-
ip_address: request.remote_ip,
|
49
|
-
refresh_token: SecureRandom.hex(64),
|
50
|
-
last_refreshed_at: Time.current,
|
51
|
-
refresh_token_expires_at: 1.week.from_now,
|
52
|
-
).tap do |session|
|
53
|
-
Current.session = session
|
54
|
-
end
|
46
|
+
Securial::Auth::SessionCreator.create_session(user, request)
|
55
47
|
end
|
56
48
|
|
57
49
|
def create_jwt_for_current_session
|
58
|
-
Securial::
|
50
|
+
Securial::Auth::AuthEncoder.encode(Current.session)
|
59
51
|
end
|
60
52
|
|
61
53
|
def internal_rails_request?
|
@@ -14,18 +14,12 @@ module Securial
|
|
14
14
|
def login
|
15
15
|
params.require([:email_address, :password])
|
16
16
|
if user = User.authenticate_by(params.permit([:email_address, :password]))
|
17
|
-
|
18
|
-
render status: :created,
|
19
|
-
json: {
|
20
|
-
access_token: create_jwt_for_current_session,
|
21
|
-
refresh_token: Current.session.refresh_token,
|
22
|
-
refresh_token_expires_at: Current.session.refresh_token_expires_at,
|
23
|
-
}
|
17
|
+
render_login_response(user)
|
24
18
|
else
|
25
19
|
render status: :unauthorized,
|
26
20
|
json: {
|
27
21
|
error: "Invalid email address or password.",
|
28
|
-
|
22
|
+
instructions: "Make sure to send the correct 'email_address' and 'password' in the payload",
|
29
23
|
}
|
30
24
|
end
|
31
25
|
end
|
@@ -72,5 +66,23 @@ module Securial
|
|
72
66
|
id = params[:id]
|
73
67
|
@securial_session = id ? Current.user.sessions.find(params.expect(:id)) : Current.session
|
74
68
|
end
|
69
|
+
|
70
|
+
def render_login_response(user)
|
71
|
+
if user.password_expired?
|
72
|
+
render status: :forbidden,
|
73
|
+
json: {
|
74
|
+
error: "Password expired",
|
75
|
+
instructions: "Please reset your password before logging in.",
|
76
|
+
}
|
77
|
+
else
|
78
|
+
start_new_session_for user
|
79
|
+
render status: :created,
|
80
|
+
json: {
|
81
|
+
access_token: create_jwt_for_current_session,
|
82
|
+
refresh_token: Current.session.refresh_token,
|
83
|
+
refresh_token_expires_at: Current.session.refresh_token_expires_at,
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
75
87
|
end
|
76
88
|
end
|
@@ -5,6 +5,8 @@ module Securial
|
|
5
5
|
included do
|
6
6
|
has_secure_password
|
7
7
|
|
8
|
+
before_save :update_password_changed_at, if: :will_save_change_to_password_digest?
|
9
|
+
|
8
10
|
validates :password,
|
9
11
|
length: {
|
10
12
|
minimum: Securial.configuration.password_min_length,
|
@@ -43,5 +45,18 @@ module Securial
|
|
43
45
|
reset_password_token_created_at: nil
|
44
46
|
)
|
45
47
|
end
|
48
|
+
|
49
|
+
def password_expired?
|
50
|
+
return false unless Securial.configuration.password_expires
|
51
|
+
return true unless password_changed_at
|
52
|
+
|
53
|
+
password_changed_at < Securial.configuration.password_expires_in.ago
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def update_password_changed_at
|
59
|
+
self.password_changed_at = Time.current
|
60
|
+
end
|
46
61
|
end
|
47
62
|
end
|
@@ -15,8 +15,8 @@ module Securial
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def refresh!
|
18
|
-
raise Securial::
|
19
|
-
raise Securial::
|
18
|
+
raise Securial::Auth::Errors::AuthRevokedError, "Session is revoked" if revoked
|
19
|
+
raise Securial::Auth::Errors::AuthExpiredError, "Session is expired" if refresh_token_expires_at < Time.current
|
20
20
|
|
21
21
|
update!(refresh_token: SecureRandom.hex(64),
|
22
22
|
refresh_count: self.refresh_count + 1,
|
@@ -6,6 +6,8 @@ json.phone securial_user.phone
|
|
6
6
|
json.username securial_user.username
|
7
7
|
json.bio securial_user.bio
|
8
8
|
|
9
|
+
json.password_expired securial_user.password_expired?
|
10
|
+
|
9
11
|
json.roles securial_user.roles, partial: "securial/roles/securial_role", as: :securial_role
|
10
12
|
|
11
13
|
json.partial! "securial/shared/timestamps", record: securial_user
|
data/config/routes.rb
CHANGED
@@ -28,7 +28,6 @@ Securial::Engine.routes.draw do
|
|
28
28
|
post "login", to: "sessions#login", as: :login
|
29
29
|
delete "logout", to: "sessions#logout", as: :logout
|
30
30
|
put "refresh", to: "sessions#refresh", as: :refresh_session
|
31
|
-
delete "revoke", to: "sessions#revoke", as: :revoke_current_session
|
32
31
|
delete "id/:id/revoke", to: "sessions#revoke", as: :revoke_session_by_id
|
33
32
|
delete "revoke_all", to: "sessions#revoke_all", as: :revoke_all_sessions
|
34
33
|
end
|
@@ -1,13 +1,16 @@
|
|
1
1
|
class CreateSecurialUsers < ActiveRecord::Migration[8.0]
|
2
2
|
def change
|
3
3
|
create_table :securial_users, id: :string do |t|
|
4
|
-
t.string
|
5
|
-
t.string
|
6
|
-
t.string
|
7
|
-
t.string
|
8
|
-
t.string
|
9
|
-
t.string
|
10
|
-
t.string
|
4
|
+
t.string :email_address
|
5
|
+
t.string :first_name
|
6
|
+
t.string :last_name
|
7
|
+
t.string :phone
|
8
|
+
t.string :username
|
9
|
+
t.string :bio
|
10
|
+
t.string :password_digest
|
11
|
+
t.string :reset_password_token
|
12
|
+
t.datetime :reset_password_token_created_at
|
13
|
+
t.datetime :password_changed_at
|
11
14
|
|
12
15
|
t.timestamps
|
13
16
|
end
|
@@ -14,7 +14,7 @@ Securial.configure do |config|
|
|
14
14
|
config.log_file_level = :info
|
15
15
|
|
16
16
|
# Set log level for stdout logger
|
17
|
-
config.log_stdout_level = :
|
17
|
+
config.log_stdout_level = :debug
|
18
18
|
|
19
19
|
##### User Roles
|
20
20
|
## Set the role for admin users
|
@@ -26,7 +26,7 @@ Securial.configure do |config|
|
|
26
26
|
# in the `/securial/superusers` namespace.
|
27
27
|
config.admin_role = :admin
|
28
28
|
|
29
|
-
|
29
|
+
##### Session Configuration
|
30
30
|
## Set the session expiration duration
|
31
31
|
# This is the time after which a session will be considered expired.
|
32
32
|
# After this time, the session will be invalidated and the user
|
@@ -35,14 +35,6 @@ Securial.configure do |config|
|
|
35
35
|
# The default is 3 minutes.
|
36
36
|
config.session_expiration_duration = 3.minutes
|
37
37
|
|
38
|
-
## Set the session renewal duration
|
39
|
-
# This is the time after which a session will be renewed.
|
40
|
-
# After this time, the session will be renewed and the expiration
|
41
|
-
# time will be extended. This is useful for keeping users logged in
|
42
|
-
# without requiring them to log in again. The renewal time is set
|
43
|
-
# in seconds, minutes, or hours. The default is 3 days.
|
44
|
-
config.session_renewal_duration = 3.days
|
45
|
-
|
46
38
|
## Set the session secret
|
47
39
|
# This secret is used to sign the session tokens and ensure
|
48
40
|
# that they cannot be tampered with. It is important to keep this
|
@@ -56,7 +48,7 @@ Securial.configure do |config|
|
|
56
48
|
# Other options include :hs256, :hs384, and :hs512
|
57
49
|
config.session_algorithm = :hs256
|
58
50
|
|
59
|
-
|
51
|
+
##### Securial Mailer Configuration
|
60
52
|
## Set the mailer sender address
|
61
53
|
# This is the email address that will be used as the sender
|
62
54
|
# for all emails sent by the Securial engine. This includes
|
@@ -64,7 +56,7 @@ Securial.configure do |config|
|
|
64
56
|
# notifications.
|
65
57
|
config.mailer_sender = "no-reply@example.com"
|
66
58
|
|
67
|
-
|
59
|
+
##### Password configuration
|
68
60
|
## Set the password reset email subject
|
69
61
|
# This is the subject line that will be used for the password reset
|
70
62
|
# email. The default is "Password Reset Instructions".
|
@@ -107,7 +99,7 @@ Securial.configure do |config|
|
|
107
99
|
# secret secure and not share it with anyone.
|
108
100
|
config.reset_password_token_secret = "reset_secret"
|
109
101
|
|
110
|
-
|
102
|
+
##### Timestamp Configuration
|
111
103
|
## Set whether to use timestamps in the json responses.
|
112
104
|
# The options are:
|
113
105
|
# :none - no timestamps will be included in the json responses.
|
@@ -117,4 +109,42 @@ Securial.configure do |config|
|
|
117
109
|
# :all - the created_at and updated_at timestamps will be included
|
118
110
|
# for all users. This is useful for debugging and development purposes.
|
119
111
|
config.timestamps_in_response = Rails.env.production? ? :admins_only : :all
|
112
|
+
|
113
|
+
##### Response Configuration
|
114
|
+
## Set the format of the JSON keys in the responses.
|
115
|
+
# The options are:
|
116
|
+
# :snake_case - the keys will be in snake_case format.
|
117
|
+
# :lowerCamelCase - the keys will be in lowerCamelCase format.
|
118
|
+
# :upperCamelCase - the keys will be in UpperCamelCase format.
|
119
|
+
config.response_keys_format = :snake_case
|
120
|
+
|
121
|
+
##### Security Configuration
|
122
|
+
## Set the security headers to be included in the responses.
|
123
|
+
# Read more about security headers here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers or in the gem documentation.
|
124
|
+
# The options are:
|
125
|
+
# :default - the default security headers will be included.
|
126
|
+
# :strict - the strict security headers will be included.
|
127
|
+
config.security_headers = :strict
|
128
|
+
## set whether to enable request rate limiting
|
129
|
+
# This is useful for preventing abuse and denial of service attacks.
|
130
|
+
config.rate_limiting_enabled = true
|
131
|
+
## Set the rate limit for requests
|
132
|
+
# This is the maximum number of requests that a user can make
|
133
|
+
# in a given time period. The default is 60 requests per minute.
|
134
|
+
# This is only applied if `rate_limiting_enabled` is set to true.
|
135
|
+
config.rate_limit_requests_per_minute = 60
|
136
|
+
## Set the rate limit response status code
|
137
|
+
# This is the status code that will be returned when a user exceeds
|
138
|
+
# the rate limit. The status code should be a 4xx or 5xx code
|
139
|
+
# to indicate an error. Commonly used codes are 429 Too Many Requests
|
140
|
+
# or 503 Service Unavailable. The default is 429 Too Many Requests.
|
141
|
+
# This is only applied if `rate_limiting_enabled` is set to true.
|
142
|
+
config.rate_limit_response_status = 429
|
143
|
+
## Set the rate limit response message
|
144
|
+
# This is the message that will be returned when a user exceeds
|
145
|
+
# the rate limit. The default is "Too many requests, please try again later."
|
146
|
+
# This is only applied if `rate_limiting_enabled` is set to true.
|
147
|
+
config.rate_limit_response_message = "Too many requests, please try again later."
|
148
|
+
|
149
|
+
|
120
150
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rake"
|
3
|
+
|
4
|
+
module Securial
|
5
|
+
module Generators
|
6
|
+
module Install
|
7
|
+
class ViewsGenerator < Rails::Generators::Base
|
8
|
+
source_root Securial::Engine.root.join("app", "views", "securial").to_s
|
9
|
+
desc "Copies Securial model-related views to your application for customization."
|
10
|
+
|
11
|
+
def copy_model_views
|
12
|
+
Dir.glob(File.join(self.class.source_root, "**/*")).each do |path|
|
13
|
+
relative_path = Pathname.new(path).relative_path_from(Pathname.new(self.class.source_root))
|
14
|
+
|
15
|
+
if File.directory?(path)
|
16
|
+
empty_directory "app/views/securial/#{relative_path}"
|
17
|
+
else
|
18
|
+
copy_file path, "app/views/securial/#{relative_path}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Securial
|
2
|
-
module
|
3
|
-
module
|
2
|
+
module Auth
|
3
|
+
module AuthEncoder
|
4
4
|
class << self
|
5
5
|
def encode(session)
|
6
6
|
return nil unless session && session.class == Securial::Session
|
@@ -21,7 +21,7 @@ module Securial
|
|
21
21
|
begin
|
22
22
|
JWT.encode(payload, secret, algorithm, { kid: "hmac" })
|
23
23
|
rescue JWT::EncodeError => e
|
24
|
-
raise Errors::
|
24
|
+
raise Errors::AuthEncodeError, "Failed to encode session: #{e.message}"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -29,7 +29,7 @@ module Securial
|
|
29
29
|
begin
|
30
30
|
decoded = JWT.decode(token, secret, true, { algorithm: algorithm, verify_jti: true, iss: "securial" })
|
31
31
|
rescue JWT::DecodeError => e
|
32
|
-
raise Securial::
|
32
|
+
raise Securial::Auth::Errors::AuthDecodeError, "Failed to decode session token: #{e.message}"
|
33
33
|
end
|
34
34
|
decoded.first
|
35
35
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Securial
|
2
|
+
module Auth
|
3
|
+
module Errors
|
4
|
+
class BaseAuthError < StandardError
|
5
|
+
def backtrace; []; end
|
6
|
+
end
|
7
|
+
|
8
|
+
class AuthEncodeError < BaseAuthError; end
|
9
|
+
class AuthDecodeError < BaseAuthError; end
|
10
|
+
|
11
|
+
class AuthRevokedError < BaseAuthError; end
|
12
|
+
class AuthExpiredError < BaseAuthError; end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Securial
|
2
|
+
module Auth
|
3
|
+
module SessionCreator
|
4
|
+
class << self
|
5
|
+
def create_session(user, request)
|
6
|
+
return nil unless user && user.persisted? && request.is_a?(ActionDispatch::Request)
|
7
|
+
|
8
|
+
user.sessions.create!(
|
9
|
+
user_agent: request.user_agent,
|
10
|
+
ip_address: request.remote_ip,
|
11
|
+
refresh_token: SecureRandom.hex(64),
|
12
|
+
last_refreshed_at: Time.current,
|
13
|
+
refresh_token_expires_at: 1.week.from_now,
|
14
|
+
).tap do |session|
|
15
|
+
Current.session = session
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,41 +1,61 @@
|
|
1
|
+
require_relative "../helpers/_index"
|
1
2
|
module Securial
|
2
3
|
module Config
|
3
4
|
VALID_SESSION_ENCRYPTION_ALGORITHMS = [:hs256, :hs384, :hs512].freeze
|
4
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
|
5
8
|
|
6
9
|
class Configuration
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
def self.config_attributes # rubocop:disable Metrics/MethodLength
|
11
|
+
{
|
12
|
+
log_to_file: !Rails.env.test?,
|
13
|
+
log_to_stdout: !Rails.env.test?,
|
14
|
+
log_file_level: :info,
|
15
|
+
log_stdout_level: :debug,
|
16
|
+
admin_role: :admin,
|
17
|
+
session_expiration_duration: 3.minutes,
|
18
|
+
session_secret: "secret",
|
19
|
+
session_algorithm: :hs256,
|
20
|
+
mailer_sender: "no-reply@example.com",
|
21
|
+
password_reset_email_subject: "SECURIAL: Password Reset Instructions",
|
22
|
+
password_min_length: 8,
|
23
|
+
password_max_length: 128,
|
24
|
+
password_complexity: Securial::RegexHelper::PASSWORD_REGEX,
|
25
|
+
password_expires: true,
|
26
|
+
password_expires_in: 90.days,
|
27
|
+
reset_password_token_expires_in: 2.hours,
|
28
|
+
reset_password_token_secret: "reset_secret",
|
29
|
+
timestamps_in_response: :all,
|
30
|
+
response_keys_format: :snake_case,
|
31
|
+
security_headers: :strict,
|
32
|
+
rate_limiting_enabled: true,
|
33
|
+
rate_limit_requests_per_minute: 60,
|
34
|
+
rate_limit_response_status: 429,
|
35
|
+
rate_limit_response_message: "Too many requests, please try again later.",
|
36
|
+
}
|
37
|
+
end
|
20
38
|
|
21
39
|
def initialize
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
self.class.config_attributes.each do |attr, default|
|
41
|
+
instance_variable_set("@#{attr}", default)
|
42
|
+
end
|
43
|
+
validate!
|
44
|
+
end
|
45
|
+
|
46
|
+
config_attributes.each_key do |attr|
|
47
|
+
define_method(attr) { instance_variable_get("@#{attr}") }
|
48
|
+
|
49
|
+
define_method("#{attr}=") do |value|
|
50
|
+
instance_variable_set("@#{attr}", value)
|
51
|
+
validate!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def validate!
|
58
|
+
Securial::Config::Validation.validate_all!(self)
|
39
59
|
end
|
40
60
|
end
|
41
61
|
end
|
@@ -13,7 +13,8 @@ module Securial
|
|
13
13
|
|
14
14
|
class ConfigMailerSenderError < BaseConfigError; end
|
15
15
|
class ConfigPasswordError < BaseConfigError; end
|
16
|
-
class
|
16
|
+
class ConfigResponseError < BaseConfigError; end
|
17
|
+
class ConfigSecurityError < BaseConfigError; end
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|