ey-hmac 2.0.2 → 2.3.1

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: 8fb0c319d78d62b3a2c0dc9f006a806612790afd
4
- data.tar.gz: 394cb290db9772f2846b2643987d1651f20848de
2
+ SHA256:
3
+ metadata.gz: '0395ec2d5516cbf5d39c1490f0f84c9674da117158e4af1de439b98e3a60b03e'
4
+ data.tar.gz: d58723b34816d4555610989bfe9e6e0a93f75e735b82ca0853966cc9e0b171fc
5
5
  SHA512:
6
- metadata.gz: f2a86f65cdfafb683257853c022cfdbfed5aaadd82eb7a602fcc629916553b5db98a2c6a28a85ac06068df176e88ed37fe5e8a0e12fe48bf88e465c1905cfd00
7
- data.tar.gz: 565198e1eb37c1695a9b776d88aa9193d24b64a7b7725bb1310a85db2f8c88196a4bc5d84fc2d90d524a04950d2b4461039ba26f9c5b1e6f02ea23827499444d
6
+ metadata.gz: eae3a563262f394b556c11833269dc222018d54218378911cc6009dc185790cbc658ceb7350be3a89e667e580e28c3b6a0838393768bf5db9d576e55da5103ce
7
+ data.tar.gz: 2a720b806b64471333982dd534687f242f306d3276f653317e049a778b0e1a8830b0f37aabca8f0c260244c52bea6c2354e60ddb1279973404125dae93466d7a
@@ -0,0 +1,70 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ master ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ master ]
20
+ schedule:
21
+ - cron: '36 16 * * 0'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'ruby' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v2
42
+
43
+ # Initializes the CodeQL tools for scanning.
44
+ - name: Initialize CodeQL
45
+ uses: github/codeql-action/init@v1
46
+ with:
47
+ languages: ${{ matrix.language }}
48
+ # If you wish to specify custom queries, you can do so here or in a config file.
49
+ # By default, queries listed here will override any specified in a config file.
50
+ # Prefix the list here with "+" to use these queries and those in the config file.
51
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
+
53
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
+ # If this step fails, then you should remove it and run the build manually (see below)
55
+ - name: Autobuild
56
+ uses: github/codeql-action/autobuild@v1
57
+
58
+ # ℹ️ Command-line programs to run using the OS shell.
59
+ # 📚 https://git.io/JvXDl
60
+
61
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
+ # and modify them (or add more) to build your code if your project
63
+ # uses a compiled language
64
+
65
+ #- run: |
66
+ # make bootstrap
67
+ # make release
68
+
69
+ - name: Perform CodeQL Analysis
70
+ uses: github/codeql-action/analyze@v1
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ['2.5', '2.6', '2.7', '3.0']
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
28
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
29
+ # uses: ruby/setup-ruby@v1
30
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
31
+ with:
32
+ ruby-version: ${{ matrix.ruby-version }}
33
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
34
+ - name: Lint
35
+ run: bundle exec rubocop -D
36
+ - name: Run tests
37
+ run: bundle exec rspec
data/.rubocop.yml ADDED
@@ -0,0 +1,29 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ # The behavior of RuboCop can be controlled via the .rubocop.yml
4
+ # configuration file. It makes it possible to enable/disable
5
+ # certain cops (checks) and to alter their behavior if they accept
6
+ # any parameters. The file can be placed either in your home
7
+ # directory or in some project directory.
8
+ #
9
+ # RuboCop will start looking for the configuration file in the directory
10
+ # where the inspected file is and continue its way up to the root directory.
11
+ #
12
+ # See https://docs.rubocop.org/rubocop/configuration
13
+ AllCops:
14
+ SuggestExtensions: false
15
+ NewCops: 'disable'
16
+ require:
17
+ - rubocop-rspec
18
+ Metrics/BlockLength:
19
+ Enabled: false
20
+ Metrics/AbcSize:
21
+ Enabled: false
22
+ Style/ClassAndModuleChildren:
23
+ EnforcedStyle: compact
24
+ Metrics/MethodLength:
25
+ Enabled: false
26
+ RSpec/ExampleLength:
27
+ Enabled: false
28
+ RSpec/MultipleExpectations:
29
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,82 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
+ # on 2022-02-08 03:20:19 UTC using RuboCop version 1.25.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'ey-hmac.gemspec'
15
+
16
+ # Offense count: 1
17
+ # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
18
+ # CheckDefinitionPathHierarchyRoots: lib, spec, test, src
19
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
20
+ Naming/FileName:
21
+ Exclude:
22
+ - 'lib/ey-hmac.rb'
23
+
24
+ # Offense count: 2
25
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
26
+ # AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to
27
+ Naming/MethodParameterName:
28
+ Exclude:
29
+ - 'lib/ey-hmac/adapter.rb'
30
+
31
+ # Offense count: 2
32
+ RSpec/BeforeAfterAll:
33
+ Exclude:
34
+ - 'spec/spec_helper.rb'
35
+ - 'spec/rails_helper.rb'
36
+ - 'spec/support/**/*.rb'
37
+ - 'spec/faraday_spec.rb'
38
+ - 'spec/rack_spec.rb'
39
+
40
+ # Offense count: 2
41
+ # Configuration parameters: IgnoredMetadata.
42
+ RSpec/DescribeClass:
43
+ Exclude:
44
+ - '**/spec/features/**/*'
45
+ - '**/spec/requests/**/*'
46
+ - '**/spec/routing/**/*'
47
+ - '**/spec/system/**/*'
48
+ - '**/spec/views/**/*'
49
+ - 'spec/faraday_spec.rb'
50
+ - 'spec/rack_spec.rb'
51
+
52
+ # Offense count: 1
53
+ RSpec/LetSetup:
54
+ Exclude:
55
+ - 'spec/faraday_spec.rb'
56
+
57
+ # Offense count: 4
58
+ # Configuration parameters: AllowedConstants.
59
+ Style/Documentation:
60
+ Exclude:
61
+ - 'spec/**/*'
62
+ - 'test/**/*'
63
+ - 'lib/ey-hmac.rb'
64
+ - 'lib/ey-hmac/adapter/faraday.rb'
65
+ - 'lib/ey-hmac/adapter/rack.rb'
66
+ - 'lib/ey-hmac/faraday.rb'
67
+
68
+ # Offense count: 3
69
+ # Configuration parameters: MinBodyLength.
70
+ Style/GuardClause:
71
+ Exclude:
72
+ - 'lib/ey-hmac/adapter.rb'
73
+ - 'lib/ey-hmac/adapter/faraday.rb'
74
+ - 'lib/ey-hmac/adapter/rack.rb'
75
+
76
+ # Offense count: 2
77
+ # Cop supports --auto-correct.
78
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
79
+ # URISchemes: http, https
80
+ Layout/LineLength:
81
+ Exclude:
82
+ - 'lib/ey-hmac/adapter.rb'
data/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Change Log
2
+
3
+ ## [v2.2.0](https://github.com/engineyard/hmac/tree/v2.2.0) (2017-01-09)
4
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v2.1.0...v2.2.0)
5
+
6
+ **Closed issues:**
7
+
8
+ - :sha512 and :sha384 [\#4](https://github.com/engineyard/hmac/issues/4)
9
+
10
+ **Merged pull requests:**
11
+
12
+ - use Base64\#strict\_encode64 when signing requests [\#5](https://github.com/engineyard/hmac/pull/5) ([lanej](https://github.com/lanej))
13
+
14
+ ## [v2.1.0](https://github.com/engineyard/hmac/tree/v2.1.0) (2015-12-03)
15
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v2.0.2...v2.1.0)
16
+
17
+ **Merged pull requests:**
18
+
19
+ - add optional server-side HMAC TTL to prevent replay attacks [\#3](https://github.com/engineyard/hmac/pull/3) ([hudon](https://github.com/hudon))
20
+
21
+ ## [v2.0.2](https://github.com/engineyard/hmac/tree/v2.0.2) (2015-09-17)
22
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v2.0.1...v2.0.2)
23
+
24
+ ## [v2.0.1](https://github.com/engineyard/hmac/tree/v2.0.1) (2015-09-17)
25
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v2.0.0...v2.0.1)
26
+
27
+ **Merged pull requests:**
28
+
29
+ - update faraday usage in the readme [\#2](https://github.com/engineyard/hmac/pull/2) ([svarks](https://github.com/svarks))
30
+
31
+ ## [v2.0.0](https://github.com/engineyard/hmac/tree/v2.0.0) (2014-08-09)
32
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v1.0.0...v2.0.0)
33
+
34
+ ## [v1.0.0](https://github.com/engineyard/hmac/tree/v1.0.0) (2014-04-29)
35
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.1.3...v1.0.0)
36
+
37
+ ## [v0.1.3](https://github.com/engineyard/hmac/tree/v0.1.3) (2014-04-29)
38
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.1.2...v0.1.3)
39
+
40
+ ## [v0.1.2](https://github.com/engineyard/hmac/tree/v0.1.2) (2014-04-01)
41
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.1.1...v0.1.2)
42
+
43
+ **Merged pull requests:**
44
+
45
+ - Fix deprecated usage of Digest::Digest. [\#1](https://github.com/engineyard/hmac/pull/1) ([ericlathrop](https://github.com/ericlathrop))
46
+
47
+ ## [v0.1.1](https://github.com/engineyard/hmac/tree/v0.1.1) (2014-02-19)
48
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.1.0...v0.1.1)
49
+
50
+ ## [v0.1.0](https://github.com/engineyard/hmac/tree/v0.1.0) (2014-02-18)
51
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.0.5...v0.1.0)
52
+
53
+ ## [v0.0.5](https://github.com/engineyard/hmac/tree/v0.0.5) (2013-10-18)
54
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.0.4...v0.0.5)
55
+
56
+ ## [v0.0.4](https://github.com/engineyard/hmac/tree/v0.0.4) (2013-02-08)
57
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.0.3...v0.0.4)
58
+
59
+ ## [v0.0.3](https://github.com/engineyard/hmac/tree/v0.0.3) (2013-02-06)
60
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.0.2...v0.0.3)
61
+
62
+ ## [v0.0.2](https://github.com/engineyard/hmac/tree/v0.0.2) (2013-02-05)
63
+ [Full Changelog](https://github.com/engineyard/hmac/compare/v0.0.1...v0.0.2)
64
+
65
+ ## [v0.0.1](https://github.com/engineyard/hmac/tree/v0.0.1) (2013-02-05)
66
+
67
+
68
+ \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
data/Gemfile CHANGED
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in ey-hmac.gemspec
4
6
  gemspec
5
7
 
8
+ gem 'rubocop', require: false
9
+ gem 'rubocop-rspec', require: false
10
+
6
11
  group(:test) do
7
12
  gem 'pry-nav'
8
13
  gem 'rspec', '~> 3.3'
@@ -10,11 +15,11 @@ end
10
15
 
11
16
  group(:rack) do
12
17
  gem 'rack'
13
- gem 'rack-test'
14
18
  gem 'rack-client'
19
+ gem 'rack-test'
15
20
  end
16
21
 
17
22
  group(:faraday) do
18
- gem 'faraday', '~> 0.9'
23
+ gem 'faraday', '>= 1.3'
19
24
  gem 'faraday_middleware'
20
25
  end
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
data/ey-hmac.gemspec CHANGED
@@ -1,21 +1,25 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/ey-hmac/version', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require File.expand_path('lib/ey-hmac/version', __dir__)
3
5
 
4
6
  Gem::Specification.new do |gem|
5
- gem.name = "ey-hmac"
7
+ gem.name = 'ey-hmac'
6
8
  gem.version = Ey::Hmac::VERSION
7
- gem.authors = ["Josh Lane & Jason Hansen"]
8
- gem.email = ["jlane@engineyard.com"]
9
- gem.description = %q{Lightweight HMAC signing libraries and middleware for Farday and Rack}
10
- gem.summary = %q{Lightweight HMAC signing libraries and middleware for Farday and Rack}
11
- gem.homepage = ""
9
+ gem.authors = ['Josh Lane']
10
+ gem.email = ['me@joshualane.com']
11
+ gem.description = 'Lightweight HMAC signing libraries and middleware for Farday and Rack'
12
+ gem.summary = 'Lightweight HMAC signing libraries and middleware for Farday and Rack'
13
+ gem.homepage = ''
12
14
 
13
- gem.files = `git ls-files`.split($/)
14
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
15
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
- gem.require_paths = ["lib"]
17
- gem.license = "MIT"
18
+ gem.require_paths = ['lib']
19
+ gem.license = 'MIT'
20
+
21
+ gem.required_ruby_version = '>= 2.5'
18
22
 
19
- gem.add_development_dependency "rake"
20
- gem.add_development_dependency "bundler", "~> 1.3"
23
+ gem.add_development_dependency 'bundler', '~> 2.3'
24
+ gem.add_development_dependency 'rake'
21
25
  end
@@ -1,36 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Ey::Hmac::Adapter::Faraday < Ey::Hmac::Adapter
2
4
  def method
3
5
  request[:method].to_s.upcase
4
6
  end
5
7
 
6
8
  def content_type
7
- %w[CONTENT-TYPE CONTENT_TYPE Content-Type Content_Type].inject(nil) { |r,h| r || request[:request_headers][h] }
9
+ @content_type ||= find_header(
10
+ 'CONTENT-TYPE', 'CONTENT_TYPE', 'Content-Type', 'Content_Type'
11
+ )
8
12
  end
9
13
 
10
14
  def content_digest
11
- if existing = %w[CONTENT-DIGEST CONTENT_DIGEST Content-Digest Content_Digest].inject(nil) { |r,h| r || request[:request_headers][h] }
12
- existing
13
- elsif body
14
- digestable = if body.respond_to?(:rewind)
15
- body.rewind
16
- body.read.tap { |_| body.rewind }
17
- else
18
- body.to_s
19
- end
20
-
21
- request[:request_headers]['Content-Digest'] = Digest::MD5.hexdigest(digestable)
15
+ @content_digest ||= find_header(
16
+ 'CONTENT-DIGEST', 'CONTENT_DIGEST', 'Content-Digest', 'Content_Digest'
17
+ )
18
+ end
19
+
20
+ def set_content_digest
21
+ return if content_digest
22
+
23
+ digestable = if body.respond_to?(:rewind)
24
+ body.rewind
25
+ body.read.tap { |_| body.rewind }
26
+ else
27
+ body.to_s
28
+ end
29
+
30
+ if digestable && digestable != ''
31
+ @content_digest = request[:request_headers]['Content-Digest'] = Digest::MD5.hexdigest(digestable)
22
32
  end
23
33
  end
24
34
 
25
35
  def body
26
- if request[:body] && request[:body].to_s != ""
27
- request[:body]
28
- end
36
+ request[:body] if request[:body] && request[:body].to_s != ''
29
37
  end
30
38
 
31
39
  def date
32
- existing = %w[DATE Date].inject(nil) { |r,h| r || request[h] }
33
- existing || (request[:request_headers]['Date'] = Time.now.httpdate)
40
+ find_header('DATE', 'Date')
41
+ end
42
+
43
+ def set_date
44
+ request[:request_headers]['Date'] = Time.now.httpdate unless date
34
45
  end
35
46
 
36
47
  def path
@@ -38,16 +49,23 @@ class Ey::Hmac::Adapter::Faraday < Ey::Hmac::Adapter
38
49
  end
39
50
 
40
51
  def sign!(key_id, key_secret)
41
- %w[CONTENT-TYPE CONTENT_TYPE Content-Type Content_Type].inject(nil) { |r,h| request[:request_headers][h] }
52
+ set_content_digest
53
+ set_date
42
54
 
43
- if options[:version]
44
- request[:request_headers]['X-Signature-Version'] = options[:version]
45
- end
55
+ request[:request_headers]['X-Signature-Version'] = options[:version] if options[:version]
46
56
 
47
57
  request[:request_headers][authorization_header] = authorization(key_id, key_secret)
48
58
  end
49
59
 
50
60
  def authorization_signature
51
- %w[Authorization AUTHORIZATION].inject(nil){|r, h| r || request[:request_headers][h]}
61
+ find_header('Authorization', 'AUTHORIZATION')
62
+ end
63
+
64
+ private
65
+
66
+ def find_header(*keys)
67
+ value = nil
68
+ keys.find { |k| value = request[:request_headers][k] }
69
+ value
52
70
  end
53
71
  end
@@ -1,12 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
 
3
5
  class Ey::Hmac::Adapter::Rack < Ey::Hmac::Adapter
4
6
  def initialize(request, options)
5
7
  super
6
- @request = if request.is_a?(Hash)
7
- ::Rack::Request.new(request)
8
- else request
9
- end
8
+ @request = request.is_a?(Hash) ? ::Rack::Request.new(request) : request
10
9
  end
11
10
 
12
11
  def method
@@ -18,25 +17,28 @@ class Ey::Hmac::Adapter::Rack < Ey::Hmac::Adapter
18
17
  end
19
18
 
20
19
  def content_digest
21
- if existing = request.env['HTTP_CONTENT_DIGEST']
22
- existing
23
- elsif digest = body && Digest::MD5.hexdigest(body)
24
- request.env['HTTP_CONTENT_DIGEST'] = digest
25
- end
20
+ request.env['HTTP_CONTENT_DIGEST']
21
+ end
22
+
23
+ def set_content_digest
24
+ request.env['HTTP_CONTENT_DIGEST'] = Digest::MD5.hexdigest(body) if body
26
25
  end
27
26
 
28
27
  def body
29
- if request.env["rack.input"]
30
- request.env["rack.input"].rewind
31
- body = request.env["rack.input"].read
32
- request.env["rack.input"].rewind
33
- body == "" ? nil : body
34
- else nil
28
+ if request.env['rack.input']
29
+ request.env['rack.input'].rewind
30
+ body = request.env['rack.input'].read
31
+ request.env['rack.input'].rewind
32
+ body == '' ? nil : body
35
33
  end
36
34
  end
37
35
 
38
36
  def date
39
- request.env['HTTP_DATE'] ||= Time.now.httpdate
37
+ request.env['HTTP_DATE']
38
+ end
39
+
40
+ def set_date
41
+ request.env['HTTP_DATE'] = Time.now.httpdate
40
42
  end
41
43
 
42
44
  def path
@@ -44,9 +46,10 @@ class Ey::Hmac::Adapter::Rack < Ey::Hmac::Adapter
44
46
  end
45
47
 
46
48
  def sign!(key_id, key_secret)
47
- if options[:version]
48
- request.env['HTTP_X_SIGNATURE_VERSION'] = options[:version]
49
- end
49
+ set_date
50
+ set_content_digest
51
+
52
+ request.env['HTTP_X_SIGNATURE_VERSION'] = options[:version] if options[:version]
50
53
 
51
54
  request.env["HTTP_#{authorization_header.to_s.upcase}"] = authorization(key_id, key_secret)
52
55
  end
@@ -1,22 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class is responsible for forming the canonical string to used to sign requests
2
4
  # @abstract override methods {#method}, {#path}, {#body}, {#content_type} and {#content_digest}
3
5
  class Ey::Hmac::Adapter
4
- AUTHORIZATION_REGEXP = /\w+ ([^:]+):(.+)$/
6
+ AUTHORIZATION_REGEXP = /\w+ ([^:]+):(.+)$/.freeze
5
7
 
6
- autoload :Rack, "ey-hmac/adapter/rack"
7
- autoload :Faraday, "ey-hmac/adapter/faraday"
8
+ autoload :Rack, 'ey-hmac/adapter/rack'
9
+ autoload :Faraday, 'ey-hmac/adapter/faraday'
8
10
 
9
11
  attr_reader :request, :options, :authorization_header, :service, :sign_with, :accept_digests
10
12
 
11
13
  # @param [Object] request signer-specific request implementation
12
14
  # @option options [Integer] :version signature version
15
+ # @option options [Integer] :ttl (nil) duration during which HMAC is valid after signed date
13
16
  # @option options [String] :authorization_header ('Authorization') Authorization header key.
14
17
  # @option options [String] :server ('EyHmac') service name prefixed to {#authorization}. set to {#service}
15
18
  # @option options [Symbol] :sign_with (:sha_256) outgoing signature digest algorithm. See {OpenSSL::Digest#new}
16
19
  # @option options [Array] :accepted_digests ([:sha_256]) accepted incoming signature digest algorithm. See {OpenSSL::Digest#new}
17
- def initialize(request, options={})
18
- @request, @options = request, options
20
+ def initialize(request, options = {})
21
+ @request = request
22
+ @options = options
19
23
 
24
+ @ttl = options[:ttl]
20
25
  @authorization_header = options[:authorization_header] || 'Authorization'
21
26
  @service = options[:service] || 'EyHmac'
22
27
  @sign_with = options[:sign_with] || :sha256
@@ -34,8 +39,12 @@ class Ey::Hmac::Adapter
34
39
  # @param [String] key_secret private HMAC key
35
40
  # @param [String] signature digest hash function. Defaults to #sign_with
36
41
  # @return [String] HMAC signature of {#request}
37
- def signature(key_secret, digest = self.sign_with)
38
- Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new(digest.to_s), key_secret, canonicalize)).strip
42
+ def signature(key_secret, digest = sign_with)
43
+ Base64.strict_encode64(
44
+ OpenSSL::HMAC.digest(
45
+ OpenSSL::Digest.new(digest.to_s), key_secret, canonicalize
46
+ )
47
+ ).strip
39
48
  end
