dynamodb 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,3 +1,2 @@
1
1
  source "https://rubygems.org"
2
- gem "rake"
3
2
  gemspec
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require "rspec/core/rake_task"
3
3
  RSpec::Core::RakeTask.new(:spec)
4
- task :default => :spec
4
+ task :default => :spec
@@ -13,8 +13,10 @@ Gem::Specification.new do |s|
13
13
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
14
  s.homepage = 'http://github.com/groupme/dynamodb'
15
15
 
16
+ s.add_runtime_dependency 'aws4', '0.0.1'
16
17
  s.add_runtime_dependency 'multi_json', '1.3.7'
17
18
 
19
+ s.add_development_dependency 'rake'
18
20
  s.add_development_dependency 'rspec', '2.8.0'
19
21
  s.add_development_dependency 'webmock', '1.8.11'
20
22
  end
@@ -9,8 +9,6 @@ module DynamoDB
9
9
  class ServerError < BaseError; end
10
10
  class AuthenticationError < BaseError; end
11
11
 
12
- Credentials = Struct.new(:access_key_id, :secret_access_key)
13
-
14
12
  require 'dynamodb/version'
15
13
  require 'dynamodb/connection'
16
14
  require 'dynamodb/http_handler'
@@ -1,3 +1,5 @@
1
+ require "aws4/signer"
2
+
1
3
  module DynamoDB
2
4
  # Establishes a connection to Amazon DynamoDB using credentials.
3
5
  class Connection
@@ -14,42 +16,48 @@ module DynamoDB
14
16
  # Create a connection
15
17
  # uri: # default 'https://dynamodb.us-east-1.amazonaws.com/'
16
18
  # timeout: # default 5 seconds
17
- # api_version: # default
19
+ # api_version: # default
18
20
  #
19
21
  def initialize(opts = {})
20
- if opts[:access_key_id] && opts[:secret_access_key]
21
- @credentials = DynamoDB::Credentials.new(opts[:access_key_id], opts[:secret_access_key])
22
- else
22
+ if !(opts[:access_key_id] && opts[:secret_access_key])
23
23
  raise ArgumentError.new("access_key_id and secret_access_key are required")
24
24
  end
25
25
 
26
- @uri = URI(opts[:uri] || "https://dynamodb.us-east-1.amazonaws.com/")
27
26
  set_timeout(opts[:timeout]) if opts[:timeout]
28
-
27
+ @uri = URI(opts[:uri] || "https://dynamodb.us-east-1.amazonaws.com/")
28
+ region = @uri.host.split(".", 4)[1] || "us-east-1"
29
29
  @api_version = opts[:api_version] || "DynamoDB_20111205"
30
+ @signer = AWS4::Signer.new(
31
+ access_key: opts[:access_key_id],
32
+ secret_key: opts[:secret_access_key],
33
+ region: region
34
+ )
30
35
  end
31
36
 
32
37
  # Create and send a request to DynamoDB
33
38
  #
34
39
  # This returns either a SuccessResponse or a FailureResponse.
35
40
  #
36
- # `operation` can be any DynamoDB operation. `data` is a hash that will be
41
+ # `operation` can be any DynamoDB operation. `body` is a hash that will be
37
42
  # used as the request body (in JSON format). More info available at:
38
43
  # http://docs.amazonwebservices.com/amazondynamodb/latest/developerguide
39
44
  #
40
- def post(operation, data={})
45
+ def post(operation, body={})
41
46
  request = DynamoDB::Request.new(
42
- uri: @uri,
43
- credentials: @credentials,
44
- api_version: @api_version,
45
- operation: operation,
46
- data: data
47
+ signer: @signer,
48
+ uri: @uri,
49
+ operation: version(operation),
50
+ body: body
47
51
  )
48
52
  http_handler.handle(request)
49
53
  end
50
54
 
51
55
  private
52
56
 
57
+ def version(op)
58
+ "#{@api_version}.#{op}"
59
+ end
60
+
53
61
  def http_handler
54
62
  self.class.http_handler
55
63
  end
