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