bollard 1.0.3 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 74dc7d2be40f5a9ec28e38b88f6c9b8bd5925523
4
- data.tar.gz: ef2dbea6f544d92e00ca47675aaadad73dcdabfa
3
+ metadata.gz: 9687b9297437ec802e5f2e5dcda2ca60354abf21
4
+ data.tar.gz: 17cc8bbce3ecf75cad13ab9a5c86b9794799979f
5
5
  SHA512:
6
- metadata.gz: cb440c2fa6eda23b75d481dd2f27bd60dc5e087ca1b336369417499ecace553a60be376eaa74181d402deab9bed09f46fba79f6551cdf86d1619a18509e3d8b3
7
- data.tar.gz: 2c39954a6066b5c7f9a2f6e01aaf1d53e35567cf9cb619de1b582682dde4b99f53218cda4338507ec1577669ecd34bac8ea0792d86396cdb51f3eaf805621c5b
6
+ metadata.gz: a0275e28c88eada5209443534874fd211b1f6a686dad5d801c77f575d992737e77395b71243fa6eef0e7d3c748df728e2c6f7f58e30a7e482febba25750b161e
7
+ data.tar.gz: f369b668086ffc82f4c129dc36b2b908675b09be7ced1ac0487d2b2e819e0a1fab1c3e1a0c415cd36045b58976098ed7963908bfb4466ef0211b4c4a79c1af5b
@@ -1,9 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bollard (1.0.2)
4
+ bollard (2.0.0)
5
5
  jwt
6
- rest-client
7
6
 
8
7
  GEM
9
8
  remote: https://rubygems.org/
@@ -13,32 +12,14 @@ GEM
13
12
  i18n (>= 0.7, < 2)
14
13
  minitest (~> 5.1)
15
14
  tzinfo (~> 1.1)
16
- addressable (2.5.2)
17
- public_suffix (>= 2.0.2, < 4.0)
18
15
  byebug (10.0.2)
19
16
  concurrent-ruby (1.0.5)
20
- crack (0.4.3)
21
- safe_yaml (~> 1.0.0)
22
17
  diff-lcs (1.3)
23
- domain_name (0.5.20180417)
24
- unf (>= 0.0.5, < 1.0.0)
25
- hashdiff (0.3.7)
26
- http-cookie (1.0.3)
27
- domain_name (~> 0.5)
28
18
  i18n (1.1.0)
29
19
  concurrent-ruby (~> 1.0)
30
20
  jwt (2.1.0)
31
- mime-types (3.2.2)
32
- mime-types-data (~> 3.2015)
33
- mime-types-data (3.2018.0812)
34
21
  minitest (5.11.3)
35
- netrc (0.11.0)
36
- public_suffix (3.0.3)
37
22
  rake (12.3.1)
38
- rest-client (2.0.2)
39
- http-cookie (>= 1.0.2, < 2.0)
40
- mime-types (>= 1.16, < 4.0)
41
- netrc (~> 0.8)
42
23
  rspec (3.8.0)
43
24
  rspec-core (~> 3.8.0)
44
25
  rspec-expectations (~> 3.8.0)
@@ -52,17 +33,9 @@ GEM
52
33
  diff-lcs (>= 1.2.0, < 2.0)
53
34
  rspec-support (~> 3.8.0)
54
35
  rspec-support (3.8.0)
55
- safe_yaml (1.0.4)
56
36
  thread_safe (0.3.6)
57
37
  tzinfo (1.2.5)
58
38
  thread_safe (~> 0.1)
59
- unf (0.1.4)
60
- unf_ext
61
- unf_ext (0.0.7.5)
62
- webmock (2.3.2)
63
- addressable (>= 2.3.6)
64
- crack (>= 0.3.2)
65
- hashdiff
66
39
 
67
40
  PLATFORMS
68
41
  ruby
@@ -73,7 +46,6 @@ DEPENDENCIES
73
46
  byebug
74
47
  rake
75
48
  rspec (~> 3.7)
76
- webmock (~> 2.3)
77
49
 
78
50
  BUNDLED WITH
79
51
  1.16.1
