class Stormpath::Http::Authc::Sauthc1Signer

Constants

ALGORITHM
AUTHENTICATION_SCHEME
AUTHORIZATION_HEADER
DATE_FORMAT
DEFAULT_ALGORITHM
HOST_HEADER
ID_TERMINATOR
NL

noinspection RubyConstantNamingConvention

SAUTHC1_ID
SAUTHC1_SIGNATURE
SAUTHC1_SIGNED_HEADERS
STORMPATH_DATE_HEADER
TIMESTAMP_FORMAT

Public Class Methods

new(uuid_generator = UUID.method(:random_create)) click to toggle source
   # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
39 def initialize(uuid_generator = UUID.method(:random_create))
40   @uuid_generator = uuid_generator
41 end

Public Instance Methods

sign_request(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
 43 def sign_request(request)
 44   request.http_headers.delete(Sauthc1Signer::AUTHORIZATION_HEADER)
 45   request.http_headers.delete(Sauthc1Signer::STORMPATH_DATE_HEADER)
 46 
 47   time = Time.now
 48   time_stamp = time.utc.strftime TIMESTAMP_FORMAT
 49   date_stamp = time.utc.strftime DATE_FORMAT
 50 
 51   nonce = @uuid_generator.call.to_s
 52 
 53   uri = request.resource_uri
 54 
 55   # SAuthc1 requires that we sign the Host header so we
 56   # have to have it in the request by the time we sign.
 57   host_header = uri.host
 58 
 59   host_header << ':' << uri.port.to_s unless default_port?(uri)
 60 
 61   request.http_headers.store HOST_HEADER, host_header
 62 
 63   request.http_headers.store STORMPATH_DATE_HEADER, time_stamp
 64 
 65   method = request.http_method
 66   canonical_resource_path = canonicalize_resource_path uri.path
 67   canonical_query_string = canonicalize_query_string request
 68   canonical_headers_string = canonicalize_headers request
 69   signed_headers_string = get_signed_headers request
 70   request_payload_hash_hex = to_hex(hash_text(get_request_payload(request)))
 71 
 72   canonical_request = [method,
 73                        canonical_resource_path,
 74                        canonical_query_string,
 75                        canonical_headers_string,
 76                        signed_headers_string,
 77                        request_payload_hash_hex].join(NL)
 78 
 79   id = [request.api_key.id, date_stamp, nonce, ID_TERMINATOR].join('/')
 80 
 81   canonical_request_hash_hex = to_hex(hash_text(canonical_request))
 82 
 83   string_to_sign = [ALGORITHM, time_stamp, id, canonical_request_hash_hex].join(NL)
 84 
 85   # SAuthc1 uses a series of derived keys, formed by hashing different pieces of data
 86   k_secret = to_utf8 AUTHENTICATION_SCHEME + request.api_key.secret
 87   k_date = sign date_stamp, k_secret, DEFAULT_ALGORITHM
 88   k_nonce = sign nonce, k_date, DEFAULT_ALGORITHM
 89   k_signing = sign ID_TERMINATOR, k_nonce, DEFAULT_ALGORITHM
 90 
 91   signature = sign to_utf8(string_to_sign), k_signing, DEFAULT_ALGORITHM
 92   signature_hex = to_hex signature
 93 
 94   authorization_header = AUTHENTICATION_SCHEME + ' ' +
 95                          create_name_value_pair(SAUTHC1_ID, id) + ', ' +
 96                          create_name_value_pair(SAUTHC1_SIGNED_HEADERS, signed_headers_string) + ', ' +
 97                          create_name_value_pair(SAUTHC1_SIGNATURE, signature_hex)
 98 
 99   request.http_headers.store AUTHORIZATION_HEADER, authorization_header
100 end
to_hex(data) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
102 def to_hex(data)
103   result = ''
104 
105   data.each_byte do |val|
106     hex = val.to_s(16)
107 
108     if hex.length == 1
109       result << '0'
110     elsif hex.length == 8
111       hex = hex[0..6]
112     end
113 
114     result << hex
115   end
116   result
117 end

Private Instance Methods

canonicalize_headers(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
160 def canonicalize_headers(request)
161   sorted_headers = request.http_headers.keys.sort!
162   result = ''
163 
164   sorted_headers.each do |header|
165     result << header.downcase << ':' << request.http_headers[header].to_s
166     result << NL
167   end
168   result
169 end
canonicalize_query_string(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
121 def canonicalize_query_string(request)
122   request.to_s_query_string true
123 end
canonicalize_resource_path(resource_path) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
152 def canonicalize_resource_path(resource_path)
153   if resource_path.nil? || resource_path.empty?
154     '/'
155   else
156     encode_url resource_path, true, true
157   end
158 end
create_name_value_pair(name, value) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
148 def create_name_value_pair(name, value)
149   "#{name}=#{value}"
150 end
get_request_payload(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
140 def get_request_payload(request)
141   get_request_payload_without_query_params request
142 end
get_request_payload_without_query_params(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
144 def get_request_payload_without_query_params(request)
145   request.body || ''
146 end
get_signed_headers(request) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
171 def get_signed_headers(request)
172   sorted_headers = request.http_headers.keys.sort!
173   result = ''
174   sorted_headers.each do |header|
175     if result.empty?
176       result << header
177     else
178       result << ';' << header
179     end
180   end
181   result.downcase
182 end
hash_text(text) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
125 def hash_text(text)
126   Digest.digest DEFAULT_ALGORITHM, to_utf8(text)
127 end
sign(data, key, algorithm) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
129 def sign(data, key, algorithm)
130   digest_data = to_utf8 data
131   digest = Digest.new(algorithm)
132   HMAC.digest(digest, key, digest_data)
133 end
to_utf8(str) click to toggle source
    # File lib/stormpath-sdk/http/authc/sauthc1_signer.rb
135 def to_utf8(str)
136   # we ask for multi line UTF-8 text
137   str.scan(/./mu).join
138 end