Class: Hooks::Plugins::Auth::Base

Inherits:
Object
  • Object
show all
Extended by:
Core::ComponentAccess
Defined in:
lib/hooks/plugins/auth/base.rb

Overview

Abstract base class for request validators via authentication

All custom Auth plugins must inherit from this class

Direct Known Subclasses

HMAC, SharedSecret

Constant Summary collapse

MAX_HEADER_VALUE_LENGTH =

Security constants shared across auth validators

ENV.fetch("HOOKS_MAX_HEADER_VALUE_LENGTH", 1024).to_i
MAX_PAYLOAD_SIZE =

10MB limit for payload validation

ENV.fetch("HOOKS_MAX_PAYLOAD_SIZE", 10 * 1024 * 1024).to_i

Class Method Summary collapse

Methods included from Core::ComponentAccess

failbot, log, method_missing, respond_to_missing?, stats

Class Method Details

.fetch_secret(config, secret_env_key_name: :secret_env_key) ⇒ String

Retrieve the secret from the environment variable based on the key set in the configuration

Note: This method is intended to be used by subclasses It is a helper method and may not work with all authentication types

Parameters:

  • config (Hash)

    Configuration hash containing :auth key

  • secret_env_key (Symbol)

    The key to look up in the config for the environment variable name

Returns:

  • (String)

    The secret

Raises:

  • (StandardError)

    if secret_env_key is missing or empty



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/hooks/plugins/auth/base.rb', line 42

def self.fetch_secret(config, secret_env_key_name: :secret_env_key)
  secret_env_key = config.dig(:auth, secret_env_key_name)
  if secret_env_key.nil? || !secret_env_key.is_a?(String) || secret_env_key.strip.empty?
    raise StandardError, "authentication configuration incomplete: missing secret_env_key"
  end

  secret = ENV[secret_env_key]

  if secret.nil? || !secret.is_a?(String) || secret.strip.empty?
    raise StandardError, "authentication configuration incomplete: missing secret value for environment variable"
  end

  return secret.strip
end

.find_header_value(headers, header_name) ⇒ String?

Note:

This method performs case-insensitive header matching

Find a header value by name with case-insensitive matching

Parameters:

  • headers (Hash)

    HTTP headers from the request

  • header_name (String)

    Name of the header to find

Returns:

  • (String, nil)

    The header value if found, nil otherwise



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/hooks/plugins/auth/base.rb', line 70

def self.find_header_value(headers, header_name)
  return nil unless headers.respond_to?(:each)
  return nil if header_name.nil? || header_name.strip.empty?

  target_header = header_name.downcase
  headers.each do |key, value|
    if key.to_s.downcase == target_header
      return value.to_s
    end
  end
  nil
end

.timestamp_validatorTimestampValidator

Get timestamp validator instance

Returns:



60
61
62
# File 'lib/hooks/plugins/auth/base.rb', line 60

def self.timestamp_validator
  TimestampValidator.new
end

.valid?(payload:, headers:, config:) ⇒ Boolean

Validate request

Parameters:

  • payload (String)

    Raw request body

  • headers (Hash<String, String>)

    HTTP headers

  • config (Hash)

    Endpoint configuration

Returns:

  • (Boolean)

    true if request is valid

Raises:

  • (NotImplementedError)

    if not implemented by subclass



29
30
31
# File 'lib/hooks/plugins/auth/base.rb', line 29

def self.valid?(payload:, headers:, config:)
  raise NotImplementedError, "Validator must implement .valid? class method"
end

.valid_header_value?(header_value, header_name) ⇒ Boolean

Validate header value for security issues

Parameters:

  • header_value (String)

    Header value to validate

  • header_name (String)

    Header name for logging

Returns:

  • (Boolean)

    true if header value is valid



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/hooks/plugins/auth/base.rb', line 114

def self.valid_header_value?(header_value, header_name)
  return false if header_value.nil? || header_value.empty?

  # Check length to prevent DoS
  if header_value.length > MAX_HEADER_VALUE_LENGTH
    log.warn("Auth validation failed: #{header_name} exceeds maximum length")
    return false
  end

  # Check for whitespace tampering
  if header_value != header_value.strip
    log.warn("Auth validation failed: #{header_name} contains leading/trailing whitespace")
    return false
  end

  # Check for control characters
  if header_value.match?(/[\u0000-\u001f\u007f-\u009f]/)
    log.warn("Auth validation failed: #{header_name} contains control characters")
    return false
  end

  true
end

.valid_headers?(headers) ⇒ Boolean

Validate headers object for security issues

Parameters:

  • headers (Object)

    Headers to validate

Returns:

  • (Boolean)

    true if headers are valid



87
88
89
90
91
92
93
# File 'lib/hooks/plugins/auth/base.rb', line 87

def self.valid_headers?(headers)
  unless headers.respond_to?(:each)
    log.warn("Auth validation failed: Invalid headers object")
    return false
  end
  true
end

.valid_payload_size?(payload) ⇒ Boolean

Validate payload size for security issues

Parameters:

  • payload (String)

    Payload to validate

Returns:

  • (Boolean)

    true if payload is valid



99
100
101
102
103
104
105
106
107
# File 'lib/hooks/plugins/auth/base.rb', line 99

def self.valid_payload_size?(payload)
  return true if payload.nil?

  if payload.bytesize > MAX_PAYLOAD_SIZE
    log.warn("Auth validation failed: Payload size exceeds maximum limit of #{MAX_PAYLOAD_SIZE} bytes")
    return false
  end
  true
end