escher 1.0.0 → 2.0.3

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: f5d7892a58786fb9802e66a734ddc2f7f84a4b42
4
- data.tar.gz: 18bf4433e019a4581eb6980f9819f2b336c3495a
2
+ SHA256:
3
+ metadata.gz: e01dd40666cb6ea783bc477f97f8ae673d5208ac8db7a5bba3ef569c95d3061d
4
+ data.tar.gz: 953b4c5dab4d7e8e832bd7bc80204f2d324114f893ae4f31df8c4d8ebf01c189
5
5
  SHA512:
6
- metadata.gz: 006d627eddd82e86ef6e803019bee58183314d85b299a82a62da7e2579b069bd11e247d20d444822dd87acba7276612d19af415f608d5f55ccb88e532bdc36e4
7
- data.tar.gz: 6d7cabbbaba10921751b255dcd9865f9bdc6abb08b1a7114190aac257f168f1118f9ea40b8399ac6502530ae3f04b38ebc7ef43cdc044eedf9f5eee9c4fcc71b
6
+ metadata.gz: 31219982da52121cc942e0bbfe7c18b0d868129c6e55c0cc46be53734d1e6c8886758ed08dba11c7b87234085e5b28c6a073612e2d811633d7196c1b54f688e1
7
+ data.tar.gz: 7c56ef27d72cf2cd80c190dabb973d5355a87eebf8860972217d521c4810086e8d8610f9a49f3d469cbe2cb674182ecb0da90f37db742b5d27e46bb5fb30f3eb
@@ -0,0 +1,33 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby-versions: ['2.6', '2.7', '3.0']
11
+
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - uses: ruby/setup-ruby@v1
15
+ with:
16
+ ruby-version: ${{ matrix.ruby-versions }}
17
+ - name: Checkout testsuite
18
+ run: ./scripts/checkout_test_suite.sh
19
+ - name: Install dependencies
20
+ run: bundle install
21
+ - name: Run tests
22
+ run: bundle exec rake
23
+ - name: Deploy
24
+ if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && matrix.ruby-versions == '3.0'
25
+ run: |
26
+ mkdir -p $HOME/.gem
27
+ touch $HOME/.gem/credentials
28
+ chmod 0600 $HOME/.gem/credentials
29
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
30
+ gem build *.gemspec
31
+ gem push *.gem
32
+ env:
33
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
data/escher.gemspec CHANGED
@@ -20,14 +20,14 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9'
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.6"
23
+ spec.add_development_dependency "bundler", "~> 2.2.20"
24
24
  spec.add_development_dependency "rails"
25
25
 
26
- spec.add_development_dependency "rake", "~> 10"
27
- spec.add_development_dependency "rspec", "~> 2"
26
+ spec.add_development_dependency "rake", "~> 13.0.6"
27
+ spec.add_development_dependency "rspec", "~> 3"
28
28
 
29
29
  spec.add_development_dependency "rack"
30
30
  spec.add_development_dependency "plissken"
31
31
 
32
- spec.add_runtime_dependency "addressable", "~> 2.3"
32
+ spec.add_runtime_dependency "addressable", "~> 2.8.0"
33
33
  end
data/lib/escher/auth.rb CHANGED
@@ -6,7 +6,7 @@ module Escher
6
6
  @algo_prefix = options[:algo_prefix] || 'ESR'
7
7
  @vendor_key = options[:vendor_key] || 'Escher'
8
8
  @hash_algo = options[:hash_algo] || 'SHA256'
9
- @current_time = options[:current_time] || Time.now
9
+ @current_time = options[:current_time]
10
10
  @auth_header_name = options[:auth_header_name] || 'X-Escher-Auth'
11
11
  @date_header_name = options[:date_header_name] || 'X-Escher-Date'
12
12
  @clock_skew = options[:clock_skew] || 300
@@ -17,15 +17,17 @@ module Escher
17
17
 
18
18
 
19
19
  def sign!(req, client, headers_to_sign = [])
20
+ current_time = @current_time || Time.now
21
+
20
22
  headers_to_sign |= [@date_header_name.downcase, 'host']
21
23
 
22
24
  request = wrap_request req
23
25
  raise EscherError, 'The host header is missing' unless request.has_header? 'host'
24
26
 
25
- request.set_header(@date_header_name.downcase, format_date_for_header) unless request.has_header? @date_header_name
27
+ request.set_header(@date_header_name.downcase, format_date_for_header(current_time)) unless request.has_header? @date_header_name
26
28
 
27
- signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values)
28
- request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")
29
+ signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values, current_time)
30
+ request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")
29
31
 
30
32
  request.request
31
33
  end
