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 CHANGED
@@ -18,43 +18,7 @@ Or install it yourself as:
18
18
 
19
19
  ## Examples
20
20
 
21
- Client example
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
 
@@ -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
@@ -1,18 +1,16 @@
1
1
  module Signauth
2
2
  class Request
3
3
 
4
- attr_reader :method
5
- attr_reader :host
6
- attr_reader :path
7
- attr_reader :params
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(method, host, path, params, signature_version = 1)
10
- sig_version = params['signature_version'] ||= signature_version
11
- extend(Signature.const_get("Version#{sig_version}"))
12
- @method = method
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'] = 'HMAC-SHA-256'
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(credentials)
16
- given = params.delete('signature')
17
- computed = signature(credentials)
18
- unless slow_string_comparison(given, computed)
19
- raise Errors::SignatureDoesNotMatch,
20
- "Invalid signature: should have sent Base64(HmacSHA256(secret, #{string_to_sign.inspect}))"\
21
- ", but given #{given}"
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 signature(credentials)
35
- Signer.sign(credentials.secret_access_key, string_to_sign, params['signature_method'])
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(credentials, skew = 15*60)
17
- validate_timestamp(skew)
18
- super(credentials)
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
@@ -1,3 +1,5 @@
1
+ require "signauth/signature/base"
2
+ require "signauth/signature/version_0"
1
3
  require "signauth/signature/version_1"
2
4
  require "signauth/signature/version_2"
3
5
 
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Signauth
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
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.3
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-24 00:00:00.000000000 Z
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: -965159027
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: -965159027
96
+ hash: 805445105
95
97
  requirements: []
96
98
  rubyforge_project:
97
99
  rubygems_version: 1.8.24