ey-hmac 2.0.2 → 2.3.1

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 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