signauth 0.0.3 → 0.1.0
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/README.md +1 -37
- data/lib/signauth/errors.rb +2 -0
- data/lib/signauth/request.rb +9 -11
- data/lib/signauth/signature/base.rb +56 -0
- data/lib/signauth/signature/version_0.rb +36 -0
- data/lib/signauth/signature/version_1.rb +16 -32
- data/lib/signauth/signature/version_2.rb +3 -21
- data/lib/signauth/signature.rb +2 -0
- data/lib/signauth/signer.rb +7 -0
- data/lib/signauth/version.rb +1 -1
- metadata +6 -4
data/README.md
CHANGED
@@ -18,43 +18,7 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Examples
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
```ruby
|
24
|
-
credentials = Signauth::Credentials.new('my_key', 'my_secret')
|
25
|
-
http_method = "GET"
|
26
|
-
host = "localhost"
|
27
|
-
path = "/action"
|
28
|
-
params = { "some" => "param" }
|
29
|
-
sig_version = 1
|
30
|
-
|
31
|
-
req = Signauth::Request.new( http_method, host, path, params, sig_version)
|
32
|
-
req.add_authorization!(credentials)
|
33
|
-
|
34
|
-
p req.params # => {"some"=>"param", "access_key_id"=>"my_key", "signature_version"=>"1", "signature_method"=>"HMAC-SHA-256", "signature"=>"7JNcJhJuzcUAGj6azz2wslmqnVomhDIFXZzpXZR1nkI="}
|
35
|
-
|
36
|
-
HTTParty.get('http://localhost/action', { :query => req.params })
|
37
|
-
```
|
38
|
-
|
39
|
-
Server example (rails)
|
40
|
-
|
41
|
-
```ruby
|
42
|
-
request; # ActionController::Request object
|
43
|
-
|
44
|
-
begin
|
45
|
-
access_key_id = request.query_parameters['access_key_id']
|
46
|
-
credentials = Signauth::Credentials.new(access_key_id, lookup_secret(access_key_id))
|
47
|
-
|
48
|
-
req = Signauth::Request.new(
|
49
|
-
request.request_method,
|
50
|
-
request.domain,
|
51
|
-
request.path,
|
52
|
-
request.query_parameters)
|
53
|
-
req.authenticate(credentials)
|
54
|
-
rescue => e
|
55
|
-
render :status => :unauthorized, :text => "401 UNAUTHORIZED: #{e.message}\n"
|
56
|
-
end
|
57
|
-
```
|
21
|
+
[Examples](https://github.com/arukoh/signauth/wiki/Examples)
|
58
22
|
|
59
23
|
## Contributing
|
60
24
|
|
data/lib/signauth/errors.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Signauth
|
2
2
|
module Errors
|
3
|
+
class MissingSecurityHeader < StandardError; end
|
4
|
+
class MissingAccessKeyId < StandardError; end
|
3
5
|
class SignatureDoesNotMatch < StandardError; end
|
4
6
|
class InvalidTimestamp < StandardError; end
|
5
7
|
class RequestTimeTooSkewed < StandardError; end
|
data/lib/signauth/request.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
module Signauth
|
2
2
|
class Request
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
attr_accessor :method
|
5
|
+
attr_accessor :host
|
6
|
+
attr_accessor :path
|
7
|
+
attr_accessor :params
|
8
|
+
attr_accessor :headers
|
8
9
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
12
|
-
@
|
13
|
-
@host = host
|
14
|
-
@path = path
|
15
|
-
@params = params.dup
|
10
|
+
def initialize(signature_version = 0)
|
11
|
+
extend(Signature.const_get("Version#{signature_version}"))
|
12
|
+
@params = {}
|
13
|
+
@headers = {}
|
16
14
|
end
|
17
15
|
|
18
16
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Signauth
|
2
|
+
module Signature
|
3
|
+
module Base
|
4
|
+
|
5
|
+
protected
|
6
|
+
ISO8601 = "%Y-%m-%dT%H:%M:%SZ" #http://www.w3.org/TR/NOTE-datetime
|
7
|
+
|
8
|
+
def algorhyzhm
|
9
|
+
'HMAC-SHA-256'
|
10
|
+
end
|
11
|
+
|
12
|
+
def signature(credentials)
|
13
|
+
Signer.sign(credentials.secret_access_key, string_to_sign, algorhyzhm)
|
14
|
+
end
|
15
|
+
|
16
|
+
def string_to_sign
|
17
|
+
[
|
18
|
+
method.to_s.upcase,
|
19
|
+
host.to_s.downcase,
|
20
|
+
path.to_s,
|
21
|
+
params.sort.collect { |n, v| encoded(n, v) }.join('&'),
|
22
|
+
].join("\n")
|
23
|
+
end
|
24
|
+
|
25
|
+
def encoded(name, value)
|
26
|
+
"#{URI.escape(name)}=#{URI.escape(value)}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate_timestamp(timestamp, skew)
|
30
|
+
begin
|
31
|
+
time = Time.iso8601(timestamp)
|
32
|
+
rescue => e
|
33
|
+
raise Errors::InvalidTimestamp, "#{e.class}-#{e.message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
current_time = Time.now
|
37
|
+
if (time.to_i - current_time.to_i).abs >= skew
|
38
|
+
raise Errors::RequestTimeTooSkewed,
|
39
|
+
"Timestamp expired: Given timestamp (#{timestamp}) "\
|
40
|
+
"not within #{skew}s of server time (#{current_time.utc.strftime(ISO8601)})"
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_signature(given, computed)
|
46
|
+
unless Signer.slow_string_comparison(given, computed)
|
47
|
+
raise Errors::SignatureDoesNotMatch,
|
48
|
+
"Invalid signature: should have sent "\
|
49
|
+
"Base64(#{algorhyzhm}(secret, #{string_to_sign.inspect}))"\
|
50
|
+
", but given #{given}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Signauth
|
2
|
+
module Signature
|
3
|
+
module Version0
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def add_authorization!(credentials, scheme = 'signauth')
|
7
|
+
access_key_id = credentials.access_key_id
|
8
|
+
signature = signature(credentials)
|
9
|
+
|
10
|
+
headers['Authorization'] = authorization(scheme, access_key_id, signature)
|
11
|
+
end
|
12
|
+
|
13
|
+
def authenticate(&block)
|
14
|
+
raise ArgumentError, "Block required" unless block_given?
|
15
|
+
|
16
|
+
scheme, access_key_id, given = authorization_parts
|
17
|
+
raise Errors::MissingSecurityHeader if scheme.nil? || access_key_id.nil? || given.nil?
|
18
|
+
|
19
|
+
credentials = yield(access_key_id)
|
20
|
+
validate_signature(given, signature(credentials))
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def authorization(scheme, access_key_id, signature)
|
27
|
+
"#{scheme} #{access_key_id}:#{signature}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorization_parts
|
31
|
+
headers['Authorization'].match(/^(\S+) (\S+):(\S+)/)[1, 3]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,28 +1,32 @@
|
|
1
1
|
module Signauth
|
2
2
|
module Signature
|
3
3
|
module Version1
|
4
|
+
include Base
|
4
5
|
|
5
6
|
def add_authorization!(credentials)
|
6
7
|
params['access_key_id'] = credentials.access_key_id
|
7
8
|
params['signature_version'] = version
|
8
|
-
params['signature_method']
|
9
|
+
params['signature_method'] ||= 'HMAC-SHA-256'
|
9
10
|
|
10
11
|
params.delete('signature')
|
11
12
|
params['signature'] = signature(credentials)
|
12
13
|
params
|
13
14
|
end
|
14
15
|
|
15
|
-
def authenticate(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def authenticate(&block)
|
17
|
+
raise ArgumentError, "Block required" unless block_given?
|
18
|
+
|
19
|
+
access_key_id = params['access_key_id']
|
20
|
+
raise Erros::MissingAccessKeyId, 'must provide access_key_id parameter' if access_key_id.nil?
|
21
|
+
|
22
|
+
credentials = yield(access_key_id)
|
23
|
+
begin
|
24
|
+
given = params.delete('signature')
|
25
|
+
validate_signature(given, signature(credentials))
|
26
|
+
ensure
|
27
|
+
params['signature'] = given
|
22
28
|
end
|
23
29
|
true
|
24
|
-
ensure
|
25
|
-
params['signature'] = given
|
26
30
|
end
|
27
31
|
|
28
32
|
protected
|
@@ -31,28 +35,8 @@ module Signauth
|
|
31
35
|
"1"
|
32
36
|
end
|
33
37
|
|
34
|
-
def
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def string_to_sign
|
39
|
-
[
|
40
|
-
method.to_s.upcase,
|
41
|
-
host.to_s.downcase,
|
42
|
-
path.to_s,
|
43
|
-
params.sort.collect { |n, v| encoded(n, v) }.join('&'),
|
44
|
-
].join("\n")
|
45
|
-
end
|
46
|
-
|
47
|
-
def encoded(name, value)
|
48
|
-
"#{URI.escape(name)}=#{URI.escape(value)}"
|
49
|
-
end
|
50
|
-
|
51
|
-
def slow_string_comparison(given, computed)
|
52
|
-
return false if given.nil? || computed.nil? || given.length != computed.length
|
53
|
-
match = true
|
54
|
-
computed.chars.each_with_index{|c, i| match &= c == given[i] }
|
55
|
-
match
|
38
|
+
def algorhyzhm
|
39
|
+
params['signature_method']
|
56
40
|
end
|
57
41
|
|
58
42
|
end
|
@@ -4,18 +4,15 @@ module Signauth
|
|
4
4
|
module Signature
|
5
5
|
module Version2
|
6
6
|
include Version1
|
7
|
-
|
8
|
-
#http://www.w3.org/TR/NOTE-datetime
|
9
|
-
ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
|
10
7
|
|
11
8
|
def add_authorization!(credentials)
|
12
9
|
params['timestamp'] = Time.now.utc.strftime(ISO8601)
|
13
10
|
super
|
14
11
|
end
|
15
12
|
|
16
|
-
def authenticate(
|
17
|
-
validate_timestamp(skew)
|
18
|
-
super(
|
13
|
+
def authenticate(skew = 15*60, &block)
|
14
|
+
validate_timestamp(params['timestamp'], skew)
|
15
|
+
super(&block)
|
19
16
|
end
|
20
17
|
|
21
18
|
protected
|
@@ -24,21 +21,6 @@ module Signauth
|
|
24
21
|
"2"
|
25
22
|
end
|
26
23
|
|
27
|
-
def validate_timestamp(skew)
|
28
|
-
begin
|
29
|
-
timestamp = Time.iso8601(params['timestamp'])
|
30
|
-
rescue => e
|
31
|
-
raise Errors::InvalidTimestamp, "#{e.class}-#{e.message}"
|
32
|
-
end
|
33
|
-
|
34
|
-
if (timestamp.to_i - Time.now.to_i).abs >= skew
|
35
|
-
raise Errors::RequestTimeTooSkewed,
|
36
|
-
"Timestamp expired: Given timestamp (#{timestamp.utc.strftime(ISO8601)}) "\
|
37
|
-
"not within #{skew}s of server time (#{Time.now.utc.strftime(ISO8601)})"
|
38
|
-
end
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
24
|
end
|
43
25
|
end
|
44
26
|
end
|
data/lib/signauth/signature.rb
CHANGED
data/lib/signauth/signer.rb
CHANGED
@@ -14,6 +14,13 @@ module Signauth
|
|
14
14
|
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(digest), key, value)
|
15
15
|
end
|
16
16
|
|
17
|
+
def slow_string_comparison(given, computed)
|
18
|
+
return false if given.nil? || computed.nil? || given.length != computed.length
|
19
|
+
match = true
|
20
|
+
computed.chars.each_with_index{|c, i| match &= c == given[i] }
|
21
|
+
match
|
22
|
+
end
|
23
|
+
|
17
24
|
private
|
18
25
|
|
19
26
|
def digest_name(algorithm)
|
data/lib/signauth/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: signauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -61,6 +61,8 @@ files:
|
|
61
61
|
- lib/signauth/errors.rb
|
62
62
|
- lib/signauth/request.rb
|
63
63
|
- lib/signauth/signature.rb
|
64
|
+
- lib/signauth/signature/base.rb
|
65
|
+
- lib/signauth/signature/version_0.rb
|
64
66
|
- lib/signauth/signature/version_1.rb
|
65
67
|
- lib/signauth/signature/version_2.rb
|
66
68
|
- lib/signauth/signer.rb
|
@@ -82,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
84
|
version: '0'
|
83
85
|
segments:
|
84
86
|
- 0
|
85
|
-
hash:
|
87
|
+
hash: 805445105
|
86
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
89
|
none: false
|
88
90
|
requirements:
|
@@ -91,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
93
|
version: '0'
|
92
94
|
segments:
|
93
95
|
- 0
|
94
|
-
hash:
|
96
|
+
hash: 805445105
|
95
97
|
requirements: []
|
96
98
|
rubyforge_project:
|
97
99
|
rubygems_version: 1.8.24
|