securial 1.0.1 → 1.0.2
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/.yardopts +4 -0
- data/README.md +14 -9
- data/app/controllers/concerns/securial/identity.rb +91 -2
- data/app/controllers/securial/accounts_controller.rb +68 -5
- data/app/controllers/securial/application_controller.rb +34 -2
- data/app/controllers/securial/passwords_controller.rb +44 -4
- data/app/controllers/securial/role_assignments_controller.rb +55 -4
- data/app/controllers/securial/roles_controller.rb +54 -0
- data/app/controllers/securial/sessions_controller.rb +77 -3
- data/app/controllers/securial/status_controller.rb +24 -0
- data/app/controllers/securial/users_controller.rb +54 -0
- data/app/jobs/securial/application_job.rb +9 -0
- data/app/mailers/securial/application_mailer.rb +12 -0
- data/app/mailers/securial/securial_mailer.rb +30 -0
- data/app/models/concerns/securial/password_resettable.rb +70 -0
- data/app/models/securial/application_record.rb +19 -0
- data/app/models/securial/current.rb +13 -0
- data/app/models/securial/role.rb +17 -0
- data/app/models/securial/role_assignment.rb +16 -0
- data/app/models/securial/session.rb +79 -1
- data/app/models/securial/user.rb +34 -0
- data/lib/generators/factory_bot/model/model_generator.rb +1 -0
- data/lib/securial/auth.rb +44 -6
- data/lib/securial/cli.rb +124 -0
- data/lib/securial/config.rb +49 -2
- data/lib/securial/engine.rb +41 -0
- data/lib/securial/error/auth.rb +52 -0
- data/lib/securial/error/base_securial_error.rb +51 -0
- data/lib/securial/error/config.rb +33 -0
- data/lib/securial/error.rb +33 -3
- data/lib/securial/helpers.rb +48 -4
- data/lib/securial/logger/broadcaster.rb +89 -1
- data/lib/securial/logger/builder.rb +54 -1
- data/lib/securial/logger/formatter.rb +73 -0
- data/lib/securial/logger.rb +42 -1
- data/lib/securial/middleware.rb +40 -9
- data/lib/securial/security/request_rate_limiter.rb +47 -1
- data/lib/securial/security.rb +37 -6
- data/lib/securial/version.rb +8 -1
- data/lib/securial.rb +36 -0
- metadata +4 -3
@@ -1,16 +1,52 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# SessionsController
|
4
|
+
#
|
5
|
+
# Controller for managing user authentication sessions.
|
6
|
+
#
|
7
|
+
# This controller handles session-related operations including:
|
8
|
+
# - User login and token issuance
|
9
|
+
# - Session listing and management
|
10
|
+
# - Token refresh
|
11
|
+
# - Session revocation and logout
|
12
|
+
#
|
13
|
+
# Session management is a critical security component, providing users
|
14
|
+
# with the ability to authenticate and manage their active sessions.
|
15
|
+
#
|
16
|
+
# Routes typically mounted at Securial/sessions/* in the host application.
|
17
|
+
#
|
2
18
|
class SessionsController < ApplicationController
|
3
19
|
skip_authentication! only: %i[login refresh]
|
4
20
|
|
5
21
|
before_action :set_session, only: %i[show revoke logout]
|
6
22
|
|
23
|
+
# Lists all active sessions for the current user.
|
24
|
+
#
|
25
|
+
# Retrieves all active sessions belonging to the authenticated user,
|
26
|
+
# enabling users to monitor their login activity across devices.
|
27
|
+
#
|
28
|
+
# @return [void] Renders index view with the user's active sessions
|
7
29
|
def index
|
8
30
|
@securial_sessions = Current.user.sessions
|
9
31
|
end
|
10
32
|
|
33
|
+
# Shows details for a specific session.
|
34
|
+
#
|
35
|
+
# Retrieves and displays information for a single session.
|
36
|
+
#
|
37
|
+
# @param [Integer] params[:id] The ID of the session to display
|
38
|
+
# @return [void] Renders show view with the specified session
|
11
39
|
def show
|
12
40
|
end
|
13
41
|
|
42
|
+
# Authenticates a user and creates a new session.
|
43
|
+
#
|
44
|
+
# Validates the provided credentials and, if successful, creates a new
|
45
|
+
# session and returns access and refresh tokens.
|
46
|
+
#
|
47
|
+
# @param [String] params[:email_address] The user's email address
|
48
|
+
# @param [String] params[:password] The user's password
|
49
|
+
# @return [void] Renders tokens with 201 Created status or error with 401 status
|
14
50
|
def login
|
15
51
|
params.require([:email_address, :password])
|
16
52
|
if user = User.authenticate_by(params.permit([:email_address, :password]))
|
@@ -18,18 +54,30 @@ module Securial
|
|
18
54
|
else
|
19
55
|
render status: :unauthorized,
|
20
56
|
json: {
|
21
|
-
|
57
|
+
errors: ["Invalid email address or password."],
|
22
58
|
instructions: "Make sure to send the correct 'email_address' and 'password' in the payload",
|
23
59
|
}
|
24
60
|
end
|
25
61
|
end
|
26
62
|
|
63
|
+
# Ends the current user's session.
|
64
|
+
#
|
65
|
+
# Revokes the active session token, effectively logging the user out.
|
66
|
+
#
|
67
|
+
# @return [void] Returns 204 No Content status
|
27
68
|
def logout
|
28
69
|
@securial_session.revoke!
|
29
70
|
Current.session = nil
|
30
71
|
head :no_content
|
31
72
|
end
|
32
73
|
|
74
|
+
# Issues new tokens using a valid refresh token.
|
75
|
+
#
|
76
|
+
# Validates the provided refresh token and, if valid, issues new
|
77
|
+
# access and refresh tokens to extend the user's session.
|
78
|
+
#
|
79
|
+
# @param [String] params[:refresh_token] The refresh token to validate
|
80
|
+
# @return [void] Renders new tokens with 201 Created status or error with 422 status
|
33
81
|
def refresh
|
34
82
|
if Current.session = Securial::Session.find_by(refresh_token: params[:refresh_token])
|
35
83
|
if Current.session.is_valid_session_request?(request)
|
@@ -43,15 +91,29 @@ module Securial
|
|
43
91
|
return
|
44
92
|
end
|
45
93
|
end
|
46
|
-
render status: :unprocessable_entity, json: {
|
94
|
+
render status: :unprocessable_entity, json: {
|
95
|
+
error: "Invalid or expired token.",
|
96
|
+
instructions: "Please log in again to obtain a new access token.",
|
97
|
+
}
|
47
98
|
end
|
48
99
|
|
100
|
+
# Revokes a specific session.
|
101
|
+
#
|
102
|
+
# Invalidates the specified session, preventing further use of its tokens.
|
103
|
+
#
|
104
|
+
# @param [Integer] params[:id] The ID of the session to revoke
|
105
|
+
# @return [void] Returns 204 No Content status
|
49
106
|
def revoke
|
50
107
|
@securial_session.revoke!
|
51
108
|
Current.session = nil if @securial_session == Current.session
|
52
109
|
head :no_content
|
53
110
|
end
|
54
111
|
|
112
|
+
# Revokes all of the current user's sessions.
|
113
|
+
#
|
114
|
+
# Invalidates all active sessions for the current user, forcing logout across all devices.
|
115
|
+
#
|
116
|
+
# @return [void] Returns 204 No Content status
|
55
117
|
def revoke_all
|
56
118
|
Current.user.sessions.each(&:revoke!)
|
57
119
|
Current.session = nil
|
@@ -60,16 +122,28 @@ module Securial
|
|
60
122
|
|
61
123
|
private
|
62
124
|
|
125
|
+
# Finds and sets the session to be manipulated.
|
126
|
+
#
|
127
|
+
# Uses the provided ID or defaults to the current session if no ID is provided.
|
128
|
+
#
|
129
|
+
# @return [void]
|
63
130
|
def set_session
|
64
131
|
id = params[:id]
|
65
132
|
@securial_session = id ? Current.user.sessions.find(params[:id]) : Current.session
|
66
133
|
end
|
67
134
|
|
135
|
+
# Renders the appropriate response after successful authentication.
|
136
|
+
#
|
137
|
+
# Checks if the user's password has expired and either prompts for reset
|
138
|
+
# or creates a new session with tokens for the authenticated user.
|
139
|
+
#
|
140
|
+
# @param [User] user The authenticated user
|
141
|
+
# @return [void] Renders tokens with 201 Created status or error with 403 status
|
68
142
|
def render_login_response(user)
|
69
143
|
if user.password_expired?
|
70
144
|
render status: :forbidden,
|
71
145
|
json: {
|
72
|
-
|
146
|
+
errors: ["Password expired"],
|
73
147
|
instructions: "Please reset your password before logging in.",
|
74
148
|
}
|
75
149
|
else
|
@@ -1,7 +1,31 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# StatusController
|
4
|
+
#
|
5
|
+
# Controller for checking system status and authentication state.
|
6
|
+
#
|
7
|
+
# This controller provides a simple endpoint for checking if the Securial
|
8
|
+
# system is operational and retrieving basic information about the current
|
9
|
+
# authentication state, if applicable. This endpoint is publicly accessible
|
10
|
+
# and does not require authentication.
|
11
|
+
#
|
12
|
+
# Typically used for:
|
13
|
+
# - Health checks from monitoring systems
|
14
|
+
# - Client applications to verify API availability
|
15
|
+
# - Testing authentication status without making authenticated requests
|
16
|
+
#
|
17
|
+
# Routes typically mounted at Securial/status/* in the host application.
|
18
|
+
#
|
2
19
|
class StatusController < ApplicationController
|
3
20
|
skip_authentication!
|
4
21
|
|
22
|
+
# Shows the current Securial Engine status.
|
23
|
+
#
|
24
|
+
# Provides information about the engine's operational state and,
|
25
|
+
# if the request includes a valid authentication token, the current user.
|
26
|
+
# This endpoint is always accessible without authentication.
|
27
|
+
#
|
28
|
+
# @return [void] Renders status information with 200 OK status
|
5
29
|
def show
|
6
30
|
@current_user = current_user
|
7
31
|
Securial.logger.info("Status check initiated")
|
@@ -1,14 +1,48 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# UsersController
|
4
|
+
#
|
5
|
+
# Controller for managing users in the Securial authentication system.
|
6
|
+
#
|
7
|
+
# This controller provides administrative CRUD operations for user management, including:
|
8
|
+
# - Listing all users in the system
|
9
|
+
# - Creating new user accounts
|
10
|
+
# - Viewing user details
|
11
|
+
# - Updating user information
|
12
|
+
# - Deleting user accounts
|
13
|
+
#
|
14
|
+
# All operations require admin authentication and are typically used for
|
15
|
+
# administrative user management rather than self-service account management.
|
16
|
+
#
|
17
|
+
# Routes typically mounted at Securial/admins/users/* in the host application.
|
18
|
+
#
|
2
19
|
class UsersController < ApplicationController
|
3
20
|
before_action :set_securial_user, only: [:show, :update, :destroy]
|
4
21
|
|
22
|
+
# Lists all users in the system.
|
23
|
+
#
|
24
|
+
# Retrieves all users for administrative display and management.
|
25
|
+
#
|
26
|
+
# @return [void] Renders index view with all users
|
5
27
|
def index
|
6
28
|
@securial_users = User.all
|
7
29
|
end
|
8
30
|
|
31
|
+
# Shows details for a specific user.
|
32
|
+
#
|
33
|
+
# Retrieves and displays information for a single user.
|
34
|
+
#
|
35
|
+
# @param [Integer] params[:id] The ID of the user to display
|
36
|
+
# @return [void] Renders show view with the specified user
|
9
37
|
def show
|
10
38
|
end
|
11
39
|
|
40
|
+
# Creates a new user in the system.
|
41
|
+
#
|
42
|
+
# Adds a new user with the provided attributes.
|
43
|
+
#
|
44
|
+
# @param [Hash] params[:securial_user] User attributes including email_address, username, etc.
|
45
|
+
# @return [void] Renders the created user with 201 Created status or errors with 422
|
12
46
|
def create
|
13
47
|
@securial_user = User.new(securial_user_params)
|
14
48
|
|
@@ -19,6 +53,13 @@ module Securial
|
|
19
53
|
end
|
20
54
|
end
|
21
55
|
|
56
|
+
# Updates an existing user.
|
57
|
+
#
|
58
|
+
# Modifies the attributes of an existing user.
|
59
|
+
#
|
60
|
+
# @param [Integer] params[:id] The ID of the user to update
|
61
|
+
# @param [Hash] params[:securial_user] Updated user attributes
|
62
|
+
# @return [void] Renders the updated user or errors with 422 status
|
22
63
|
def update
|
23
64
|
if @securial_user.update(securial_user_params)
|
24
65
|
render :show, status: :ok, location: @securial_user
|
@@ -27,6 +68,12 @@ module Securial
|
|
27
68
|
end
|
28
69
|
end
|
29
70
|
|
71
|
+
# Deletes an existing user.
|
72
|
+
#
|
73
|
+
# Permanently removes a user from the system.
|
74
|
+
#
|
75
|
+
# @param [Integer] params[:id] The ID of the user to delete
|
76
|
+
# @return [void] Returns 204 No Content status
|
30
77
|
def destroy
|
31
78
|
@securial_user.destroy
|
32
79
|
head :no_content
|
@@ -34,10 +81,17 @@ module Securial
|
|
34
81
|
|
35
82
|
private
|
36
83
|
|
84
|
+
# Finds and sets a specific user for show, update and destroy actions.
|
85
|
+
#
|
86
|
+
# @return [void]
|
87
|
+
#
|
37
88
|
def set_securial_user
|
38
89
|
@securial_user = User.find(params[:id])
|
39
90
|
end
|
40
91
|
|
92
|
+
# Permits and extracts user parameters from the request.
|
93
|
+
#
|
94
|
+
# @return [ActionController::Parameters] Permitted user parameters
|
41
95
|
def securial_user_params
|
42
96
|
params.expect(securial_user: [
|
43
97
|
:email_address,
|
@@ -1,4 +1,13 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# ApplicationJob
|
4
|
+
#
|
5
|
+
# This class serves as the base job class for all Securial jobs.
|
6
|
+
#
|
7
|
+
# It inherits from ActiveJob::Base, allowing it to be used with Rails' job
|
8
|
+
# processing framework. This class can be extended to create specific jobs
|
9
|
+
# related to the Securial authentication and authorization system.
|
10
|
+
#
|
2
11
|
class ApplicationJob < ActiveJob::Base
|
3
12
|
end
|
4
13
|
end
|
@@ -1,4 +1,16 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# ApplicationMailer
|
4
|
+
#
|
5
|
+
# This class serves as the base mailer class for all Securial mailers.
|
6
|
+
#
|
7
|
+
# It inherits from ActionMailer::Base, allowing it to be used for sending
|
8
|
+
# emails related to the Securial authentication and authorization system.
|
9
|
+
#
|
10
|
+
# This class can be extended to create specific mailers for notifications,
|
11
|
+
# user communications, or other email-related functionality within the Securial
|
12
|
+
# system.
|
13
|
+
#
|
2
14
|
class ApplicationMailer < ActionMailer::Base
|
3
15
|
default from: "from@example.com"
|
4
16
|
layout "mailer"
|
@@ -1,11 +1,32 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# SecurialMailer
|
4
|
+
#
|
5
|
+
# # This class serves as the mailer for Securial, handling email notifications
|
6
|
+
# related to user account management, such as welcome emails, sign-in notifications,
|
7
|
+
# account updates, and password recovery.
|
8
|
+
#
|
9
|
+
# It inherits from ApplicationMailer, allowing it to utilize the base mailer functionality
|
10
|
+
# provided by Securial::ApplicationMailer.
|
11
|
+
#
|
12
|
+
# This class can be extended to add more email functionalities as needed.
|
13
|
+
#
|
2
14
|
class SecurialMailer < ApplicationMailer
|
15
|
+
# Sends a welcome email to a new user.
|
16
|
+
#
|
17
|
+
# @param [Securial::User] securial_user The user to whom the welcome email will be sent.
|
18
|
+
# @return [void] Sends an email with the subject defined in Securial configuration.
|
3
19
|
def welcome(securial_user)
|
4
20
|
@user = securial_user
|
5
21
|
subject = Securial.configuration.mailer_sign_up_subject
|
6
22
|
mail subject: subject, to: securial_user.email_address
|
7
23
|
end
|
8
24
|
|
25
|
+
# Sends a sign-in notification email.
|
26
|
+
#
|
27
|
+
# @param [Securial::User] securial_user The user who signed in.
|
28
|
+
# @param [<Type>] securial_session The session information for the sign-in.
|
29
|
+
# @return [void] Sends an email with the subject defined in Securial configuration
|
9
30
|
def sign_in(securial_user, securial_session)
|
10
31
|
@user = securial_user
|
11
32
|
@session = securial_session
|
@@ -13,12 +34,21 @@ module Securial
|
|
13
34
|
mail subject: subject, to: securial_user.email_address
|
14
35
|
end
|
15
36
|
|
37
|
+
# Sends an account update notification email.
|
38
|
+
#
|
39
|
+
# @param [Securial::User] securial_user The user whose account was updated.
|
40
|
+
# @return [void] Sends an email with the subject defined in Securial configuration.
|
16
41
|
def update_account(securial_user)
|
17
42
|
@user = securial_user
|
18
43
|
subject = Securial.configuration.mailer_update_account_subject
|
19
44
|
mail subject: subject, to: securial_user.email_address
|
20
45
|
end
|
21
46
|
|
47
|
+
# Sends a password recovery email.
|
48
|
+
#
|
49
|
+
# @param [Securial::User] securial_user The user who requested a password reset.
|
50
|
+
# @return [void] Sends an email with the subject defined in Securial configuration
|
51
|
+
# containing instructions for resetting the password.
|
22
52
|
def forgot_password(securial_user)
|
23
53
|
@user = securial_user
|
24
54
|
subject = Securial.configuration.mailer_forgot_password_subject
|
@@ -1,4 +1,37 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# PasswordResettable Concern
|
4
|
+
#
|
5
|
+
# This module provides functionality for managing password reset tokens and
|
6
|
+
# password expiration for user accounts. It includes methods to generate,
|
7
|
+
# validate, and clear reset password tokens, as well as to check if a user's
|
8
|
+
# password has expired.
|
9
|
+
#
|
10
|
+
# It also includes validations for password complexity and length.
|
11
|
+
#
|
12
|
+
# ## Usage
|
13
|
+
# Include this module in your User model to enable password reset functionality.
|
14
|
+
# It requires the model to have a `password_digest` attribute for secure password storage.
|
15
|
+
# The module also provides methods to handle password reset tokens and password expiration.
|
16
|
+
#
|
17
|
+
# ## Example
|
18
|
+
# class User < ApplicationRecord
|
19
|
+
# include Securial::PasswordResettable
|
20
|
+
# # Additional user model code...
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# ## Configuration
|
24
|
+
# The module uses the Securial configuration for password complexity, length,
|
25
|
+
# and reset password token expiration settings. You can configure these settings
|
26
|
+
# in your Securial initializer.
|
27
|
+
#
|
28
|
+
# ## Validations
|
29
|
+
# - Password must meet complexity requirements defined in Securial.configuration
|
30
|
+
# - Password must be at least Securial.configuration.password_min_length characters long
|
31
|
+
# - Password must be at most Securial.configuration.password_max_length characters long
|
32
|
+
# - Password confirmation must be present if a new password is being set or if the password is not nil
|
33
|
+
# - Reset password token must be generated and cleared appropriately
|
34
|
+
# - Password expiration is managed based on the Securial.configuration.password_expires_in setting
|
2
35
|
module PasswordResettable
|
3
36
|
extend ActiveSupport::Concern
|
4
37
|
|
@@ -23,6 +56,9 @@ module Securial
|
|
23
56
|
if: -> { new_record? || !password.nil? }
|
24
57
|
end
|
25
58
|
|
59
|
+
# Generates a secure reset password token for the user.
|
60
|
+
#
|
61
|
+
# @return [void] Updates the user's reset_password_token and reset_password_token_created_at attributes.
|
26
62
|
def generate_reset_password_token!
|
27
63
|
update!(
|
28
64
|
reset_password_token: Auth::TokenGenerator.generate_password_reset_token,
|
@@ -30,6 +66,17 @@ module Securial
|
|
30
66
|
)
|
31
67
|
end
|
32
68
|
|
69
|
+
# Checks if the reset password token is valid.
|
70
|
+
#
|
71
|
+
# The token is considered valid if it was created within the configured expiration duration.
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# user.reset_password_token_valid? # => true or false
|
75
|
+
# @note The method checks both the presence of the token and its creation time.
|
76
|
+
# @note If the token is blank or the creation time is blank, it returns false.
|
77
|
+
# @note If the token is expired, it returns false.
|
78
|
+
# @note The method uses the configured expiration duration from Securial.configuration.
|
79
|
+
# @return [Boolean] Returns true if the reset password token is valid, false otherwise.
|
33
80
|
def reset_password_token_valid?
|
34
81
|
return false if reset_password_token.blank? || reset_password_token_created_at.blank?
|
35
82
|
|
@@ -39,6 +86,12 @@ module Securial
|
|
39
86
|
reset_password_token_created_at > duration.ago
|
40
87
|
end
|
41
88
|
|
89
|
+
# Clears the reset password token and its creation time.
|
90
|
+
#
|
91
|
+
# This method is typically called after a successful password reset
|
92
|
+
# to prevent the token from being reused.
|
93
|
+
#
|
94
|
+
# @return [void] Updates the user's reset_password_token and reset_password_token_created_at attributes to nil.
|
42
95
|
def clear_reset_password_token!
|
43
96
|
update!(
|
44
97
|
reset_password_token: nil,
|
@@ -46,6 +99,18 @@ module Securial
|
|
46
99
|
)
|
47
100
|
end
|
48
101
|
|
102
|
+
# Checks if the user's password has expired.
|
103
|
+
#
|
104
|
+
# The password is considered expired if the last time it was changed
|
105
|
+
# is older than the configured expiration duration.
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# user.password_expired? # => true or false
|
109
|
+
# @note The method checks both the presence of the password_changed_at timestamp
|
110
|
+
# and the configured expiration duration.
|
111
|
+
# @note If the password_changed_at timestamp is blank, it returns false.
|
112
|
+
# @note If the password is expired, it returns true.
|
113
|
+
# @return [Boolean] Returns true if the password is expired, false otherwise.
|
49
114
|
def password_expired?
|
50
115
|
return false unless Securial.configuration.password_expires
|
51
116
|
return true unless password_changed_at
|
@@ -55,6 +120,11 @@ module Securial
|
|
55
120
|
|
56
121
|
private
|
57
122
|
|
123
|
+
# Updates the password_changed_at timestamp to the current time.
|
124
|
+
#
|
125
|
+
# This method is called before saving the user record if the password digest has changed.
|
126
|
+
#
|
127
|
+
# @return [void] Sets the password_changed_at attribute to the current time.
|
58
128
|
def update_password_changed_at
|
59
129
|
self.password_changed_at = Time.current
|
60
130
|
end
|
@@ -1,4 +1,18 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# ApplicationRecord
|
4
|
+
#
|
5
|
+
# This class serves as the base model class for all Securial models.
|
6
|
+
#
|
7
|
+
# It inherits from ActiveRecord::Base and provides a common functionality for all models in the
|
8
|
+
# Securial engine, including:
|
9
|
+
# - UUIDv7 generation for the `id` field
|
10
|
+
# - Abstract class definition to ensure it is not instantiated directly
|
11
|
+
#
|
12
|
+
# - Custom behavior for the `before_create` callback to set the `id` field
|
13
|
+
#
|
14
|
+
# This class can be extended to add more model functionalities as needed.
|
15
|
+
#
|
2
16
|
class ApplicationRecord < ActiveRecord::Base
|
3
17
|
self.abstract_class = true
|
4
18
|
|
@@ -7,8 +21,13 @@ module Securial
|
|
7
21
|
private
|
8
22
|
|
9
23
|
# Generates a UUIDv7 for the `id` field if it is blank.
|
24
|
+
#
|
10
25
|
# This method is triggered by the `before_create` callback.
|
11
26
|
# The generated ID is expected to be a UUIDv7 string.
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
# @note This method will only set the `id` if it is not already present
|
30
|
+
# and if the `id` field is of type string.
|
12
31
|
def generate_uuid_v7
|
13
32
|
return if self.id.present? || self.class.type_for_attribute(:id).type != :string
|
14
33
|
|
@@ -1,4 +1,17 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# Current
|
4
|
+
#
|
5
|
+
# This class provides a way to access the current session and user
|
6
|
+
# throughout the application. It uses ActiveSupport::CurrentAttributes to
|
7
|
+
# store the session and delegate the user method to it.
|
8
|
+
#
|
9
|
+
# It allows you to access the current user in controllers, views, and jobs
|
10
|
+
# without having to pass the user object explicitly.
|
11
|
+
#
|
12
|
+
# Example usage:
|
13
|
+
# Current.session = session
|
14
|
+
# Current.user # => returns the current user object or nil if not authenticated
|
2
15
|
class Current < ActiveSupport::CurrentAttributes
|
3
16
|
attribute :session
|
4
17
|
delegate :user, to: :session, allow_nil: true
|
data/app/models/securial/role.rb
CHANGED
@@ -1,4 +1,21 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# Role
|
4
|
+
#
|
5
|
+
# This class represents a role in the Securial authorization system.
|
6
|
+
#
|
7
|
+
# Roles are used to define permissions and access levels for users within the application.
|
8
|
+
#
|
9
|
+
# ## Attributes
|
10
|
+
# - `role_name`: The name of the role, which is normalized to ensure consistency.
|
11
|
+
#
|
12
|
+
# ## Validations
|
13
|
+
# - `role_name` must be present and unique (case insensitive).
|
14
|
+
#
|
15
|
+
# ## Associations
|
16
|
+
# - Has many role assignments, allowing users to be associated with this role
|
17
|
+
# - Has many users through role assignments, enabling flexible permission management
|
18
|
+
#
|
2
19
|
class Role < ApplicationRecord
|
3
20
|
normalizes :role_name, with: ->(e) { Securial::Helpers::NormalizingHelper.normalize_role_name(e) }
|
4
21
|
|
@@ -1,4 +1,20 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# RoleAssignment
|
4
|
+
#
|
5
|
+
# This class represents the association between a user and a role in the Securial system.
|
6
|
+
#
|
7
|
+
# It is used to manage which roles are assigned to which users, allowing for
|
8
|
+
# flexible permission management within the application.
|
9
|
+
#
|
10
|
+
### Attributes
|
11
|
+
# - `user_id`: The ID of the user to whom the role is assigned
|
12
|
+
# - `role_id`: The ID of the role being assigned to the user
|
13
|
+
#
|
14
|
+
# ## Associations
|
15
|
+
# - Belongs to a user, linking the assignment to a specific user
|
16
|
+
# - Belongs to a role, linking the assignment to a specific role
|
17
|
+
#
|
2
18
|
class RoleAssignment < ApplicationRecord
|
3
19
|
belongs_to :user
|
4
20
|
belongs_to :role
|
@@ -1,4 +1,29 @@
|
|
1
1
|
module Securial
|
2
|
+
#
|
3
|
+
# Session
|
4
|
+
#
|
5
|
+
# # This class represents a user session in the Securial authentication system.
|
6
|
+
# # It is used to manage user sessions, including session creation, validation,
|
7
|
+
# and refresh functionality.
|
8
|
+
#
|
9
|
+
# ## Attributes
|
10
|
+
# - `user_id`: The ID of the user associated with the session
|
11
|
+
# - `ip_address`: The IP address from which the session was created
|
12
|
+
# - `user_agent`: The user agent string of the browser or client used to create
|
13
|
+
# the session
|
14
|
+
# - `refresh_token`: A token used to refresh the session
|
15
|
+
# - `refresh_token_expires_at`: The expiration time of the refresh token
|
16
|
+
# - `refresh_count`: The number of times the session has been refreshed
|
17
|
+
# - `last_refreshed_at`: The timestamp of the last time the session was refreshed
|
18
|
+
# - `revoked`: A boolean indicating whether the session has been revoked
|
19
|
+
#
|
20
|
+
# ## Associations
|
21
|
+
# - Belongs to a user, linking the session to a specific user
|
22
|
+
#
|
23
|
+
# ## Validations
|
24
|
+
# - `ip_address`: Must be present
|
25
|
+
# - `user_agent`: Must be present
|
26
|
+
# - `refresh_token`: Must be present
|
2
27
|
class Session < ApplicationRecord
|
3
28
|
belongs_to :user
|
4
29
|
|
@@ -6,18 +31,56 @@ module Securial
|
|
6
31
|
validates :user_agent, presence: true
|
7
32
|
validates :refresh_token, presence: true
|
8
33
|
|
34
|
+
# Revokes the session by setting the `revoked` attribute to true.
|
35
|
+
#
|
36
|
+
# This method updates the session record in the database to indicate that
|
37
|
+
# the session is no longer valid.
|
38
|
+
#
|
39
|
+
# @return [void] Updates the `revoked` attribute to true.
|
40
|
+
# @raise [ActiveRecord::RecordInvalid] if the update fails due to validation errors
|
41
|
+
# @example
|
42
|
+
# session.revoke! # => Updates the session to be revoked
|
43
|
+
# @note This method does not delete the session record; it only marks it as revoked.
|
44
|
+
#
|
9
45
|
def revoke!
|
10
46
|
update!(revoked: true)
|
11
47
|
end
|
12
48
|
|
49
|
+
# Checks if the session is valid based on its state.
|
50
|
+
#
|
51
|
+
# A session is considered valid if it is not revoked and has not expired.
|
52
|
+
#
|
53
|
+
# @return [Boolean] Returns true if the session is valid, false otherwise.
|
13
54
|
def is_valid_session?
|
14
|
-
revoked? || expired?
|
55
|
+
!(revoked? || expired?)
|
15
56
|
end
|
16
57
|
|
58
|
+
# Checks if the session is valid for a specific request.
|
59
|
+
#
|
60
|
+
# A session is valid for a request if it is not revoked, has not expired,
|
61
|
+
# and the IP address and user agent match those of the request.
|
62
|
+
#
|
63
|
+
# @param [ActionDispatch::Request] request The request to validate against
|
64
|
+
# @return [Boolean] Returns true if the session is valid for the request, false otherwise.
|
17
65
|
def is_valid_session_request?(request)
|
18
66
|
is_valid_session? && ip_address == request.ip && user_agent == request.user_agent
|
19
67
|
end
|
20
68
|
|
69
|
+
# Refreshes the session by generating a new refresh token and updating
|
70
|
+
# the session attributes.
|
71
|
+
#
|
72
|
+
# This method raises an error if the session is revoked or expired.
|
73
|
+
#
|
74
|
+
# @raise [Securial::Error::Auth::TokenRevokedError] if the session is revoked
|
75
|
+
# @raise [Securial::Error::Auth::TokenExpiredError] if the session is expired
|
76
|
+
# @return [void] Updates the session with a new refresh token and updates the refresh count and timestamps.
|
77
|
+
# @example
|
78
|
+
# session.refresh! # => Updates the session with a new refresh token
|
79
|
+
# @note This method uses the Securial::Auth::TokenGenerator to
|
80
|
+
# generate a new refresh token and updates the session's attributes accordingly.
|
81
|
+
# @note The refresh token expiration duration is configured in Securial.configuration.session_refresh_token_expires_in.
|
82
|
+
#
|
83
|
+
# @see Securial::Auth::TokenGenerator
|
21
84
|
def refresh!
|
22
85
|
raise Securial::Error::Auth::TokenRevokedError if revoked?
|
23
86
|
raise Securial::Error::Auth::TokenExpiredError if expired?
|
@@ -32,7 +95,22 @@ module Securial
|
|
32
95
|
refresh_token_expires_at: refresh_token_duration.from_now)
|
33
96
|
end
|
34
97
|
|
98
|
+
# Checks if the session is revoked.
|
99
|
+
#
|
100
|
+
# @return [Boolean] Returns true if the session is revoked, false otherwise.
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# session.revoked? # => true or false
|
104
|
+
# @note This method checks the `revoked` attribute of the session.
|
105
|
+
# @see Securial::Session#revoked
|
106
|
+
# @note This method is used to determine if the session is still active or has been revoked
|
35
107
|
def revoked?; revoked; end
|
108
|
+
|
109
|
+
# Checks if the session has expired based on the refresh token expiration time.
|
110
|
+
#
|
111
|
+
# A session is considered expired if the `refresh_token_expires_at` time is in the past.
|
112
|
+
#
|
113
|
+
# @return [Boolean] Returns true if the session has expired, false otherwise.
|
36
114
|
def expired?; refresh_token_expires_at < Time.current; end
|
37
115
|
end
|
38
116
|
end
|