signauth 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|