smaak 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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