Class: HaystackRuby::Auth::Scram::Conversation

Inherits:
Object
  • Object
show all
Defined in:
lib/haystack_ruby/auth/conversation.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user, url) ⇒ Conversation

Returns a new instance of Conversation.



10
11
12
13
14
15
16
# File 'lib/haystack_ruby/auth/conversation.rb', line 10

def initialize(user, url)
  @user = user
  @url = url
  @nonce = SecureRandom.base64.tr('=','') #TODO check if problem to potentially strip =
  @digest = OpenSSL::Digest::SHA256.new 
  @handshake_token = Base64.strict_encode64(@user.username)
end

Instance Attribute Details

#auth_tokenObject (readonly)

Returns the value of attribute auth_token.



8
9
10
# File 'lib/haystack_ruby/auth/conversation.rb', line 8

def auth_token
  @auth_token
end

#nonceObject (readonly)

Returns the value of attribute nonce.



8
9
10
# File 'lib/haystack_ruby/auth/conversation.rb', line 8

def nonce
  @nonce
end

#server_nonceObject (readonly)

Returns the value of attribute server_nonce.



8
9
10
# File 'lib/haystack_ruby/auth/conversation.rb', line 8

def server_nonce
  @server_nonce
end

#server_saltObject (readonly)

Returns the value of attribute server_salt.



8
9
10
# File 'lib/haystack_ruby/auth/conversation.rb', line 8

def server_salt
  @server_salt
end

Instance Method Details

#authorizeObject



18
19
20
21
22
23
# File 'lib/haystack_ruby/auth/conversation.rb', line 18

def authorize
  res = send_first_message
  parse_first_response(res)
  res = send_second_message
  parse_second_response(res)
end

#connectionObject



25
26
27
28
29
30
31
32
# File 'lib/haystack_ruby/auth/conversation.rb', line 25

def connection 
  @connection ||= Faraday.new(:url => @url) do |faraday|
    # faraday.response :logger                  # log requests to STDOUT
    faraday.adapter  Faraday.default_adapter  # make requests with Net::HTTP
    faraday.headers['Accept'] = 'application/json' #TODO enable more formats
    faraday.headers['Content-Type'] = 'text/plain'
  end
end

#parse_first_response(response) ⇒ Object

pull server data out of response to first message



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/haystack_ruby/auth/conversation.rb', line 44

def parse_first_response(response)

  # parse server response to first message
  response_str = response.env.response_headers['www-authenticate']
  unless response_str.index('scram ') == 0
    throw 'Invalid response from server'
  end
  response_str.slice! 'scram '
  response_vars = {}
  response_str.split(', ').each do |pair|
    key,value = pair.split('=')
    response_vars[key] = value
  end
  unless response_vars['hash'] == 'SHA-256'
    throw "Server requested unsupported hash algorithm: #{response_vars['hash']}"
  end

  # todo check handshake token (should be base64 encode of username)

  @server_first_msg = Base64.decode64(response_vars["data"])
  server_vars = {}
  @server_first_msg.split(',').each do |pair|
    key,value = pair.split '='
    server_vars[key] = value
  end
  @server_nonce = server_vars['r']
  @server_salt = server_vars['s'] #Base64.decode64(server_vars['s'])
  @server_iterations = server_vars['i'].to_i
end

#parse_second_response(response) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/haystack_ruby/auth/conversation.rb', line 81

def parse_second_response(response)
  begin
    response_str = response.env.response_headers['authentication-info']
    response_vars = {}
    response_str.split(', ').each do |pair|
      key,value = pair.split('=')
      response_vars[key] = value
    end
    # decode data attribute to check server signature is as expected
    key,val = Base64.decode64(response_vars['data']).split('=')
    response_vars[key] = val
    server_sig = response_vars['v']
    unless server_sig == expected_server_signature
      throw "invalid signature from server"
    end
    @auth_token = response_vars['authToken']
  # rescue Exception => e
  #   raise
  end
  
end

#send_first_messageObject

first message sent by client to server



35
36
37
38
39
40
41
# File 'lib/haystack_ruby/auth/conversation.rb', line 35

def send_first_message
  res = connection.get('about') do |req|
    req.headers['Authorization'] = "SCRAM handshakeToken=#{@handshake_token},data=#{Base64.urlsafe_encode64(first_message).tr('=','')}"
  end
  res
  
end

#send_second_messageObject



74
75
76
77
78
79
80
# File 'lib/haystack_ruby/auth/conversation.rb', line 74

def send_second_message
  res = connection.get('about') do |req|
    req.headers['Authorization'] = "SCRAM handshakeToken=#{@handshake_token},data=#{Base64.strict_encode64(client_final).tr('=','')}"
  end
  res
  
end

#test_auth_tokenObject



103
104
105
106
107
# File 'lib/haystack_ruby/auth/conversation.rb', line 103

def test_auth_token
  res = connection.get('about') do |req|
    req.headers['Authorization'] = "BEARER authToken=#{@auth_token}"
  end
end