awsnap 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/awsnap.gemspec +23 -0
- data/lib/awsnap.rb +11 -0
- data/lib/awsnap/request.rb +60 -0
- data/lib/awsnap/version.rb +3 -0
- data/readme.md +40 -0
- data/spec/request_spec.rb +86 -0
- data/spec/spec_helper.rb +4 -0
- metadata +87 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/awsnap.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "awsnap/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "awsnap"
|
7
|
+
s.version = Awsnap::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Scott Burton"]
|
10
|
+
s.email = ["scott.burton@chaione.com"]
|
11
|
+
s.homepage = "http://www.chaione.com"
|
12
|
+
s.summary = %q{AWSnap signs your AWS requests for you.}
|
13
|
+
s.description = %q{AWSnap signs your AWS requests for you. No request processing, no response handling, no extra nonsense. Works for all AWS endpoints.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "awsnap"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency("activesupport")
|
23
|
+
end
|
data/lib/awsnap.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
2
|
+
require 'active_support/core_ext/numeric/time'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'uri'
|
5
|
+
require 'hmac-sha2'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module Awsnap
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
autoload :Request
|
11
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Awsnap
|
2
|
+
class Request
|
3
|
+
|
4
|
+
attr_reader :verb, :params, :access_key_id, :secret_access_key
|
5
|
+
|
6
|
+
def initialize(url, verb = :get, params)
|
7
|
+
@verb = verb
|
8
|
+
@url = URI.parse(url)
|
9
|
+
@access_key_id = params.delete(:access_key_id)
|
10
|
+
@secret_access_key = params.delete(:secret_access_key)
|
11
|
+
@expires = params.delete(:expires) || params.delete("Expires")
|
12
|
+
@params = params.stringify_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def canonicalized_params
|
16
|
+
normalized_params.sort.inject([]) {|params, pair| params.push(pair.map {|i| URI.encode(i, reserved_characters)}); params}
|
17
|
+
end
|
18
|
+
|
19
|
+
def params_string
|
20
|
+
canonicalized_params.map{|pair| "#{pair[0]}=#{pair[1]}" }.join("&")
|
21
|
+
end
|
22
|
+
|
23
|
+
def string_to_sign
|
24
|
+
"#{verb.to_s.upcase}\n#{@url.host}\n#{@url.path}\n#{params_with_access_key}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def hmac_signature
|
28
|
+
HMAC::SHA256.digest(secret_access_key, string_to_sign)
|
29
|
+
end
|
30
|
+
|
31
|
+
def signature
|
32
|
+
URI.encode(Base64.encode64(hmac_signature).strip, reserved_characters)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"https://#{@url.host}#{@url.path}?#{params_with_access_key}&Signature=#{signature}"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def normalized_params
|
42
|
+
params.merge!({"Expires" => expires_iso8601, "SignatureVersion" => "2", "SignatureMethod" => "HmacSHA256"})
|
43
|
+
end
|
44
|
+
|
45
|
+
def expires_iso8601
|
46
|
+
return @expires.iso8601 if @expires.respond_to?(:iso8601)
|
47
|
+
return @expires if @expires
|
48
|
+
(Time.now + 15.seconds).iso8601
|
49
|
+
end
|
50
|
+
|
51
|
+
def reserved_characters
|
52
|
+
%r|[^\w\d\-_\.~]+|
|
53
|
+
end
|
54
|
+
|
55
|
+
def params_with_access_key
|
56
|
+
"AWSAccessKeyId=#{access_key_id}&#{params_string}"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
AWSnap!
|
2
|
+
=======
|
3
|
+
Ever felt like AWS libraries do to much? Like they wrap your workflow in lots of request processing, response handling and other gunk? AWSnap cuts to the chase: just give it an AWS API endpoint and some params, and it'll spit out a signed URI. You take it from there.
|
4
|
+
|
5
|
+
Ever felt like AWS libraries don't do enough? They make it easy to do S3, Cloudfront, maybe some SQS. But if you wander off the path, you're on your own. AWSnap has your back: integrate any AWS API on your own, and sign requests with AWSnap.
|
6
|
+
|
7
|
+
Examples
|
8
|
+
--------
|
9
|
+
request = Awsnap::Request.new("https://sqs.us-east-1.amazonaws.com/",
|
10
|
+
:get,
|
11
|
+
{"Action" => "ListQueues",
|
12
|
+
"Version" => "2009-02-01",
|
13
|
+
:access_key_id => <Your-Access-Key-ID>,
|
14
|
+
:secret_access_key => <Your-Secret-Access-Key>,
|
15
|
+
:expires => 10.minutes.from_now}
|
16
|
+
)
|
17
|
+
|
18
|
+
request.to_s
|
19
|
+
#=> "https://sqs.us-east-1.amazonaws.com/?AWSAccessKeyId=Your-Access-Key-ID&Action=ListQueues&Expires=2011-06-15T10%3A21%3A27-07%3A00&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2009-02-01&Signature=CrazySignatureMadnessHere"
|
20
|
+
|
21
|
+
Alternatives
|
22
|
+
------------
|
23
|
+
If you're looking for a library that does a little more, these are great:
|
24
|
+
|
25
|
+
* [aws-s3](https://github.com/marcel/aws-s3)
|
26
|
+
* [RightAWS](https://github.com/rightscale/right_aws)
|
27
|
+
* [Happening](https://github.com/peritor/happening)
|
28
|
+
* [Fog](https://github.com/geemus/fog)
|
29
|
+
|
30
|
+
Copyright
|
31
|
+
---------
|
32
|
+
(The MIT License)
|
33
|
+
|
34
|
+
Copyright © 2011 (Scott Burton)
|
35
|
+
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
37
|
+
|
38
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
39
|
+
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Awsnap
|
4
|
+
|
5
|
+
describe Request do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
time = Time.utc(2012, 7, 15, 13, 27)
|
9
|
+
Time.stub(:now).and_return(time)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "query string parameters" do
|
13
|
+
subject {Request.new("url.example.foo", :get, params)}
|
14
|
+
|
15
|
+
describe "normalizing the expires param" do
|
16
|
+
context "when the expires param is a Ruby Time object" do
|
17
|
+
let(:params) {{:expires => Time.utc(2012, 7, 15, 13, 27), :other => "Params"}}
|
18
|
+
it "converts the date to ISO8601 representation" do
|
19
|
+
subject.send(:expires_iso8601).should eql("2012-07-15T13:27:00Z")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when the expires param is a String" do
|
24
|
+
let(:params) {{:expires => "2012-07-15T13:27:00Z", :other => "Params"}}
|
25
|
+
it "passes the date through" do
|
26
|
+
subject.send(:expires_iso8601).should eql("2012-07-15T13:27:00Z")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when the expires param is missing" do
|
31
|
+
let(:params) {{:other => "Params"}}
|
32
|
+
it "generates an expires param fifteen seconds from now" do
|
33
|
+
subject.send(:expires_iso8601).should eql("2012-07-15T13:27:15Z")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the params contain unreserved characters" do
|
39
|
+
# These unreserved characters are A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), and tilde ( ~ ).
|
40
|
+
let(:params) {{"one-POW" => "two", "Three" => "four.omg~heloooo", "five" => "six~seven~eight"}}
|
41
|
+
|
42
|
+
its(:canonicalized_params) {
|
43
|
+
should eql [["Expires", "2012-07-15T13%3A27%3A15Z"], ["SignatureMethod", "HmacSHA256"], ["SignatureVersion", "2"], ["Three", "four.omg~heloooo"], ["five", "six~seven~eight"], ["one-POW", "two"]]
|
44
|
+
}
|
45
|
+
|
46
|
+
its(:params_string) {
|
47
|
+
should eql "Expires=2012-07-15T13%3A27%3A15Z&SignatureMethod=HmacSHA256&SignatureVersion=2&Three=four.omg~heloooo&five=six~seven~eight&one-POW=two"
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the params contain reserved characters" do
|
52
|
+
let(:params) {{"POW!" => "Frazz!!*", "Bam!" => "Bibim bap (Korean pancake)", "Ammount" => "$21.67"}}
|
53
|
+
|
54
|
+
its(:canonicalized_params) {
|
55
|
+
should eql [["Ammount", "%2421.67"], ["Bam%21", "Bibim%20bap%20%28Korean%20pancake%29"], ["Expires", "2012-07-15T13%3A27%3A15Z"], ["POW%21", "Frazz%21%21%2A"], ["SignatureMethod", "HmacSHA256"], ["SignatureVersion", "2"]]
|
56
|
+
}
|
57
|
+
|
58
|
+
its(:params_string) {
|
59
|
+
should eql "Ammount=%2421.67&Bam%21=Bibim%20bap%20%28Korean%20pancake%29&Expires=2012-07-15T13%3A27%3A15Z&POW%21=Frazz%21%21%2A&SignatureMethod=HmacSHA256&SignatureVersion=2"
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "AWS credentials" do
|
65
|
+
subject {Request.new("some.example.url", :get, params)}
|
66
|
+
let(:params) {{:access_key_id => "1234567890", :secret_access_key => "123456789012", :some => "other", :params => "here"}}
|
67
|
+
its(:access_key_id) {should eql("1234567890")}
|
68
|
+
its(:secret_access_key) {should eql("123456789012")}
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "implicit verb" do
|
72
|
+
subject {Request.new("http://some.example.url", {:some => "params"})}
|
73
|
+
its(:verb) {should be :get}
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "signing" do
|
77
|
+
subject {Request.new(url, verb, params)}
|
78
|
+
let(:verb) {:get}
|
79
|
+
let(:url) {"https://sqs.us-east-1.amazonaws.com/770098461991/queue2"}
|
80
|
+
let(:params) {{:access_key_id => "1234567890", :secret_access_key => "123456789012", "POW!" => "Frazz!!*", "Bam!" => "Bibim bap (Korean pancake)", "Ammount" => "$21.67"}}
|
81
|
+
|
82
|
+
its(:string_to_sign) {should eql %Q{GET\nsqs.us-east-1.amazonaws.com\n/770098461991/queue2\nAWSAccessKeyId=1234567890&Ammount=%2421.67&Bam%21=Bibim%20bap%20%28Korean%20pancake%29&Expires=2012-07-15T13%3A27%3A15Z&POW%21=Frazz%21%21%2A&SignatureMethod=HmacSHA256&SignatureVersion=2}}
|
83
|
+
its(:hmac_signature) {should eql %Q{\xFC\x8E%\xA7\x84[\xA8\xC6\xF4M\x91=\xEF\eZ$m\xB9MPu\xD7\x80\xE6\x97\xE5\x87\xE9\xB9\xD9\x95\xD3}}
|
84
|
+
its(:signature) {should eql %Q{%2FI4lp4RbqMb0TZE97xtaJG25TVB114Dml%2BWH6bnZldM%3D}}
|
85
|
+
end
|
86
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: awsnap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Scott Burton
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-15 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description: AWSnap signs your AWS requests for you. No request processing, no response handling, no extra nonsense. Works for all AWS endpoints.
|
34
|
+
email:
|
35
|
+
- scott.burton@chaione.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Rakefile
|
46
|
+
- awsnap.gemspec
|
47
|
+
- lib/awsnap.rb
|
48
|
+
- lib/awsnap/request.rb
|
49
|
+
- lib/awsnap/version.rb
|
50
|
+
- readme.md
|
51
|
+
- spec/request_spec.rb
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://www.chaione.com
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project: awsnap
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: AWSnap signs your AWS requests for you.
|
85
|
+
test_files:
|
86
|
+
- spec/request_spec.rb
|
87
|
+
- spec/spec_helper.rb
|