jeff 1.5.0 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: adbc3ba7741d6e6a2c3189261d378a7cb656c96d
4
- data.tar.gz: d5bd6f51e1f17d0e9b930ef0caef33e1f2f3da97
2
+ SHA256:
3
+ metadata.gz: dc9894a6715f02b0f754506e90c496c8d7e1b6cdb946f3c77ce7faa631326753
4
+ data.tar.gz: 9c6682d0ac05ecf197e63d699a26fa03b511217f99f6c5b27116d94b42a3c09d
5
5
  SHA512:
6
- metadata.gz: 6ac54c0f6fb21fcc6809a84ee58df5f90e1b2ea34b31bb16a2cf33f376b12849e39ac1507475b5f06699cc4b9cd890ff3da935374e2b8f319681a72aaca46282
7
- data.tar.gz: 4cf3385e17a3803ff09d0faed83ea476a6c54da32636162adee8bdb6c82a3316cf6ae64c8f53ea11f11a63222571074fc0c20e85d8a7d1e52ae0bfe5b662c14e
6
+ metadata.gz: ee5723d12feee028535333062240988d6784073b31d5da7e523eac3f522dde158da75aeed3ff82b814121b9eda018a5085a9bb0587f250e4d059c69910e378b4
7
+ data.tar.gz: a4d102f669bdd2cef1ceff5b169a6c2d63582b7314d613ea50538b08df81fcdcc60e69f23db541a395570f6d09fd69b83c1072207a3eff119518a4a003e888ac
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # Jeff
2
2
 