40
49
 
41
50
  # @param [String] key_id public HMAC key
@@ -102,7 +111,7 @@ class Ey::Hmac::Adapter
102
111
  # @yieldparam key_id [String] public HMAC key
103
112
  # @return [Boolean] true if block yields matching private key and signature matches, else false
104
113
  # @see #authenticated!
105
- def authenticated?(options={}, &block)
114
+ def authenticated?(_options = {}, &block)
106
115
  authenticated!(&block)
107
116
  rescue Ey::Hmac::Error
108
117
  false
@@ -110,35 +119,60 @@ class Ey::Hmac::Adapter
110
119
 
111
120
  # @see Ey::Hmac#authenticate!
112
121
  def authenticated!(&block)
113
- unless authorization_match = AUTHORIZATION_REGEXP.match(authorization_signature)
114
- raise(Ey::Hmac::MissingAuthorization, "Failed to parse authorization_signature #{authorization_signature}")
122
+ key_id, signature_value = check_signature!
123
+ key_secret = block.call(key_id)
124
+
125
+ unless key_secret
126
+ raise Ey::Hmac::MissingSecret,
127
+ "Failed to find secret matching #{key_id.inspect}"
115
128
  end
116
129
 
117
- key_id = authorization_match[1]
118
- signature_value = authorization_match[2]
130
+ check_ttl!
119
131
 
