http_signature 0.0.2
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 +7 -0
- data/.circleci/config.yml +0 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/README.md +168 -0
- data/Rakefile +8 -0
- data/examples/faraday_middleware.rb +31 -0
- data/examples/rack_middleware.rb +72 -0
- data/http_signature.gemspec +23 -0
- data/lib/http_signature.rb +162 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce69899c93af8245c7a11f207ae55d487ecc360df04c231d5ae1089afe897a6b
|
4
|
+
data.tar.gz: c26e3a5d0a06e97b03354cab9f3e20dc0fee3d06aca01ddd03fb55a7ab9c022e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0b96af0ec445b8b0f298d74e0743035ff2818d85c827892a8366cdf171338b9bf24cd5183b3317de58bcff01e191b60935e0ec4bc84fef7dcf05bc4d6d622666
|
7
|
+
data.tar.gz: 5b709409aa43df9606c5fadfc202721664db25005b7935e5aeb62df7755f22b8fd536f13c5972fb321a361d11e6c9e02ef77e96d553bf7766b9417b40958f6c6
|
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
http-signature (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.10.3)
|
10
|
+
rake (12.2.1)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
bundler
|
17
|
+
http-signature!
|
18
|
+
minitest
|
19
|
+
rake
|
20
|
+
|
21
|
+
BUNDLED WITH
|
22
|
+
1.16.0
|
data/README.md
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# HTTP Signature
|
2
|
+
[](https://circleci.com/gh/bolmaster2/http-signature)
|
3
|
+
|
4
|
+
Create and validate HTTP request signature according to this draft: https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
5
|
+
|
6
|
+
Aims to only implement the creation and validation of the signature without any external dependencies.
|
7
|
+
The idea is to implement adapters to popular http libraries to make it easy to use.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Basic
|
12
|
+
The most basic usage without any extra headers. The default algorithm is `hmac-sha256`.
|
13
|
+
```ruby
|
14
|
+
HTTPSignature.create(
|
15
|
+
url: 'https://example.com/foo',
|
16
|
+
key_id: 'Test',
|
17
|
+
key: 'secret 🙈'
|
18
|
+
)
|
19
|
+
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target)",signature="OQ/dHqRW9vFmrW/RCHg7O2Fqx+3uqxJw81p6k9Rcyo4="'
|
20
|
+
```
|
21
|
+
|
22
|
+
### With headers, query parameters and a body
|
23
|
+
Uses both query parameters (in query string) and a `json` body as a `POST` request.
|
24
|
+
Also shows how to set `rsa-sha256` as algorithm. The `digest` is as you see basically
|
25
|
+
a `sha-256` digest of the request body.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
params = {
|
29
|
+
param: 'value',
|
30
|
+
pet: 'dog'
|
31
|
+
}
|
32
|
+
|
33
|
+
body = '{"hello": "world"}'
|
34
|
+
|
35
|
+
headers = {
|
36
|
+
'date': 'Thu, 05 Jan 2014 21:31:40 GMT',
|
37
|
+
'content-type': 'application/json',
|
38
|
+
'digest': HTTPSignature.create_digest(body),
|
39
|
+
'content-length': body.length
|
40
|
+
}
|
41
|
+
|
42
|
+
HTTPSignature.create(
|
43
|
+
url: 'https://example.com/foo',
|
44
|
+
method: :post,
|
45
|
+
query_string_params: params,
|
46
|
+
headers: headers,
|
47
|
+
key_id: 'Test',
|
48
|
+
algorithm: 'rsa-sha256',
|
49
|
+
key: File.read('key.pem'),
|
50
|
+
body: body
|
51
|
+
)
|
52
|
+
```
|
53
|
+
|
54
|
+
### With digest header auto-added
|
55
|
+
When digest header is omitted it's auto added as last header generated from the `body`:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
body = '{"foo": "bar"}'
|
59
|
+
|
60
|
+
HTTPSignature.create(
|
61
|
+
url: 'https://example.com/foo',
|
62
|
+
key_id: 'Test',
|
63
|
+
key: 'secret 🙈',
|
64
|
+
body: body
|
65
|
+
)
|
66
|
+
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target) digest",signature="3Jm5jnCSKX3fYLd58RqRdafZKeuSbUEPhn7grCGx4vg="'
|
67
|
+
```
|
68
|
+
|
69
|
+
### Validate asymmetric signature
|
70
|
+
With an asymmetric algorithm you can't just recreate the same header and see if they
|
71
|
+
check out, because you need the private key to do that and because the one validating
|
72
|
+
the signature should only have access to the public key, you need to validate it with that.
|
73
|
+
|
74
|
+
Imagine the incoming HTTP request looks like this:
|
75
|
+
```
|
76
|
+
POST /foo HTTP/1.1
|
77
|
+
Host: example.com
|
78
|
+
Date: Thu, 05 Jan 2014 21:31:40 GMT
|
79
|
+
Content-Type: application/json
|
80
|
+
Content-Length: 18
|
81
|
+
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
|
82
|
+
Signature: keyId="Test-1",algorithm="rsa-sha256",headers="(request-target) host date content-type content-length digest",signature="YGPVM1tGHD7CHgTmroy9apLtVazdESzMl4vj1koYHNCMmTEDor4Om5TDZDFaJdny5dF3gq+PQQuPwyknNEvACmSjwVXzljPFxaY/JMZTqAdD0yHTP2Rx0Y/J4GwgKARWTZUmccfVYsXp86PhIlCymzleZzYCzj6shyg9NB7Ht+k="
|
83
|
+
|
84
|
+
{"hello": "world"}
|
85
|
+
```
|
86
|
+
|
87
|
+
Let's assume we have this request ☝️ in a `request` object for the sake of the example:
|
88
|
+
```ruby
|
89
|
+
HTTPSignature.valid?(
|
90
|
+
url: request.url,
|
91
|
+
method: request.method,
|
92
|
+
headers: request.headers,
|
93
|
+
body: request.body,
|
94
|
+
key: OpenSSL::PKey::RSA.new('public_key.pem'),
|
95
|
+
algorithm: 'rsa-sha256'
|
96
|
+
)
|
97
|
+
```
|
98
|
+
|
99
|
+
## Setup
|
100
|
+
```
|
101
|
+
bundle install
|
102
|
+
```
|
103
|
+
|
104
|
+
## Test
|
105
|
+
The tests are written with `minitest` using specs. Run them all with `rake`:
|
106
|
+
```bash
|
107
|
+
rake test
|
108
|
+
```
|
109
|
+
Or a single with pattern matching:
|
110
|
+
```bash
|
111
|
+
rake test TEST=test/http_signature_test.rb TESTOPTS="--name=/appends\ the\ query_string_params/"
|
112
|
+
```
|
113
|
+
|
114
|
+
## Example usage
|
115
|
+
### Faraday middleware on outgoing requests
|
116
|
+
Example of using it on an outgoing request.
|
117
|
+
```ruby
|
118
|
+
# TODO: Move this into gem
|
119
|
+
class AddRequestSignature < Faraday::Middleware
|
120
|
+
def call(env)
|
121
|
+
if env[:body]
|
122
|
+
env[:request_headers].merge!('Digest' => HTTPSignature.create_digest(env[:body]))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Choose which headers to sign
|
126
|
+
headers_filter = %w{ Host Date Digest }
|
127
|
+
headers_to_sign = env[:request_headers].select { |k, v| headers_filter.include?(k.to_s) }
|
128
|
+
|
129
|
+
signature = HTTPSignature.create(
|
130
|
+
url: env[:url],
|
131
|
+
method: env[:method],
|
132
|
+
headers: headers_to_sign,
|
133
|
+
key: ENV.fetch('REQUEST_SIGNATURE_KEY'),
|
134
|
+
key_id: ENV.fetch('REQUEST_SIGNATURE_KEY_ID'),
|
135
|
+
algorithm: 'hmac-sha256',
|
136
|
+
body: env[:body] ? env[:body] : ''
|
137
|
+
)
|
138
|
+
|
139
|
+
env[:request_headers].merge!('Signature' => signature)
|
140
|
+
|
141
|
+
@app.call(env)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Tell faraday to use the middleware. Read more about it here: https://github.com/lostisland/faraday#advanced-middleware-usage
|
146
|
+
Faraday.new('http://example.com') do |faraday|
|
147
|
+
faraday.use(AddRequestSignature)
|
148
|
+
faraday.adapter(Faraday.default_adapter)
|
149
|
+
end
|
150
|
+
|
151
|
+
response = conn.get('/')
|
152
|
+
```
|
153
|
+
|
154
|
+
### Rack middleware
|
155
|
+
I've written a quite sloppy but totally usable rack middleware that validates every incoming request.
|
156
|
+
[See it here](examples/rack_middleware.rb). Soon I'll add it to the gem.
|
157
|
+
|
158
|
+
## License
|
159
|
+
This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
160
|
+
|
161
|
+
## Todo
|
162
|
+
- Structure and add middlewares into gem
|
163
|
+
- Add more example of use with different http libraries
|
164
|
+
- Implement algorithms:
|
165
|
+
- ecdsa-sha256
|
166
|
+
- When creating the signing string, follow the spec exactly:
|
167
|
+
https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3,
|
168
|
+
e.g, concatenate multiple instances of the same headers and remove surrounding whitespaces
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'http_signature'
|
4
|
+
|
5
|
+
class AddRequestSignature < Faraday::Middleware
|
6
|
+
def call(env)
|
7
|
+
if env[:body]
|
8
|
+
env[:request_headers].merge!('Digest' => HTTPSignature.create_digest(env[:body]))
|
9
|
+
end
|
10
|
+
|
11
|
+
# Choose which headers to sign
|
12
|
+
filtered_headers = %w{ Host Date Digest }
|
13
|
+
headers_to_sign = env[:request_headers].select { |k, v| filtered_headers.include?(k.to_s) }
|
14
|
+
|
15
|
+
headers.select { |header| headers_to_sign.includes(header) }.to_h
|
16
|
+
|
17
|
+
signature = HTTPSignature.create(
|
18
|
+
url: env[:url],
|
19
|
+
method: env[:method],
|
20
|
+
headers: headers,
|
21
|
+
key: ENV.fetch('REQUEST_SIGNATURE_KEY'),
|
22
|
+
key_id: ENV.fetch('REQUEST_SIGNATURE_KEY_ID'),
|
23
|
+
algorithm: 'hmac-sha256',
|
24
|
+
body: env[:body] ? env[:body] : ''
|
25
|
+
)
|
26
|
+
|
27
|
+
env[:request_headers].merge!('Signature' => signature)
|
28
|
+
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'http_signature'
|
4
|
+
|
5
|
+
# Rack middleware using http-signature gem to validate signature on every incoming request
|
6
|
+
class ValidateRequestSignature
|
7
|
+
KEY = ENV.fetch('REQUEST_SIGNATURE_KEY')
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
return [401, {}, ['No signature header']] unless request.get_header("HTTP_SIGNATURE")
|
16
|
+
|
17
|
+
request_body = request.body.gets
|
18
|
+
request_headers = parse_request_headers(request)
|
19
|
+
begin
|
20
|
+
parsed_signature = parse_signature(request_headers)
|
21
|
+
rescue
|
22
|
+
return [401, {}, ['Invalid signature :(']]
|
23
|
+
end
|
24
|
+
headers_to_sign = request_headers.select { |k, v| parsed_signature['headers'].include?(k) }
|
25
|
+
|
26
|
+
params = {
|
27
|
+
url: request.path,
|
28
|
+
method: request.request_method,
|
29
|
+
headers: headers_to_sign,
|
30
|
+
key: KEY,
|
31
|
+
key_id: parsed_signature['keyId'],
|
32
|
+
algorithm: parsed_signature['algorithm'],
|
33
|
+
body: request_body ? request_body : '',
|
34
|
+
query_string_params: Rack::Utils.parse_nested_query(request.query_string)
|
35
|
+
}
|
36
|
+
|
37
|
+
valid_signature =
|
38
|
+
if parsed_signature['algorithm'].include?('rsa')
|
39
|
+
HTTPSignature.valid?(**params)
|
40
|
+
else
|
41
|
+
HTTPSignature.create(**params) == request_headers['signature']
|
42
|
+
end
|
43
|
+
|
44
|
+
if valid_signature
|
45
|
+
@app.call(env)
|
46
|
+
else
|
47
|
+
[401, {}, ['Invalid signature :(']]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def parse_request_headers(request)
|
54
|
+
request_headers = {}
|
55
|
+
|
56
|
+
request.each_header do |header|
|
57
|
+
if header[0].include?('HTTP_') && header[0] != 'HTTP_VERSION'
|
58
|
+
request_headers[header[0].gsub('HTTP_', '').gsub("_", "-").downcase] = header[1]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
request_headers
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_signature(request_headers)
|
66
|
+
Rack::Utils.parse_nested_query(
|
67
|
+
request_headers['signature'].gsub(',', '&')
|
68
|
+
).map do |k, v|
|
69
|
+
[k, v.tr('"', '')]
|
70
|
+
end.to_h
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'http_signature'
|
6
|
+
spec.version = '0.0.2'
|
7
|
+
spec.authors = ['Joel Larsson']
|
8
|
+
spec.email = ['bolmaster2@gmail.com']
|
9
|
+
|
10
|
+
spec.summary = 'Create and validate HTTP request signature'
|
11
|
+
spec.description = 'Create and validate HTTP request signature according to this draft: https://tools.ietf.org/html/draft-cavage-http-signatures-08'
|
12
|
+
spec.homepage = 'https://github.com/bolmaster2/http-signature'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = 'exe'
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'minitest'
|
23
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'base64'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module HTTPSignature
|
9
|
+
# Create signature based on the data sent in
|
10
|
+
#
|
11
|
+
# @param url [String] Full request url, can include query string as well
|
12
|
+
# @param query_string_params [Hash] Query string parameters, appends params to
|
13
|
+
# url if query string is already found in it
|
14
|
+
# @param body [String] Request body as a string, i.e., the "raw" request body
|
15
|
+
# @param headers [Hash] Request headers to include in the signature
|
16
|
+
# @param key [String] Key/secret that is used by the corresponding `algorithm`
|
17
|
+
# @param key_id [String] Key id
|
18
|
+
# @param method [Symbol] Request method, default is `:get`
|
19
|
+
# @param algorithm [String] Algorithm to use when signing, check `supported_algorithms` for
|
20
|
+
# @return [String] The signature header value to use in "Signature" header
|
21
|
+
def self.create(url:, query_string_params: {}, body: '', headers: {}, key:,
|
22
|
+
key_id: SecureRandom.hex(8),
|
23
|
+
method: :get,
|
24
|
+
algorithm: 'hmac-sha256'
|
25
|
+
)
|
26
|
+
|
27
|
+
raise 'Unsupported algorithm :(' unless supported_algorithms.include?(algorithm)
|
28
|
+
|
29
|
+
uri = URI(url)
|
30
|
+
path = uri.path
|
31
|
+
headers = add_digest(headers, body)
|
32
|
+
headers = convert_headers(headers)
|
33
|
+
query = create_query_string(uri, query_string_params)
|
34
|
+
|
35
|
+
string_to_sign = create_signing_string(method: method, path: path,
|
36
|
+
query: query, headers: headers)
|
37
|
+
|
38
|
+
signature = sign(string_to_sign, key: key, algorithm: algorithm)
|
39
|
+
create_signature_header(key_id: key_id, headers: headers, signature: signature,
|
40
|
+
algorithm: algorithm)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.sign(string, key:, algorithm:)
|
44
|
+
case algorithm
|
45
|
+
when 'hmac-sha256'
|
46
|
+
OpenSSL::HMAC.digest('SHA256', key, string)
|
47
|
+
when 'hmac-sha512'
|
48
|
+
OpenSSL::HMAC.digest('SHA512', key, string)
|
49
|
+
when 'rsa-sha256'
|
50
|
+
k = OpenSSL::PKey::RSA.new(key)
|
51
|
+
k.sign(OpenSSL::Digest::SHA256.new, string)
|
52
|
+
when 'rsa-sha512'
|
53
|
+
k = OpenSSL::PKey::RSA.new(key)
|
54
|
+
k.sign(OpenSSL::Digest::SHA512.new, string)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.create_signature_header(key_id:, headers: [], signature:, algorithm:)
|
59
|
+
headers = headers.map { |h| h.split(':').first }
|
60
|
+
header_fields = ['(request-target)'].concat(headers).join(' ')
|
61
|
+
|
62
|
+
[
|
63
|
+
"keyId=\"#{key_id}\"",
|
64
|
+
"algorithm=\"#{algorithm}\"",
|
65
|
+
"headers=\"#{header_fields}\"",
|
66
|
+
"signature=\"#{Base64.strict_encode64(signature)}\""
|
67
|
+
].join(',')
|
68
|
+
end
|
69
|
+
|
70
|
+
# TODO: Support them all: rsa-sha1, rsa-sha512, dsa-sha1, hmac-sha1
|
71
|
+
def self.supported_algorithms
|
72
|
+
['hmac-sha256', 'hmac-sha512', 'rsa-sha256', 'rsa-sha512']
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create the digest header based on the request body
|
76
|
+
# @param body [String] Raw request body string
|
77
|
+
# @return [String] SHA256 and base64 digested string with prefix: 'SHA-256='
|
78
|
+
def self.create_digest(body)
|
79
|
+
'SHA-256=' + Digest::SHA256.base64digest(body)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Creates the string to sign
|
83
|
+
# See details here: https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3
|
84
|
+
# TODO: Concatenate multiple instances of the same headers
|
85
|
+
# Also remove leading and trailing whitespace
|
86
|
+
# @return [String]
|
87
|
+
def self.create_signing_string(method:, path:, query:, headers:)
|
88
|
+
[
|
89
|
+
"(request-target): #{method.upcase} #{path}#{query}",
|
90
|
+
].concat(headers).join("\n")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if signature is valid. Using the exact same parameters as .create
|
94
|
+
# minus `key_id`
|
95
|
+
#
|
96
|
+
# @param url [String] Full request url, can include query string as well
|
97
|
+
# @param query_string_params [Hash] Query string parameters, appends params to
|
98
|
+
# url if query string is already found in it
|
99
|
+
# @param body [String] Request body as a string, i.e., the "raw" request body
|
100
|
+
# @param headers [Hash] Request headers to include in the signature
|
101
|
+
# @param key [String] Key/secret that is used by the corresponding `algorithm`
|
102
|
+
# @param method [Symbol] Request method, default is `:get`
|
103
|
+
# @param algorithm [String] Algorithm to use when signing, check `supported_algorithms` for
|
104
|
+
# @return [Boolean] Valid or not, Crypto is kinda binary in this case :)
|
105
|
+
def self.valid?(url:, query_string_params: {}, body: '', headers: {}, key:, method:, algorithm:)
|
106
|
+
raise 'Key needs to be public' unless key.public?
|
107
|
+
|
108
|
+
# TODO: A lot of the code here is exactly as `.create`, i.e., this could be DRYed :point_down:
|
109
|
+
uri = URI(url)
|
110
|
+
path = uri.path
|
111
|
+
signature = headers.delete(:signature)
|
112
|
+
headers = convert_headers(headers)
|
113
|
+
query = create_query_string(uri, query_string_params)
|
114
|
+
|
115
|
+
string_to_sign = create_signing_string(
|
116
|
+
method: method, path: path, query: query, headers: headers
|
117
|
+
)
|
118
|
+
|
119
|
+
key.verify(
|
120
|
+
get_digest(algorithm), get_signature_from_header(signature), string_to_sign
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Maps algoritgm string to digest object
|
125
|
+
# @param algorithm [String]
|
126
|
+
# @return [OpenSSL::Digest] Instance of `OpenSSL::Digest::SHA256` or OpenSSL::Digest::SHA512
|
127
|
+
def self.get_digest(algorithm)
|
128
|
+
{
|
129
|
+
'rsa-sha256' => OpenSSL::Digest::SHA256.new,
|
130
|
+
'rsa-sha512' => OpenSSL::Digest::SHA512.new
|
131
|
+
}[algorithm]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Extract the actual signature from the whole "Signature" header
|
135
|
+
# @param header [String]
|
136
|
+
# @return [String]
|
137
|
+
def self.get_signature_from_header(header)
|
138
|
+
Base64.strict_decode64(header.match(/signature\=\"(.*)\"/)[1])
|
139
|
+
end
|
140
|
+
|
141
|
+
# When query string params is also set on the url, append the params defined
|
142
|
+
# in `query_string_params` and make a joint query string
|
143
|
+
def self.create_query_string(uri, query_string_params)
|
144
|
+
if uri.query || !query_string_params.empty?
|
145
|
+
delimiter = uri.query.nil? ? '' : '&'
|
146
|
+
'?' + (query_string_params.empty? ? '' : [uri.query.to_s, delimiter, URI.encode_www_form(query_string_params)].join)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
# Convert a header hash into an array with header strings
|
150
|
+
# { header: 'value'} -> ['header: value']
|
151
|
+
def self.convert_headers(headers)
|
152
|
+
headers.map do |key, value|
|
153
|
+
[key.to_s.downcase.strip, value.strip].join(': ')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.add_digest(headers, body)
|
158
|
+
headers[:digest] = create_digest(body) unless body.empty?
|
159
|
+
|
160
|
+
headers
|
161
|
+
end
|
162
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http_signature
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joel Larsson
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: 'Create and validate HTTP request signature according to this draft:
|
56
|
+
https://tools.ietf.org/html/draft-cavage-http-signatures-08'
|
57
|
+
email:
|
58
|
+
- bolmaster2@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".circleci/config.yml"
|
64
|
+
- Gemfile
|
65
|
+
- Gemfile.lock
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- examples/faraday_middleware.rb
|
69
|
+
- examples/rack_middleware.rb
|
70
|
+
- http_signature.gemspec
|
71
|
+
- lib/http_signature.rb
|
72
|
+
homepage: https://github.com/bolmaster2/http-signature
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.7.3
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Create and validate HTTP request signature
|
96
|
+
test_files: []
|