hmac_signature 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/hmac_signature.gemspec +24 -0
- data/lib/hmac_signature/credentials.rb +49 -0
- data/lib/hmac_signature/exceptions.rb +5 -0
- data/lib/hmac_signature/query_encoder.rb +46 -0
- data/lib/hmac_signature/request.rb +86 -0
- data/lib/hmac_signature/signature.rb +36 -0
- data/lib/hmac_signature/strategy/headers/request.rb +17 -0
- data/lib/hmac_signature/strategy/params/request.rb +18 -0
- data/lib/hmac_signature/token.rb +10 -0
- data/lib/hmac_signature/version.rb +3 -0
- data/lib/hmac_signature.rb +10 -31
- data/spec/hmac_signature_spec.rb +247 -0
- data/spec/spec_helper.rb +8 -0
- metadata +80 -11
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Erik Lott
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# HmacSignature
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'hmac_signature'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install hmac_signature
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hmac_signature/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hmac_signature"
|
8
|
+
spec.version = HmacSignature::VERSION
|
9
|
+
spec.authors = ["Erik Lott"]
|
10
|
+
spec.email = ["erik.lott@evrium.com"]
|
11
|
+
spec.summary = %q{Simple key/secret based hmac authentication via headers or query string}
|
12
|
+
spec.description = %q{Simple key/secret based hmac authentication via headers or query string}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "rspec"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module HmacSignature
|
2
|
+
class Credentials
|
3
|
+
HEADER_CREDENTIAL_REGEXES = [/^x-auth-(.+)$/i, /^http_x_auth_(.+)$/i]
|
4
|
+
PARAM_CREDENTIAL_REGEX = /^auth_(.+)$/
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def from_params params={}
|
8
|
+
hash = params.inject({}) do |memo, (k,v)|
|
9
|
+
if match = k.to_s.match(PARAM_CREDENTIAL_REGEX)
|
10
|
+
memo[match[1]] = v
|
11
|
+
end
|
12
|
+
memo
|
13
|
+
end
|
14
|
+
new hash['key'], hash['expiry'], hash['version'], hash['signature']
|
15
|
+
end
|
16
|
+
|
17
|
+
def from_headers headers={}
|
18
|
+
hash = headers.inject({}) do |memo, (k,v)|
|
19
|
+
HEADER_CREDENTIAL_REGEXES.each do |regex|
|
20
|
+
if match = k.match(regex)
|
21
|
+
new_key = match[1].downcase.gsub('-', '_')
|
22
|
+
memo[new_key] = v
|
23
|
+
break
|
24
|
+
end
|
25
|
+
end
|
26
|
+
memo
|
27
|
+
end
|
28
|
+
new hash['key'], hash['expiry'], hash['version'], hash['signature']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :key, :expiry, :version, :signature
|
33
|
+
|
34
|
+
def initialize key, expiry, version, signature
|
35
|
+
@key = key
|
36
|
+
@expiry = expiry
|
37
|
+
@version = version
|
38
|
+
@signature = signature
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
{'auth_key' => key, 'auth_expiry' => expiry, 'auth_version' => version, 'auth_signature' => signature}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_headers
|
46
|
+
{'X-Auth-Key' => key, 'X-Auth-Expiry' => expiry, 'X-Auth-Version' => version, 'X-Auth-Signature' => signature}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module HmacSignature
|
2
|
+
module QueryEncoder
|
3
|
+
class << self
|
4
|
+
# URL encodes query parameters:
|
5
|
+
# single k=v, or a URL encoded array, if v is an array of values
|
6
|
+
def encode_param(k, v)
|
7
|
+
if v.is_a?(Array)
|
8
|
+
v.map { |e| escape(k) + "[]=" + escape(e) }.join("&")
|
9
|
+
else
|
10
|
+
escape(k) + "=" + escape(v)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Like encode_param, but doesn't url escape keys or values
|
15
|
+
def encode_param_without_escaping(k, v)
|
16
|
+
if v.is_a?(Array)
|
17
|
+
v.map { |e| k + "[]=" + e }.join("&")
|
18
|
+
else
|
19
|
+
"#{k}=#{v}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def escape(s)
|
26
|
+
if defined?(EscapeUtils)
|
27
|
+
EscapeUtils.escape_url(s.to_s)
|
28
|
+
else
|
29
|
+
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) {
|
30
|
+
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if ''.respond_to?(:bytesize)
|
36
|
+
def bytesize(string)
|
37
|
+
string.bytesize
|
38
|
+
end
|
39
|
+
else
|
40
|
+
def bytesize(string)
|
41
|
+
string.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module HmacSignature
|
2
|
+
class Request
|
3
|
+
VALID_METHODS = %w(GET POST)
|
4
|
+
|
5
|
+
# http://www.w3.org/TR/NOTE-datetime
|
6
|
+
ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
|
7
|
+
|
8
|
+
attr_reader :method, :path, :params, :credentials
|
9
|
+
|
10
|
+
def initialize method, path, params, credentials=nil
|
11
|
+
raise ArgumentError, "Expected GET or POST" unless method.is_a?(String) && VALID_METHODS.include?(method)
|
12
|
+
raise ArgumentError, "Expected string" unless path.is_a?(String) && !path.empty?
|
13
|
+
raise ArgumentError, "Expected hash" unless params.is_a?(Hash)
|
14
|
+
raise ArgumentError, "Expected credentials" unless credentials.nil? || credentials.is_a?(HmacSignature::Credentials)
|
15
|
+
|
16
|
+
@method = method
|
17
|
+
@path = path
|
18
|
+
@params = params
|
19
|
+
@credentials = credentials
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign token, expires_in=600
|
23
|
+
expires_at = (Time.now.utc.to_i + expires_in).to_s
|
24
|
+
version = Signature::VERSION
|
25
|
+
key = token.key
|
26
|
+
secret = token.secret
|
27
|
+
signature = generate_signature(token, expires_at, version).to_s
|
28
|
+
|
29
|
+
HmacSignature::Credentials.new(key, expires_at, version, signature)
|
30
|
+
end
|
31
|
+
|
32
|
+
def authenticate
|
33
|
+
raise ArgumentError, "Block required" unless block_given?
|
34
|
+
raise AuthenticationError, "Missing parameter: key" unless credentials.key
|
35
|
+
token = yield credentials.key
|
36
|
+
unless token
|
37
|
+
raise AuthenticationError, "Unknown key"
|
38
|
+
end
|
39
|
+
authenticate_by_token! token
|
40
|
+
return token
|
41
|
+
end
|
42
|
+
|
43
|
+
def authenticate_by_token token
|
44
|
+
authenticate_by_token! token
|
45
|
+
rescue
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def authenticate_by_token! token
|
50
|
+
raise HmacSignature::AuthenticationError, "Credentials required" unless credentials
|
51
|
+
|
52
|
+
if token.secret.nil? || token.secret.empty?
|
53
|
+
raise ArgumentError, "Provided token is missing secret"
|
54
|
+
end
|
55
|
+
|
56
|
+
# authenticates version
|
57
|
+
raise HmacSignature::AuthenticationError, "Version required" unless credentials.version
|
58
|
+
raise HmacSignature::AuthenticationError, "Version not supported" unless credentials.version == '1.0'
|
59
|
+
|
60
|
+
# authenticates expiry
|
61
|
+
raise HmacSignature::AuthenticationError, "Expiration timestamp required" unless credentials.expiry
|
62
|
+
if Time.now.utc.to_i > credentials.expiry.to_i
|
63
|
+
raise HmacSignature::AuthenticationError, "Timestamp expired: Given timestamp "\
|
64
|
+
"(#{Time.at(credentials.expiry.to_i).utc.strftime(ISO8601)}) "\
|
65
|
+
"has expired. The current server time is "\
|
66
|
+
"(#{Time.now.utc.strftime(ISO8601)})"
|
67
|
+
end
|
68
|
+
|
69
|
+
# authenticates signature
|
70
|
+
valid_signature = generate_signature(token, credentials.expiry, credentials.version)
|
71
|
+
unless credentials.signature == valid_signature.to_s
|
72
|
+
raise HmacSignature::AuthenticationError, "Invalid signature: you should have "\
|
73
|
+
"sent HmacSHA256Hex(#{valid_signature.string_to_sign.inspect}, your_secret_key)"\
|
74
|
+
", but you sent #{credentials.signature.inspect}"
|
75
|
+
end
|
76
|
+
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def generate_signature token, timestamp, version
|
83
|
+
HmacSignature::Signature.new(method, path, params, timestamp, version, token.key, token.secret)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module HmacSignature
|
4
|
+
class Signature < Struct.new(:method, :path, :params, :timestamp, :version, :key, :secret)
|
5
|
+
VERSION = "1.0"
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
digest = OpenSSL::Digest::SHA256.new
|
9
|
+
OpenSSL::HMAC.hexdigest(digest, secret, string_to_sign)
|
10
|
+
end
|
11
|
+
|
12
|
+
def string_to_sign
|
13
|
+
[method, path, parameter_string].join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def credential_hash
|
19
|
+
{ :auth_expiry => timestamp, :auth_version => version, :auth_key => key }
|
20
|
+
end
|
21
|
+
|
22
|
+
def parameter_string
|
23
|
+
hash = params.merge credential_hash
|
24
|
+
|
25
|
+
# Convert keys to lowercase strings
|
26
|
+
hash = hash.inject({}) do |memo, (k,v)|
|
27
|
+
memo[k.to_s.downcase] = v
|
28
|
+
memo
|
29
|
+
end
|
30
|
+
|
31
|
+
hash.sort.map do |k, v|
|
32
|
+
QueryEncoder.encode_param_without_escaping(k, v)
|
33
|
+
end.join('&')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module HmacSignature
|
2
|
+
module Strategy
|
3
|
+
module Headers
|
4
|
+
class Request < HmacSignature::Request
|
5
|
+
def initialize method, path, params, headers={}
|
6
|
+
credentials = HmacSignature::Credentials.from_headers(headers)
|
7
|
+
super method, path, params, credentials
|
8
|
+
end
|
9
|
+
|
10
|
+
def sign token, expires_in=600
|
11
|
+
credentials = super(token, expires_in)
|
12
|
+
credentials.to_headers
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module HmacSignature
|
2
|
+
module Strategy
|
3
|
+
module Params
|
4
|
+
class Request < HmacSignature::Request
|
5
|
+
def initialize method, path, params
|
6
|
+
credentials = HmacSignature::Credentials.from_params params
|
7
|
+
params_with_auth_removed = params.reject{|k,v| k.match HmacSignature::Credentials::PARAM_CREDENTIAL_REGEX }
|
8
|
+
super method, path, params_with_auth_removed, credentials
|
9
|
+
end
|
10
|
+
|
11
|
+
def sign token, expires_in=600
|
12
|
+
credentials = super(token, expires_in)
|
13
|
+
credentials.to_hash
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/hmac_signature.rb
CHANGED
@@ -1,33 +1,12 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'hmac_signature/version'
|
2
|
+
require 'hmac_signature/exceptions'
|
3
|
+
require 'hmac_signature/token'
|
4
|
+
require 'hmac_signature/credentials'
|
5
|
+
require 'hmac_signature/query_encoder'
|
6
|
+
require 'hmac_signature/signature'
|
7
|
+
require 'hmac_signature/request'
|
8
|
+
require 'hmac_signature/strategy/headers/request'
|
9
|
+
require 'hmac_signature/strategy/params/request'
|
3
10
|
|
4
|
-
|
5
|
-
def initialize(key)
|
6
|
-
@key = key
|
7
|
-
end
|
8
|
-
|
9
|
-
def sign(verb, host, path, query_params)
|
10
|
-
# sort the params alphabetically by key and join
|
11
|
-
# them with '='
|
12
|
-
sorted_query_params = query_params.sort.map do |param|
|
13
|
-
param.join("=")
|
14
|
-
end # => ["user=mat", "tag=ruby"]
|
15
|
-
|
16
|
-
# join the sorted params into one string
|
17
|
-
canonicalized_params = sorted_query_params.join("&")
|
18
|
-
# => "user=mat&tag=ruby"
|
19
|
-
|
20
|
-
# Construct the string to sign by concatenating the
|
21
|
-
# various parts of the request.
|
22
|
-
string_to_sign = verb + host + path + canonicalized_params
|
23
|
-
|
24
|
-
# Construct an hmac signer using our secret key
|
25
|
-
digest = OpenSSL::HMAC.digest('sha256', @key, string_to_sign)
|
26
|
-
# digest = Digest::SHA256.new(@key)
|
27
|
-
# digest.update(string_to_sign)
|
28
|
-
|
29
|
-
# Encrypt the string and Base64 encode it (to
|
30
|
-
# make it cleaner when putting it into the request).
|
31
|
-
Base64.encode64(digest).chomp
|
32
|
-
end
|
11
|
+
module HmacSignature
|
33
12
|
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HmacSignature do
|
4
|
+
let(:timestamp) { Time.at(2345) }
|
5
|
+
let(:method) { 'GET' }
|
6
|
+
let(:path) { '/test/path' }
|
7
|
+
let(:params) {{:param1 => 'param1', :param2 => 'param2'}}
|
8
|
+
let(:key) { 'key123' }
|
9
|
+
let(:secret) { 'secret123' }
|
10
|
+
let(:version) { HmacSignature::Signature::VERSION }
|
11
|
+
let(:expiry) { (timestamp.to_i + 600).to_s }
|
12
|
+
let(:signature) { '00d639f0c7561b976893deed5360b339a90971aa8bd8e1bd9f1aab30f303ac3a' }
|
13
|
+
|
14
|
+
let(:token_key) { key }
|
15
|
+
let(:token_secret) { secret }
|
16
|
+
let(:token) { HmacSignature::Token.new token_key, token_secret }
|
17
|
+
|
18
|
+
let(:cred_key) { key }
|
19
|
+
let(:cred_expiry) { expiry }
|
20
|
+
let(:cred_version) { version }
|
21
|
+
let(:cred_signature) { signature }
|
22
|
+
let(:credentials) { HmacSignature::Credentials.new cred_key, cred_expiry, cred_version, cred_signature }
|
23
|
+
|
24
|
+
let(:req_headers_a) {{ 'X-Auth-Key' => key, 'X-Auth-Expiry' => expiry, 'X-Auth-Version' => version, 'X-Auth-Signature' => signature, 'Content-Type' => 'text/plain', 'Content-Length' => 12345 }}
|
25
|
+
let(:req_headers_b) {{ 'HTTP_X_AUTH_KEY' => key, 'HTTP_X_AUTH_EXPIRY' => expiry, 'HTTP_X_AUTH_VERSION' => version, 'HTTP_X_AUTH_SIGNATURE' => signature, 'HTTP_CONTENT_TYPE' => 'text/plain', 'HTTP_CONTENT_LENGTH' => 12345 }}
|
26
|
+
let(:req_method) { method }
|
27
|
+
let(:req_path) { path }
|
28
|
+
let(:req_params) { params }
|
29
|
+
let(:req_credentials) { credentials }
|
30
|
+
let(:request) { HmacSignature::Request.new req_method, req_path, req_params, req_credentials}
|
31
|
+
|
32
|
+
before do
|
33
|
+
Time.stub!(:now).and_return(timestamp)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe HmacSignature::Token do
|
37
|
+
end
|
38
|
+
|
39
|
+
describe HmacSignature::Credentials do
|
40
|
+
describe "::from_params" do
|
41
|
+
context "when symbol keys" do
|
42
|
+
hash = {:blah => 'blah', :auth_key => 'key', :auth_expiry => 'expiry', :auth_version => 'version', :auth_signature => 'signature' }
|
43
|
+
credentials = HmacSignature::Credentials.from_params(hash)
|
44
|
+
credentials.key.should == 'key'
|
45
|
+
credentials.expiry.should == 'expiry'
|
46
|
+
credentials.version.should == 'version'
|
47
|
+
credentials.signature.should == 'signature'
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when string keys" do
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "::from_headers" do
|
55
|
+
subject(:response){ HmacSignature::Credentials.from_headers(headers) }
|
56
|
+
|
57
|
+
context "when headers in normal form" do
|
58
|
+
let(:headers){ req_headers_a }
|
59
|
+
|
60
|
+
its(:key){ should == key }
|
61
|
+
its(:expiry){ should == expiry }
|
62
|
+
its(:expiry){ should == expiry }
|
63
|
+
its(:version){ should == version }
|
64
|
+
its(:signature){ should == signature }
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when headers in normal form" do
|
68
|
+
let(:headers){ req_headers_b }
|
69
|
+
|
70
|
+
its(:key){ should == key }
|
71
|
+
its(:expiry){ should == expiry }
|
72
|
+
its(:expiry){ should == expiry }
|
73
|
+
its(:version){ should == version }
|
74
|
+
its(:signature){ should == signature }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#to_hash" do
|
79
|
+
it "returns a credential hash" do
|
80
|
+
credentials.to_hash.should == {
|
81
|
+
"auth_key" => key,
|
82
|
+
"auth_expiry" => "2945",
|
83
|
+
"auth_version" => version,
|
84
|
+
"auth_signature" => signature
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe HmacSignature::Signature do
|
91
|
+
subject{ HmacSignature::Signature.new method, path, params, expiry, version, key, secret }
|
92
|
+
its(:to_s){ should == signature }
|
93
|
+
end
|
94
|
+
|
95
|
+
describe HmacSignature::Request do
|
96
|
+
describe "::new" do
|
97
|
+
context "when method is nil" do
|
98
|
+
let(:req_method){ nil }
|
99
|
+
it "raises error" do
|
100
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected GET or POST')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when method is empty string" do
|
105
|
+
let(:req_method){ '' }
|
106
|
+
it "raises error" do
|
107
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected GET or POST')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when path is nil" do
|
112
|
+
let(:req_path){ nil }
|
113
|
+
it "raises error" do
|
114
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected string')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when path is empty string" do
|
119
|
+
let(:req_path){ '' }
|
120
|
+
it "raises error" do
|
121
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected string')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "when params is not a hash" do
|
126
|
+
let(:req_params){ '' }
|
127
|
+
it "raises error" do
|
128
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected hash')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when credentials is not an instance of Credentials" do
|
133
|
+
let(:req_credentials){ '' }
|
134
|
+
it "raises error" do
|
135
|
+
expect{ request }.to raise_error(ArgumentError, 'Expected credentials')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#sign" do
|
141
|
+
it "returns credentials instance" do
|
142
|
+
creds = request.sign(token)
|
143
|
+
creds.expiry.should == expiry
|
144
|
+
creds.signature.should == signature
|
145
|
+
creds.version.should == version
|
146
|
+
creds.key.should == key
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "authenticate_by_token!" do
|
151
|
+
subject(:auth){ request.authenticate_by_token! token }
|
152
|
+
|
153
|
+
context "when params are valid" do
|
154
|
+
it "returns true" do
|
155
|
+
auth.should be_true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "when token is missing secret" do
|
160
|
+
it "raises exception" do
|
161
|
+
tok = HmacSignature::Token.new token_key, ''
|
162
|
+
expect do
|
163
|
+
request.authenticate_by_token! tok
|
164
|
+
end.to raise_error(ArgumentError, 'Provided token is missing secret')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "when signature is invalid" do
|
169
|
+
let(:cred_signature) { 'incorrect123' }
|
170
|
+
it "returns true" do
|
171
|
+
expect { auth }.to raise_error(HmacSignature::AuthenticationError, 'Invalid signature: you should have sent HmacSHA256Hex("GET\n/test/path\nauth_expiry=2945&auth_key=key123&auth_version=1.0¶m1=param1¶m2=param2", your_secret_key), but you sent "incorrect123"')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when expiry timestamp is expired" do
|
176
|
+
let(:req_credentials) do
|
177
|
+
HmacSignature::Request.new(req_method, req_path, req_params).sign(token, -600)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "returns true" do
|
181
|
+
expect { auth }.to raise_error(HmacSignature::AuthenticationError, 'Timestamp expired: Given timestamp (1970-01-01T00:29:05Z) has expired. The current server time is (1970-01-01T00:39:05Z)')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when expiry timestamp is really expired" do
|
186
|
+
let(:req_credentials) do
|
187
|
+
HmacSignature::Request.new(req_method, req_path, req_params).sign(token, -4600)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "returns true" do
|
191
|
+
expect { auth }.to raise_error(HmacSignature::AuthenticationError, 'Timestamp expired: Given timestamp (1969-12-31T23:22:25Z) has expired. The current server time is (1970-01-01T00:39:05Z)')
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "when expiry timestamp is slightly expired" do
|
196
|
+
let(:req_credentials) do
|
197
|
+
HmacSignature::Request.new(req_method, req_path, req_params).sign(token, -10)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "returns true" do
|
201
|
+
expect { auth }.to raise_error(HmacSignature::AuthenticationError, 'Timestamp expired: Given timestamp (1970-01-01T00:38:55Z) has expired. The current server time is (1970-01-01T00:39:05Z)')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe HmacSignature::Strategy::Headers::Request do
|
208
|
+
let(:header_request) { HmacSignature::Strategy::Headers::Request.new req_method, req_path, req_params, req_headers_a }
|
209
|
+
describe "#sign" do
|
210
|
+
it "returns a hash of header values" do
|
211
|
+
header_request.sign(token).should == {
|
212
|
+
"X-Auth-Key"=> key,
|
213
|
+
"X-Auth-Expiry"=>"2945",
|
214
|
+
"X-Auth-Version"=> version,
|
215
|
+
"X-Auth-Signature"=> signature
|
216
|
+
}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#authenticate_by_token!" do
|
221
|
+
it "returns true" do
|
222
|
+
header_request.authenticate_by_token!(token).should be_true
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe HmacSignature::Strategy::Params::Request do
|
228
|
+
let(:params_request_params) { params.merge credentials.to_hash }
|
229
|
+
let(:params_request) { HmacSignature::Strategy::Params::Request.new req_method, req_path, params_request_params }
|
230
|
+
describe "#sign" do
|
231
|
+
it "returns a hash of header values" do
|
232
|
+
params_request.sign(token).should == {
|
233
|
+
"auth_key"=> key,
|
234
|
+
"auth_expiry"=>"2945",
|
235
|
+
"auth_version"=> version,
|
236
|
+
"auth_signature"=> signature
|
237
|
+
}
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "#authenticate_by_token!" do
|
242
|
+
it "returns true" do
|
243
|
+
params_request.authenticate_by_token!(token).should be_true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,25 +1,92 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hmac_signature
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Erik Lott
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
12
|
+
date: 2013-06-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Simple key/secret based hmac authentication via headers or query string
|
63
|
+
email:
|
64
|
+
- erik.lott@evrium.com
|
16
65
|
executables: []
|
17
66
|
extensions: []
|
18
67
|
extra_rdoc_files: []
|
19
68
|
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- hmac_signature.gemspec
|
20
75
|
- lib/hmac_signature.rb
|
21
|
-
|
22
|
-
|
76
|
+
- lib/hmac_signature/credentials.rb
|
77
|
+
- lib/hmac_signature/exceptions.rb
|
78
|
+
- lib/hmac_signature/query_encoder.rb
|
79
|
+
- lib/hmac_signature/request.rb
|
80
|
+
- lib/hmac_signature/signature.rb
|
81
|
+
- lib/hmac_signature/strategy/headers/request.rb
|
82
|
+
- lib/hmac_signature/strategy/params/request.rb
|
83
|
+
- lib/hmac_signature/token.rb
|
84
|
+
- lib/hmac_signature/version.rb
|
85
|
+
- spec/hmac_signature_spec.rb
|
86
|
+
- spec/spec_helper.rb
|
87
|
+
homepage: ''
|
88
|
+
licenses:
|
89
|
+
- MIT
|
23
90
|
post_install_message:
|
24
91
|
rdoc_options: []
|
25
92
|
require_paths:
|
@@ -38,9 +105,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
105
|
version: '0'
|
39
106
|
requirements: []
|
40
107
|
rubyforge_project:
|
41
|
-
rubygems_version: 1.8.
|
108
|
+
rubygems_version: 1.8.25
|
42
109
|
signing_key:
|
43
110
|
specification_version: 3
|
44
|
-
summary:
|
45
|
-
test_files:
|
111
|
+
summary: Simple key/secret based hmac authentication via headers or query string
|
112
|
+
test_files:
|
113
|
+
- spec/hmac_signature_spec.rb
|
114
|
+
- spec/spec_helper.rb
|
46
115
|
has_rdoc:
|