securial 1.0.1 → 1.0.3

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +19 -12
  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/auth_encoder.rb +78 -0
  25. data/lib/securial/auth/session_creator.rb +49 -0
  26. data/lib/securial/auth/token_generator.rb +74 -0
  27. data/lib/securial/auth.rb +44 -6
  28. data/lib/securial/cli.rb +124 -0
  29. data/lib/securial/config/signature.rb +116 -5
  30. data/lib/securial/config/validation.rb +91 -0
  31. data/lib/securial/config.rb +49 -2
  32. data/lib/securial/engine.rb +41 -0
  33. data/lib/securial/error/auth.rb +52 -0
  34. data/lib/securial/error/base_securial_error.rb +51 -0
  35. data/lib/securial/error/config.rb +33 -0
  36. data/lib/securial/error.rb +33 -3
  37. data/lib/securial/helpers/key_transformer.rb +106 -0
  38. data/lib/securial/helpers/normalizing_helper.rb +69 -0
  39. data/lib/securial/helpers/regex_helper.rb +122 -0
  40. data/lib/securial/helpers/roles_helper.rb +71 -2
  41. data/lib/securial/helpers.rb +48 -4
  42. data/lib/securial/logger/broadcaster.rb +89 -1
  43. data/lib/securial/logger/builder.rb +54 -1
  44. data/lib/securial/logger/formatter.rb +73 -0
  45. data/lib/securial/logger.rb +42 -1
  46. data/lib/securial/middleware/request_tag_logger.rb +80 -0
  47. data/lib/securial/middleware/response_headers.rb +51 -3
  48. data/lib/securial/middleware/transform_request_keys.rb +143 -20
  49. data/lib/securial/middleware/transform_response_keys.rb +84 -4
  50. data/lib/securial/middleware.rb +40 -9
  51. data/lib/securial/security/request_rate_limiter.rb +47 -1
  52. data/lib/securial/security.rb +37 -6
  53. data/lib/securial/version.rb +8 -1
  54. data/lib/securial.rb +36 -0
  55. metadata +21 -15
@@ -1,4 +1,34 @@
1
1
  module Securial
2
+ #
3
+ # User
4
+ #
5
+ # This class represents a user in the Securial authentication and authorization system.
6
+ #
7
+ # Users can have multiple roles assigned to them, which define their permissions
8
+ # and access levels within the application.
9
+ #
10
+ # The User model includes functionality for password reset, email normalization,
11
+ # and various validations to ensure data integrity.
12
+ #
13
+ # ## Attributes
14
+ # - `email_address`: The user's email address, which is normalized
15
+ # - `username`: A unique username for the user, with specific format requirements
16
+ # - `first_name`: The user's first name, required and limited in length
17
+ # - `last_name`: The user's last name, required and limited in length
18
+ # - `phone`: An optional phone number, limited in length
19
+ # - `bio`: An optional biography, limited in length
20
+ #
21
+ # ## Validations
22
+ # - Email address must be present, unique, and formatted correctly
23
+ # - Username must be present, unique (case insensitive), and formatted correctly
24
+ # - First and last names must be present and limited in length
25
+ # - Phone and bio fields are optional but have length restrictions
26
+ #
27
+ # ## Associations
28
+ # - Has many role assignments, allowing users to have multiple roles
29
+ # - Has many roles through role assignments, enabling flexible permission management
30
+ # - Has many sessions, allowing for session management and tracking
31
+ #
2
32
  class User < ApplicationRecord
3
33
  include Securial::PasswordResettable
4
34
 
@@ -45,6 +75,10 @@ module Securial
45
75
  has_many :sessions, dependent: :destroy
46
76
 
47
77
 
78
+ # Checks if the user has the specified role.
79
+ #
80
+ # @param [String] role_name The name of the role to check
81
+ # @return [Boolean] Returns true if the user has the specified role, false otherwise.
48
82
  def is_admin?
49
83
  roles.exists?(role_name: Securial.titleized_admin_role)
50
84
  end
@@ -1,6 +1,7 @@
1
1
  require "rails/generators"
2
2
  require "rails/generators/named_base"
3
3
 
4
+ # @!ignore
4
5
  module FactoryBot
5
6
  module Generators
6
7
  class ModelGenerator < Rails::Generators::NamedBase