@@ -1,107 +1,42 @@
1
- require "base64"
2
- require "openssl"
3
-
4
1
  module DynamoDB
5
2
  class Request
6
- class << self
7
- def digest(signing_string, key)
8
- Base64.encode64(
9
- OpenSSL::HMAC.digest('sha256', key, Digest::SHA256.digest(signing_string))
10
- ).strip
11
- end
12
- end
3
+ RFC1123 = "%a, %d %b %Y %H:%M:%S GMT"
4
+ ISO8601 = "%Y%m%dT%H%M%SZ"
13
5
 
14
- attr_reader :uri, :datetime, :credentials, :region, :data, :service, :operation, :api_version
6
+ attr_reader :uri, :operation, :body, :signer
15
7
 
16
8
  def initialize(args = {})
17
- @uri = args[:uri]
18
- @credentials = args[:credentials]
19
- @operation = args[:operation]
20
- @data = args[:data]
21
- @api_version = args[:api_version]
22
- @region = args[:region] || "us-east-1"
23
- @datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
24
- @service = "dynamodb"
25
- end
26
-
27
- def our_headers
28
- {
29
- "user-agent" => "groupme/dynamodb",
30
- "host" => uri.host,
31
- "content-type" => "application/x-amz-json-1.0",
32
- "content-length" => body.size,
33
- "x-amz-date" => datetime,
34
- "x-amz-target" => "#{api_version}.#{operation}",
35
- "x-amz-content-sha256" => hexdigest(body || '')
36
- }
9
+ @uri = args[:uri]
10
+ @operation = args[:operation]
11
+ @body = MultiJson.dump(args[:body])
12
+ @signer = args[:signer]
37
13
  end
38
14
 
39
15
  def headers
40
- @headers ||= our_headers.merge("authorization" => authorization)
41
- end
42
-
43
- def body
44
- @body ||= MultiJson.dump(data)
45
- end
46
-
47
- def authorization
48
- parts = []
49
- parts << "AWS4-HMAC-SHA256 Credential=#{credentials.access_key_id}/#{credential_string}"
50
- parts << "SignedHeaders=#{our_headers.keys.sort.join(";")}"
51
- parts << "Signature=#{signature}"
52
- parts.join(', ')
53
- end
54
-
55
- def signature
56
- k_secret = credentials.secret_access_key
57
- k_date = hmac("AWS4" + k_secret, datetime[0,8])
58
- k_region = hmac(k_date, region)
59
- k_service = hmac(k_region, service)
60
- k_credentials = hmac(k_service, 'aws4_request')
61
- hexhmac(k_credentials, string_to_sign)
62
- end
63
-
64
- def string_to_sign
65
- parts = []
66
- parts << 'AWS4-HMAC-SHA256'
67
- parts << datetime
68
- parts << credential_string
69
- parts << hexdigest(canonical_request)
70
- parts.join("\n")
71
- end
72
-
73
- def credential_string
74
- parts = []
75
- parts << datetime[0,8]
76
- parts << region
77
- parts << service
78
- parts << 'aws4_request'
79
- parts.join("/")
80
- end
81
-
82
- def canonical_request
83
- parts = []
84
- parts << "POST"
85
- parts << uri.path
86
- parts << uri.query
87
- parts << our_headers.sort.map {|k, v| [k,v].join(':')}.join("\n") + "\n"
88
- parts << "content-length;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target"
89
- parts << our_headers['x-amz-content-sha256']
90
- parts.join("\n")
16
+ @headers ||= signed_headers
17
+ end
18
+
19
+ private
20
+
21
+ def signed_headers
22
+ date = Time.now.utc
23
+ h = {
24
+ "Date" => date.strftime(RFC1123),
25
+ "User-Agent" => "groupme/dynamodb",
26
+ "Host" => uri.host,
27
+ "Content-Type" => "application/x-amz-json-1.0",
28
+ "Content-Length" => body.size.to_s,
29
+ "X-AMZ-Date" => date.strftime(ISO8601),
30
+ "X-AMZ-Target" => operation,
31
+ "X-AMZ-Content-SHA256" => hexdigest(body || '')
32
+ }
33
+ signer.sign("POST", uri, h, body)
91
34
  end
