smaak 0.0.10 → 0.1.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.
@@ -3,70 +3,183 @@ require 'smaak'
3
3
 
4
4
  describe Smaak::Client do
5
5
  before :all do
6
- @test_service_identity = 'service-to-talk-to'
6
+ @test_uri = "http://rubygems.org:80/gems/smaak"
7
+ @test_encrypt = false
8
+ @test_service_identifier = 'service-to-talk-to'
7
9
  @test_service_psk = 'testsharedsecret'
8
10
  @test_client_private_key = OpenSSL::PKey::RSA.new(4096)
9
11
  @test_service_private_key = OpenSSL::PKey::RSA.new(4096)
10
12
  @test_service_public_key = @test_service_private_key.public_key
11
13
  @test_data = {}
12
14
  @iut = Smaak::Client.new
13
- @test_identity = 'test-client'
15
+ @test_identifier = 'test-client-1.cpt1.host-h.net'
14
16
  @test_token_life = 5
15
- @iut.set_identity(@test_identity)
17
+ @iut.set_identifier(@test_identifier)
16
18
  @iut.set_private_key(@test_client_private_key)
17
19
  @iut.set_token_life(@test_token_life)
18
- @iut.add_association(@test_service_identity, @test_service_public_key, @test_service_psk)
20
+ @iut.add_association(@test_service_identifier, @test_service_public_key, @test_service_psk, @test_encrypt)
21
+
22
+ @request = Net::HTTP::Post.new(@test_uri)
23
+ @request.body = "body-data"
24
+ @test_adaptor = Smaak::NetHttpAdaptor.new(@request)
19
25
  end
20
26
 
21
- context "when given an identity" do
22
- it "should remember an identity provided" do
27
+ context "when given an identifier" do
28
+ it "should remember an identifier provided" do
23
29
  iut = Smaak::Client.new
24
- expect(iut.identity).to eq(nil)
25
- iut.set_identity('test-client')
26
- expect(iut.identity).to eq('test-client')
30
+ expect(iut.identifier).to eq(nil)
31
+ iut.set_identifier(@test_identifier)
32
+ expect(iut.identifier).to eq(@test_identifier)
33
+ end
34
+
35
+ it "should raise an ArgumentError if an identifier is not provided" do
36
+ expect {
37
+ @iut.set_identifier(nil)
38
+ }.to raise_error ArgumentError, "Invalid identifier"
39
+ expect {
40
+ @iut.set_identifier("")
41
+ }.to raise_error ArgumentError, "Invalid identifier"
42
+ expect {
43
+ @iut.set_identifier(" ")
44
+ }.to raise_error ArgumentError, "Invalid identifier"
27
45
  end
28
46
  end
29
47
 
30
- context "when asked to build an auth header destined for an associate" do
48
+ context "when asked to sign a request destined for an associate" do
31
49
  it "should raise an ArgumentError if the associate is unknown" do
32
50
  expect{
33
- @iut.build_auth_header("unknown")
51
+ @iut.sign_request("unknown", nil)
34
52
  }.to raise_error ArgumentError, "Associate invalid"
35
53
  expect{
36
- @iut.build_auth_header(nil)
54
+ @iut.sign_request(nil, nil)
37
55
  }.to raise_error ArgumentError, "Associate invalid"
38
56
  end
39
57
 
40
- it "should compile the auth message data using the associate details" do
41
- expect(Smaak).to receive(:compile_auth_message_data).with(@test_service_public_key, @test_service_psk, @test_token_life, @test_identity, @test_data)
42
- @iut.build_auth_header(@test_service_identity)
58
+ it "should raise an ArgumentError if an adaptor was not provided" do
59
+ expect {
60
+ @iut.sign_request(@test_service_identifier, nil)
61
+ }.to raise_error ArgumentError, "Invalid adaptor"
43
62
  end
44
63
 