@@ -1,10 +1,58 @@
1
+ # @title Securial Authentication Token Encoder
2
+ #
3
+ # JWT token encoding and decoding for session authentication.
4
+ #
5
+ # This module provides secure JWT token creation and validation for user sessions
6
+ # in the Securial authentication system. It handles the encoding of session data
7
+ # into JWT tokens and the secure decoding/validation of those tokens, including
8
+ # signature verification and claim validation.
9
+ #
10
+ # @example Encoding a session into a JWT token
11
+ # session = Securial::Session.find(session_id)
12
+ # token = Securial::Auth::AuthEncoder.encode(session)
13
+ # # => "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIiwic3ViIjoiM..."
14
+ #
15
+ # @example Decoding and validating a JWT token
16
+ # begin
17
+ # payload = Securial::Auth::AuthEncoder.decode(token)
18
+ # session_id = payload['jti']
19
+ # # Use session_id to retrieve session from database
20
+ # rescue Securial::Error::Auth::TokenDecodeError => e
21
+ # # Handle invalid token
22
+ # end
1
23
  require "jwt"
2
24
 
3
25
  module Securial
4
26
  module Auth
27
+ # Handles JWT token encoding and decoding for session authentication.
28
+ #
29
+ # This module provides methods to securely encode session information into
30
+ # JWT tokens and decode/validate those tokens. It uses HMAC signing with
31
+ # a configurable secret and includes standard JWT claims for security.
32
+ #
33
+ # The tokens include:
34
+ # - Session ID (jti claim) for session lookup
35
+ # - Expiration time (exp claim) for automatic invalidation
36
+ # - Subject (sub claim) identifying the token type
37
+ # - Custom claims for IP address and user agent validation
38
+ #
39
+ # @see https://datatracker.ietf.org/doc/html/rfc7519 JWT RFC
5
40
  module AuthEncoder
6
41
  extend self
7
42
 
43
+ # Encodes a session object into a signed JWT token.
44
+ #
45
+ # Creates a JWT token containing the session ID and metadata for later
46
+ # validation. The token is signed using the configured secret and algorithm.
47
+ #
48
+ # @param [Securial::Session] session The session object to encode
49
+ # @return [String, nil] The encoded JWT token, or nil if session is invalid
50
+ # @raise [Securial::Error::Auth::TokenEncodeError] If JWT encoding fails
51
+ #
52
+ # @example
53
+ # session = Securial::Session.create!(user: current_user)
54
+ # token = AuthEncoder.encode(session)
55
+ # # => "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkw..."
8
56
  def encode(session)
9
57
  return nil unless session && session.class == Securial::Session
10
58
 
@@ -28,6 +76,21 @@ module Securial
28
76
  end
29
77
  end
30
78
 
79
+ # Decodes and validates a JWT token, returning the payload.
80
+ #
81
+ # Verifies the token signature, expiration, and other claims before
82
+ # returning the decoded payload. The token must have been signed with
83
+ # the current secret and must not be expired.
84
+ #
85
+ # @param [String] token The JWT token to decode and validate
86
+ # @return [Hash] The decoded token payload containing session information
87
+ # @raise [Securial::Error::Auth::TokenDecodeError] If token is invalid, expired, or malformed
88
+ #
89
+ # @example
90
+ # payload = AuthEncoder.decode("eyJhbGciOiJIUzI1NiJ9...")
91
+ # session_id = payload['jti']
92
+ # ip_address = payload['ip']
93
+ # user_agent = payload['agent']
31
94
  def decode(token)
32
95
  begin
33
96
  decoded = ::JWT.decode(token, secret, true, { algorithm: algorithm, verify_jti: true, iss: "securial" })
@@ -39,14 +102,29 @@ module Securial
39
102
 
40
103
  private
41
104
 
105
+ # Returns the secret key used for JWT signing and verification.
106
+ #
107
+ # @return [String] The configured session secret
108
+ # @api private
109
+ #
42
110
  def secret
43
111
  Securial.configuration.session_secret
44
112
  end
45
113
 
114
+ # Returns the algorithm used for JWT signing.
115
+ #
116
+ # @return [String] The configured session algorithm in uppercase
117
+ # @api private
118
+ #
46
119
  def algorithm
47
120
  Securial.configuration.session_algorithm.to_s.upcase
48
121
  end
49
122
 
123
+ # Returns the token expiration duration.
124
+ #
125
+ # @return [ActiveSupport::Duration] The configured session expiration duration
126
+ # @api private
127
+ #
50
128
  def expiry_duration
51
129
  Securial.configuration.session_expiration_duration
52
130
  end
