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.
- checksums.yaml +4 -4
- data/.yardopts +4 -0
- data/README.md +19 -12
- 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/auth_encoder.rb +78 -0
- data/lib/securial/auth/session_creator.rb +49 -0
- data/lib/securial/auth/token_generator.rb +74 -0
- data/lib/securial/auth.rb +44 -6
- data/lib/securial/cli.rb +124 -0
- data/lib/securial/config/signature.rb +116 -5
- data/lib/securial/config/validation.rb +91 -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/key_transformer.rb +106 -0
- data/lib/securial/helpers/normalizing_helper.rb +69 -0
- data/lib/securial/helpers/regex_helper.rb +122 -0
- data/lib/securial/helpers/roles_helper.rb +71 -2
- 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/request_tag_logger.rb +80 -0
- data/lib/securial/middleware/response_headers.rb +51 -3
- data/lib/securial/middleware/transform_request_keys.rb +143 -20
- data/lib/securial/middleware/transform_response_keys.rb +84 -4
- 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 +21 -15
data/app/models/securial/user.rb
CHANGED
@@ -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,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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|