@@ -18,11 +18,9 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {spec,gemfiles}/*`.split("\n")
19
19
 
20
20
  s.add_dependency "jwt"
21
- s.add_dependency "rest-client"
22
21
 
23
22
  s.add_development_dependency "activesupport", ">= 3.1"
24
23
  s.add_development_dependency "rspec", "~> 3.7"
25
- s.add_development_dependency "webmock", "~> 2.3"
26
24
  s.add_development_dependency "byebug"
27
25
  s.add_development_dependency "rake"
28
26
  end
@@ -1,14 +1,31 @@
1
+ require 'bollard/version'
2
+ require 'bollard/token'
1
3
  require 'bollard/signature'
2
- require 'bollard/post'
3
4
 
4
- module Bollard
5
- def self.secure_post(url, payload, signing_secret, extra_headers: {}, auth_header: 'Bollard-Signature')
6
- post = Post.new(url, payload, signing_secret, extra_headers, auth_header)
7
- post.perform
5
+ require 'securerandom'
6
+
7
+ class Bollard
8
+ def self.generate_secret(length: 32)
9
+ SecureRandom.hex((length / 2.0).ceil)[0...length]
10
+ end
11
+
12
+
13
+ def initialize(signing_secret)
14
+ @signing_secret = signing_secret
15
+ end
16
+
17
+
18
+ def generate_token(payload, **args)
19
+ Token.generate(payload, signing_secret, **args)
8
20
  end
9
21
 
10
22
 
11
- def self.verify_post(payload, header, signing_secret, tolerance: nil)
12
- Signature.verify(payload, header, signing_secret, tolerance: tolerance)
23
+ def verify_payload(payload, token, **args)
24
+ Token.new(token, signing_secret).verify_payload(payload, **args)
13
25
  end
26
+
27
+
28
+ private
29
+
30
+ attr_reader :signing_secret
14
31
  end
@@ -1,64 +1,32 @@
1
1
  require 'digest'
2
- require 'jwt'
3
-
4
- module Bollard
5
- class SignatureVerificationError < RuntimeError
6
- attr_reader :message, :sig_header, :http_body
7
-
8
- def initialize(message, sig_header, http_body: nil)
9
- @message = message
10
- @sig_header = sig_header
11
- @http_body = http_body
12
- end
13
- end
14
2
 
3
+ class Bollard
15
4
  class Signature
16
5
  EXPECTED_ALGORITHM = "sig_v1".freeze
17
6
 
18
-
19
- def self.generate(data, secret, ttl: 600)
20
- iat = Time.now.to_i
21
- payload = { iat: iat, exp: iat + ttl, sig_v1: Digest::SHA256.hexdigest(data) }
22
- JWT.encode(payload, secret, 'HS256')
7
+ def self.calculate_signature(payload)
8
+ Digest::SHA256.hexdigest(payload)
23
9
  end
24
10
 
25
11
 
26
- # Verifies the signature header for a given payload.
27
- #
28
- # Raises a SignatureVerificationError in the following cases:
29
- # - the header does not match the expected format
30
- # - no hash found with the expected algorithm
31
- # - hash doesn't match the expected hash
32
- #
33
- # Returns true otherwise
34
- def self.verify(payload, header, secret, tolerance: nil)
35
- begin
36
- decoded_token = JWT.decode(header, secret, true, { exp_leeway: tolerance })
37
- rescue JWT::DecodeError => e
38
- raise SignatureVerificationError.new(e.message, header, http_body: payload)
39
- end
40
-
41
- provided_hash = decoded_token[0][EXPECTED_ALGORITHM]
42
- if provided_hash.blank?
43
- raise SignatureVerificationError.new(
44
- "No hash found with expected algorithm #{EXPECTED_ALGORITHM}",
45
- header, http_body: payload
46
- )
47
- end
12
+ def initialize(signature)
13
+ @signature = signature
14
+ end
48
15
 
49
- expected_hash = Digest::SHA256.hexdigest(payload)
50
- unless secure_compare(provided_hash, expected_hash)
51
- raise SignatureVerificationError.new("Hash mismatch for payload", header, http_body: payload)
52
- end
53
16
 
54
- true
17
+ def match?(payload)
18
+ secure_compare(signature, self.class.calculate_signature(payload))
55
19
  end
56
20
 
57
21
 
22
+ private
23
+
24
+ attr_reader :signature
25
+
58
26
  # Constant time string comparison to prevent timing attacks
59
27
 
60
28
  # Code borrowed from ActiveSupport
61
- def self.secure_compare(a, b)
29
+ def secure_compare(a, b)
62
30
  return false unless a.bytesize == b.bytesize
63
31
 
64
32
  l = a.unpack "C#{a.bytesize}"
@@ -67,6 +35,5 @@ module Bollard
67
35
  b.each_byte { |byte| res |= byte ^ l.shift }
68
36
  res.zero?
69
37
  end
70
- private_class_method :secure_compare
71
38
  end
72
39
  end
@@ -0,0 +1,64 @@
1
+ require 'jwt'
2
+
3
+ class Bollard
4
+ SignatureVerificationError = Class.new(RuntimeError)
5
+
6
+ class Token
7
+
8
+ # Generate the token header for a given payload.
9
+ # The token becomes invalid after `ttl` seconds.
10
+ #
11
+ # Returns a JWT with an iat, exp, and signature data
12
+ def self.generate(payload, signing_secret, ttl: 600)
13
+ iat = Time.now.to_i
14
+ signature = Signature.calculate_signature(payload)
15
+ jwt_payload = { iat: iat, exp: iat + ttl, Signature::EXPECTED_ALGORITHM => signature }
16
+ JWT.encode(jwt_payload, signing_secret, 'HS256')
17
+ end
18
+
19
+
20
+ def initialize(token, signing_secret)
21
+ @token = token
22
+ @signing_secret = signing_secret
23
+ end
24
+
25
+
26
+ # Verifies the token header for a given payload.
27
+ #
28
+ # Raises a SignatureVerificationError in the following cases:
29
+ # - the header does not match the expected format
30
+ # - no hash found with the expected algorithm
31
+ # - hash doesn't match the expected hash
32
+ #
33
+ # Returns true otherwise
34
+ def verify_payload(payload, tolerance: nil)
35
+ token_data, header = decode_token(tolerance)
36
+ signature = extract_signature(token_data)
37
+ verify_data(signature, payload)
38
+
39
+ true
40
+ end
41
+
42
+
43
+ private
44
+
45
+ attr_reader :token, :signing_secret
46
+
47
+ def decode_token(tolerance)
48
+ JWT.decode(token, signing_secret, true, { exp_leeway: tolerance })
49
+ rescue JWT::DecodeError => e
50
+ raise SignatureVerificationError.new(e.message)
51
+ end
52
+
53
+ def extract_signature(token_data)
54
+ signature = token_data[Signature::EXPECTED_ALGORITHM]
55
+ return Signature.new(signature) unless signature.blank?
56
+ raise SignatureVerificationError.new("No signature found with expected algorithm #{Signature::EXPECTED_ALGORITHM}")
57
+ end
58
+
59
+ def verify_data(signature, payload)
60
+ return true if signature.match?(payload)
61
+ raise SignatureVerificationError.new("Hash mismatch for payload")
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
- module Bollard
2
- VERSION = "1.0.3"
1
+ class Bollard
2
+ VERSION = "2.0.0"
3
3
  end
@@ -2,74 +2,26 @@ require "spec_helper"
2
2
  require 'digest'
3
3
 
4
4
  RSpec.describe Bollard::Signature do
5
- describe ".generate" do
6
- let(:jwt) { Bollard::Signature.generate("Some data", "super-secret") }
7
- let(:jwt_payload) { JWT.decode(jwt, "super-secret", true)[0] }
8
-
9
- it "generates a valid JWT for the given payload" do
10
- expect { JWT.decode(jwt, "super-secret", true) }.not_to raise_error
11
- end
12
-
13
- it "adds an issued-at field" do
14
- travel_to(Time.now) do
15
- expect(jwt_payload["iat"]).to eq Time.now.to_i
16
- end
5
+ describe ".calculate_signature" do
6
+ it "returns a signature" do
7
+ expect(Bollard::Signature.calculate_signature("Some test data")).to be_present
17
8
  end
18
9
 
19
- it "adds an expires-at field" do
20
- travel_to(Time.now) do
21
- expect(jwt_payload["exp"]).to eq(Time.now.to_i + 600)
22
- end
23
- end
24
-
25
- context "when given a ttl" do
26
- let(:jwt) { Bollard::Signature.generate("Some data", "super-secret", ttl: 100) }
27
-
28
- it "sets the expires-at field using the configurable ttl" do
29
- travel_to(Time.now) do
30
- expect(jwt_payload["exp"]).to eq(Time.now.to_i + 100)
31
- end
32
- end
33
- end
34
-
35
- it "adds a hash of the data" do
36
- expect(jwt_payload["sig_v1"]).to eq(Digest::SHA256.hexdigest("Some data"))
37
- end
38
-
39
- it "signs the jwt with the secret so it can be verified" do
40
- expect { JWT.decode(jwt, "not-the-same-secret", true) }.to raise_error(JWT::VerificationError, "Signature verification raised")
10
+ it "returns an SHA256 hex digest of the given data" do
11
+ expected_digest = "6e6ff23ec852afdf8fc294da163a55b2d246ec45b9659d290dc8871aea1502c0"
12
+ expect(Bollard::Signature.calculate_signature("Some test data")).to eq expected_digest
41
13
  end
42
14
  end
43
15
 
44
- describe ".verify" do
45
- it "verifies the signing secret matches the given secret" do
46
- jwt = Bollard::Signature.generate("Some data", "super-secret")
47
- expect { Bollard::Signature.verify("Some data", jwt, "different-super-secret") }.to raise_error(Bollard::SignatureVerificationError, "Signature verification raised")
48
- end
49
-
50
- it "verifies the payload matches the given hash" do
51
- jwt = Bollard::Signature.generate("Some data", "super-secret")
52
- expect { Bollard::Signature.verify("Some different data", jwt, "super-secret") }.to raise_error(Bollard::SignatureVerificationError, "Hash mismatch for payload")
53
- end
54
-
55
- it "verifies the format of the token" do
56
- jwt = JWT.encode({ sig_v2: Digest::SHA256.hexdigest("Some data") }, "super-secret", 'HS256')
57
-
58
- expect { Bollard::Signature.verify("Some data", jwt, "super-secret") }.to raise_error(Bollard::SignatureVerificationError, "No hash found with expected algorithm #{Bollard::Signature::EXPECTED_ALGORITHM}")
59
- end
60
-
61
- it "ensures that the jwt hasn't expired" do
62
- jwt = Bollard::Signature.generate("Some data", "super-secret")
63
- travel_to(Time.now + 1200) do
64
- expect { Bollard::Signature.verify("Some data", jwt, "super-secret") }.to raise_error(Bollard::SignatureVerificationError, "Signature has expired")
65
- end
16
+ describe "#match?" do
17
+ it "returns true if the given signature matches the given payload" do
18
+ expected_digest = "6e6ff23ec852afdf8fc294da163a55b2d246ec45b9659d290dc8871aea1502c0"
19
+ expect(Bollard::Signature.new(expected_digest).match?("Some test data")).to be true
66
20
  end
67
21
 
68
- it "allows leeway for the expiry if set" do
69
- jwt = Bollard::Signature.generate("Some data", "super-secret")
70
- travel_to(Time.now + 1200) do
71
- expect { Bollard::Signature.verify("Some data", jwt, "super-secret", tolerance: 1200) }.not_to raise_error
72
- end
22
+ it "returns false if the given signature doesn't match the given payload" do
23
+ expected_digest = "6e6ff23ec852afdf8fc294da163a55b2d246ec45b9659d290dc8871aea1502c0"
24
+ expect(Bollard::Signature.new(expected_digest).match?("Some other test data")).to be false
73
25
  end
74
26
  end
75
27
  end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Bollard::Token do
4
+ describe ".generate" do
5
+ it "returns a valid JWT" do
6
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
7
+ expect { JWT.decode(token, nil, false) }.not_to raise_error
8
+ end
9
+
10
+ it "returns a JWT signed with the given secret" do
11
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
12
+ expect { JWT.decode(token, "-signing-secret-", true) }.not_to raise_error
13
+ end
14
+
15
+ it "returns a JWT with an issued at timestamp (iat)" do
16
+ travel_to(Time.now) do
17
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
18
+ payload, header = JWT.decode(token, nil, false)
19
+ expect(payload['iat']).to eq Time.now.to_i
20
+ end
21
+ end
22
+
23
+ it "returns a JWT with an expiry timestamp (exp)" do
24
+ travel_to(Time.now) do
25
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
26
+ payload, header = JWT.decode(token, nil, false)
27
+ expect(payload['exp']).to eq Time.now.to_i + 600
28
+ end
29
+ end
30
+
31
+ it "returns a JWT with an expiry timestamp (exp) set to the expire after the given TTL" do
32
+ travel_to(Time.now) do
33
+ token = Bollard::Token.generate("Some test data", "-signing-secret-", ttl: 10)
34
+ payload, header = JWT.decode(token, nil, false)
35
+ expect(payload['exp']).to eq Time.now.to_i + 10
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "#verify_payload" do
41
+ it "returns true if given a payload that matches the signature from the token" do
42
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
43
+ expect { Bollard::Token.new(token, "-signing-secret-").verify_payload("Some test data") }.not_to raise_error
44
+ end
45
+
46
+ it "raises an error if given a token that wasn't signed with the signing secret" do
47
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
48
+ expect { Bollard::Token.new(token, "-different-secret-").verify_payload("Some test data") }.to raise_error(
49
+ Bollard::SignatureVerificationError, "Signature verification raised"
50
+ )
51
+ end
52
+
53
+ it "raises an error if given a token that has expired" do
54
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
55
+ travel_to(Time.now + 1000) do
56
+ expect do
57
+ Bollard::Token.new(token, "-signing-secret-").verify_payload("Some test data")
58
+ end.to raise_error(Bollard::SignatureVerificationError, "Signature has expired")
59
+ end
60
+ end
61
+
62
+ it "allows some tolerance in the expiry if custom tolerance passed in" do
63
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
64
+ travel_to(Time.now + 1000) do
65
+ expect do
66
+ Bollard::Token.new(token, "-signing-secret-").verify_payload("Some test data", tolerance: 1000)
67
+ end.not_to raise_error
68
+ end
69
+ end
70
+
71
+ it "raises an error if given a token that doesn't contain expected signature information" do
72
+ token = JWT.encode({}, "-signing-secret-")
73
+ expected_error_message = "No signature found with expected algorithm #{Bollard::Signature::EXPECTED_ALGORITHM}"
74
+ expect do
75
+ Bollard::Token.new(token, "-signing-secret-").verify_payload("Some test data")
76
+ end.to raise_error(Bollard::SignatureVerificationError, expected_error_message)
77
+ end
78
+
79
+ it "raises an error if given a payload that doesn't match the signature contained in the token" do
80
+ token = Bollard::Token.generate("Some test data", "-signing-secret-")
81
+ expect do
82
+ Bollard::Token.new(token, "-signing-secret-").verify_payload("Some other test data")
83
+ end.to raise_error(Bollard::SignatureVerificationError, "Hash mismatch for payload")
84
+ end
85
+ end
86
+ end
@@ -1,45 +1,70 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe Bollard do
4
- describe ".secure_post" do
5
- let(:post) { instance_double(Bollard::Post, perform: true) }
4
+ describe ".generate_secret" do
5
+ it "generates a secret key" do
6
+ expect(Bollard.generate_secret).to be_present
7
+ end
6
8
 
7
- before do
8
- allow(Bollard::Post).to receive(:new).and_return(post)
9
+ it "generates a secret key of the desired length" do
10
+ secret_key = Bollard.generate_secret(length: 67)
11
+ expect(secret_key.length).to be(67)
9
12
  end
10
13
 
11
- it "delegates everything to Post" do
12
- Bollard.secure_post("https://url.com", "{}", "secrets", extra_headers: { header_1: "1" }, auth_header: "Authorization")
13
- expect(Bollard::Post).to have_received(:new).with("https://url.com", "{}", "secrets", { header_1: "1" }, "Authorization")
14
- expect(post).to have_received(:perform)
14
+ it "doesn't generate the same secret key twice" do
15
+ secret_key = Bollard.generate_secret
16
+ other_secret_key = Bollard.generate_secret
17
+
18
+ expect(secret_key).not_to eq other_secret_key
15
19
  end
20
+ end
21
+
16
22
 
17
- it "provides defaults for extra_headers" do
18
- Bollard.secure_post("https://url.com", "{}", "secrets", auth_header: "Authorization")
19
- expect(Bollard::Post).to have_received(:new).with("https://url.com", "{}", "secrets", {}, "Authorization")
23
+ describe "#generate_token" do
24
+ let(:bollard) { Bollard.new("-signing-secret-") }
25
+
26
+ it "returns a token" do
27
+ expect(bollard.generate_token("Some test data")).to be_present
20
28
  end
21
29
 
22
- it "provides defaults for auth_header" do
23
- Bollard.secure_post("https://url.com", "{}", "secrets", extra_headers: { header_1: "1" })
24
- expect(Bollard::Post).to have_received(:new).with("https://url.com", "{}", "secrets", { header_1: "1" }, "Bollard-Signature")
30
+ it "generates a new token using Bollard::Token with the given payload" do
31
+ expect(Bollard::Token).to receive(:generate).with("Some test data", "-signing-secret-", {})
32
+
33
+ bollard.generate_token("Some test data")
34
+ end
35
+
36
+ it "passes on any given arguments" do
37
+ expect(Bollard::Token).to receive(:generate).with("Some test data", "-signing-secret-", ttl: 10)
38
+
39
+ bollard.generate_token("Some test data", ttl: 10)
25
40
  end
26
41
  end
27
42
 
28
- describe ".verify_post" do
29
- before do
30
- allow(Bollard::Signature).to receive(:verify)
43
+
44
+ describe "#verify_payload" do
45
+ let(:bollard) { Bollard.new("-signing-secret-") }
46
+
47
+ it "verifies a payload" do
48
+ token = bollard.generate_token("Some test data")
49
+ expect(bollard.verify_payload("Some test data", token)).to eq true
31
50
  end
32
51
 
33
- it "delegates everything to Signature" do
34
- Bollard.verify_post("{}", "abc123", "secrets", tolerance: 100)
52
+ it "verifies the payload using Bollard::Token" do
53
+ token = instance_double(Bollard::Token, verify_payload: true)
54
+ allow(Bollard::Token).to receive(:new).with("-token-", "-signing-secret-").and_return(token)
55
+
56
+ bollard.verify_payload("Some test data", "-token-")
35
57
 
36
- expect(Bollard::Signature).to have_received(:verify).with("{}", "abc123", "secrets", tolerance: 100)
58
+ expect(token).to have_received(:verify_payload).with("Some test data", {})
37
59
  end
38
60
 
39
- it "provides a default for tolerance" do
40
- Bollard.verify_post("{}", "abc123", "secrets")
61
+ it "passes on any given arguments" do
62
+ token = instance_double(Bollard::Token, verify_payload: true)
63
+ allow(Bollard::Token).to receive(:new).with("-token-", "-signing-secret-").and_return(token)
64
+
65
+ bollard.verify_payload("Some test data", "-token-", tolerance: 100)
41
66
 
42
- expect(Bollard::Signature).to have_received(:verify).with("{}", "abc123", "secrets", tolerance: nil)
67
+ expect(token).to have_received(:verify_payload).with("Some test data", tolerance: 100)
43
68
  end
44
69
  end
45
70
  end
@@ -1,7 +1,6 @@
1
1
  require 'byebug'
2
2
  require 'active_support/testing/time_helpers'
3
3
 
4
- require 'webmock/rspec'
5
4
  require File.expand_path('../../lib/bollard', __FILE__)
6
5
  Dir[File.expand_path('../spec/support/**/*.rb', __FILE__)].each { |f| require f }