@@ -1,8 +1,57 @@
1
+ # @title Securial Session Creator
2
+ #
3
+ # Session creation utilities for the Securial authentication system.
4
+ #
5
+ # This module provides functionality to create authenticated user sessions with
6
+ # proper token generation and metadata tracking. It handles the validation of
7
+ # user and request objects before creating database records and setting up
8
+ # the current session context.
9
+ #
10
+ # @example Creating a session for a user
11
+ # user = Securial::User.find_by(email: "user@example.com")
12
+ # session = Securial::Auth::SessionCreator.create_session!(user, request)
13
+ # # => #<Securial::Session id: "abc123", user_id: "user123", ...>
14
+ #
15
+ # @example Handling invalid inputs
16
+ # invalid_session = Securial::Auth::SessionCreator.create_session!(nil, request)
17
+ # # => nil
1
18
  module Securial
2
19
  module Auth
20
+ # Creates and manages user authentication sessions.
21
+ #
22
+ # This module provides methods to create new authenticated sessions for users,
23
+ # including proper validation of inputs, generation of refresh tokens, and
24
+ # setting up session metadata such as IP addresses and user agents.
25
+ #
26
+ # Created sessions are automatically set as the current session context and
27
+ # include all necessary tokens and expiration information for secure
28
+ # authentication management.
29
+ #
3
30
  module SessionCreator
4
31
  extend self
5
32
 
33
+ # Creates a new authenticated session for the given user and request.
34
+ #
35
+ # Validates the provided user and request objects, then creates a new session
36
+ # record with appropriate metadata and tokens. The newly created session is
37
+ # automatically set as the current session context.
38
+ #
39
+ # @param [Securial::User] user The user object to create a session for
40
+ # @param [ActionDispatch::Request] request The HTTP request object containing metadata
41
+ # @return [Securial::Session, nil] The created session object, or nil if validation fails
42
+ #
43
+ # @example Creating a session after successful authentication
44
+ # user = Securial::User.authenticate(email, password)
45
+ # if user
46
+ # session = SessionCreator.create_session!(user, request)
47
+ # # User is now authenticated with active session
48
+ # end
49
+ #
50
+ # @example Handling validation failures
51
+ # # Invalid user (not persisted)
52
+ # new_user = Securial::User.new
53
+ # session = SessionCreator.create_session!(new_user, request)
54
+ # # => nil
6
55
  def create_session!(user, request)
7
56
  valid_user = user && user.is_a?(Securial::User) && user.persisted?
8
57
  valid_request = request.is_a?(ActionDispatch::Request)
@@ -1,11 +1,48 @@
1
+ # @title Securial Token Generator
2
+ #
3
+ # Secure token generation utilities for the Securial authentication system.
4
+ #
5
+ # This module provides cryptographically secure token generation for various
6
+ # authentication purposes, including refresh tokens and password reset tokens.
7
+ # All tokens are generated using secure random sources and include appropriate
8
+ # entropy for their intended use cases.
9
+ #
10
+ # @example Generating a refresh token
11
+ # refresh_token = Securial::Auth::TokenGenerator.generate_refresh_token
12
+ # # => "a1b2c3d4e5f6...9876543210abcdef1234567890"
13
+ #
14
+ # @example Generating a password reset token
15
+ # reset_token = Securial::Auth::TokenGenerator.generate_password_reset_token
16
+ # # => "aBc123-DeF456"
17
+ #
1
18
  require "openssl"
2
19
  require "securerandom"
3
20
 
4
21
  module Securial
5
22
  module Auth
23
+ # Generates secure tokens for authentication operations.
24
+ #
25
+ # This module provides methods to generate cryptographically secure tokens
26
+ # for different authentication scenarios. All tokens use secure random
27
+ # generation and appropriate cryptographic techniques to ensure uniqueness
28
+ # and security.
6
29
  module TokenGenerator
7
30
  extend self
8
31
 
32
+ # Generates a secure refresh token using HMAC and random data.
33
+ #
34
+ # Creates a refresh token by combining an HMAC signature with random data,
35
+ # providing both integrity verification and sufficient entropy. The token
36
+ # is suitable for long-term storage and session refresh operations.
37
+ #
38
+ # @return [String] A secure refresh token (96 characters hexadecimal)
39
+ #
40
+ # @example
41
+ # token = TokenGenerator.generate_refresh_token
42
+ # # => "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456789012345678901234567890abcdef"
43
+ #
44
+ # @see #generate_password_reset_token
45
+ #
9
46
  def generate_refresh_token