3
- Jeff is a small mixin that [signs requests][signature] to some Amazon Web Services. My gems [Vacuum][vacuum] and [Peddler][peddler] build on Jeff.
3
+ [![Build](https://github.com/hakanensari/jeff/workflows/build/badge.svg)](https://github.com/hakanensari/jeff/actions)
4
+
5
+ Jeff is a small mixin that signs requests to Amazon with their legacy [Signature Version 2][signature]. Currently, my [Peddler][peddler] gem still uses Jeff.
4
6
 
5
7
  <img src="http://cl.ly/XatM/jeff.gif" style="display: inline-block; max-width: 300px; margin: 1em auto;">
6
8
 
7
- [signature]: http://docs.amazonwebservices.com/general/latest/gr/signature-version-2.html
8
- [vacuum]: https://github.com/hakanensari/vacuum
9
+ [signature]: https://docs.amazonwebservices.com/general/latest/gr/signature-version-2.html
9
10
  [peddler]: https://github.com/papercavalier/peddler
@@ -1,12 +1,12 @@
1
- # Jeff's only external dependency.
2
- require "excon"
1
+ # frozen_string_literal: true
3
2
 
4
- # Standard library dependencies.
5
- require "base64"
6
- require "openssl"
7
- require "time"
3
+ require 'excon'
8
4
 
9
- require "jeff/version"
5
+ require 'base64'
6
+ require 'openssl'
7
+ require 'time'
8
+
9
+ require 'jeff/version'
10
10
 
11
11
  # Jeff mixes in client behaviour for Amazon Web Services (AWS) that require
12
12
  # Signature version 2 authentication.
@@ -22,7 +22,7 @@ module Jeff
22
22
  def to_s
23
23
  values
24
24
  .sort { |a, b| a[0].to_s <=> b[0].to_s }
25
- .map { |k, v| "#{k}=#{Utils.escape(v)}" }.join("&")
25
+ .map { |k, v| "#{k}=#{Utils.escape(v)}" }.join('&')
26
26
  end
27
27
  end
28
28
 
@@ -61,7 +61,7 @@ module Jeff
61
61
 
62
62
  # Calculates an RFC 2104-compliant HMAC signature.
63
63
  class Signature
64
- SHA256 = OpenSSL::Digest::SHA256.new
64
+ SHA256 = OpenSSL::Digest.new('SHA256')
65
65
 
66
66
  def initialize(secret)
67
67
  @secret = secret
@@ -72,18 +72,18 @@ module Jeff
72
72
  end
73
73
 
74
74
  def secret
75
- @secret || fail(ArgumentError, "Missing secret")
75
+ @secret || raise(ArgumentError, 'Missing secret')
76
76
  end
77
77
  end
78
78
 
79
79
  # Because Ruby's CGI escapes tilde, use a custom escape.
80
80
  module Utils
81
- UNRESERVED = /([^\w.~-]+)/
81
+ UNRESERVED = /([^\w.~-]+)/.freeze
82
82
 
83
83
  def self.escape(val)
84
84
  val.to_s.gsub(UNRESERVED) do
85
85
  match = Regexp.last_match[1]
86
- "%" + match.unpack("H2" * match.bytesize).join("%").upcase
86
+ "%#{match.unpack('H2' * match.bytesize).join('%')}".upcase
87
87
  end
88
88
  end
89
89
  end
@@ -96,11 +96,13 @@ module Jeff
96
96
  # Add other common parameters using `Jeff.params` if required in your
97
97
  # implementation.
98
98
  base.params(
99
- "AWSAccessKeyId" => -> { aws_access_key_id },
100
- "SignatureVersion" => "2",
101
- "SignatureMethod" => "HmacSHA256",
102
- "Timestamp" => -> { Time.now.utc.iso8601 }
99
+ 'AWSAccessKeyId' => -> { aws_access_key_id },
100
+ 'SignatureVersion' => '2',
101
+ 'SignatureMethod' => 'HmacSHA256',
102
+ 'Timestamp' => -> { Time.now.utc.iso8601 }
103
103
  )
104
+
105
+ super
104
106
  end
105
107
 
106
108
  # A reusable HTTP connection.
@@ -117,11 +119,11 @@ module Jeff
117
119
  attr_writer :aws_access_key_id, :aws_secret_access_key
118
120
 
119
121
  def aws_access_key_id
120
- @aws_access_key_id || ENV["AWS_ACCESS_KEY_ID"]
122
+ @aws_access_key_id || ENV['AWS_ACCESS_KEY_ID']
121
123
  end
122
124
 
123
125
  def aws_secret_access_key
124
- @aws_secret_access_key || ENV["AWS_SECRET_ACCESS_KEY"]
126
+ @aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY']
125
127
  end
126
128
 
127
129
  def proxy=(url)
@@ -130,28 +132,22 @@ module Jeff
130
132
 
131
133
  # Generate HTTP request verb methods.
132
134
  Excon::HTTP_VERBS.each do |method|
133
- eval <<-DEF
135
+ eval <<-RUBY, binding, __FILE__, __LINE__ + 1
134
136
  def #{method}(options = {})
135
137
  options.store(:method, :#{method})
136
138
  add_md5_digest options
137
139
  sign options
138
-
139
- begin
140
- connection.request(options)
141
- rescue Excon::Errors::RequestURITooLong
142
- raise if options[:body] || options[:method] != :post
143
- move_query_to_body options
144
- retry
145
- end
140
+ #{'move_query_to_body options' if method == 'post'}
141
+ connection.request(options)
146
142
  end
147
- DEF
143
+ RUBY
148
144
  end
149
145
 
150
146
  private
151
147
 
152
148
  def default_connection_params
153
149
  {
154
- headers: { "User-Agent" => self.class.user_agent },
150
+ headers: { 'User-Agent' => self.class.user_agent },
155
151
  expects: 200,
156
152
  omit_default_port: true
157
153
  }
@@ -159,8 +155,10 @@ module Jeff
159
155
 
160
156
  def add_md5_digest(options)
161
157
  return unless options.key?(:body)
158
+
162
159
  md5 = Content.new(options[:body]).md5
163
- (options[:headers] ||= {}).store("Content-MD5", md5)
160
+ query = options[:query] ||= {}
161
+ query.store('ContentMD5Value', md5)
164
162
  end
165
163
 
166
164
  def sign(options)
@@ -170,29 +168,32 @@ module Jeff
170
168
 
171
169
  # Generate signature.
172
170
  signature = Signer
173
- .new(options[:method], connection.data[:host], options[:path] || connection.data[:path], query_string)
174
- .sign_with(aws_secret_access_key)
171
+ .new(options[:method], connection.data[:host], options[:path] || connection.data[:path], query_string)
172
+ .sign_with(aws_secret_access_key)
175
173
 
176
174
  # Append escaped signature to query.
177
175
  options.store(:query, "#{query_string}&Signature=#{Utils.escape(signature)}")
178
176
  end
179
177
 
180
178
  def move_query_to_body(options)
179
+ return if options[:body]
180
+
181
181
  options[:headers] ||= {}
182
- options[:headers].store("Content-Type", "application/x-www-form-urlencoded")
182
+ options[:headers].store('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
183
183
  options.store(:body, options.delete(:query))
184
184
  end
185
185
 
186
186
  def default_query_values
187
187
  self.class.params
188
- .reduce({}) do |qv, (k, v)|
189
- v = v.respond_to?(:call) ? instance_exec(&v) : v
188
+ .reduce({}) do |qv, (k, v)|
189
+ v = v.respond_to?(:call) ? instance_exec(&v) : v
190
190
 
191
- # Ignore keys with nil values
192
- v.nil? ? qv : qv.update(k => v)
193
- end
191
+ # Ignore keys with nil values
192
+ v.nil? ? qv : qv.update(k => v)
193
+ end
194
194
  end
195
195
 
196
+ # Defines class-level methods
196
197
  module ClassMethods
197
198
  # Gets/updates default request parameters.
198
199
  def params(hsh = {})
@@ -211,7 +212,7 @@ module Jeff
211
212
  # identify the application, its version number, programming language, and
212
213
  # host.
213
214
  def default_user_agent
214
- "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
215
+ "Jeff/#{VERSION} (Language=Ruby; #{Socket.gethostname})"
215
216
  end
216
217
  end
217
218
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jeff
2
- VERSION = "1.5.0"
4
+ VERSION = '2.0.2'
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hakan Ensari
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-15 00:00:00.000000000 Z
11
+ date: 2020-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.22.1
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.22.1
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: An Amazon Web Services client
56
70
  email:
57
71
  - me@hakanensari.com
@@ -59,23 +73,15 @@ executables: []
59
73
  extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
62
- - ".gitignore"
63
- - ".rubocop.yml"
64
- - ".travis.yml"
65
- - CHANGELOG.md
66
- - Gemfile
67
76
  - LICENSE
68
77
  - README.md
69
- - Rakefile
70
- - jeff.gemspec
71
78
  - lib/jeff.rb
72
79
  - lib/jeff/version.rb
73
- - test/test_jeff.rb
74
80
  homepage: https://github.com/hakanensari/jeff
75
81
  licenses:
76
82
  - MIT
77
83
  metadata: {}
78
- post_install_message:
84
+ post_install_message:
79
85
  rdoc_options: []
80
86
  require_paths:
81
87
  - lib
@@ -83,17 +89,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
89
  requirements:
84
90
  - - ">="
85
91
  - !ruby/object:Gem::Version
86
- version: '1.9'
92
+ version: '2.5'
87
93
  required_rubygems_version: !ruby/object:Gem::Requirement
88
94
  requirements:
89
95
  - - ">="
90
96
  - !ruby/object:Gem::Version
91
97
  version: '0'
92
98
  requirements: []
93
- rubyforge_project:
94
- rubygems_version: 2.4.5
95
- signing_key:
99
+ rubygems_version: 3.1.2
100
+ signing_key:
96
101
  specification_version: 4
97
102
  summary: An AWS client
98
- test_files:
99
- - test/test_jeff.rb
103
+ test_files: []
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- Gemfile.lock
2
- .bundle
3
- bin
4
- pkg
@@ -1,2 +0,0 @@
1
- StringLiterals:
2
- EnforcedStyle: double_quotes
@@ -1,6 +0,0 @@
1
- rvm:
2
- - 1.9.3
3
- - 2.0.0
4
- - 2.1.0
5
- - jruby-19mode
6
- - rbx-2
@@ -1,20 +0,0 @@
1
- # 1.3.0 15/4/2015
2
-
3
- - Handle overly long request queries
4
- See https://github.com/hakanensari/peddler/issues/36
5
-
6
- # 1.2.0 23/1/2015
7
-
8
- - Discard parameters with nil values
9
-
10
- # 1.1.0 15/10/2014
11
-
12
- - Allow customising user agent
13
-
14
- # 1.0.1 18/12/2013
15
-
16
- - Relax Excon dependency version
17
-
18
- # 1.0.0 25/11/2013
19
-
20
- - First stable release
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- source "https://rubygems.org"
2
- gemspec
3
-
4
- gem "jruby-openssl", platform: :jruby
5
- gem "rubysl", platform: :rbx
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new do |t|
5
- t.libs.push "lib"
6
- t.test_files = FileList["test/test_*.rb"]
7
- end
8
-
9
- task default: :test
@@ -1,24 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require File.expand_path("../lib/jeff/version.rb", __FILE__)
4
-
5
- Gem::Specification.new do |gem|
6
- gem.authors = ["Hakan Ensari"]
7
- gem.email = ["me@hakanensari.com"]
8
- gem.description = %q{An Amazon Web Services client}
9
- gem.summary = %q{An AWS client}
10
- gem.homepage = "https://github.com/hakanensari/jeff"
11
- gem.license = "MIT"
12
-
13
- gem.files = `git ls-files`.split($\)
14
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
- gem.name = "jeff"
16
- gem.require_paths = ["lib"]
17
- gem.version = Jeff::VERSION
18
-
19
- gem.add_dependency "excon", ">= 0.22.1"
20
- gem.add_development_dependency "minitest"
21
- gem.add_development_dependency "rake"
22
-
23
- gem.required_ruby_version = ">= 1.9"
24
- end
@@ -1,138 +0,0 @@
1
- require "minitest/autorun"
2
- require "minitest/pride"
3
- require_relative "../lib/jeff"
4
-
5
- class TestJeff < Minitest::Test
6
- def setup
7
- @klass = Class.new { include Jeff }
8
- end
9
-
10
- def test_delegates_unset_aws_credential_to_env_vars
11
- key = "123456"
12
- client = @klass.new
13
- %w(aws_access_key_id aws_secret_access_key).each do |attr|
14
- ENV[attr.upcase] = key
15
- assert_equal key, client.send(attr)
16
- ENV[attr.upcase] = nil
17
- refute_equal key, client.send(attr)
18
- end
19
- end
20
-
21
- def test_has_required_request_query_parameters
22
- %w(AWSAccessKeyId SignatureMethod SignatureVersion Timestamp).each do |key|
23
- assert @klass.params.key?(key)
24
- end
25
- end
26
-
27
- def test_configures_request_query_parameters
28
- @klass.params "Foo" => "bar"
29
- assert @klass.params.key?("Foo")
30
- end
31
-
32
- def test_allows_dynamic_values_for_request_query_parameters
33
- @klass.params "Foo" => -> { bar }
34
- client = @klass.new
35
- def client.bar; "baz"; end
36
- assert client.bar, client.send(:default_query_values).fetch("Foo")
37
- assert_kind_of Proc, @klass.params["Foo"]
38
- end
39
-
40
- def test_discards_request_query_parameters_with_nil_values
41
- @klass.params "Foo" => -> { bar }
42
- client = @klass.new
43
- def client.bar; end
44
- refute client.send(:default_query_values).key?("Foo")
45
- end
46
-
47
- def test_requires_signature
48
- signature = Jeff::Signature.new(nil)
49
- assert_raises(ArgumentError) { signature.sign("foo") }
50
- end
51
-
52
- def test_sorts_request_query_parameters_lexicographically
53
- query = Jeff::Query.new("A10" => 1, "A1" => 1)
54
- assert_equal "A1=1&A10=1", query.to_s
55
- end
56
-
57
- def test_handles_symbol_keys
58
- query = Jeff::Query.new(foo: 1, bar: 2)
59
- assert_equal "bar=2&foo=1", query.to_s
60
- end
61
-
62
- def test_sets_user_agent_header
63
- client = @klass.new
64
- client.aws_endpoint = "http://example.com/"
65
- assert_includes client.connection.data[:headers]["User-Agent"], "Jeff"
66
- end
67
-
68
- def test_allows_customizing_user_agent
69
- @klass.user_agent = "CustomApp/1.0"
70
- client = @klass.new
71
- client.aws_endpoint = "http://example.com/"
72
- assert_equal "CustomApp/1.0", client.connection.data[:headers]["User-Agent"]
73
- end
74
-
75
- def test_does_not_escape_tilde
76
- assert_equal "~%2C", Jeff::Utils.escape("~,")
77
- end
78
- end
79
-
80
- class TestJeffInAction < Minitest::Test
81
- def setup
82
- klass = Class.new { include Jeff }
83
- @client = klass.new
84
- @client.aws_endpoint = "http://example.com/"
85
- @client.aws_access_key_id = "foo"
86
- @client.aws_secret_access_key = "bar"
87
- end
88
-
89
- def teardown
90
- Excon.stubs.clear
91
- end
92
-
93
- Excon::HTTP_VERBS.each do |method|
94
- define_method "test_makes_#{method}_request" do
95
- Excon.stub({}, status: 200)
96
- assert_equal 200, @client.send(method, mock: true).status
97
- end
98
- end
99
-
100
- def test_adds_content_md5_request_header_if_given_a_request_body
101
- Excon.stub({}) do |request_params|
102
- { body: request_params[:headers]["Content-MD5"] }
103
- end
104
- refute_empty @client.post(body: "foo", mock: true).body
105
- end
106
-
107
- def test_moves_query_to_body_if_uri_is_too_long
108
- Excon.stub({}) do |request_params|
109
- { status: request_params[:query] ? 414 : 200 }
110
- end
111
-
112
- res = @client.post(query: { foo: "bar" }, mock: true)
113
- assert_equal 200, res.status
114
-
115
- assert_raises(Excon::Errors::RequestURITooLong) do
116
- @client.post(query: { foo: "bar" }, body: "baz", mock: true)
117
- end
118
-
119
- assert_raises(Excon::Errors::RequestURITooLong) do
120
- @client.get(query: { foo: "bar" }, mock: true)
121
- end
122
- end
123
-
124
- def test_gets_from_an_actual_endpoint
125
- @client.aws_endpoint = "https://mws.amazonservices.com/Sellers/2011-07-01"
126
- res = @client.post(query: { "Action" => "GetServiceStatus" })
127
- assert_equal 200, res.status
128
- end
129
-
130
- def test_has_no_proxy_by_default
131
- refute @client.connection.proxy
132
- end
133
-
134
- def test_sets_proxy
135
- @client.proxy = "http://my.proxy:4321"
136
- assert @client.connection.proxy
137
- end
138
- end