Class: Rack::SimpleAuth::HMAC

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/simple_auth/hmac.rb

Overview

HMAC Middleware uses HMAC Authorization for Securing an REST API

Instance Method Summary collapse

Constructor Details

#initialize(app, config) ⇒ HMAC

Constructor for Rack Middleware (passing the rack stack)

Parameters:

  • app (Rack Application)
    next middleware or rack app which gets called
  • config (Hash)
    config hash where tolerance, secret, signature etc.. are set


9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/rack/simple_auth/hmac.rb', line 9

def initialize(app, config)
  @app = app
  @signature = config['signature'] || ''
  @secret = config['secret'] || ''
  @tolerance = config['tolerance'] || 1 # 0 if tolerance not set in config hash
  @logpath = config['logpath']
  @steps = config['steps'] || 1

  valid_stepsize?(0.01)
  valid_tolerance?

  @config = config
end

Instance Method Details

#allowed_messagesArray (private)

Builds Array of allowed message hashs

Returns:

  • (Array)

    hash_array [allowed message hashes as array]



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rack/simple_auth/hmac.rb', line 68

def allowed_messages
  messages = []

  @date = Time.now.to_i.freeze
  (-(@tolerance)..@tolerance).step(@steps) do |i|
    i = i.round(2)
    messages << OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, message(i))
  end

  messages
end

#call(env) ⇒ Object

call Method for Rack Middleware/Application

Parameters:

  • env (Hash)
    Rack Env Hash which contains headers etc..


25
26
27
28
29
30
31
32
33
34
# File 'lib/rack/simple_auth/hmac.rb', line 25

def call(env)
  @request = Rack::Request.new(env)

  if valid_request?
    @app.call(env)
  else
    response = Rack::Response.new('Unauthorized', 401, 'Content-Type' => 'text/html')
    response.finish
  end
end

#log(hash_array) ⇒ Object (private)

Log to @logpath if request is unathorized Contains:

- allowed messages and received message
- time when request was made
- type of request
- requested path


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/rack/simple_auth/hmac.rb', line 121

def log(hash_array)
  if @logpath
    msg = "#{Time.new} - #{@request.request_method} #{@request.path} - 400 Unauthorized\n"
    msg << "HTTP_AUTHORIZATION: #{@request.env['HTTP_AUTHORIZATION']}\n"
    msg << "Auth Message Config: #{@config[@request.request_method]}\n"

    if hash_array
      msg << "Allowed Encrypted Messages:\n"
      hash_array.each do |hash|
        msg << "#{hash}\n"
      end
    end

    msg << "Auth Signature: #{@signature}"

    Rack::SimpleAuth::Logger.log(@logpath, ENV['RACK_ENV'], msg)
  end
end

#message(delay = 0) ⇒ Hash (private)

Get Message for current Request and delay

Parameters:

  • delay (Fixnum) (defaults to: 0)
    delay in timestamp format

Returns:

  • (Hash)

    message [message which will be encrypted]



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rack/simple_auth/hmac.rb', line 83

def message(delay = 0)
  date = @date + delay
  date = date.to_i if delay.eql?(0.0)

  # Print out Delay and Timestamp for each range step in development environment
  puts "Delay: #{delay}, Timestamp: #{date}" if ENV['RACK_ENV'].eql?('development')

  case @request.request_method
  when 'GET'
    return { 'method' => @request.request_method, 'date' => date, 'data' => request_data(@config) }.to_json
  when 'POST'
    return { 'method' => @request.request_method, 'date' => date, 'data' => request_data(@config) }.to_json
  when 'DELETE'
    return { 'method' => @request.request_method, 'date' => date, 'data' => request_data(@config) }.to_json
  when 'PUT'
    return { 'method' => @request.request_method, 'date' => date, 'data' => request_data(@config) }.to_json
  when 'PATCH'
    return { 'method' => @request.request_method, 'date' => date, 'data' => request_data(@config) }.to_json
  end
end

#request_data(config) ⇒ String|Hash (private)

Get Request Data specified by Config

Parameters:

  • config (Hash)
    Config Hash containing what type of info is data for each request

Returns:

  • (String|Hash)

    data [Data for each request]



107
108
109
110
111
112
113
# File 'lib/rack/simple_auth/hmac.rb', line 107

def request_data(config)
  if config[@request.request_method] == 'path' || config[@request.request_method] == 'params'
    @request.send(config[@request.request_method].to_sym)
  else
    fail "Not a valid option #{config[@request.request_method]} - Use either params or path"
  end
end

#request_messageObject (private)

Get encrypted request message



62
63
64
# File 'lib/rack/simple_auth/hmac.rb', line 62

def request_message
  @request.env['HTTP_AUTHORIZATION'].split(':').first
end

#request_signatureObject (private)

Get request signature



57
58
59
# File 'lib/rack/simple_auth/hmac.rb', line 57

def request_signature
  @request.env['HTTP_AUTHORIZATION'].split(':').last
end

#valid_request?boolean

checks for valid HMAC Request

Returns:

  • (boolean)

    ValidationStatus [If authorized returns true, else false]



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rack/simple_auth/hmac.rb', line 38

def valid_request?
  if @request.env['HTTP_AUTHORIZATION'].nil?
    log(allowed_messages)

    return false
  end

  if request_signature == @signature && allowed_messages.include?(request_message)
    true
  else
    log(allowed_messages)

    false
  end
end

#valid_stepsize?(min) ⇒ Boolean (private)

Check if Stepsize is valid, if > min ensure stepsize is min stepsize

Parameters:

  • min (float)
    minimum allowed stepsize

Returns:

  • (Boolean)


142
143
144
145
146
# File 'lib/rack/simple_auth/hmac.rb', line 142

def valid_stepsize?(min)
  if @steps < min
    fail "Minimum allowed stepsize is #{min}"
  end
end

#valid_tolerance?Boolean (private)

Check if tolerance is valid, tolerance must be greater than stepsize

Returns:

  • (Boolean)


149
150
151
152
153
# File 'lib/rack/simple_auth/hmac.rb', line 149

def valid_tolerance?
  if @tolerance < @steps
    fail "Tolerance must be greater than stepsize - Tolerance: #{@tolerance}, Stepsize: #{@steps}"
  end
end