120
- unless key_secret = block.call(key_id)
121
- raise(Ey::Hmac::MissingSecret, "Failed to find secret matching #{key_id.inspect}")
122
- end
132
+ calculated_signatures = accept_digests.map { |ad| signature(key_secret, ad) }
133
+ matching_signature = calculated_signatures.any? { |cs| secure_compare(signature_value, cs) }
123
134
 
124
- calculated_signatures = self.accept_digests.map { |ad| signature(key_secret, ad) }
135
+ raise Ey::Hmac::SignatureMismatch unless matching_signature
125
136
 
126
- unless calculated_signatures.any? { |cs| secure_compare(signature_value, cs) }
127
- raise(Ey::Hmac::SignatureMismatch, "Calculated signature #{signature_value} does not match #{calculated_signatures.inspect} using #{canonicalize.inspect}")
128
- end
129
137
  true
130
138
  end
131
139
  alias authenticate! authenticated!
132
140
 
141
+ protected
142
+
133
143
  # Constant time string comparison.
134
144
  # pulled from https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L399
135
145
  def secure_compare(a, b)
136
146
  return false unless a.bytesize == b.bytesize
137
147
 
138
- l = a.unpack("C*")
148
+ l = a.unpack('C*')
149
+
150
+ r = 0
151
+ i = -1
152
+ b.each_byte { |v| r |= v ^ l[i += 1] }
153
+ r.zero?
154
+ end
155
+
156
+ def check_ttl!
157
+ if @ttl && date
158
+ expiry = Time.parse(date).to_i + @ttl
159
+ current_time = Time.now.to_i
160
+
161
+ unless expiry > current_time
162
+ raise Ey::Hmac::ExpiredHmac,
163
+ "Signature has expired passed #{expiry}. Current time is #{current_time}"
164
+ end
165
+ end
166
+ end
167
+
168
+ def check_signature!
169
+ authorization_match = AUTHORIZATION_REGEXP.match(authorization_signature)
170
+
171
+ unless authorization_match
172
+ raise Ey::Hmac::MissingAuthorization,
173
+ "Failed to parse authorization_signature #{authorization_signature}"
174
+ end
139
175
 
140
- r, i = 0, -1
141
- b.each_byte { |v| r |= v ^ l[i+=1] }
142
- r == 0
176
+ [authorization_match[1], authorization_match[2]]
143
177
  end
144
178
  end