ey_api_hmac 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/Gemfile.lock +2 -0
- data/README.md +32 -0
- data/ey_api_hmac.gemspec +1 -0
- data/lib/ey_api_hmac/sso.rb +27 -0
- data/lib/ey_api_hmac/version.rb +1 -1
- data/lib/ey_api_hmac.rb +6 -17
- data/spec/sso_spec.rb +21 -10
- metadata +22 -6
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Engine Yard HMAC api implementation
|
2
|
+
|
3
|
+
HMAC basic implementation for Engine Yard services.
|
4
|
+
|
5
|
+
# How to use it
|
6
|
+
|
7
|
+
Server Rack middleware:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
use EY::ApiHMAC::ApiAuth::Server, Consumer
|
11
|
+
```
|
12
|
+
|
13
|
+
Where `Consumer` is a class that responds to find_by_auth_id(auth_id), and returns an object that responds to `id` and `auth_key`.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
use EY::ApiHMAC::ApiAuth::LookupServer do |env, auth_id|
|
17
|
+
#return the appropriate auth_key here
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
this will validate the Authorization header for all requests and raise on failures
|
22
|
+
|
23
|
+
Rack-Client middleware:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
client = Rack::Client.new do
|
27
|
+
use EY::ApiHMAC::ApiAuth::Client, auth_id_arg, auth_key_arg
|
28
|
+
run Rack::Client::Handler::NetHTTP
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
this will add the correct Authorization header to all requests made with this rack-client.
|
data/ey_api_hmac.gemspec
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
module EY
|
2
|
+
module ApiHMAC
|
3
|
+
module SSO
|
4
|
+
|
5
|
+
def self.sign(url, parameters, auth_id, auth_key)
|
6
|
+
uri = URI.parse(url)
|
7
|
+
raise ArgumentError, "use parameters argument, got query: '#{uri.query}'" if uri.query
|
8
|
+
uri.query = parameters.sort.map {|e| e.map{|str| CGI.escape(str.to_s)}.join '='}.join '&'
|
9
|
+
signature = CGI.escape(signature_param(uri.to_s, auth_id, auth_key))
|
10
|
+
uri.query += "&signature=#{signature}"
|
11
|
+
uri.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.authenticated?(url, auth_id, auth_key)
|
15
|
+
uri = URI.parse(url)
|
16
|
+
signature = CGI.unescape(uri.query.match(/&signature=(.*)$/)[1])
|
17
|
+
signed_string = uri.to_s.gsub(/&signature=(.*)$/,"")
|
18
|
+
signature_param(signed_string.to_s, auth_id, auth_key) == signature
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.signature_param(signed_string, auth_id, auth_key)
|
22
|
+
ApiHMAC.auth_string(auth_id, ApiHMAC.base64digest(signed_string, auth_key))
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/ey_api_hmac/version.rb
CHANGED
data/lib/ey_api_hmac.rb
CHANGED
@@ -1,28 +1,13 @@
|
|
1
1
|
require 'ey_api_hmac/base_connection'
|
2
2
|
require 'ey_api_hmac/api_auth'
|
3
|
+
require 'ey_api_hmac/sso'
|
3
4
|
|
4
5
|
module EY
|
5
6
|
module ApiHMAC
|
6
7
|
require 'openssl'
|
7
8
|
|
8
|
-
def self.sign_for_sso(url, parameters, auth_id, auth_key)
|
9
|
-
uri = URI.parse(url)
|
10
|
-
raise ArgumentError, "use parameters argument, got query: '#{uri.query}'" if uri.query
|
11
|
-
uri.query = parameters.sort.map {|e| e.map{|str| CGI.escape(str.to_s)}.join '='}.join '&'
|
12
|
-
signature = CGI.escape(base64digest(uri.query.to_s, auth_key))
|
13
|
-
uri.query += "&signature=#{signature}"
|
14
|
-
uri.to_s
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.verify_for_sso(url, auth_id, auth_key)
|
18
|
-
uri = URI.parse(url)
|
19
|
-
signature = CGI.unescape(uri.query.match(/&signature=(.*)$/)[1])
|
20
|
-
signed_string = uri.query.gsub(/&signature=(.*)$/,"")
|
21
|
-
base64digest(signed_string.to_s, auth_key) == signature
|
22
|
-
end
|
23
|
-
|
24
9
|
def self.sign!(env, key_id, secret, strict = false)
|
25
|
-
env["HTTP_AUTHORIZATION"] =
|
10
|
+
env["HTTP_AUTHORIZATION"] = auth_string(key_id, signature(env, secret, strict))
|
26
11
|
end
|
27
12
|
|
28
13
|
def self.canonical_string(env, strict = false)
|
@@ -45,6 +30,10 @@ module EY
|
|
45
30
|
parts.join("\n")
|
46
31
|
end
|
47
32
|
|
33
|
+
def self.auth_string(key_id, signature)
|
34
|
+
"AuthHMAC #{key_id}:#{signature}"
|
35
|
+
end
|
36
|
+
|
48
37
|
def self.signature(env, secret, strict = false)
|
49
38
|
base64digest(canonical_string(env, strict), secret)
|
50
39
|
end
|
data/spec/sso_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe EY::ApiHMAC do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it "can sign sso calls" do
|
20
|
-
signed_url = EY::ApiHMAC.
|
20
|
+
signed_url = EY::ApiHMAC::SSO.sign(@url, @parameters, @auth_id, @auth_key)
|
21
21
|
uri = URI.parse(signed_url)
|
22
22
|
|
23
23
|
uri.scheme.should eq 'http'
|
@@ -25,13 +25,28 @@ describe EY::ApiHMAC do
|
|
25
25
|
uri.path.should eq '/sign_test'
|
26
26
|
|
27
27
|
parameters = CGI::parse(uri.query)
|
28
|
-
parameters["signature"].first.should eq EY::ApiHMAC.
|
28
|
+
parameters["signature"].first.should eq EY::ApiHMAC::SSO.signature_param(
|
29
|
+
"http://example.com/sign_test?foo=bar&xargs=5&zarg=boot", @auth_id, @auth_key)
|
29
30
|
end
|
30
31
|
|
31
32
|
it "can verify signed requests" do
|
32
|
-
signed_url = EY::ApiHMAC.
|
33
|
-
EY::ApiHMAC.
|
34
|
-
EY::ApiHMAC.
|
33
|
+
signed_url = EY::ApiHMAC::SSO.sign(@url, @parameters, @auth_id, @auth_key)
|
34
|
+
EY::ApiHMAC::SSO.authenticated?(signed_url, @auth_id, @auth_key).should be_true
|
35
|
+
EY::ApiHMAC::SSO.authenticated?(signed_url + 'a', @auth_id, @auth_key).should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "catches changes to the url" do
|
39
|
+
signed_url = EY::ApiHMAC::SSO.sign(@url, @parameters, @auth_id, @auth_key)
|
40
|
+
EY::ApiHMAC::SSO.authenticated?(signed_url, @auth_id, @auth_key).should be_true
|
41
|
+
tampered_url = signed_url.gsub("sign_test", "admin")
|
42
|
+
EY::ApiHMAC::SSO.authenticated?(tampered_url, @auth_id, @auth_key).should be_false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "catches changes to the parameters" do
|
46
|
+
signed_url = EY::ApiHMAC::SSO.sign(@url, @parameters, @auth_id, @auth_key)
|
47
|
+
EY::ApiHMAC::SSO.authenticated?(signed_url, @auth_id, @auth_key).should be_true
|
48
|
+
tampered_url = signed_url.gsub("foo", "fool")
|
49
|
+
EY::ApiHMAC::SSO.authenticated?(tampered_url, @auth_id, @auth_key).should be_false
|
35
50
|
end
|
36
51
|
|
37
52
|
#TODO: write a test that fails if we skip the CGI.unescape
|
@@ -40,13 +55,9 @@ describe EY::ApiHMAC do
|
|
40
55
|
|
41
56
|
#TODO: test that you get an error when you try to sign a url with any of the "Reserved" parameters (signature or timestamp)
|
42
57
|
|
43
|
-
#TODO: send the auth_id in the params too
|
44
|
-
|
45
58
|
#TODO: Rename "signature" to "ey_api_sso_hmac_signature"
|
46
59
|
|
47
|
-
#TODO: provide a time
|
48
|
-
|
49
|
-
#TODO: maybe an expiry time would be better
|
60
|
+
#TODO: provide a timestamp? maybe an expiry time would be better
|
50
61
|
|
51
62
|
#TODO: should the other params be part of the gem?
|
52
63
|
# ey_user_id – the unique identifier for the user.
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey_api_hmac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Jacob Burkhart & Thorben Schr\xC3\xB6der & David Calavera & others"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-16 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
36
|
+
name: json
|
37
37
|
prerelease: false
|
38
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
@@ -44,8 +44,22 @@ dependencies:
|
|
44
44
|
segments:
|
45
45
|
- 0
|
46
46
|
version: "0"
|
47
|
-
type: :
|
47
|
+
type: :runtime
|
48
48
|
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
49
63
|
description: basic wrapper for rack-client + middlewares for HMAC auth + helpers for SSO auth
|
50
64
|
email:
|
51
65
|
- jacob@engineyard.com
|
@@ -59,11 +73,13 @@ files:
|
|
59
73
|
- Gemfile
|
60
74
|
- Gemfile.lock
|
61
75
|
- LICENSE
|
76
|
+
- README.md
|
62
77
|
- Rakefile
|
63
78
|
- ey_api_hmac.gemspec
|
64
79
|
- lib/ey_api_hmac.rb
|
65
80
|
- lib/ey_api_hmac/api_auth.rb
|
66
81
|
- lib/ey_api_hmac/base_connection.rb
|
82
|
+
- lib/ey_api_hmac/sso.rb
|
67
83
|
- lib/ey_api_hmac/version.rb
|
68
84
|
- spec/api_auth_spec.rb
|
69
85
|
- spec/sso_spec.rb
|