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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +14 -9
  4. data/app/controllers/concerns/securial/identity.rb +91 -2
  5. data/app/controllers/securial/accounts_controller.rb +68 -5
  6. data/app/controllers/securial/application_controller.rb +34 -2
  7. data/app/controllers/securial/passwords_controller.rb +44 -4
  8. data/app/controllers/securial/role_assignments_controller.rb +55 -4
  9. data/app/controllers/securial/roles_controller.rb +54 -0
  10. data/app/controllers/securial/sessions_controller.rb +77 -3
  11. data/app/controllers/securial/status_controller.rb +24 -0
  12. data/app/controllers/securial/users_controller.rb +54 -0
  13. data/app/jobs/securial/application_job.rb +9 -0
  14. data/app/mailers/securial/application_mailer.rb +12 -0
  15. data/app/mailers/securial/securial_mailer.rb +30 -0
  16. data/app/models/concerns/securial/password_resettable.rb +70 -0
  17. data/app/models/securial/application_record.rb +19 -0
  18. data/app/models/securial/current.rb +13 -0
  19. data/app/models/securial/role.rb +17 -0
  20. data/app/models/securial/role_assignment.rb +16 -0
  21. data/app/models/securial/session.rb +79 -1
  22. data/app/models/securial/user.rb +34 -0
  23. data/lib/generators/factory_bot/model/model_generator.rb +1 -0
  24. data/lib/securial/auth.rb +44 -6
  25. data/lib/securial/cli.rb +124 -0
  26. data/lib/securial/config.rb +49 -2
  27. data/lib/securial/engine.rb +41 -0
  28. data/lib/securial/error/auth.rb +52 -0
  29. data/lib/securial/error/base_securial_error.rb +51 -0
  30. data/lib/securial/error/config.rb +33 -0
  31. data/lib/securial/error.rb +33 -3
  32. data/lib/securial/helpers.rb +48 -4
  33. data/lib/securial/logger/broadcaster.rb +89 -1
  34. data/lib/securial/logger/builder.rb +54 -1
  35. data/lib/securial/logger/formatter.rb +73 -0
  36. data/lib/securial/logger.rb +42 -1
  37. data/lib/securial/middleware.rb +40 -9
  38. data/lib/securial/security/request_rate_limiter.rb +47 -1
  39. data/lib/securial/security.rb +37 -6
  40. data/lib/securial/version.rb +8 -1
  41. data/lib/securial.rb +36 -0
  42. 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
- error: "Invalid email address or password.",
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: { error: "Invalid or expired token." }
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
- error: "Password expired",
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
@@ -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? ? false : true
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