tak1n/rack-simple_auth

View on GitHub
lib/rack/simple_auth/hmac/request.rb

Summary

Maintainability
A
0 mins
Test Coverage
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