net-http-signature 1.0.0
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/lib/net-http-signature.rb +1 -0
- data/lib/net/http/signature.rb +45 -0
- data/lib/net/http/signature/request.rb +36 -0
- data/lib/net/http/signature/signer.rb +47 -0
- data/lib/net/http/signature/version.rb +7 -0
- data/spec/lib/net/http/signature/request_spec.rb +52 -0
- data/spec/lib/net/http/signature/signer_spec.rb +68 -0
- data/spec/lib/net/http/signature/version_spec.rb +0 -0
- data/spec/lib/net/http/signature_spec.rb +124 -0
- data/spec/spec_helper.rb +3 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 791d4891ed58fa87c4b08c571899d6aeba4bf48a
|
4
|
+
data.tar.gz: 00f3c6d94312b5bd6db8312c5091e0d54c0dbbf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a24a2b2ce36768bc0fd3d9dffa5859bdecd1d2541411834278eba26ef231b92da015b5475a45865057c2619404efaae8280de3187f191954435933459ee28906
|
7
|
+
data.tar.gz: eafde53865e3469a14256af39513f7ee1363a93f2cea5750cade230df65ada2ff991042e619f53ea5c8e3fb06a1854688524fa633c7a88f72bbc062320efe382
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "net/http/signature"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "openssl"
|
3
|
+
|
4
|
+
module Net
|
5
|
+
class HTTP
|
6
|
+
class Signature
|
7
|
+
require_relative "signature/request"
|
8
|
+
require_relative "signature/signer"
|
9
|
+
require_relative "signature/version"
|
10
|
+
|
11
|
+
HEADER = "Signature"
|
12
|
+
|
13
|
+
def initialize(key:, signer:)
|
14
|
+
@key = key
|
15
|
+
@signer = signer
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?(string)
|
19
|
+
to_s == string
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"key=#{key} algorithm=#{algorithm} token=#{signer}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
{
|
28
|
+
HEADER => to_s
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
private def key
|
33
|
+
@key
|
34
|
+
end
|
35
|
+
|
36
|
+
private def algorithm
|
37
|
+
@signer.algorithm
|
38
|
+
end
|
39
|
+
|
40
|
+
private def signer
|
41
|
+
@signer
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Net
|
2
|
+
class HTTP
|
3
|
+
class Signature
|
4
|
+
class Request
|
5
|
+
def initialize(verb:, uri:, headers:, body:)
|
6
|
+
@verb = verb
|
7
|
+
@uri = uri
|
8
|
+
@headers = headers
|
9
|
+
@body = body
|
10
|
+
|
11
|
+
fail ArgumentError, "Date header required" unless @headers.key?("Date")
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{verb} #{uri} #{headers} #{body}"
|
16
|
+
end
|
17
|
+
|
18
|
+
private def headers
|
19
|
+
@headers.to_a.sort_by(&:first).map { |(key, value)| "#{key}: #{value}" }.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
private def verb
|
23
|
+
@verb.upcase
|
24
|
+
end
|
25
|
+
|
26
|
+
private def uri
|
27
|
+
@uri
|
28
|
+
end
|
29
|
+
|
30
|
+
private def body
|
31
|
+
@body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Net
|
2
|
+
class HTTP
|
3
|
+
class Signature
|
4
|
+
class Signer
|
5
|
+
def initialize(request:, algorithm:, secret:)
|
6
|
+
@request = request
|
7
|
+
@algorithm = algorithm
|
8
|
+
@secret = secret
|
9
|
+
|
10
|
+
unless @algorithm =~ /.+-.+/
|
11
|
+
fail ArgumentError, "Invalid algorithm format: {{key}}-{{digester}}"
|
12
|
+
end
|
13
|
+
|
14
|
+
unless key
|
15
|
+
fail ArgumentError, "Crypto scheme not supported"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def algorithm
|
20
|
+
@algorithm
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
Base64.encode64(key.update(request).digest)
|
25
|
+
end
|
26
|
+
|
27
|
+
private def secret
|
28
|
+
@secret
|
29
|
+
end
|
30
|
+
|
31
|
+
private def request
|
32
|
+
@request.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
private def key
|
36
|
+
case algorithm.split("-").first
|
37
|
+
when "hmac" then OpenSSL::HMAC.new(secret, digester)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private def digester
|
42
|
+
algorithm.split("-").last.upcase
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Net::HTTP::Signature::Request do
|
4
|
+
let(:verb) { "GET" }
|
5
|
+
let(:uri) { "http://api.example.com" }
|
6
|
+
let(:headers) do
|
7
|
+
{
|
8
|
+
"Host" => "api.example.com",
|
9
|
+
"Date" => "2014-10-13 00:48:28 -0500",
|
10
|
+
"Content-Type" => "application/json",
|
11
|
+
"Accept" => "application/json",
|
12
|
+
"Authentication" => "Bearer fas425fmig.idfiodf"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
let(:body) { "examplebody" }
|
16
|
+
let(:request) { described_class.new(verb: verb, uri: uri, headers: headers, body: body) }
|
17
|
+
|
18
|
+
context "with an missing Date header" do
|
19
|
+
let(:headers) do
|
20
|
+
{
|
21
|
+
"Host" => "api.example.com",
|
22
|
+
"Content-Type" => "application/json",
|
23
|
+
"Accept" => "application/json",
|
24
|
+
"Authentication" => "Bearer fas425fmig.idfiodf"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises an error" do
|
29
|
+
expect { request }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#to_s" do
|
34
|
+
let(:to_s) { request.to_s }
|
35
|
+
|
36
|
+
it "includes the verb" do
|
37
|
+
expect(to_s).to include(verb)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "includes the uri" do
|
41
|
+
expect(to_s).to include(uri)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes the headers" do
|
45
|
+
expect(to_s).to include(headers.to_a.sort_by(&:first).map { |(key, value)| "#{key}: #{value}" }.join("\n"))
|
46
|
+
end
|
47
|
+
|
48
|
+
it "includes the body" do
|
49
|
+
expect(to_s).to include(body)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Net::HTTP::Signature::Signer do
|
4
|
+
let(:verb) { "GET" }
|
5
|
+
let(:uri) { "http://api.example.com" }
|
6
|
+
let(:headers) do
|
7
|
+
{
|
8
|
+
"Host" => "api.example.com",
|
9
|
+
"Date" => "2014-10-13 00:48:28 -0500",
|
10
|
+
"Content-Type" => "application/json",
|
11
|
+
"Accept" => "application/json",
|
12
|
+
"Authentication" => "Bearer fas425fmig.idfiodf"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
let(:body) { "examplebody" }
|
16
|
+
let(:request) { Net::HTTP::Signature::Request.new(verb: verb, uri: uri, headers: headers, body: body) }
|
17
|
+
let(:digester) { "hmac" }
|
18
|
+
let(:sha) { "sha512" }
|
19
|
+
let(:algorithm_name) { "#{digester}-#{sha}" }
|
20
|
+
let(:secret) { "foo" }
|
21
|
+
let(:signer) { described_class.new(request: request, algorithm: algorithm_name, secret: secret) }
|
22
|
+
|
23
|
+
context "with an invalid digester" do
|
24
|
+
let(:algorithm_name) { "example-sha512" }
|
25
|
+
|
26
|
+
it "raises an error" do
|
27
|
+
expect { signer }.to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with an invalid sha" do
|
32
|
+
let(:algorithm_name) { "hmac-sha666" }
|
33
|
+
|
34
|
+
it "raises an error" do
|
35
|
+
expect { signer }.to raise_error(RuntimeError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with an invalid algorithm name" do
|
40
|
+
let(:algorithm_name) { "hmac sha512" }
|
41
|
+
|
42
|
+
it "raises an error" do
|
43
|
+
expect { signer }.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#algorithm" do
|
48
|
+
let(:algorithm) { signer.algorithm }
|
49
|
+
|
50
|
+
it "includes the digester" do
|
51
|
+
expect(algorithm).to include(digester)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "includes the sha" do
|
55
|
+
expect(algorithm).to include(sha)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#to_s" do
|
60
|
+
let(:to_s) { signer.to_s }
|
61
|
+
let(:signature) { OpenSSL.const_get(digester.upcase).digest(sha, secret, request.to_s) }
|
62
|
+
let(:encoded_signature) { Base64.encode64(signature) }
|
63
|
+
|
64
|
+
it "includes the base64'd signature token" do
|
65
|
+
expect(to_s).to eq(encoded_signature)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
File without changes
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Net::HTTP::Signature do
|
4
|
+
let(:verb) { "GET" }
|
5
|
+
let(:uri) { "http://api.example.com" }
|
6
|
+
let(:headers) do
|
7
|
+
{
|
8
|
+
"Host" => "api.example.com",
|
9
|
+
"Date" => "2014-10-13 00:48:28 -0500",
|
10
|
+
"Content-Type" => "application/json",
|
11
|
+
"Accept" => "application/json",
|
12
|
+
"Authentication" => "Bearer fas425fmig.idfiodf"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
let(:body) { "" }
|
16
|
+
let(:secret) { "examplesecret" }
|
17
|
+
let(:algorithm) { "hmac-sha512" }
|
18
|
+
let(:request) { Net::HTTP::Signature::Request.new(verb: verb, uri: uri, headers: headers, body: body) }
|
19
|
+
let(:signer) { Net::HTTP::Signature::Signer.new(request: request, algorithm: algorithm, secret: secret) }
|
20
|
+
let(:key) { "examplekey" }
|
21
|
+
let(:signature) { described_class.new(signer: signer, key: key) }
|
22
|
+
|
23
|
+
let(:comparison_verb) { verb }
|
24
|
+
let(:comparison_uri) { uri }
|
25
|
+
let(:comparison_headers) { headers }
|
26
|
+
let(:comparison_body) { body }
|
27
|
+
let(:comparison_secret) { secret }
|
28
|
+
let(:comparison_algorithm) { algorithm }
|
29
|
+
let(:comparison_request) { Net::HTTP::Signature::Request.new(verb: comparison_verb, uri: comparison_uri, headers: comparison_headers, body: comparison_body) }
|
30
|
+
let(:comparison_signer) { Net::HTTP::Signature::Signer.new(request: comparison_request, algorithm: comparison_algorithm, secret: comparison_secret) }
|
31
|
+
let(:comparison_key) { key }
|
32
|
+
let(:comparison_signature) { described_class.new(signer: comparison_signer, key: comparison_key) }
|
33
|
+
let(:comparison) { comparison_signature.to_h["Signature"] }
|
34
|
+
|
35
|
+
describe "#valid?" do
|
36
|
+
let(:valid?) { signature.valid?(comparison) }
|
37
|
+
|
38
|
+
context "with a valid secret" do
|
39
|
+
it "returns true" do
|
40
|
+
expect(valid?).to be(true)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with an non-same secret" do
|
45
|
+
let(:comparison_secret) { "nonexamplesecret" }
|
46
|
+
|
47
|
+
it "returns false" do
|
48
|
+
expect(valid?).to be(false)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with an non-same verb" do
|
53
|
+
let(:comparison_verb) { "POST" }
|
54
|
+
|
55
|
+
it "returns false" do
|
56
|
+
expect(valid?).to be(false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with non-same body" do
|
61
|
+
let(:comparison_body) { "f" }
|
62
|
+
|
63
|
+
it "returns false" do
|
64
|
+
expect(valid?).to be(false)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with non-same headers" do
|
69
|
+
let(:comparison_headers) do
|
70
|
+
{
|
71
|
+
"Date" => "2014-10-13 00:48:20 -0500"
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns false" do
|
76
|
+
expect(valid?).to be(false)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with non-same uri" do
|
81
|
+
let(:comparison_uri) { "http://aii.example.com" }
|
82
|
+
|
83
|
+
it "returns false" do
|
84
|
+
expect(valid?).to be(false)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with an invalid string" do
|
89
|
+
let(:comparison) { "foobar" }
|
90
|
+
|
91
|
+
it "returns false" do
|
92
|
+
expect(valid?).to be(false)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#to_s" do
|
98
|
+
let(:to_s) { signature.to_s }
|
99
|
+
|
100
|
+
it "includes the algorithm" do
|
101
|
+
expect(to_s).to include("algorithm=#{algorithm}")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "includes the key" do
|
105
|
+
expect(to_s).to include("key=#{key}")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "includes the token" do
|
109
|
+
expect(to_s).to include("token=#{signer}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#to_h" do
|
114
|
+
let(:to_h) { signature.to_h }
|
115
|
+
|
116
|
+
it "includes the Signature key" do
|
117
|
+
expect(to_h.keys).to include("Signature")
|
118
|
+
end
|
119
|
+
|
120
|
+
it "includes the string form as a value to Signature key" do
|
121
|
+
expect(to_h["Signature"]).to eq(signature.to_s)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: net-http-signature
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kurtis Rainbolt-Greene
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-14 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: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-doc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.6'
|
83
|
+
description: A signing library for HTTP requests
|
84
|
+
email:
|
85
|
+
- me@kurtisrainboltgreene.name
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/net-http-signature.rb
|
91
|
+
- lib/net/http/signature.rb
|
92
|
+
- lib/net/http/signature/request.rb
|
93
|
+
- lib/net/http/signature/signer.rb
|
94
|
+
- lib/net/http/signature/version.rb
|
95
|
+
- spec/lib/net/http/signature/request_spec.rb
|
96
|
+
- spec/lib/net/http/signature/signer_spec.rb
|
97
|
+
- spec/lib/net/http/signature/version_spec.rb
|
98
|
+
- spec/lib/net/http/signature_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
homepage: http://krainboltgreene.github.io/net-http-signature
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.2.2
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: A signing library for HTTP requests
|
124
|
+
test_files:
|
125
|
+
- spec/lib/net/http/signature/request_spec.rb
|
126
|
+
- spec/lib/net/http/signature/signer_spec.rb
|
127
|
+
- spec/lib/net/http/signature/version_spec.rb
|
128
|
+
- spec/lib/net/http/signature_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
has_rdoc:
|