45
- it "should build the message" do
46
- expect(Smaak).to receive(:generate_nonce).twice().and_return(12345)
47
- test_message_data = Smaak::compile_auth_message_data(@test_service_public_key, @test_service_psk, @test_token_life, @test_identity, @test_data)
48
- expect(Smaak).to receive(:build_message).with(test_message_data)
49
- expect{
50
- @iut.build_auth_header(@test_service_identity)
51
- }.to raise_error
64
+ it "should create a new auth message using the associate details" do
65
+ expect(Smaak::AuthMessage).to receive(:create).with(@test_service_public_key.export, @test_service_psk, @test_token_life, @test_identifier, @test_encrypt)
66
+ expect {
67
+ @iut.sign_request(@test_service_identifier, @test_adaptor)
68
+ }.to raise_error NoMethodError
69
+ end
70
+
71
+ it "should encrypt the request body if the associate is configured for encryption" do
72
+ @iut.add_association('encrypted', @test_service_public_key, @test_service_psk, true)
73
+ expect(Smaak::Crypto).to receive(:encrypt).with(@test_adaptor.body, @test_service_public_key).and_return(@test_adaptor.body)
74
+ @iut.sign_request('encrypted', @test_adaptor)
75
+ end
76
+
77
+ it "should not encrypt the request body if the associate is not configure for encryption" do
78
+ expect(Smaak::Crypto).not_to receive(:encrypt)
79
+ @iut.sign_request(@test_service_identifier, @test_adaptor)
52
80
  end
53
81
 
54
82
  it "should sign the message" do
