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 +5 -5
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/ruby.yml +37 -0
- data/.rubocop.yml +29 -0
- data/.rubocop_todo.yml +82 -0
- data/CHANGELOG.md +68 -0
- data/Gemfile +7 -2
- data/Rakefile +3 -1
- data/ey-hmac.gemspec +18 -14
- data/lib/ey-hmac/adapter/faraday.rb +40 -22
- data/lib/ey-hmac/adapter/rack.rb +22 -19
- data/lib/ey-hmac/adapter.rb +57 -23
- data/lib/ey-hmac/faraday.rb +8 -5
- data/lib/ey-hmac/rack.rb +4 -1
- data/lib/ey-hmac/version.rb +4 -2
- data/lib/ey-hmac.rb +94 -94
- data/spec/faraday_spec.rb +70 -68
- data/spec/rack_spec.rb +47 -42
- data/spec/shared/authenticated.rb +43 -21
- data/spec/spec_helper.rb +6 -3
- metadata +24 -21
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0395ec2d5516cbf5d39c1490f0f84c9674da117158e4af1de439b98e3a60b03e'
|
4
|
+
data.tar.gz: d58723b34816d4555610989bfe9e6e0a93f75e735b82ca0853966cc9e0b171fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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', '
|
23
|
+
gem 'faraday', '>= 1.3'
|
19
24
|
gem 'faraday_middleware'
|
20
25
|
end
|
data/Rakefile
CHANGED
data/ey-hmac.gemspec
CHANGED
@@ -1,21 +1,25 @@
|
|
1
|
-
#
|
2
|
-
|
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 =
|
7
|
+
gem.name = 'ey-hmac'
|
6
8
|
gem.version = Ey::Hmac::VERSION
|
7
|
-
gem.authors = [
|
8
|
-
gem.email = [
|
9
|
-
gem.description =
|
10
|
-
gem.summary =
|
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 = [
|
17
|
-
gem.license =
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
gem.license = 'MIT'
|
20
|
+
|
21
|
+
gem.required_ruby_version = '>= 2.5'
|
18
22
|
|
19
|
-
gem.add_development_dependency
|
20
|
-
gem.add_development_dependency
|
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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/ey-hmac/adapter/rack.rb
CHANGED
@@ -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 =
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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[
|
30
|
-
request.env[
|
31
|
-
body = request.env[
|
32
|
-
request.env[
|
33
|
-
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']
|
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
|
-
|
48
|
-
|
49
|
-
|
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
|
data/lib/ey-hmac/adapter.rb
CHANGED
@@ -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,
|
7
|
-
autoload :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
|
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 =
|
38
|
-
Base64.
|
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?(
|
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
|
-
|
114
|
-
|
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
|
-
|
118
|
-
signature_value = authorization_match[2]
|
130
|
+
check_ttl!
|
119
131
|
|
120
|
-
|
121
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|