mongo 1.3.0 → 1.12.5

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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -0,0 +1,556 @@
1
+ # Copyright (C) 2014 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'base64'
16
+ require 'securerandom'
17
+ require 'openssl'
18
+ require 'digest/md5'
19
+
20
+ module Mongo
21
+ module Authentication
22
+
23
+ # Defines behaviour around a single SCRAM-SHA-1 conversation between the
24
+ # client and server.
25
+ #
26
+ # @since 1.12.0
27
+ class SCRAM
28
+
29
+ # The client key string.
30
+ #
31
+ # @since 1.12.0
32
+ CLIENT_KEY = 'Client Key'.freeze
33
+
34
+ # The digest to use for encryption.
35
+ #
36
+ # @since 1.12.0
37
+ DIGEST = OpenSSL::Digest::SHA1.new.freeze
38
+
39
+ # The key for the done field in the responses.
40
+ #
41
+ # @since 1.12.0
42
+ DONE = 'done'.freeze
43
+
44
+ # The conversation id field.
45
+ #
46
+ # @since 1.12.0
47
+ ID = 'conversationId'.freeze
48
+
49
+ # The iterations key in the responses.
50
+ #
51
+ # @since 1.12.0
52
+ ITERATIONS = /i=(\d+)/.freeze
53
+
54
+ # The payload field.
55
+ #
56
+ # @since 1.12.0
57
+ PAYLOAD = 'payload'.freeze
58
+
59
+ # The rnonce key in the responses.
60
+ #
61
+ # @since 1.12.0
62
+ RNONCE = /r=([^,]*)/.freeze
63
+
64
+ # The salt key in the responses.
65
+ #
66
+ # @since 1.12.0
67
+ SALT = /s=([^,]*)/.freeze
68
+
69
+ # The server key string.
70
+ #
71
+ # @since 1.12.0
72
+ SERVER_KEY = 'Server Key'.freeze
73
+
74
+ # The server signature verifier in the response.
75
+ #
76
+ # @since 1.12.0
77
+ VERIFIER = /v=([^,]*)/.freeze
78
+
79
+ # @return [ String ] nonce The initial user nonce.
80
+ attr_reader :nonce
81
+
82
+ # @return [ BSON::OrderedHash ] reply The current reply in the conversation.
83
+ attr_reader :reply
84
+
85
+ # @return [ Hash ] auth The authentication details.
86
+ attr_reader :auth
87
+
88
+ # @return [ String ] hashed_password The user's hashed password
89
+ attr_reader :hashed_password
90
+
91
+ # Continue the SCRAM conversation. This sends the client final message
92
+ # to the server after setting the reply from the previous server
93
+ # communication.
94
+ #
95
+ # @example Continue the conversation.
96
+ # conversation.continue(reply)
97
+ #
98
+ # @param [ BSON::OrderedHash ] reply The reply of the previous
99
+ # message.
100
+ #
101
+ # @return [ BSON::OrderedHash ] The next message to send.
102
+ #
103
+ # @since 1.12.0
104
+ def continue(reply)
105
+ validate_first_message!(reply)
106
+ command = BSON::OrderedHash.new
107
+ command['saslContinue'] = 1
108
+ command[PAYLOAD] = client_final_message
109
+ command[ID] = id
110
+ command
111
+ end
112
+
113
+ # Continue the SCRAM conversation for copydb. This sends the client final message
114
+ # to the server after setting the reply from the previous server
115
+ # communication.
116
+ #
117
+ # @example Continue the conversation when copying a database.
118
+ # conversation.copy_db_continue(reply)
119
+ #
120
+ # @param [ BSON::OrderedHash ] reply The reply of the previous
121
+ # message.
122
+ #
123
+ # @return [ BSON::OrderedHash ] The next message to send.
124
+ #
125
+ # @since 1.12.0
126
+ def copy_db_continue(reply)
127
+ validate_first_message!(reply)
128
+ command = BSON::OrderedHash.new
129
+ command['copydb'] = 1
130
+ command['fromhost'] = @copy_db[:from_host]
131
+ command['fromdb'] = @copy_db[:from_db]
132
+ command['todb'] = @copy_db[:to_db]
133
+ command[PAYLOAD] = client_final_message
134
+ command[ID] = id
135
+ command
136
+ end
137
+
138
+ # Finalize the SCRAM conversation. This is meant to be iterated until
139
+ # the provided reply indicates the conversation is finished.
140
+ #
141
+ # @example Finalize the conversation.
142
+ # conversation.finalize(reply)
143
+ #
144
+ # @param [ BSON::OrderedHash ] reply The reply of the previous
145
+ # message.
146
+ #
147
+ # @return [ BSON::OrderedHash ] The next message to send.
148
+ #
149
+ # @since 1.12.0
150
+ def finalize(reply)
151
+ validate_final_message!(reply)
152
+ command = BSON::OrderedHash.new
153
+ command['saslContinue'] = 1
154
+ command[PAYLOAD] = client_empty_message
155
+ command[ID] = id
156
+ command
157
+ end
158
+
159
+ # Start the SCRAM conversation. This returns the first message that
160
+ # needs to be send to the server.
161
+ #
162
+ # @example Start the conversation.
163
+ # conversation.start
164
+ #
165
+ # @return [ BSON::OrderedHash ] The first SCRAM conversation message.
166
+ #
167
+ # @since 1.12.0
168
+ def start
169
+ command = BSON::OrderedHash.new
170
+ command['saslStart'] = 1
171
+ command['autoAuthorize'] = 1
172
+ command[PAYLOAD] = client_first_message
173
+ command['mechanism'] = 'SCRAM-SHA-1'
174
+ command
175
+ end
176
+
177
+ # Start the SCRAM conversation for copying a database.
178
+ # This returns the first message that needs to be sent to the server.
179
+ #
180
+ # @example Start the copydb conversation.
181
+ # conversation.copy_db_start
182
+ #
183
+ # @return [ BSON::OrderedHash ] The first SCRAM copy_db conversation message.
184
+ #
185
+ # @since 1.12.0
186
+ def copy_db_start
187
+ command = BSON::OrderedHash.new
188
+ command['copydbsaslstart'] = 1
189
+ command['autoAuthorize'] = 1
190
+ command['fromhost'] = @copy_db[:from_host]
191
+ command['fromdb'] = @copy_db[:from_db]
192
+ command[PAYLOAD] = client_first_message
193
+ command['mechanism'] = 'SCRAM-SHA-1'
194
+ command
195
+ end
196
+
197
+ # Get the id of the conversation.
198
+ #
199
+ # @example Get the id of the conversation.
200
+ # conversation.id
201
+ #
202
+ # @return [ Integer ] The conversation id.
203
+ #
204
+ # @since 1.12.0
205
+ def id
206
+ reply[ID]
207
+ end
208
+
209
+ # Create the new conversation.
210
+ #
211
+ # @example Create the new conversation.
212
+ # Conversation.new(auth, password)
213
+ #
214
+ # @since 1.12.0
215
+ def initialize(auth, hashed_password, opts={})
216
+ @auth = auth
217
+ @hashed_password = hashed_password
218
+ @nonce = SecureRandom.base64
219
+ @copy_db = opts[:copy_db] if opts[:copy_db]
220
+ end
221
+
222
+ private
223
+
224
+ # Auth message algorithm implementation.
225
+ #
226
+ # @api private
227
+ #
228
+ # @see http://tools.ietf.org/html/rfc5802#section-3
229
+ #
230
+ # @since 1.12.0
231
+ def auth_message
232
+ @auth_message ||= "#{first_bare},#{payload_data},#{without_proof}"
233
+ end
234
+
235
+ # Get the empty client message.
236
+ #
237
+ # @api private
238
+ #
239
+ # @since 1.12.0
240
+ def client_empty_message
241
+ BSON::Binary.new('')
242
+ end
243
+
244
+ # Get the final client message.
245
+ #
246
+ # @api private
247
+ #
248
+ # @see http://tools.ietf.org/html/rfc5802#section-3
249
+ #
250
+ # @since 1.12.0
251
+ def client_final_message
252
+ BSON::Binary.new("#{without_proof},p=#{client_final}")
253
+ end
254
+
255
+ # Get the client first message
256
+ #
257
+ # @api private
258
+ #
259
+ # @see http://tools.ietf.org/html/rfc5802#section-3
260
+ #
261
+ # @since 1.12.0
262
+ def client_first_message
263
+ BSON::Binary.new("n,,#{first_bare}")
264
+ end
265
+
266
+ # Client final implementation.
267
+ #
268
+ # @api private
269
+ #
270
+ # @see http://tools.ietf.org/html/rfc5802#section-7
271
+ #
272
+ # @since 1.12.0
273
+ def client_final
274
+ @client_final ||= client_proof(client_key, client_signature(stored_key(client_key), auth_message))
275
+ end
276
+
277
+ # Client key algorithm implementation.
278
+ #
279
+ # @api private
280
+ #
281
+ # @see http://tools.ietf.org/html/rfc5802#section-3
282
+ #
283
+ # @since 1.12.0
284
+ def client_key
285
+ @client_key ||= hmac(salted_password, CLIENT_KEY)
286
+ end
287
+
288
+ if Base64.respond_to?(:strict_encode64)
289
+
290
+ # Client proof algorithm implementation.
291
+ #
292
+ # @api private
293
+ #
294
+ # @see http://tools.ietf.org/html/rfc5802#section-3
295
+ #
296
+ # @since 1.12.0
297
+ def client_proof(key, signature)
298
+ @client_proof ||= Base64.strict_encode64(xor(key, signature))
299
+ end
300
+ else
301
+
302
+ # Client proof algorithm implementation.
303
+ #
304
+ # @api private
305
+ #
306
+ # @see http://tools.ietf.org/html/rfc5802#section-3
307
+ #
308
+ # @since 1.12.0
309
+ def client_proof(key, signature)
310
+ @client_proof ||= Base64.encode64(xor(key, signature)).gsub("\n",'')
311
+ end
312
+ end
313
+
314
+ # Client signature algorithm implementation.
315
+ #
316
+ # @api private
317
+ #
318
+ # @see http://tools.ietf.org/html/rfc5802#section-3
319
+ #
320
+ # @since 1.12.0
321
+ def client_signature(key, message)
322
+ @client_signature ||= hmac(key, message)
323
+ end
324
+
325
+ if Base64.respond_to?(:strict_decode64)
326
+
327
+ # Get the base 64 decoded salt.
328
+ #
329
+ # @api private
330
+ #
331
+ # @since 1.12.0
332
+ def decoded_salt
333
+ @decoded_salt ||= Base64.strict_decode64(salt)
334
+ end
335
+ else
336
+
337
+ # Get the base 64 decoded salt.
338
+ #
339
+ # @api private
340
+ #
341
+ # @since 1.12.0
342
+ def decoded_salt
343
+ @decoded_salt ||= Base64.decode64(salt)
344
+ end
345
+ end
346
+
347
+ # First bare implementation.
348
+ #
349
+ # @api private
350
+ #
351
+ # @see http://tools.ietf.org/html/rfc5802#section-7
352
+ #
353
+ # @since 1.12.0
354
+ def first_bare
355
+ @first_bare ||= "n=#{auth[:username].gsub('=','=3D').gsub(',','=2C')},r=#{nonce}"
356
+ end
357
+
358
+ # H algorithm implementation.
359
+ #
360
+ # @api private
361
+ #
362
+ # @see http://tools.ietf.org/html/rfc5802#section-2.2
363
+ #
364
+ # @since 1.12.0
365
+ def h(string)
366
+ DIGEST.digest(string)
367
+ end
368
+
369
+ if defined?(OpenSSL::PKCS5)
370
+
371
+ # HI algorithm implementation.
372
+ #
373
+ # @api private
374
+ #
375
+ # @see http://tools.ietf.org/html/rfc5802#section-2.2
376
+ #
377
+ # @since 1.12.0
378
+ def hi(data)
379
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(data, decoded_salt, iterations, DIGEST.size)
380
+ end
381
+ else
382
+
383
+ # HI algorithm implementation.
384
+ #
385
+ # @api private
386
+ #
387
+ # @see http://tools.ietf.org/html/rfc5802#section-2.2
388
+ #
389
+ # @since 1.12.0
390
+ def hi(data)
391
+ u = hmac(data, decoded_salt + [1].pack("N"))
392
+ v = u
393
+ 2.upto(iterations) do |i|
394
+ u = hmac(data, u)
395
+ v = xor(v, u)
396
+ end
397
+ v
398
+ end
399
+ end
400
+
401
+ # HMAC algorithm implementation.
402
+ #
403
+ # @api private
404
+ #
405
+ # @see http://tools.ietf.org/html/rfc5802#section-2.2
406
+ #
407
+ # @since 1.12.0
408
+ def hmac(data, key)
409
+ OpenSSL::HMAC.digest(DIGEST, data, key)
410
+ end
411
+
412
+ # Get the iterations from the server response.
413
+ #
414
+ # @api private
415
+ #
416
+ # @since 1.12.0
417
+ def iterations
418
+ @iterations ||= payload_data.match(ITERATIONS)[1].to_i
419
+ end
420
+
421
+ # Get the data from the returned payload.
422
+ #
423
+ # @api private
424
+ #
425
+ # @since 1.12.0
426
+ def payload_data
427
+ reply[PAYLOAD].to_s
428
+ end
429
+
430
+ # Get the server nonce from the payload.
431
+ #
432
+ # @api private
433
+ #
434
+ # @since 1.12.0
435
+ def rnonce
436
+ @rnonce ||= payload_data.match(RNONCE)[1]
437
+ end
438
+
439
+ # Gets the salt from the server response.
440
+ #
441
+ # @api private
442
+ #
443
+ # @since 1.12.0
444
+ def salt
445
+ @salt ||= payload_data.match(SALT)[1]
446
+ end
447
+
448
+ # Salted password algorithm implementation.
449
+ #
450
+ # @api private
451
+ #
452
+ # @see http://tools.ietf.org/html/rfc5802#section-3
453
+ #
454
+ # @since 1.12.0
455
+ def salted_password
456
+ @salted_password ||= hi(hashed_password)
457
+ end
458
+
459
+ # Server key algorithm implementation.
460
+ #
461
+ # @api private
462
+ #
463
+ # @see http://tools.ietf.org/html/rfc5802#section-3
464
+ #
465
+ # @since 1.12.0
466
+ def server_key
467
+ @server_key ||= hmac(salted_password, SERVER_KEY)
468
+ end
469
+
470
+ if Base64.respond_to?(:strict_encode64)
471
+
472
+ # Server signature algorithm implementation.
473
+ #
474
+ # @api private
475
+ #
476
+ # @see http://tools.ietf.org/html/rfc5802#section-3
477
+ #
478
+ # @since 1.12.0
479
+ def server_signature
480
+ @server_signature ||= Base64.strict_encode64(hmac(server_key, auth_message))
481
+ end
482
+ else
483
+
484
+ # Server signature algorithm implementation.
485
+ #
486
+ # @api private
487
+ #
488
+ # @see http://tools.ietf.org/html/rfc5802#section-3
489
+ #
490
+ # @since 1.12.0
491
+ def server_signature
492
+ @server_signature ||= Base64.encode64(hmac(server_key, auth_message)).gsub("\n",'')
493
+ end
494
+ end
495
+
496
+ # Stored key algorithm implementation.
497
+ #
498
+ # @api private
499
+ #
500
+ # @see http://tools.ietf.org/html/rfc5802#section-3
501
+ #
502
+ # @since 1.12.0
503
+ def stored_key(key)
504
+ h(key)
505
+ end
506
+
507
+ # Get the verifier token from the server response.
508
+ #
509
+ # @api private
510
+ #
511
+ # @since 1.12.0
512
+ def verifier
513
+ @verifier ||= payload_data.match(VERIFIER)[1]
514
+ end
515
+
516
+ # Get the without proof message.
517
+ #
518
+ # @api private
519
+ #
520
+ # @see http://tools.ietf.org/html/rfc5802#section-7
521
+ #
522
+ # @since 1.12.0
523
+ def without_proof
524
+ @without_proof ||= "c=biws,r=#{rnonce}"
525
+ end
526
+
527
+ # XOR operation for two strings.
528
+ #
529
+ # @api private
530
+ #
531
+ # @since 1.12.0
532
+ def xor(first, second)
533
+ first.bytes.zip(second.bytes).map{ |(a,b)| (a ^ b).chr }.join('')
534
+ end
535
+
536
+ def validate_final_message!(reply)
537
+ validate!(reply)
538
+ unless verifier == server_signature
539
+ raise InvalidSignature.new(verifier, server_signature)
540
+ end
541
+ end
542
+
543
+ def validate_first_message!(reply)
544
+ validate!(reply)
545
+ raise InvalidNonce.new(nonce, rnonce) unless rnonce.start_with?(nonce)
546
+ end
547
+
548
+ def validate!(reply)
549
+ unless Support.ok?(reply)
550
+ raise AuthenticationError, "Could not authorize user #{auth[:username]} on database #{auth[:db_name]}."
551
+ end
552
+ @reply = reply
553
+ end
554
+ end
555
+ end
556
+ end