55
- expect(Smaak).to receive(:generate_nonce).twice().and_return(12345)
56
- test_message_data = Smaak::compile_auth_message_data(@test_service_public_key, @test_service_psk, @test_token_life, @test_identity, @test_data)
57
- expect(Smaak).to receive(:sign_message_data).with(test_message_data, @test_client_private_key)
58
- expect{
59
- @iut.build_auth_header(@test_service_identity)
83
+ expect(Smaak).to receive(:sign_authorization_headers)
84
+ @iut.sign_request(@test_service_identifier, @test_adaptor)
85
+ end
86
+
87
+ it "should return the adaptor with the signed request" do
88
+ expect(@iut.sign_request(@test_service_identifier, @test_adaptor)).to eq(@test_adaptor)
89
+ end
90
+ end
91
+
92
+ context "when asked to help the developer by providing GET and POST requests given an associate, URI, body, ssl and ssl verify option" do
93
+ it "should compile a Net::HTTP request" do
94
+ url = URI.parse(@test_uri)
95
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port)
96
+ expect {
97
+ @iut.get(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
98
+ }.to raise_error
99
+ end
100
+
101
+ it "should set the method to GET if requested" do
102
+ url = URI.parse(@test_uri)
103
+ expect(Net::HTTP::Get).to receive(:new).with(url.to_s)
104
+ expect {
105
+ @iut.get(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
60
106
  }.to raise_error
61
107
  end
62
108
 
63
- it "should place the message and its signature (encoded base64) in a JSON dictionary" do
64
- expect(Smaak).to receive(:generate_nonce).twice().and_return(12345)
65
- test_message_data = Smaak::compile_auth_message_data(@test_service_public_key, @test_service_psk, @test_token_life, @test_identity, @test_data)
66
- test_signature = Smaak::sign_message_data(test_message_data, @test_client_private_key)
67
- test_message = Smaak::build_message(test_message_data)
68
- test_envelope = { 'message' => test_message, 'signature' => Base64.encode64(test_signature) }.to_json
69
- expect(@iut.build_auth_header(@test_service_identity)).to eq(test_envelope)
109
+ it "should set the method to POST if requested" do
110
+ url = URI.parse(@test_uri)
111
+ expect(Net::HTTP::Post).to receive(:new).with(url.to_s)
112
+ expect {
113
+ @iut.post(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
114
+ }.to raise_error
115
+ end
116
+
117
+ it "should set use_ssl appropriately" do
118
+ url = URI.parse(@test_uri)
119
+ mock_http = double(Net::HTTP)
120
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port).and_return(mock_http)
121
+ expect(mock_http).to receive(:use_ssl=).with(false)
122
+ expect {
123
+ @iut.get(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
124
+ }.to raise_error
125
+ end
126
+
127
+ it "should configure ssl verification appropriately" do
128
+ url = URI.parse(@test_uri)
129
+ mock_http = double(Net::HTTP)
130
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port).and_return(mock_http)
131
+ expect(mock_http).to receive(:use_ssl=).with(false)
132
+ expect(mock_http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
133
+ expect {
134
+ @iut.get(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
135
+ }.to raise_error
136
+ end
137
+
138
+ it "should set the body appropriately" do
139
+ url = URI.parse(@test_uri)
140
+ mock_req = double(Net::HTTP::Post)
141
+ expect(Net::HTTP::Post).to receive(:new).with(url.to_s).and_return(mock_req)
142
+ expect(mock_req).to receive(:body=).with(@test_body)
143
+ expect {
144
+ @iut.post(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
145
+ }.to raise_error
146
+ end
147
+
148
+ it "should connect using the URI provided" do
149
+ url = URI.parse(@test_uri)
150
+ mock_http = Net::HTTP.new(url.host, url.port)
151
+ mock_req = Net::HTTP::Post.new(url.to_s)
152
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port).and_return(mock_http)
153
+ expect(Net::HTTP::Post).to receive(:new).with(url.to_s).and_return(mock_req)
154
+ expect(mock_http).to receive(:request).with(mock_req)
155
+ @iut.post(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
156
+ end
157
+
158
+ it "should decrypt the response body if the request was encrypted" do
159
+ @iut.add_association('encrypted', @test_service_public_key, @test_service_psk, true)
160
+ url = URI.parse(@test_uri)
161
+ mock_http = Net::HTTP.new(url.host, url.port)
162
+ mock_req = Net::HTTP::Post.new(url.to_s)
163
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port).and_return(mock_http)
164
+ expect(Net::HTTP::Post).to receive(:new).with(url.to_s).and_return(mock_req)
165
+ mock_response = ""
166
+ expect(mock_response).to receive(:body).and_return ""
167
+ expect(mock_response).to receive(:body=)
168
+ expect(mock_http).to receive(:request).with(mock_req).and_return(mock_response)
169
+ expect(Smaak::Crypto).to receive(:encrypt).and_return ""
170
+ expect(Smaak::Crypto).to receive(:decrypt).with("", @iut.key)
171
+
172
+ @iut.post('encrypted', @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)
173
+ end
174
+
175
+ it "should return the response" do
176
+ url = URI.parse(@test_uri)
177
+ mock_http = Net::HTTP.new(url.host, url.port)
178
+ mock_req = Net::HTTP::Post.new(url.to_s)
179
+ expect(Net::HTTP).to receive(:new).with(url.host, url.port).and_return(mock_http)
180
+ expect(Net::HTTP::Post).to receive(:new).with(url.to_s).and_return(mock_req)
181
+ expect(mock_http).to receive(:request).with(mock_req).and_return "response"
182
+ expect(@iut.post(@test_service_identifier, @test_uri, @test_body, false, OpenSSL::SSL::VERIFY_NONE)).to eql("response")
70
183
  end
71
184
  end
72
185
  end
@@ -0,0 +1,94 @@
1
+ require './spec/spec_helper.rb'
2
+
3
+ describe Smaak::Crypto do
4
+ before :all do
5
+ @private_key = OpenSSL::PKey::RSA.new(4096)
6
+ @data = {'a' => 'B'}.to_json
7
+ @bdata = Base64::strict_encode64(@data)
8
+ @digest = OpenSSL::Digest::SHA256.new
9
+ @signature = @private_key.sign(@digest, @bdata)
10
+ @public_key = @private_key.public_key
11
+ end
12
+
13
+ context "when asked to obfuscate a clear-text psk" do
14
+ it "should reverse the psk and apply an MD5 hexadecimal digest to the result" do
15
+ expect(Smaak::Crypto::obfuscate_psk('sharedsecret')).to eq(Digest::MD5.hexdigest('sharedsecret'.reverse))
16
+ end
17
+ end
18
+
19
+ context "when asked to generate a nonce" do
20
+ it "should generate a random nonce with at most 1 in a ten billion probability of a consecutive clash" do
21
+ expect(SecureRandom).to receive(:random_number).with(10000000000)
22
+ Smaak::Crypto::generate_nonce
23
+ end
24
+
25
+ it "should generate a different nonce every time with high probability (less than 1 in 10000) given a history of 1000000 nonces" do
26
+ repeat = {}
27
+ failed = 0
28
+ threshold = 1000000
29
+ for i in 1..threshold do
30
+ value = Smaak::Crypto::generate_nonce
31
+ failed = failed + 1 if repeat[value] == 1
32
+ repeat[value] = 1
33
+ end
34
+ failed_p = (failed.to_f / threshold) * 100
35
+ puts "I've seen #{failed_p} % of nonces before in #{threshold} generations"
36
+ expect(failed_p < 0.01).to eq(true)
37
+ end
38
+ end
39
+
40
+ context "when asked to encode data using base 64" do
41
+ it "should encode the data without newlines or line feeds using base 64 (strict)" do
42
+ data = "some data"
43
+ expect(Smaak::Crypto::encode64(data)).to eq(Base64.strict_encode64(data))
44
+ end
45
+ end
46
+
47
+ context "when asked to decode data using base 64" do
48
+ it "should decode the data using base 64 (strict)" do
49
+ expect(Smaak::Crypto::decode64(Base64.strict_encode64("some data"))).to eq("some data")
50
+ end
51
+ end
52
+
53
+ context "when asked to sign data given a private key" do
54
+ it "should sign a strict Base64 representation of the data with the key using a 256 bit SHA digest" do
55
+ expect(Smaak::Crypto::sign_data(@data, @private_key)).to eq(@signature)
56
+ end
57
+ end
58
+
59
+ context "when asked to verify a signature given data and a public key" do
60
+ it "should verify using a 256 bit SHA digest" do
61
+ expect(OpenSSL::Digest::SHA256).to receive(:new).and_return(@digest)
62
+ Smaak::Crypto::verify_signature(@signature, @bdata, @public_key)
63
+ end
64
+
65
+ it "should return true when the signature is verified by the public key" do
66
+ expect(Smaak::Crypto::verify_signature(@signature, @bdata, @public_key)).to eq(true)
67
+ end
68
+
69
+ it "should return false when the signature cannot be verified by the public key" do
70
+ other_key = OpenSSL::PKey::RSA.new(4096).public_key
71
+ expect(Smaak::Crypto::verify_signature(@signature, @bdata, other_key)).to eq(false)
72
+ end
73
+ end
74
+
75
+ context "when asked to encrypt data given a public key" do
76
+ it "should return a base64 representation of the data encrypted with the public key" do
77
+ expect(@private_key.private_decrypt(Base64.strict_decode64(Smaak::Crypto::encrypt(@data, @public_key)))).to eq(@data)
78
+ end
79
+ end
80
+
81
+ context "when asked to decrypt encrypted data given a private key" do
82
+ it "should return a decryption using the private key of a base64 decode of the data" do
83
+ expect(Smaak::Crypto::decrypt(Base64.strict_encode64(@public_key.public_encrypt(@data)), @private_key)).to eq(@data)
84
+ end
85
+ end
86
+
87
+ context "when asked to sync data from an IO stream" do
88
+ it "should sink all data from the stream, collating when nil is read" do
89
+ mock_stream = double(Object)
90
+ allow(mock_stream).to receive(:gets).and_return("a", "b", nil)
91
+ expect(Smaak::Crypto::sink(mock_stream)).to eq("ab")
92
+ end
93
+ end
94
+ end
@@ -2,26 +2,34 @@ require './spec/spec_helper.rb'
2
2
  require 'smaak'
3
3
  require 'mock/request.rb'
4
4
 
5
+ def mock_auth_message(env)
6
+ request = Rack::Request.new(env)
7
+ adaptor = Smaak::create_adaptor(request)
8
+ @iut.build_auth_message_from_request(adaptor)
9
+ end
10
+
5
11
  describe Smaak::Server do
6
12
  before :all do
7
13
  @iut = Smaak::Server.new
8
14
  @iut.set_token_life(2)
9
- @test_nonce = 1234567890
15
+ @test_nonce = "1234567890"
10
16
  @test_server_private_key = OpenSSL::PKey::RSA.new(4096)
11
17
  @test_psk = "testpresharedkey"
12
18
  @test_server_public_key = @test_server_private_key.public_key
13
- @test_identity = 'test-service'
19
+ @test_identifier = 'test-service-1.cpt1.host-h.net'
20
+ @message = Smaak::AuthMessage.new(@test_identifier, @test_nonce, Time.now.to_i, @test_psk, @test_server_public_key.export, false)
21
+ @iut.add_association(@test_identifier, @test_server_public_key, @test_psk, false)
22
+ @iut.set_public_key(@test_server_public_key)
23
+ @iut.set_private_key(@test_server_private_key)
14
24
  end
15
25
 
16
26
  before :each do
17
- @test_message_data = { 'recipient' => @test_server_public_key.export,
18
- 'identity' => @test_identity,
19
- 'psk' => Smaak::obfuscate_psk(@test_psk),
20
- 'expires' => Time.now.to_i + 10,
21
- 'nonce' => @test_nonce }
22
- @message = Smaak::AuthMessage.new(Smaak::build_message(@test_message_data))
23
27
  @iut.nonce_store[@test_nonce] = nil
24
28
  expect(@iut.nonce_store[@test_nonce]).to eq(nil)
29
+
30
+ @test_expires = "#{Time.now.to_i + 5}"
31
+ @env = {"CONTENT_LENGTH" => "25", "REQUEST_METHOD" => "POST", "PATH_INFO" => "/gems/smaak", "HTTP_X_SMAAK_ENCRYPT" => "false", "HTTP_X_SMAAK_RECIPIENT" => Base64.strict_encode64(@test_server_public_key.export), "HTTP_X_SMAAK_IDENTIFIER" => @test_identifier, "HTTP_X_SMAAK_NONCE" => @test_nonce, "HTTP_X_SMAAK_EXPIRES" => @test_expires, "HTTP_X_SMAAK_PSK" => Smaak::Crypto::obfuscate_psk(@test_psk) }
32
+ @auth_message = mock_auth_message(@env)
25
33
  end
26
34
 
27
35
  context "when initialized" do
@@ -31,7 +39,36 @@ describe Smaak::Server do
31
39
 
32
40
  it "should not know its own public key" do
33
41
  iut = Smaak::Server.new
34
- expect(iut.public_key).to eq(nil)
42
+ expect(iut.key).to eq(nil)
43
+ end
44
+
45
+ it "should not know its own private key" do
46
+ iut = Smaak::Server.new
47
+ expect(iut.private_key).to eq(nil)
48
+ end
49
+ end
50
+
51
+ context "when given a private key" do
52
+ it "should remember its private key" do
53
+ @iut.set_private_key(@test_server_private_key)
54
+ expect(@iut.private_key).to eql(@test_server_private_key)
55
+ end
56
+
57
+ it "should validate and adapt the key before assignment" do
58
+ expect(@iut).to receive(:adapt_rsa_key).with(@test_server_private_key)
59
+ @iut.set_private_key(@test_server_private_key)
60
+ end
61
+ end
62
+
63
+ context "when given a public key" do
64
+ it "should remember its public key" do
65
+ @iut.set_key(@test_server_public_key)
66
+ expect(@iut.key).to eql(@test_server_public_key)
67
+ end
68
+
69
+ it "should validate and adapt the key before assignment" do
70
+ expect(@iut).to receive(:adapt_rsa_key).with(@test_server_public_key)
71
+ @iut.set_private_key(@test_server_public_key)
35
72
  end
36
73
  end
37
74
 
@@ -69,27 +106,140 @@ describe Smaak::Server do
69
106
  end
70
107
  end
71
108
 
72
- context "when asked to verify a message given a signature" do
73
- before :each do
74
- @signature = Smaak::sign_message_data(@test_message_data, @test_server_private_key)
75
- @iut.set_public_key(@test_server_public_key.export)
76
- @iut.add_association(@test_identity, @test_server_public_key.export, @test_psk)
109
+ context "when asked to build an AuthMessage from a request received" do
110
+ it "should decode the x-smaak-recipient header using base64 to obtain the recipient publc key" do
111
+ expect(@auth_message.recipient).to eql(@test_server_public_key.export)
77
112
  end
78
113
 
79
- it "should look up the identity specified in the message and retrieve its public key from the associations store" do
80
- expect(@iut.verify_auth_message(@message, @signature)).to eq(@test_identity)
114
+ it "should set the psk to the x-smaak-psk header value" do
115
+ expect(@auth_message.psk).to eql(Smaak::Crypto::obfuscate_psk(@test_psk))
81
116
  end
82
117
 
83
- it "should look up the identity specified in the message and retrieve its PSK from the associations store" do
84
- expect(@iut.verify_auth_message(@message, @signature)).to eq(@test_identity)
118
+ it "should set expires to the x-smaak-expires header value" do
119
+ expect(@auth_message.expires).to eql(@test_expires)
85
120
  end
86
121
 
87
- it "should check nonce uniqueness and return false if not unique" do
88
- expect(@iut.auth_message_unique?(@message)).to eq(true)
89
- expect(@iut.verify_auth_message(@message, @signature)).to eq(false)
122
+ it "should set the identifier to the x-smaak-identifier header value" do
123
+ expect(@auth_message.identifier).to eql(@test_identifier)
124
+ end
125
+
126
+ it "should set the nonce to the x-smaak-nonce header value" do
127
+ expect(@auth_message.nonce).to eql(@test_nonce)
128
+ end
129
+
130
+ it "should return the auth_message" do
131
+ expect(@auth_message.is_a?(Smaak::AuthMessage)).to eql(true)
90
132
  end
91
133
  end
92
134
 
93
- context "when asked to get the message and signature from a signed auth request" do
135
+ context "when asked to verify an auth message" do
136
+ it "should return false if the auth message is not unique" do
137
+ @iut.nonce_store[@test_nonce] = 1
138
+ expect(@iut.verify_auth_message(@auth_message)).to eql(false)
139
+ end
140
+
141
+ it "should return false if the auth_message is not intended for the recipient" do
142
+ env = @env
143
+ env["HTTP_X_SMAAK_RECIPIENT"] = Base64.strict_encode64("another-recipient")
144
+ auth_message = mock_auth_message(env)
145
+ expect(@iut.verify_auth_message(auth_message)).to eql(false)
146
+ end
147
+
148
+ it "should return false if the auth_message's pre-shared key does not match the association's, indexed by the auth message's identifier field" do
149
+ env = @env
150
+ env["HTTP_X_SMAAK_PSK"] = "doesnotmatch"
151
+ auth_message = mock_auth_message(env)
152
+ expect(@iut.verify_auth_message(auth_message)).to eql(false)
153
+ end
154
+
155
+ it "should return true if the message successfully verifies" do
156
+ expect(@iut.verify_auth_message(@auth_message)).to eql(true)
157
+ end
158
+ end
159
+
160
+ context "when asked to verify a signed request" do
161
+ it "should create an adaptor for the request" do
162
+ expect(Smaak).to receive(:create_adaptor).with(@request)
163
+ expect {
164
+ @iut.verify_signed_request(@request)
165
+ }.to raise_error
166
+ end
167
+
168
+ it "should build an auth message using the adaptor" do
169
+ mock_adaptor = double(Smaak::RackAdaptor)
170
+ expect(Smaak).to receive(:create_adaptor).with(@request).and_return(mock_adaptor)
171
+ expect(@iut).to receive(:build_auth_message_from_request).with(mock_adaptor)
172
+ expect {
173
+ @iut.verify_signed_request(@request)
174
+ }.to raise_error
175
+ end
176
+
177
+ it "should return false if the constructed auth message cannot be verified" do
178
+ mock_adaptor = double(Smaak::RackAdaptor)
179
+ expect(Smaak).to receive(:create_adaptor).with(@request).and_return(mock_adaptor)
180
+ expect(@iut).to receive(:build_auth_message_from_request).with(mock_adaptor).and_return(@auth_message)
181
+ expect(@iut).to receive(:verify_auth_message).and_return false
182
+ expect(@iut.verify_signed_request(@request)).to eql(false)
183
+ end
184
+
185
+ it "should decrypt the request body if the auth_message indicates encryption" do
186
+ mock_adaptor = double(Smaak::RackAdaptor)
187
+ expect(Smaak).to receive(:create_adaptor).with(@request).and_return(mock_adaptor)
188
+ expect(@iut).to receive(:build_auth_message_from_request).with(mock_adaptor).and_return(@auth_message)
189
+ expect(@iut).to receive(:verify_auth_message).and_return true
190
+ allow(@auth_message).to receive(:encrypt).and_return true
191
+ expect(mock_adaptor).to receive(:body).and_return("body")
192
+ expect(Smaak::Crypto).to receive(:sink).with("body").and_return("body")
193
+ expect(Smaak::Crypto).to receive(:decrypt).with("body", @iut.private_key)
194
+ expect {
195
+ @iut.verify_signed_request(@request)
196
+ }.to raise_error
197
+ end
198
+
199
+ it "should return false, nil if the headers could not be authorized" do
200
+ mock_adaptor = double(Smaak::RackAdaptor)
201
+ expect(Smaak).to receive(:create_adaptor).with(@request).and_return(mock_adaptor)
202
+ expect(@iut).to receive(:build_auth_message_from_request).with(mock_adaptor).and_return(@auth_message)
203
+ expect(@iut).to receive(:verify_auth_message).and_return true
204
+ allow(@auth_message).to receive(:encrypt).and_return true
205
+ expect(mock_adaptor).to receive(:body).and_return("body")
206
+ expect(Smaak::Crypto).to receive(:sink).with("body").and_return("body")
207
+ expect(Smaak::Crypto).to receive(:decrypt).with("body", @iut.private_key).and_return("body")
208
+ expect(Smaak).to receive(:verify_authorization_headers).with(mock_adaptor, @test_server_public_key).and_return(false)
209
+ auth_message, body = @iut.verify_signed_request(@request)
210
+ expect(auth_message).to eql(false)
211
+ expect(body).to eql(nil)
212
+ end
213
+
214
+ it "should return the auth_message and the body if successfully verified" do
215
+ mock_adaptor = double(Smaak::RackAdaptor)
216
+ expect(Smaak).to receive(:create_adaptor).with(@request).and_return(mock_adaptor)
217
+ expect(@iut).to receive(:build_auth_message_from_request).with(mock_adaptor).and_return(@auth_message)
218
+ expect(@iut).to receive(:verify_auth_message).and_return true
219
+ allow(@auth_message).to receive(:encrypt).and_return true
220
+ expect(mock_adaptor).to receive(:body).and_return("body")
221
+ expect(Smaak::Crypto).to receive(:sink).with("body").and_return("body")
222
+ expect(Smaak::Crypto).to receive(:decrypt).with("body", @iut.private_key).and_return("body")
223
+ expect(Smaak).to receive(:verify_authorization_headers).with(mock_adaptor, @test_server_public_key).and_return(true)
224
+ auth_message, body = @iut.verify_signed_request(@request)
225
+ expect(auth_message).to eql(@auth_message)
226
+ expect(body).to eql("body")
227
+ end
228
+ end
229
+
230
+
231
+ context "when asked to compile a response" do
232
+ it "should return the data provided if the auth_message provided does not indicate encryption" do
233
+ expect(Smaak::Crypto).not_to receive(:encrypt)
234
+ result = @iut.compile_response(@auth_message, "data")
235
+ expect(result).to eql("data")
236
+ end
237
+
238
+ it "should return the data provided, encrypted with the public key of the associate identified by the auth_message if the auth_message provided indicates encryption" do
239
+ allow(@auth_message).to receive(:encrypt).and_return true
240
+ expect(Smaak::Crypto).to receive(:encrypt).with("data", @test_server_public_key).and_return("encrypted_data")
241
+ result = @iut.compile_response(@auth_message, "data")
242
+ expect(result).to eql("encrypted_data")
243
+ end
94
244
  end
95
245
  end
@@ -0,0 +1,52 @@
1
+ require './spec/spec_helper.rb'
2
+
3
+ class Tester < Smaak::SmaakService
4
+ attr_reader :configured
5
+
6
+ def self.mutex
7
+ @@mutex
8
+ end
9
+
10
+ def self.instance
11
+ @@instance
12
+ end
13
+
14
+ def configure_service
15
+ super
16
+ @configured = true
17
+ end
18
+ end
19
+
20
+ describe Smaak::SmaakService do
21
+ before :all do
22
+ @iut = Tester.get_instance
23
+ end
24
+
25
+ context "when initialized" do
26
+ it "should instantiate itself and remember it" do
27
+ expect(@iut.smaak_server.nil?).to eq(false)
28
+ expect(@iut.smaak_server.is_a?(Smaak::Server)).to eq(true)
29
+ end
30
+
31
+ it "should call configure_service as an IOC seam" do
32
+ expect(@iut.configured).to eq(true)
33
+ end
34
+ end
35
+
36
+ context "when asked for an instance of itself" do
37
+ it "should always return the same instance" do
38
+ a = Smaak::SmaakService.get_instance
39
+ b = Smaak::SmaakService.get_instance
40
+ c = Smaak::SmaakService.get_instance
41
+ expect(a).to eq(b)
42
+ expect(b).to eq(c)
43
+ end
44
+ end
45
+
46
+ context "as a singleton" do
47
+ it "should implement the singleton pattern and be thread-safe" do
48
+ expect(Tester::mutex.is_a? Mutex).to eq(true)
49
+ expect(Tester::instance.nil?).to eq(false)
50
+ end
51
+ end
52
+ end