@@ -44,6 +46,7 @@ module Escher
44
46
 
45
47
 
46
48
  def authenticate(req, key_db, mandatory_signed_headers = nil)
49
+ current_time = @current_time || Time.now
47
50
  request = wrap_request req
48
51
  method = request.method
49
52
  body = request.body
@@ -78,10 +81,9 @@ module Escher
78
81
  raise EscherError, 'Invalid Escher key' unless api_secret
79
82
  raise EscherError, 'Invalid hash algorithm, only SHA256 and SHA512 are allowed' unless %w(SHA256 SHA512).include?(algorithm)
80
83
  raise EscherError, 'The request method is invalid' unless valid_request_method?(method)
81
- raise EscherError, "The request body shouldn't be empty if the request method is POST" if (method.upcase == 'POST' && body.empty?)
82
84
  raise EscherError, "The request url shouldn't contains http or https" if path.match /^https?:\/\//
83
85
  raise EscherError, 'Invalid date in authorization header, it should equal with date header' unless short_date(date) == short_date
84
- raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires)
86
+ raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires, current_time)
85
87
  raise EscherError, 'Invalid Credential Scope' unless credential_scope == @credential_scope
86
88
  raise EscherError, 'The mandatorySignedHeaders parameter must be undefined or array of strings' unless mandatory_signed_headers_valid?(mandatory_signed_headers)
87
89
  raise EscherError, 'The host header is not signed' unless signed_headers.include? 'host'
@@ -94,7 +96,7 @@ module Escher
94
96
  raise EscherError, 'The date header is not signed' if !signature_from_query && !signed_headers.include?(@date_header_name.downcase)
95
97
 
96
98
  escher = reconfig(algorithm, credential_scope, date)
97
- expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
99
+ expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, date)
98
100
  raise EscherError, 'The signatures do not match' unless signature == expected_signature
99
101
  api_key_id
100
102
  end
@@ -116,6 +118,7 @@ module Escher
116
118
 
117
119
 
118
120
  def generate_signed_url(url_to_sign, client, expires = 86400)
121
+ current_time = @current_time || Time.now
119
122
  uri = Addressable::URI.parse(url_to_sign)
120
123
 
121
124
  if (not uri.port.nil?) && (uri.port != uri.default_port)
@@ -137,13 +140,13 @@ module Escher
137
140
  body = 'UNSIGNED-PAYLOAD'
138
141
  query_parts += [
139
142
  ['Algorithm', @algo_id],
140
- ['Credentials', "#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}"],
141
- ['Date', long_date(@current_time)],
143
+ ['Credentials', "#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}"],
144
+ ['Date', long_date(current_time)],
142
145
  ['Expires', expires.to_s],
143
146
  ['SignedHeaders', headers_to_sign.join(';')],
144
147
  ].map { |k, v| query_pair(k, v) }
145
148
 
146
- signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts)
149
+ signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts, current_time)
147
150
  query_parts_with_signature = (query_parts.map { |k, v| [uri_encode(k), uri_encode(v)] } << query_pair('Signature', signature))
148
151
  "#{uri.scheme}://#{host}#{path}?#{query_parts_with_signature.map { |k, v| k + '=' + v }.join('&')}#{(fragment === nil ? '' : '#' + fragment)}"
149
152
  end
@@ -189,11 +192,11 @@ module Escher
189
192
 
190
193
 
191
194
 
192
- def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
195
+ def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, current_time)
193
196
  canonicalized_request = canonicalize(method, path, query_parts, body, headers, signed_headers.uniq)
194
- string_to_sign = get_string_to_sign(canonicalized_request)
197
+ string_to_sign = get_string_to_sign(canonicalized_request, current_time)
195
198
 
196
- signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(@current_time))
199
+ signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(current_time))
197
200
  @credential_scope.split('/').each { |data|
198
201
  signing_key = OpenSSL::HMAC.digest(@algo, signing_key, data)
199
202
  }
@@ -203,8 +206,8 @@ module Escher
203
206
 
204
207
 
205
208
 
206
- def format_date_for_header
207
- @date_header_name.downcase == 'date' ? @current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(@current_time)
209
+ def format_date_for_header(current_time)
210
+ @date_header_name.downcase == 'date' ? current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(current_time)
208
211
  end
209
212
 
210
213
 
@@ -239,11 +242,11 @@ module Escher
239
242
 
240
243
 
241
244
 
242
- def get_string_to_sign(canonicalized_request)
245
+ def get_string_to_sign(canonicalized_request, current_time)
243
246
  [
244
247
  @algo_id,
245
- long_date(@current_time),
246
- short_date(@current_time) + '/' + @credential_scope,
248
+ long_date(current_time),
249
+ short_date(current_time) + '/' + @credential_scope,
247
250
  @algo.new.hexdigest(canonicalized_request)
248
251
  ].join("\n")