92
35
 
93
- def hexdigest value
36
+ def hexdigest(value)
94
37
  digest = Digest::SHA256.new
95
38
  digest.update(value)
96
39
  digest.hexdigest
97
40
  end
98
-
99
- def hmac key, value
100
- OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, value)
101
- end
102
-
103
- def hexhmac key, value
104
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha256'), key, value)
105
- end
106
41
  end
107
42
  end
@@ -1,3 +1,3 @@
1
1
  module DynamoDB
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -1,31 +1,32 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe DynamoDB::Request do
4
- let(:uri) { URI("https://dynamodb.us-east-1.amazonaws.com/") }
5
- let(:credentials) { DynamoDB::Credentials.new("access_key_id", "secret_access_key") }
6
- let(:data) { {} }
7
- let(:request) {
8
- DynamoDB::Request.new(
9
- uri: uri,
10
- api_version: "DynamoDB_20111205",
11
- credentials: credentials,
12
- data: data,
13
- operation: "ListTables",
14
- )
15
- }
16
-
17
4
  it "signs the request" do
18
5
  Time.stub(now: Time.parse("20130508T201304Z"))
19
6
 
7
+ uri = URI("https://dynamodb.us-east-1.amazonaws.com/")
8
+ signer = AWS4::Signer.new(
9
+ access_key: "access_key_id",
10
+ secret_key: "secret_access_key",
11
+ region: "us-east-1"
12
+ )
13
+ request = DynamoDB::Request.new(
14
+ uri: uri,
15
+ signer: signer,
16
+ body: {},
17
+ operation: "DynamoDB_20111205.ListTables",
18
+ )
19
+
20
20
  request.headers.should == {
21
- "content-type"=>"application/x-amz-json-1.0",
22
- "x-amz-target"=>"DynamoDB_20111205.ListTables",
23
- "content-length"=>2,
24
- "user-agent"=>"groupme/dynamodb",
25
- "host"=>"dynamodb.us-east-1.amazonaws.com",
26
- "x-amz-date"=>"20130508T201304Z",
27
- "x-amz-content-sha256"=>"44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
28
- "authorization"=>"AWS4-HMAC-SHA256 Credential=access_key_id/20130508/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target, Signature=52fab5c720b35ce247f8388c5c8f66b300074ae88b666985ee26ca70d923be1d"
21
+ "Content-Type"=>"application/x-amz-json-1.0",
22
+ "Content-Length"=>"2",
23
+ "Date"=>"Wed, 08 May 2013 20:13:04 GMT",
24
+ "User-Agent"=>"groupme/dynamodb",
25
+ "Host"=>"dynamodb.us-east-1.amazonaws.com",
26
+ "X-AMZ-Target"=>"DynamoDB_20111205.ListTables",
27
+ "X-AMZ-Date"=>"20130508T201304Z",
28
+ "X-AMZ-Content-SHA256"=>"44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
29
+ "Authorization"=>"AWS4-HMAC-SHA256 Credential=access_key_id/20130508/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;date;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target, Signature=9e551627237673b4deaa2c22fd3b3777d6d0705facd35b56b289e357c4995c46"
29
30
  }
30
31
  end
31
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamodb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,8 +10,24 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-05-08 00:00:00.000000000 Z
13
+ date: 2013-05-17 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: aws4
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - '='
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - '='
29
+ - !ruby/object:Gem::Version
30
+ version: 0.0.1
15
31
  - !ruby/object:Gem::Dependency
16
32
  name: multi_json
17
33
  requirement: !ruby/object:Gem::Requirement
@@ -28,6 +44,22 @@ dependencies:
28
44
  - - '='
29
45
  - !ruby/object:Gem::Version
30
46
  version: 1.3.7
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
31
63
  - !ruby/object:Gem::Dependency
32
64
  name: rspec
33
65
  requirement: !ruby/object:Gem::Requirement