10
47
  secret = Securial.configuration.session_secret
11
48
  algo = "SHA256"
@@ -17,10 +54,47 @@ module Securial
17
54
  "#{hmac}#{random_data}"
18
55
  end
19
56
 
57
+ # Generates a user-friendly password reset token.
58
+ #
59
+ # Creates a short, alphanumeric token formatted for easy user entry.
60
+ # The token is suitable for password reset flows where users need to
61
+ # manually enter the token from an email or SMS message.
62
+ #
63
+ # @return [String] A formatted password reset token (format: "ABC123-DEF456")
64
+ #
65
+ # @example
66
+ # token = TokenGenerator.generate_password_reset_token
67
+ # # => "aBc123-DeF456"
68
+ #
69
+ # @note This token has lower entropy than refresh tokens and should have
70
+ # shorter expiration times and rate limiting protection.
71
+ #
72
+ # @see #generate_refresh_token
73
+ #
20
74
  def generate_password_reset_token
21
75
  token = SecureRandom.alphanumeric(12)
22
76
  "#{token[0, 6]}-#{token[6, 6]}"
23
77
  end
78
+
79
+ # Generates a URL-safe friendly token for general use.
80
+ #
81
+ # Creates a secure, URL-safe token suitable for various authentication
82
+ # operations that require a balance between security and usability.
83
+ #
84
+ # @param [Integer] length The desired length of the generated token (default: 20)
85
+ # @return [String] A URL-safe token containing letters, numbers, and safe symbols
86
+ #
87
+ # @example
88
+ # token = TokenGenerator.friendly_token
89
+ # # => "aBcDeF123456GhIjKl78"
90
+ #
91
+ # @example With custom length
92
+ # token = TokenGenerator.friendly_token(32)
93
+ # # => "aBcDeF123456GhIjKl789012MnOpQr34"
94
+ #
95
+ def friendly_token(length = 20)
96
+ SecureRandom.urlsafe_base64(length).tr("lIO0", "sxyz")[0, length]
97
+ end
24
98
  end
25
99
  end
26
100
  end
data/lib/securial/auth.rb CHANGED
@@ -1,12 +1,50 @@
1
+ # @title Securial Authentication System
2
+ #
3
+ # Core authentication components for the Securial framework.
4
+ #
5
+ # This file serves as the entry point for authentication-related functionality in Securial,
6
+ # loading specialized modules that handle token generation, encoding/decoding, and session management.
7
+ # These components work together to provide a secure, flexible authentication system supporting
8
+ # token-based and session-based authentication patterns.
9
+ #
10
+ # @example Encoding a session token
11
+ # # Create an encoded JWT representing a user session
12
+ # token = Securial::Auth::AuthEncoder.encode(user_session)
13
+ # # => "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIiwic3ViIjoiM..."
14
+ #
15
+ # @example Creating a new user session
16
+ # # Authenticate user and create a new session with tokens
17
+ # Securial::Auth::SessionCreator.create_session!(user, request)
18
+ #
19
+ # # Access the newly created session
20
+ # current_session = Current.session
21
+ #
22
+ # @example Generating secure tokens
23
+ # # Generate a password reset token
24
+ # reset_token = Securial::Auth::TokenGenerator.friendly_token
25
+ # # => "aBc123DeF456gHi789"
26
+ #
1
27
  require "securial/auth/auth_encoder"
2
28
  require "securial/auth/session_creator"
3
29
  require "securial/auth/token_generator"
4
30
 
5
31
  module Securial
6
- module Auth
7
- # This is a placeholder for the Auth module.
8
- # It can be used to include authentication-related methods or constants
9
- # that are not specific to encoding or session creation.
10
- # Currently, it serves as a namespace for the AuthEncoder and SessionCreator modules.
11
- end
32
+ # Namespace for authentication-related functionality in the Securial framework.
33
+ #
34
+ # The Auth module contains components that work together to provide a complete
35
+ # authentication system for Securial-powered applications:
36
+ #
37
+ # - {AuthEncoder} - Handles JWT token encoding and decoding with security measures
38
+ # - {SessionCreator} - Creates and manages authenticated user sessions
39
+ # - {TokenGenerator} - Generates secure random tokens for various authentication needs
40
+ #
41
+ # These components handle the cryptographic and security aspects of user authentication,
42
+ # ensuring that best practices are followed for token generation, validation, and session
43
+ # management throughout the application lifecycle.
44
+ #
45
+ # @see Securial::Auth::AuthEncoder
46
+ # @see Securial::Auth::SessionCreator
47
+ # @see Securial::Auth::TokenGenerator
48
+ #
49
+ module Auth; end
12
50
  end