7
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bollard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Dilley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-29 00:00:00.000000000 Z
11
+ date: 2018-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: rest-client
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: activesupport
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +52,6 @@ dependencies:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
54
  version: '3.7'
69
- - !ruby/object:Gem::Dependency
70
- name: webmock
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.3'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '2.3'
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: byebug
85
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,11 +94,11 @@ files:
122
94
  - Rakefile
123
95
  - bollard.gemspec
124
96
  - lib/bollard.rb
125
- - lib/bollard/post.rb
126
97
  - lib/bollard/signature.rb
98
+ - lib/bollard/token.rb
127
99
  - lib/bollard/version.rb
128
- - spec/lib/bollard/post_spec.rb
129
100
  - spec/lib/bollard/signature_spec.rb
101
+ - spec/lib/bollard/token_spec.rb
130
102
  - spec/lib/bollard_spec.rb
131
103
  - spec/spec_helper.rb
132
104
  homepage: https://github.com/vinomofo/bollard
@@ -154,7 +126,7 @@ signing_key:
154
126
  specification_version: 4
155
127
  summary: Send a secure post somewhere
156
128
  test_files:
157
- - spec/lib/bollard/post_spec.rb
158
129
  - spec/lib/bollard/signature_spec.rb
130
+ - spec/lib/bollard/token_spec.rb
159
131
  - spec/lib/bollard_spec.rb