249
252
  end
@@ -255,7 +258,7 @@ module Escher
255
258
  when 'SHA256'
256
259
  @algo = OpenSSL::Digest::SHA256.new
257
260
  when 'SHA512'
258
- @algo = OpenSSL::Digest::SHA521.new
261
+ @algo = OpenSSL::Digest::SHA512.new
259
262
  else
260
263
  raise EscherError, 'Unidentified hash algorithm'
261
264
  end
@@ -275,8 +278,8 @@ module Escher
275
278
 
276
279
 
277
280
 
278
- def is_date_within_range?(request_date, expires)
279
- (request_date - @clock_skew .. request_date + expires + @clock_skew).cover? @current_time
281
+ def is_date_within_range?(request_date, expires, current_time)
282
+ (request_date - @clock_skew .. request_date + expires + @clock_skew).cover? current_time
280
283
  end
281
284
 
282
285
 
@@ -1,3 +1,3 @@
1
1
  module Escher
2
- VERSION = '1.0.0'
2
+ VERSION = '2.0.3'
3
3
  end
data/repo-info.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "is_in_production": false,
3
+ "is_scannable": true,
4
+ "is_critical": false,
5
+ "contact": "G-GSUITE-Security@emarsys.com",
6
+ "hosted": null
7
+ }
@@ -35,11 +35,5 @@ module Escher
35
35
  expect(request).to eq(test_case.expected_request)
36
36
  end
37
37
  end
38
-
39
-
40
- xspecify "every case in the test suite is being used" do
41
- expect(::EmarsysTestSuiteHelpers::TestSuite.in_use_size).to eq ::EmarsysTestSuiteHelpers::TestSuite.size
42
- end
43
-
44
38
  end
45
39
  end
@@ -101,7 +101,7 @@ module Escher
101
101
  headers_to_sign = headers.map { |k| k[0].downcase }
102
102
  path, query_parts = escher.parse_uri(request_uri)
103
103
  canonicalized_request = escher.canonicalize(method, path, query_parts, body, headers, headers_to_sign)
104
- string_to_sign = escher.get_string_to_sign(canonicalized_request)
104
+ string_to_sign = escher.get_string_to_sign(canonicalized_request, Time.parse(date))
105
105
  expect(string_to_sign).to eq(fixture(suite, test, 'sts'))
106
106
  end
107
107
  end
@@ -484,7 +484,7 @@ module Escher
484
484
 
485
485
  it 'should convert dates' do
486
486
  date_str = 'Fri, 09 Sep 2011 23:36:00 GMT'
487
- expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header).to eq date_str
487
+ expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header(Time.parse(date_str))).to eq date_str
488
488
  end
489
489
 
490
490
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: escher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andras Barthazi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-23 00:00:00.000000000 Z
11
+ date: 2021-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: 2.2.20
20
20
  type: :development
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: '1.6'
26
+ version: 2.2.20
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10'
47
+ version: 13.0.6
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10'
54
+ version: 13.0.6
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2'
61
+ version: '3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2'
68
+ version: '3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '2.3'
103
+ version: 2.8.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '2.3'
110
+ version: 2.8.0
111
111
  description: Escher helps you creating secure HTTP requests (for APIs) by signing
112
112
  HTTP(s) requests.
113
113
  email:
@@ -116,8 +116,8 @@ executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
+ - ".github/workflows/ruby.yml"
119
120
  - ".gitignore"
120
- - ".travis.yml"
121
121
  - Gemfile
122
122
  - LICENSE
123
123
  - README.md
@@ -135,6 +135,7 @@ files:
135
135
  - lib/escher/request/legacy_request.rb
136
136
  - lib/escher/request/rack_request.rb
137
137
  - lib/escher/version.rb
138
+ - repo-info.json
138
139
  - scripts/checkout_test_suite.sh
139
140
  - spec/aws4_testsuite/get-header-key-duplicate.authz
140
141
  - spec/aws4_testsuite/get-header-key-duplicate.creq
@@ -340,8 +341,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
340
341
  - !ruby/object:Gem::Version
341
342
  version: '0'
342
343
  requirements: []
343
- rubyforge_project:
344
- rubygems_version: 2.6.8
344
+ rubygems_version: 3.2.22
345
345
  signing_key:
346
346
  specification_version: 4
347
347
  summary: Library for HTTP request signing (Ruby implementation)
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.3
4
- - 2.3.0
5
- before_script: ./scripts/checkout_test_suite.sh