data/lib/securial/cli.rb CHANGED
@@ -1,13 +1,51 @@
1
+ # @title Securial Command Line Interface
2
+ #
3
+ # Command line interface for the Securial framework.
4
+ #
5
+ # This file implements the CLI tool for Securial, providing command-line utilities
6
+ # for creating new Rails applications with Securial pre-installed. It handles
7
+ # command parsing, option flags, and orchestrates the application setup process.
8
+ #
9
+ # @example Creating a new Rails application with Securial
10
+ # # Create a basic Securial application
11
+ # $ securial new myapp
12
+ #
13
+ # # Create an API-only Securial application with PostgreSQL
14
+ # $ securial new myapi --api --database=postgresql
15
+ #
1
16
  require "optparse"
2
17
 
3
18
  # rubocop:disable Rails/Exit, Rails/Output
4
19
 
5
20
  module Securial
21
+ # Command-line interface for the Securial gem.
22
+ #
23
+ # This class provides the command-line functionality for Securial, enabling users
24
+ # to create new Rails applications with Securial pre-installed and configured.
25
+ # It handles command parsing, flag processing, and orchestrates the setup of
26
+ # new applications.
27
+ #
6
28
  class CLI
29
+ # Entry point for the CLI application.
30
+ #
31
+ # Creates a new CLI instance and delegates to its start method.
32
+ #
33
+ # @param argv [Array<String>] command line arguments
34
+ # @return [Integer] exit status code (0 for success, non-zero for errors)
35
+ #
7
36
  def self.start(argv)
8
37
  new.start(argv)
9
38
  end
10
39
 
40
+ # Processes command line arguments and executes the appropriate action.
41
+ #
42
+ # This method handles both option flags (like --version) and commands
43
+ # (like 'new'), delegating to specialized handlers and returning
44
+ # the appropriate exit code.
45
+ #
46
+ # @param argv [Array<String>] command line arguments
47
+ # @return [Integer] exit status code (0 for success, non-zero for errors)
48
+ #
11
49
  def start(argv)
12
50
  # Process options and exit if a flag was handled
13
51
  result = handle_flags(argv)
@@ -19,6 +57,14 @@ module Securial
19
57
 
20
58
  private
21
59
 
60
+ # Processes option flags from the command line arguments.
61
+ #
62
+ # Parses options like --version and --help, executing their actions
63
+ # if present and removing them from the argument list.
64
+ #
65
+ # @param argv [Array<String>] command line arguments
66
+ # @return [nil, Integer] nil to continue processing, or exit code
67
+ #
22
68
  def handle_flags(argv)
23
69
  parser = create_option_parser
24
70
 
@@ -32,6 +78,14 @@ module Securial
32
78
  end
33
79
  end
34
80
 
81
+ # Processes commands from the command line arguments.
82
+ #
83
+ # Identifies the command (e.g., 'new') and delegates to the appropriate
84
+ # handler method for that command.
85
+ #
86
+ # @param argv [Array<String>] command line arguments
87
+ # @return [Integer] exit status code (0 for success, non-zero for errors)
88
+ #
35
89
  def handle_commands(argv)
36
90
  cmd = argv.shift
37
91
 
@@ -44,6 +98,14 @@ module Securial
44
98
  end
45
99
  end
46
100
 
101
+ # Handles the 'new' command for creating Rails applications.
102
+ #
103
+ # Validates that an application name is provided, then delegates to
104
+ # securial_new to create the application with Securial.
105
+ #
106
+ # @param argv [Array<String>] remaining command line arguments
107
+ # @return [Integer] exit status code (0 for success, non-zero for errors)
108
+ #
47
109
  def handle_new_command(argv)
48
110
  app_name = argv.shift
49
111
 
@@ -57,6 +119,12 @@ module Securial
57
119
  0
58
120
  end
59
121
 
122
+ # Creates and configures the option parser.
123
+ #
124
+ # Sets up the command-line options and their handlers.
125
+ #
126
+ # @return [OptionParser] configured option parser instance
127
+ #
60
128
  def create_option_parser
61
129
  OptionParser.new do |opts|
62
130
  opts.banner = "Usage: securial [options] <command> [command options]\n\n"
