securial 1.0.0 → 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/bin/securial +6 -23
- data/lib/generators/factory_bot/model/model_generator.rb +1 -0
- data/lib/generators/securial/install/install_generator.rb +2 -2
- data/lib/generators/securial/install/views_generator.rb +2 -1
- data/lib/generators/securial/jbuilder/jbuilder_generator.rb +2 -0
- data/lib/generators/securial/scaffold/scaffold_generator.rb +2 -0
- data/lib/securial/auth/auth_encoder.rb +3 -3
- data/lib/securial/auth/session_creator.rb +1 -1
- data/lib/securial/auth/token_generator.rb +13 -13
- data/lib/securial/auth.rb +44 -6
- data/lib/securial/cli.rb +282 -0
- data/lib/securial/config/signature.rb +1 -1
- data/lib/securial/config/validation.rb +44 -45
- data/lib/securial/config.rb +63 -17
- data/lib/securial/engine.rb +41 -0
- data/lib/securial/error/auth.rb +52 -0
- data/lib/securial/error/base_securial_error.rb +56 -3
- data/lib/securial/error/config.rb +33 -0
- data/lib/securial/error.rb +33 -3
- data/lib/securial/helpers/key_transformer.rb +1 -1
- data/lib/securial/helpers/normalizing_helper.rb +1 -1
- data/lib/securial/helpers/regex_helper.rb +6 -7
- data/lib/securial/helpers/roles_helper.rb +6 -7
- 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 +48 -8
- data/lib/securial/middleware.rb +40 -9
- data/lib/securial/security/request_rate_limiter.rb +48 -2
- data/lib/securial/security.rb +37 -6
- data/lib/securial/version.rb +8 -1
- data/lib/securial.rb +40 -4
- metadata +15 -11
- data/lib/securial/cli/run.rb +0 -11
- data/lib/securial/cli/securial_new.rb +0 -53
- data/lib/securial/cli/show_help.rb +0 -26
- data/lib/securial/cli/show_version.rb +0 -9
data/lib/securial/config.rb
CHANGED
@@ -1,28 +1,74 @@
|
|
1
|
+
# @title Securial Configuration System
|
2
|
+
#
|
3
|
+
# Configuration management for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines the configuration system for Securial, providing mechanisms
|
6
|
+
# to set, retrieve, and validate configuration options. It uses a conventional
|
7
|
+
# Ruby configuration block pattern to allow applications to customize Securial's
|
8
|
+
# behavior through an easy-to-use interface.
|
9
|
+
#
|
10
|
+
# @example Basic configuration
|
11
|
+
# # In config/initializers/securial.rb
|
12
|
+
# Securial.configure do |config|
|
13
|
+
# config.jwt_secret = ENV["JWT_SECRET"]
|
14
|
+
# config.token_expiry = 2.hours
|
15
|
+
# config.refresh_token_expiry = 7.days
|
16
|
+
# config.session_timeout = 1.hour
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Accessing configuration values
|
20
|
+
# # Get the configured token expiry
|
21
|
+
# expiry = Securial.configuration.token_expiry
|
22
|
+
#
|
1
23
|
require "securial/config/configuration"
|
2
24
|
require "securial/config/signature"
|
3
25
|
require "securial/config/validation"
|
4
26
|
|
5
|
-
|
6
27
|
module Securial
|
7
|
-
|
8
|
-
attr_accessor :configuration
|
9
|
-
|
10
|
-
def configuration
|
11
|
-
@configuration ||= Config::Configuration.new
|
12
|
-
end
|
28
|
+
extend self
|
13
29
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
30
|
+
# Gets the current configuration instance.
|
31
|
+
#
|
32
|
+
# Returns the existing configuration or creates a new default configuration
|
33
|
+
# if one hasn't been set yet.
|
34
|
+
#
|
35
|
+
# @return [Securial::Config::Configuration] the current configuration instance
|
36
|
+
def configuration
|
37
|
+
@configuration ||= Config::Configuration.new
|
38
|
+
end
|
22
39
|
|
23
|
-
|
24
|
-
|
40
|
+
# Sets the configuration instance.
|
41
|
+
#
|
42
|
+
# Validates the provided configuration to ensure all required settings
|
43
|
+
# are present and have valid values.
|
44
|
+
#
|
45
|
+
# @param config [Securial::Config::Configuration] the configuration instance to use
|
46
|
+
# @raise [ArgumentError] if the provided object is not a Configuration instance
|
47
|
+
# @raise [Securial::Error::Config::ValidationError] if the configuration is invalid
|
48
|
+
# @return [Securial::Config::Configuration] the newly set configuration
|
49
|
+
#
|
50
|
+
def configuration=(config)
|
51
|
+
if config.is_a?(Config::Configuration)
|
52
|
+
@configuration = config
|
25
53
|
Securial::Config::Validation.validate_all!(configuration)
|
54
|
+
else
|
55
|
+
raise ArgumentError, "Expected an instance of Securial::Config::Configuration"
|
26
56
|
end
|
27
57
|
end
|
58
|
+
|
59
|
+
# Configures the Securial framework using a block.
|
60
|
+
#
|
61
|
+
# This method provides the conventional Ruby configuration block pattern,
|
62
|
+
# yielding the current configuration instance to the provided block for
|
63
|
+
# modification. After the block executes, the configuration is validated.
|
64
|
+
#
|
65
|
+
# @yield [config] Yields the configuration instance to the block
|
66
|
+
# @yieldparam config [Securial::Config::Configuration] the configuration instance
|
67
|
+
# @raise [Securial::Error::Config::ValidationError] if the configuration is invalid
|
68
|
+
# @return [Securial::Config::Configuration] the updated configuration
|
69
|
+
def configure
|
70
|
+
yield(configuration) if block_given?
|
71
|
+
Securial::Config::Validation.validate_all!(configuration)
|
72
|
+
configuration
|
73
|
+
end
|
28
74
|
end
|
data/lib/securial/engine.rb
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
# @title Securial Rails Engine
|
2
|
+
#
|
3
|
+
# Rails engine configuration for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines the core Rails engine that powers Securial, setting up
|
6
|
+
# namespace isolation, generator defaults, and loading all required components.
|
7
|
+
# The engine configuration establishes how Securial integrates with host Rails
|
8
|
+
# applications and defines the conventions used throughout the codebase.
|
9
|
+
#
|
10
|
+
# @example Mounting the engine in a Rails application
|
11
|
+
# # In config/routes.rb
|
12
|
+
# Rails.application.routes.draw do
|
13
|
+
# mount Securial::Engine => '/securial'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @example Accessing engine routes in views or controllers
|
17
|
+
# # Generate path to Securial login
|
18
|
+
# securial.login_path
|
19
|
+
#
|
20
|
+
# # Generate URL to Securial user profile
|
21
|
+
# securial.user_url(@user)
|
22
|
+
#
|
1
23
|
require "securial/auth"
|
2
24
|
require "securial/config"
|
3
25
|
require "securial/error"
|
@@ -7,11 +29,29 @@ require "securial/middleware"
|
|
7
29
|
require "securial/security"
|
8
30
|
|
9
31
|
module Securial
|
32
|
+
# Rails engine implementation for the Securial framework.
|
33
|
+
#
|
34
|
+
# This class defines the core engine that powers Securial's integration with
|
35
|
+
# Rails applications. It isolates all Securial components within their own
|
36
|
+
# namespace to prevent conflicts with host application code and configures
|
37
|
+
# generator defaults to ensure consistent code generation.
|
38
|
+
#
|
39
|
+
# The engine handles:
|
40
|
+
# - Namespace isolation to prevent conflicts
|
41
|
+
# - Configuration of generators for consistent code generation
|
42
|
+
# - Setting up Securial's API-only mode for JSON responses
|
43
|
+
# - Loading initializers for various framework components
|
44
|
+
#
|
45
|
+
# @see https://guides.rubyonrails.org/engines.html Rails Engines Guide
|
46
|
+
#
|
10
47
|
class Engine < ::Rails::Engine
|
48
|
+
# Isolates the Securial namespace to prevent conflicts with host applications
|
11
49
|
isolate_namespace Securial
|
12
50
|
|
51
|
+
# Configures the engine for API-only mode by default
|
13
52
|
config.generators.api_only = true
|
14
53
|
|
54
|
+
# Sets up standard generator configurations for consistent code generation
|
15
55
|
config.generators do |g|
|
16
56
|
g.orm :active_record, primary_key_type: :string
|
17
57
|
g.test_framework :rspec,
|
@@ -28,4 +68,5 @@ module Securial
|
|
28
68
|
end
|
29
69
|
end
|
30
70
|
|
71
|
+
# Load engine initializers that configure various aspects of the framework
|
31
72
|
require_relative "engine_initializers"
|
data/lib/securial/error/auth.rb
CHANGED
@@ -1,18 +1,70 @@
|
|
1
|
+
# @title Securial Authentication Errors
|
2
|
+
#
|
3
|
+
# Authentication-related errors for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines error classes for authentication-related issues in the
|
6
|
+
# Securial framework, such as token encoding/decoding failures, expired tokens,
|
7
|
+
# and revoked sessions. These errors help identify and troubleshoot problems
|
8
|
+
# with the authentication system during runtime.
|
9
|
+
#
|
10
|
+
# @example Handling authentication errors
|
11
|
+
# begin
|
12
|
+
# decoded_token = Securial::Auth::AuthEncoder.decode(token)
|
13
|
+
# rescue Securial::Error::Auth::TokenDecodeError => e
|
14
|
+
# # Handle invalid token format
|
15
|
+
# logger.warn("Invalid token format: #{e.message}")
|
16
|
+
# rescue Securial::Error::Auth::TokenExpiredError => e
|
17
|
+
# # Handle expired token
|
18
|
+
# render json: { error: "Your session has expired. Please log in again." }, status: :unauthorized
|
19
|
+
# rescue Securial::Error::Auth::TokenRevokedError => e
|
20
|
+
# # Handle revoked token
|
21
|
+
# render json: { error: "Your session has been revoked." }, status: :unauthorized
|
22
|
+
# end
|
23
|
+
#
|
1
24
|
module Securial
|
2
25
|
module Error
|
26
|
+
# Namespace for authentication-related errors in the Securial framework.
|
27
|
+
#
|
28
|
+
# These errors are raised during authentication operations such as
|
29
|
+
# token encoding/decoding, session validation, and access control checks.
|
30
|
+
#
|
3
31
|
module Auth
|
32
|
+
# Error raised when a JWT token cannot be encoded properly.
|
33
|
+
#
|
34
|
+
# This may occur due to invalid payload data, missing required claims,
|
35
|
+
# or issues with the signing key.
|
36
|
+
#
|
37
|
+
# @see Securial::Auth::AuthEncoder#encode
|
38
|
+
#
|
4
39
|
class TokenEncodeError < BaseError
|
5
40
|
default_message "Error while encoding session token"
|
6
41
|
end
|
7
42
|
|
43
|
+
# Error raised when a JWT token cannot be decoded or validated.
|
44
|
+
#
|
45
|
+
# This may occur due to malformed tokens, invalid signatures,
|
46
|
+
# or tokens that don't match the expected format.
|
47
|
+
#
|
48
|
+
# @see Securial::Auth::AuthEncoder#decode
|
49
|
+
#
|
8
50
|
class TokenDecodeError < BaseError
|
9
51
|
default_message "Error while decoding session token"
|
10
52
|
end
|
11
53
|
|
54
|
+
# Error raised when attempting to use a token from a revoked session.
|
55
|
+
#
|
56
|
+
# This occurs when a token references a session that has been
|
57
|
+
# explicitly revoked, such as after a user logout or account suspension.
|
58
|
+
#
|
12
59
|
class TokenRevokedError < BaseError
|
13
60
|
default_message "Session token is revoked"
|
14
61
|
end
|
15
62
|
|
63
|
+
# Error raised when attempting to use an expired token.
|
64
|
+
#
|
65
|
+
# This occurs when a token's expiration time (exp claim) has passed,
|
66
|
+
# indicating that the token is no longer valid for authentication.
|
67
|
+
#
|
16
68
|
class TokenExpiredError < BaseError
|
17
69
|
default_message "Session token is expired"
|
18
70
|
end
|
@@ -1,15 +1,68 @@
|
|
1
|
+
# @title Securial Base Error
|
2
|
+
#
|
3
|
+
# Base error class for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines the base error class that all other specialized Securial
|
6
|
+
# errors inherit from. It provides functionality for default error messages
|
7
|
+
# and customized backtrace handling.
|
8
|
+
#
|
1
9
|
module Securial
|
2
10
|
module Error
|
11
|
+
# Base error class for all Securial-specific exceptions.
|
12
|
+
#
|
13
|
+
# This class serves as the foundation for Securial's error hierarchy, providing
|
14
|
+
# common functionality like default messages and backtrace customization.
|
15
|
+
# All specialized error types in Securial should inherit from this class.
|
16
|
+
#
|
17
|
+
# @example Defining a custom error class
|
18
|
+
# module Securial
|
19
|
+
# module Error
|
20
|
+
# class AuthError < BaseError
|
21
|
+
# default_message "Authentication failed"
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @example Raising a custom error
|
27
|
+
# raise Securial::Error::AuthError
|
28
|
+
# # => Authentication failed (Securial::Error::AuthError)
|
29
|
+
#
|
30
|
+
# raise Securial::Error::AuthError, "Invalid token provided"
|
31
|
+
# # => Invalid token provided (Securial::Error::AuthError)
|
32
|
+
#
|
3
33
|
class BaseError < StandardError
|
34
|
+
class_attribute :_default_message, instance_writer: false
|
35
|
+
|
36
|
+
# Sets or gets the default message for this error class.
|
37
|
+
#
|
38
|
+
# This class method allows error classes to define a standard message
|
39
|
+
# that will be used when no explicit message is provided at instantiation.
|
40
|
+
#
|
41
|
+
# @param message [String, nil] The default message to set for this error class
|
42
|
+
# @return [String, nil] The current default message for this error class
|
43
|
+
#
|
4
44
|
def self.default_message(message = nil)
|
5
|
-
|
6
|
-
|
45
|
+
self._default_message = message if message
|
46
|
+
self._default_message
|
7
47
|
end
|
8
48
|
|
49
|
+
# Initializes a new error instance with the provided message or default.
|
50
|
+
#
|
51
|
+
# @param message [String, nil] The error message or nil to use default
|
52
|
+
# @return [BaseError] New error instance
|
53
|
+
#
|
9
54
|
def initialize(message = nil)
|
10
|
-
super(message || self.class.
|
55
|
+
super(message || self.class._default_message || "An error occurred in Securial")
|
11
56
|
end
|
12
57
|
|
58
|
+
# Returns an empty backtrace.
|
59
|
+
#
|
60
|
+
# This method overrides the standard backtrace behavior to return an empty array,
|
61
|
+
# which helps keep error responses clean without showing internal implementation
|
62
|
+
# details in production environments.
|
63
|
+
#
|
64
|
+
# @return [Array] An empty array
|
65
|
+
#
|
13
66
|
def backtrace; []; end
|
14
67
|
end
|
15
68
|
end
|
@@ -1,6 +1,39 @@
|
|
1
|
+
# @title Securial Configuration Errors
|
2
|
+
#
|
3
|
+
# Configuration-related errors for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines error classes for configuration-related issues in the
|
6
|
+
# Securial framework, such as missing or invalid configuration options.
|
7
|
+
# These errors help identify and troubleshoot problems with application setup.
|
8
|
+
#
|
1
9
|
module Securial
|
2
10
|
module Error
|
11
|
+
# Namespace for configuration-related errors in the Securial framework.
|
12
|
+
#
|
13
|
+
# These errors are raised when the Securial configuration is invalid,
|
14
|
+
# missing required values, or contains values that don't meet validation
|
15
|
+
# requirements.
|
16
|
+
#
|
17
|
+
# @example Handling configuration errors
|
18
|
+
# begin
|
19
|
+
# Securial.configure do |config|
|
20
|
+
# # Configure Securial settings
|
21
|
+
# end
|
22
|
+
# rescue Securial::Error::Config::InvalidConfigurationError => e
|
23
|
+
# Rails.logger.error("Securial configuration error: #{e.message}")
|
24
|
+
# # Handle configuration error
|
25
|
+
# end
|
26
|
+
#
|
3
27
|
module Config
|
28
|
+
# Error raised when Securial configuration is invalid.
|
29
|
+
#
|
30
|
+
# This error is typically raised during application initialization when
|
31
|
+
# the provided configuration doesn't meet requirements, such as missing
|
32
|
+
# required settings or having values that fail validation.
|
33
|
+
#
|
34
|
+
# @example Raising a configuration error
|
35
|
+
# raise InvalidConfigurationError, "JWT secret is missing"
|
36
|
+
#
|
4
37
|
class InvalidConfigurationError < Securial::Error::BaseError
|
5
38
|
default_message "Invalid configuration for Securial"
|
6
39
|
end
|
data/lib/securial/error.rb
CHANGED
@@ -1,9 +1,39 @@
|
|
1
|
+
# Requires all error classes used in the Securial framework.
|
2
|
+
#
|
3
|
+
# This file serves as the central error management system for Securial,
|
4
|
+
# loading all specialized error types and establishing the Error namespace.
|
5
|
+
# Each error type extends from BaseSecurialError and provides specific
|
6
|
+
# error handling for different parts of the framework.
|
7
|
+
#
|
8
|
+
# @example Accessing a specific error type
|
9
|
+
# begin
|
10
|
+
# # Some operation that might fail
|
11
|
+
# rescue Securial::Error::Auth::TokenDecodeError => e
|
12
|
+
# # Handle token decoding errors
|
13
|
+
# rescue Securial::Error::Config::ValidationError => e
|
14
|
+
# # Handle configuration validation errors
|
15
|
+
# rescue Securial::Error::BaseError => e
|
16
|
+
# # Handle any other Securial errors
|
17
|
+
# end
|
18
|
+
#
|
1
19
|
require "securial/error/base_securial_error"
|
2
20
|
require "securial/error/config"
|
3
21
|
require "securial/error/auth"
|
4
22
|
|
5
23
|
module Securial
|
6
|
-
|
7
|
-
|
8
|
-
|
24
|
+
# Namespace for all Securial-specific errors and exceptions.
|
25
|
+
#
|
26
|
+
# The Error module contains specialized error classes for different
|
27
|
+
# components of the Securial framework, organized into submodules
|
28
|
+
# for better categorization:
|
29
|
+
#
|
30
|
+
# - {BaseError} - Base class for all Securial errors
|
31
|
+
# - {Config} - Configuration-related errors
|
32
|
+
# - {Auth} - Authentication and authorization errors
|
33
|
+
#
|
34
|
+
# @see Securial::Error::BaseError
|
35
|
+
# @see Securial::Error::Config
|
36
|
+
# @see Securial::Error::Auth
|
37
|
+
#
|
38
|
+
module Error; end
|
9
39
|
end
|
@@ -5,14 +5,13 @@ module Securial
|
|
5
5
|
USERNAME_REGEX = /\A(?![0-9])[a-zA-Z](?:[a-zA-Z0-9]|[._](?![._]))*[a-zA-Z0-9]\z/
|
6
6
|
PASSWORD_REGEX = %r{\A(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])[a-zA-Z].*\z}
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
extend self
|
9
|
+
def valid_email?(email)
|
10
|
+
email.match?(EMAIL_REGEX)
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
13
|
+
def valid_username?(username)
|
14
|
+
username.match?(USERNAME_REGEX)
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
@@ -3,14 +3,13 @@ module Securial
|
|
3
3
|
module RolesHelper
|
4
4
|
# This module provides helper methods related to roles.
|
5
5
|
# It can be extended with additional role-related functionality as needed.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
extend self
|
7
|
+
def protected_namespace
|
8
|
+
Securial.configuration.admin_role.to_s.strip.underscore.pluralize
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
11
|
+
def titleized_admin_role
|
12
|
+
Securial.configuration.admin_role.to_s.strip.titleize
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
data/lib/securial/helpers.rb
CHANGED
@@ -1,11 +1,55 @@
|
|
1
|
+
# @title Securial Helper Utilities
|
2
|
+
#
|
3
|
+
# Centralized collection of helper modules for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file serves as the entry point for all helper functionality in Securial,
|
6
|
+
# loading specialized utility modules that provide common functions used
|
7
|
+
# throughout the framework. These helpers handle tasks such as normalization,
|
8
|
+
# pattern matching, role management, and data transformation, making common
|
9
|
+
# operations consistent across the codebase.
|
10
|
+
#
|
11
|
+
# @example Normalizing user input
|
12
|
+
# # For normalizing email addresses (converts to lowercase)
|
13
|
+
# email = Securial::Helpers::NormalizingHelper.normalize_email("USER@EXAMPLE.COM")
|
14
|
+
# # => "user@example.com"
|
15
|
+
#
|
16
|
+
# @example Validating input format
|
17
|
+
# # For validating email format
|
18
|
+
# valid = Securial::Helpers::RegexHelper.valid_email?("user@example.com")
|
19
|
+
# # => true
|
20
|
+
#
|
21
|
+
# invalid = Securial::Helpers::RegexHelper.valid_email?("not-an-email")
|
22
|
+
# # => false
|
23
|
+
#
|
24
|
+
# @example Transforming data structure keys
|
25
|
+
# # For transforming keys in response data (snake_case to camelCase)
|
26
|
+
# data = { user_id: 1, first_name: "John", last_name: "Doe" }
|
27
|
+
# json = Securial::Helpers::KeyTransformer.transform(data, :lower_camel_case)
|
28
|
+
# # => { userId: 1, firstName: "John", lastName: "Doe" }
|
29
|
+
#
|
1
30
|
require "securial/helpers/normalizing_helper"
|
2
31
|
require "securial/helpers/regex_helper"
|
3
32
|
require "securial/helpers/roles_helper"
|
4
33
|
require "securial/helpers/key_transformer"
|
5
34
|
|
6
35
|
module Securial
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
36
|
+
# Namespace containing utility modules for common operations.
|
37
|
+
#
|
38
|
+
# The Helpers module serves as a container for specialized utility modules
|
39
|
+
# that provide reusable functionality across the Securial framework:
|
40
|
+
#
|
41
|
+
# - {NormalizingHelper} - Methods for normalizing user input (emails, usernames, etc.)
|
42
|
+
# - {RegexHelper} - Regular expressions and validation methods for input validation
|
43
|
+
# - {RolesHelper} - Functions for working with user roles and permissions management
|
44
|
+
# - {KeyTransformer} - Tools for transforming data structure keys between formats (snake_case, camelCase)
|
45
|
+
#
|
46
|
+
# These helpers ensure consistent behavior across the application and reduce
|
47
|
+
# code duplication by centralizing common operations.
|
48
|
+
#
|
49
|
+
# @see Securial::Helpers::NormalizingHelper
|
50
|
+
# @see Securial::Helpers::RegexHelper
|
51
|
+
# @see Securial::Helpers::RolesHelper
|
52
|
+
# @see Securial::Helpers::KeyTransformer
|
53
|
+
#
|
54
|
+
module Helpers; end
|
11
55
|
end
|
@@ -1,36 +1,109 @@
|
|
1
|
+
# @title Securial Logger Broadcaster
|
2
|
+
#
|
3
|
+
# Logger broadcasting utility for the Securial framework.
|
4
|
+
#
|
5
|
+
# This file defines a broadcaster class that sends logging messages to multiple
|
6
|
+
# destination loggers simultaneously. This enables unified logging across different
|
7
|
+
# outputs (like console and file) while maintaining appropriate formatting for each.
|
8
|
+
#
|
9
|
+
# @example Creating a broadcaster with multiple loggers
|
10
|
+
# # Create individual loggers
|
11
|
+
# file_logger = Logger.new('application.log')
|
12
|
+
# stdout_logger = Logger.new(STDOUT)
|
13
|
+
#
|
14
|
+
# # Combine them into a broadcaster
|
15
|
+
# broadcaster = Securial::Logger::Broadcaster.new([file_logger, stdout_logger])
|
16
|
+
#
|
17
|
+
# # Log messages go to both destinations
|
18
|
+
# broadcaster.info("Processing authentication request")
|
19
|
+
#
|
1
20
|
module Securial
|
2
21
|
module Logger
|
22
|
+
# Broadcasts log messages to multiple logger instances simultaneously.
|
23
|
+
#
|
24
|
+
# This class implements the Logger interface and forwards all logging calls
|
25
|
+
# to a collection of underlying loggers. This allows unified logging to
|
26
|
+
# multiple destinations (e.g., file and stdout) while maintaining specific
|
27
|
+
# formatting for each destination.
|
28
|
+
#
|
29
|
+
# @example Using a broadcaster with two loggers
|
30
|
+
# file_logger = Logger.new('app.log')
|
31
|
+
# console_logger = Logger.new(STDOUT)
|
32
|
+
# broadcaster = Broadcaster.new([file_logger, console_logger])
|
33
|
+
#
|
34
|
+
# # This logs to both destinations
|
35
|
+
# broadcaster.info("User logged in")
|
36
|
+
#
|
3
37
|
class Broadcaster
|
38
|
+
# Initializes a new broadcaster with the provided loggers.
|
39
|
+
#
|
40
|
+
# @param loggers [Array<Logger>] Collection of logger instances
|
41
|
+
# @return [Broadcaster] New broadcaster instance
|
4
42
|
def initialize(loggers)
|
5
43
|
@loggers = loggers
|
6
44
|
end
|
7
45
|
|
46
|
+
# Dynamically define logging methods for each severity level
|
47
|
+
# (debug, info, warn, error, fatal, unknown)
|
8
48
|
::Logger::Severity.constants.each do |severity|
|
9
49
|
define_method(severity.downcase) do |*args, &block|
|
10
50
|
@loggers.each { |logger| logger.public_send(severity.downcase, *args, &block) }
|
11
51
|
end
|
12
52
|
end
|
13
53
|
|
54
|
+
# Writes a message to each underlying logger.
|
55
|
+
#
|
56
|
+
# @param msg [String] Message to be written
|
57
|
+
# @return [Array] Results from each logger's << method
|
14
58
|
def <<(msg)
|
15
59
|
@loggers.each { |logger| logger << msg }
|
16
60
|
end
|
17
61
|
|
62
|
+
# Closes all underlying loggers.
|
63
|
+
#
|
64
|
+
# @return [void]
|
18
65
|
def close
|
19
66
|
@loggers.each(&:close)
|
20
67
|
end
|
21
68
|
|
69
|
+
# No-op method to satisfy the Logger interface.
|
70
|
+
#
|
71
|
+
# Since each underlying logger has its own formatter, setting a
|
72
|
+
# formatter on the broadcaster is not supported.
|
73
|
+
#
|
74
|
+
# @param _formatter [Object] Ignored formatter object
|
75
|
+
# @return [void]
|
22
76
|
def formatter=(_formatter)
|
23
|
-
# Do nothing
|
77
|
+
# Do nothing - each logger maintains its own formatter
|
24
78
|
end
|
25
79
|
|
80
|
+
# Returns nil to satisfy the Logger interface.
|
81
|
+
#
|
82
|
+
# Since each underlying logger has its own formatter, there is
|
83
|
+
# no single formatter for the broadcaster.
|
84
|
+
#
|
85
|
+
# @return [nil] Always returns nil
|
26
86
|
def formatter
|
27
87
|
nil
|
28
88
|
end
|
29
89
|
|
90
|
+
# Returns the collection of managed loggers.
|
91
|
+
#
|
92
|
+
# @return [Array<Logger>] All loggers managed by this broadcaster
|
93
|
+
#
|
30
94
|
def loggers
|
31
95
|
@loggers
|
32
96
|
end
|
33
97
|
|
98
|
+
# Executes a block with the specified tags added to the log.
|
99
|
+
#
|
100
|
+
# Supports ActiveSupport::TaggedLogging by forwarding tagged blocks
|
101
|
+
# to all underlying loggers that support tagging.
|
102
|
+
#
|
103
|
+
# @param tags [Array<String>] Tags to apply to log messages
|
104
|
+
# @yield Block to be executed with the tags applied
|
105
|
+
# @return [Object] Result of the block
|
106
|
+
#
|
34
107
|
def tagged(*tags, &block)
|
35
108
|
# If all loggers support tagged, nest the calls, otherwise just yield.
|
36
109
|
taggable_loggers = @loggers.select { |logger| logger.respond_to?(:tagged) }
|
@@ -44,10 +117,25 @@ module Securial
|
|
44
117
|
end
|
45
118
|
end
|
46
119
|
|
120
|
+
# Checks if the broadcaster responds to the given method.
|
121
|
+
#
|
122
|
+
# @param method [Symbol] Method name to check
|
123
|
+
# @param include_private [Boolean] Whether to include private methods
|
124
|
+
# @return [Boolean] True if any of the underlying loggers respond to the method
|
47
125
|
def respond_to_missing?(method, include_private = false)
|
48
126
|
@loggers.any? { |logger| logger.respond_to?(method, include_private) } || super
|
49
127
|
end
|
50
128
|
|
129
|
+
# Delegates missing methods to all underlying loggers.
|
130
|
+
#
|
131
|
+
# If all loggers respond to the method, it will be called on each logger.
|
132
|
+
# Otherwise, raises a NoMethodError.
|
133
|
+
#
|
134
|
+
# @param method [Symbol] Method name to call
|
135
|
+
# @param args [Array] Arguments to pass to the method
|
136
|
+
# @param block [Proc] Block to pass to the method
|
137
|
+
# @return [Array] Results from each logger's method call
|
138
|
+
# @raise [NoMethodError] If any logger doesn't respond to the method
|
51
139
|
def method_missing(method, *args, &block)
|
52
140
|
if @loggers.all? { |logger| logger.respond_to?(method) }
|
53
141
|
@loggers.map { |logger| logger.send(method, *args, &block) }
|