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 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