160
132
  - spec/spec_helper.rb
@@ -1,40 +0,0 @@
1
- require 'rest-client'
2
-
3
- module Bollard
4
- class PostError < RuntimeError
5
- attr_reader :response
6
-
7
- def initialize(message, response: nil)
8
- @response = response
9
- super(message)
10
- end
11
- end
12
-
13
- class Post
14
- def initialize(url, payload, signing_secret, extra_headers, auth_header)
15
- @url = url
16
- @payload = payload
17
- @signing_secret = signing_secret
18
- @auth_header = auth_header
19
- @extra_headers = extra_headers
20
- end
21
-
22
- def perform
23
- RestClient.post(@url, @payload, headers)
24
- rescue RestClient::ExceptionWithResponse => e
25
- raise PostError.new(e.response.body, response: e.response)
26
- rescue RestClient::Exception => e
27
- raise PostError.new(e.message)
28
- end
29
-
30
- private
31
-
32
- def headers
33
- @extra_headers.merge({ @auth_header => signature })
34
- end
35
-
36
- def signature
37
- Signature.generate(@payload, @signing_secret)
38
- end
39
- end
40
- end
@@ -1,33 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Bollard::Post do
4
- it "posts the payload to the given URL" do
5
- stub_request(:post, "https://test.localhost/")
6
-
7
- post = Bollard::Post.new("https://test.localhost/", "{}", "secret", {}, "Bollard-Signature")
8
- post.perform
9
-
10
- expect(WebMock).to have_requested(:post, "https://test.localhost").with(body: "{}")
11
- end
12
-
13
- it "adds the correct signature header to the request" do
14
- allow(Bollard::Signature).to receive(:generate).and_return("valid_signature")
15
- stub_request(:post, "https://test.localhost/")
16
-
17
- post = Bollard::Post.new("https://test.localhost/", "{}", "secret", {}, "Bollard-Signature")
18
- post.perform
19
-
20
- expect(WebMock).to have_requested(:post, "https://test.localhost")
21
- .with(headers: { "Bollard-Signature" => "valid_signature" })
22
- end
23
-
24
- it "adds extra headers to the request" do
25
- stub_request(:post, "https://test.localhost/")
26
-
27
- post = Bollard::Post.new("https://test.localhost/", "{}", "secret", { content_type: :json, accept: :json }, "Bollard-Signature")
28
- post.perform
29
-
30
- expect(WebMock).to have_requested(:post, "https://test.localhost")
31
- .with(headers: { content_type: 'application/json', accept: 'application/json' })
32
- end
33
- end