@@ -79,6 +147,10 @@ module Securial
79
147
  end
80
148
  end
81
149
 
150
+ # Displays the current Securial version.
151
+ #
152
+ # @return [void]
153
+ #
82
154
  def show_version
83
155
  require "securial/version"
84
156
  puts "Securial v#{Securial::VERSION}"
@@ -86,6 +158,15 @@ module Securial
86
158
  puts "Securial version information not available."
87
159
  end
88
160
 
161
+ # Creates a new Rails application with Securial pre-installed.
162
+ #
163
+ # Orchestrates the process of creating a Rails application, adding the
164
+ # Securial gem, installing dependencies, and configuring the application.
165
+ #
166
+ # @param app_name [String] name of the Rails application to create
167
+ # @param rails_options [Array<String>] options to pass to 'rails new'
168
+ # @return [void]
169
+ #
89
170
  def securial_new(app_name, rails_options)
90
171
  puts "🏗️ Creating new Rails app: #{app_name}"
91
172
 
@@ -97,27 +178,53 @@ module Securial
97
178
  print_final_instructions(app_name)
98
179
  end
99
180
 
181
+ # Creates a new Rails application.
182
+ #
183
+ # @param app_name [String] name of the Rails application to create
184
+ # @param rails_options [Array<String>] options to pass to 'rails new'
185
+ # @return [Integer] command exit status
186
+ #
100
187
  def create_rails_app(app_name, rails_options)
101
188
  rails_command = ["rails", "new", app_name, *rails_options]
102
189
  run(rails_command)
103
190
  end
104
191
 
192
+ # Adds the Securial gem to the application's Gemfile.
193
+ #
194
+ # @param app_name [String] name of the Rails application
195
+ # @return [void]
196
+ #
105
197
  def add_securial_gem(app_name)
106
198
  puts "📦 Adding Securial gem to Gemfile"
107
199
  gemfile_path = File.join(app_name, "Gemfile")
108
200
  File.open(gemfile_path, "a") { |f| f.puts "\ngem 'securial'" }
109
201
  end
110
202
 
203
+ # Installs gems for the application using Bundler.
204
+ #
205
+ # @param app_name [String] name of the Rails application
206
+ # @return [Integer] command exit status
207
+ #
111
208
  def install_gems(app_name)
112
209
  run("bundle install", chdir: app_name)
113
210
  end
114
211
 
212
+ # Installs and configures Securial in the application.
213
+ #
214
+ # @param app_name [String] name of the Rails application
215
+ # @return [Integer] command exit status
216
+ #
115
217
  def install_securial(app_name)
116
218
  puts "🔧 Installing Securial"
117
219
  run("bin/rails generate securial:install", chdir: app_name)
118
220
  run("bin/rails db:migrate", chdir: app_name)
119
221
  end
120
222
 
223
+ # Mounts the Securial engine in the application's routes.
224
+ #
225
+ # @param app_name [String] name of the Rails application
226
+ # @return [void]
227
+ #
121
228
  def mount_securial_engine(app_name)
122
229
  puts "🔗 Mounting Securial engine in routes"
123
230
  routes_path = File.join(app_name, "config/routes.rb")
@@ -128,6 +235,11 @@ module Securial
128
235
  File.write(routes_path, updated)
129
236
  end
130
237
 
238
+ # Prints final setup instructions after successful installation.
239
+ #
240
+ # @param app_name [String] name of the Rails application
241
+ # @return [void]
242
+ #
131
243
  def print_final_instructions(app_name)
132
244
  puts <<~INSTRUCTIONS
133
245
  🎉 Securial has been successfully installed in your Rails app!
@@ -140,6 +252,16 @@ module Securial
140
252
  INSTRUCTIONS
141
253
  end
142
254
 
255
+ # Runs a system command with optional directory change.
256
+ #
257
+ # Executes the provided command, optionally in a different directory,
258
+ # and handles success/failure conditions.
259
+ #
260
+ # @param command [String, Array<String>] command to run
261
+ # @param chdir [String, nil] directory to change to before running command
262
+ # @return [Integer] command exit status code (0 for success)
263
+ # @raise [SystemExit] if the command fails
264
+ #
143
265
  def run(command, chdir: nil)
144
266
  puts "→ #{command.inspect}"
145
267
  result =
@@ -156,3 +278,5 @@ module Securial
156
278
  end
157
279
  end
158
280
  end
281
+
282
+ # rubocop:enable Rails/Exit, Rails/Output