jeff 1.5.0 → 2.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.
- checksums.yaml +5 -5
- data/README.md +4 -3
- data/lib/jeff.rb +40 -39
- data/lib/jeff/version.rb +3 -1
- metadata +24 -20
- data/.gitignore +0 -4
- data/.rubocop.yml +0 -2
- data/.travis.yml +0 -6
- data/CHANGELOG.md +0 -20
- data/Gemfile +0 -5
- data/Rakefile +0 -9
- data/jeff.gemspec +0 -24
- data/test/test_jeff.rb +0 -138
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dc9894a6715f02b0f754506e90c496c8d7e1b6cdb946f3c77ce7faa631326753
|
4
|
+
data.tar.gz: 9c6682d0ac05ecf197e63d699a26fa03b511217f99f6c5b27116d94b42a3c09d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee5723d12feee028535333062240988d6784073b31d5da7e523eac3f522dde158da75aeed3ff82b814121b9eda018a5085a9bb0587f250e4d059c69910e378b4
|
7
|
+
data.tar.gz: a4d102f669bdd2cef1ceff5b169a6c2d63582b7314d613ea50538b08df81fcdcc60e69f23db541a395570f6d09fd69b83c1072207a3eff119518a4a003e888ac
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# Jeff
|
2
2
|
|
3
|
-
|
3
|
+
[](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]:
|
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
|
data/lib/jeff.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
#
|
2
|
-
require "excon"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
require "base64"
|
6
|
-
require "openssl"
|
7
|
-
require "time"
|
3
|
+
require 'excon'
|
8
4
|
|
9
|
-
require
|
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
|
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 ||
|
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
|
-
"
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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[
|
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[
|
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 <<-
|
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
|
-
|
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
|
-
|
143
|
+
RUBY
|
148
144
|
end
|
149
145
|
|
150
146
|
private
|
151
147
|
|
152
148
|
def default_connection_params
|
153
149
|
{
|
154
|
-
headers: {
|
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
|
-
|
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
|
-
|
174
|
-
|
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(
|
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
|
-
|
189
|
-
|
188
|
+
.reduce({}) do |qv, (k, v)|
|
189
|
+
v = v.respond_to?(:call) ? instance_exec(&v) : v
|
190
190
|
|
191
|
-
|
192
|
-
|
193
|
-
|
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; #{
|
215
|
+
"Jeff/#{VERSION} (Language=Ruby; #{Socket.gethostname})"
|
215
216
|
end
|
216
217
|
end
|
217
218
|
end
|
data/lib/jeff/version.rb
CHANGED
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:
|
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:
|
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
|
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
|
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: '
|
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
|
-
|
94
|
-
|
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
data/.rubocop.yml
DELETED
data/.travis.yml
DELETED
data/CHANGELOG.md
DELETED
@@ -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
data/Rakefile
DELETED
data/jeff.gemspec
DELETED
@@ -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
|
data/test/test_jeff.rb
DELETED
@@ -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
|