lib/rack/simple_auth/hmac/request.rb
module Rack
module SimpleAuth
module HMAC
class Request < Rack::Request
attr_reader :env, :config, :allowed_messages
def initialize(env, config)
@env = env
@config = config
@allowed_messages = build_allowed_messages
end
##
# Checks for valid HMAC Request
#
# @return [TrueClass] if request is authorized
# @return [FalseClass] if request is not authorized or HTTP_AUTHORIZATION Header is not set
#
def valid?
# log
return false if empty_header? || !authorized?
true
end
private
##
# Builds Array of allowed message hashs between tolerance via {#message}
#
# @return [Array]
def build_allowed_messages
messages = []
# Timestamp with milliseconds as Fixnum
date = (Time.now.to_f.freeze * 1000).to_i
(-(config.tolerance)..0).step(1) do |i|
messages << OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), config.secret, build_message(date, i))
end
messages
end
##
# Build Message for current Request and delay
#
# @param [Fixnum] date [current date in timestamp format]
# @param [Fixnum] delay [delay in timestamp format]
#
# @return [String] message
def build_message(date, delay = 0)
date += delay
{ 'method' => request_method, 'date' => date, 'data' => data }.to_json
end
##
# Get Request Data specified by config.request_config
#
# @return [String|Hash] data
#
# Note: REFACTOR this shit..
def data
return send(config.request_config[request_method].to_sym) if valid_message_type?
fail "Not a valid option #{config.request_config[request_method]} - Use either params or path"
end
##
# Check if HTTP_AUTHORIZATION Header is set
#
# @return [TrueClass] if header is set
# @return [FalseClass] if header is not set
#
def empty_header?
env['HTTP_AUTHORIZATION'].nil?
end
##
# Check if request is authorized
#
# @return [TrueClass] if request is authorized -> {#signature} is correct & {#message} is included
# in {#allowed_messages}
# @return [FalseClass] if request is not authorized
#
def authorized?
signature.eql?(config.signature) && allowed_messages.include?(message)
end
##
# Get request signature
#
# @return [String] signature of current request
#
def signature
env['HTTP_AUTHORIZATION'].split(':').last
end
##
# Get encrypted request message
#
# @return [String] message of current request
#
def message
env['HTTP_AUTHORIZATION'].split(':').first
end
##
# Check if message type for current request is valid
#
# @return [TrueClass] if message type for current request is path or params
# @return [FalseClass] if message type is invalid
#
def valid_message_type?
config.request_config[request_method] == 'path' || config.request_config[request_method] == 'params'
end
##
# Log to config.logpath
# Contains:
# - allowed messages and received message
# - time when request was made
# - type of request
# - requested path
#
# Note: This is kinda slow under Rubinius
# (Rack::SimpleAuth::Logger.log has IO action, i think there are some performance issues)
#
def log
msg = "#{Time.new} - #{request_method} #{path} - 400 Unauthorized\n"
msg << "HTTP_AUTHORIZATION: #{env['HTTP_AUTHORIZATION']}\n"
msg << "Auth Message Config: #{config.request_config[request_method]}\n"
if allowed_messages
msg << "Allowed Encrypted Messages:\n"
allowed_messages.each do |hash|
msg << "#{hash}\n"
end
end
msg << "Auth Signature: #{config.signature}"
Rack::SimpleAuth::Logger.log(config.logpath, config.verbose, ENV['RACK_ENV'], msg)
end
end
end
end
end