PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha1AuthenticatorTests.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 312 lines | 255 code | 39 blank | 18 comment | 13 complexity | cf4887334ce2a17f336db9264b1416a4 MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Copyright 2013-present 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. using System;
  16. using System.Collections.Generic;
  17. using System.Net;
  18. using System.Threading;
  19. using FluentAssertions;
  20. using MongoDB.Bson;
  21. using MongoDB.Driver.Core.Clusters;
  22. using MongoDB.Driver.Core.Servers;
  23. using MongoDB.Driver.Core.Helpers;
  24. using MongoDB.Driver.Core.Misc;
  25. using MongoDB.Driver.Core.WireProtocol.Messages;
  26. using Xunit;
  27. using MongoDB.Driver.Core.Connections;
  28. using MongoDB.Bson.TestHelpers.XunitExtensions;
  29. using System.Linq;
  30. namespace MongoDB.Driver.Core.Authentication
  31. {
  32. public class ScramSha1AuthenticatorTests
  33. {
  34. private static readonly UsernamePasswordCredential __credential = new UsernamePasswordCredential("source", "user", "pencil");
  35. private static readonly ClusterId __clusterId = new ClusterId();
  36. private static readonly ServerId __serverId = new ServerId(__clusterId, new DnsEndPoint("localhost", 27017));
  37. private static readonly ConnectionDescription __description = new ConnectionDescription(
  38. new ConnectionId(__serverId),
  39. new IsMasterResult(new BsonDocument("ok", 1).Add("ismaster", 1)),
  40. new BuildInfoResult(new BsonDocument("version", "2.6.0")));
  41. [Fact]
  42. public void Constructor_should_throw_an_ArgumentNullException_when_credential_is_null()
  43. {
  44. Action act = () => new ScramSha1Authenticator(null);
  45. act.ShouldThrow<ArgumentNullException>();
  46. }
  47. [Theory]
  48. [ParameterAttributeData]
  49. public void Authenticate_should_throw_an_AuthenticationException_when_authentication_fails(
  50. [Values(false, true)]
  51. bool async)
  52. {
  53. var subject = new ScramSha1Authenticator(__credential);
  54. var reply = MessageHelper.BuildNoDocumentsReturnedReply<RawBsonDocument>();
  55. var connection = new MockConnection(__serverId);
  56. connection.EnqueueReplyMessage(reply);
  57. Action act;
  58. if (async)
  59. {
  60. act = () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
  61. }
  62. else
  63. {
  64. act = () => subject.Authenticate(connection, __description, CancellationToken.None);
  65. }
  66. act.ShouldThrow<MongoAuthenticationException>();
  67. }
  68. [Theory]
  69. [ParameterAttributeData]
  70. public void Authenticate_should_throw_when_server_provides_invalid_r_value(
  71. [Values(false, true)]
  72. bool async)
  73. {
  74. var randomStringGenerator = new ConstantRandomStringGenerator("fyko+d2lbbFgONRv9qkxdawL");
  75. var subject = new ScramSha1Authenticator(__credential, randomStringGenerator);
  76. var saslStartReply = MessageHelper.BuildReply<RawBsonDocument>(
  77. RawBsonDocumentHelper.FromJson("{conversationId: 1, payload: BinData(0,'cj1meWtvLWQybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw'), done: false, ok: 1}"));
  78. var connection = new MockConnection(__serverId);
  79. connection.EnqueueReplyMessage(saslStartReply);
  80. Action act;
  81. if (async)
  82. {
  83. act = () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
  84. }
  85. else
  86. {
  87. act = () => subject.Authenticate(connection, __description, CancellationToken.None);
  88. }
  89. act.ShouldThrow<MongoAuthenticationException>();
  90. }
  91. [Theory]
  92. [ParameterAttributeData]
  93. public void Authenticate_should_throw_when_server_provides_invalid_serverSignature(
  94. [Values(false, true)]
  95. bool async)
  96. {
  97. var randomStringGenerator = new ConstantRandomStringGenerator("fyko+d2lbbFgONRv9qkxdawL");
  98. var subject = new ScramSha1Authenticator(__credential, randomStringGenerator);
  99. var saslStartReply = MessageHelper.BuildReply<RawBsonDocument>(
  100. RawBsonDocumentHelper.FromJson("{conversationId: 1, payload: BinData(0,'cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw'), done: false, ok: 1}"));
  101. var saslContinueReply = MessageHelper.BuildReply<RawBsonDocument>(
  102. RawBsonDocumentHelper.FromJson("{conversationId: 1, payload: BinData(0,'dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTBh'), done: true, ok: 1}"));
  103. var connection = new MockConnection(__serverId);
  104. connection.EnqueueReplyMessage(saslStartReply);
  105. connection.EnqueueReplyMessage(saslContinueReply);
  106. Action act;
  107. if (async)
  108. {
  109. act = () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
  110. }
  111. else
  112. {
  113. act = () => subject.Authenticate(connection, __description, CancellationToken.None);
  114. }
  115. act.ShouldThrow<MongoAuthenticationException>();
  116. }
  117. [Theory]
  118. [ParameterAttributeData]
  119. public void Authenticate_should_not_throw_when_authentication_succeeds(
  120. [Values(false, true)] bool useSpeculativeAuthenticate,
  121. [Values(false, true)] bool useLongAuthentication,
  122. [Values(false, true)] bool async)
  123. {
  124. var randomStringGenerator = new ConstantRandomStringGenerator("fyko+d2lbbFgONRv9qkxdawL");
  125. var subject = new ScramSha1Authenticator(__credential, randomStringGenerator);
  126. var saslStartReply = MessageHelper.BuildReply<RawBsonDocument>(
  127. RawBsonDocumentHelper.FromJson("{ conversationId : 1, payload : BinData(0,'cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw'), done : false, ok : 1}"));
  128. var saslContinueReply = MessageHelper.BuildReply<RawBsonDocument>(RawBsonDocumentHelper.FromJson(
  129. @"{ conversationId : 1,
  130. payload : BinData(0,'dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9')," +
  131. $" done : {new BsonBoolean(!useLongAuthentication)}, " +
  132. @" ok : 1}"));
  133. var saslLastStepReply = MessageHelper.BuildReply<RawBsonDocument>(RawBsonDocumentHelper.FromJson(
  134. @"{ conversationId : 1,
  135. payload : BinData(0,''),
  136. done : true,
  137. ok : 1 }"));
  138. var connection = new MockConnection(__serverId);
  139. var isMasterResult = (BsonDocument)__description.IsMasterResult.Wrapped.Clone();
  140. if (useSpeculativeAuthenticate)
  141. {
  142. isMasterResult.Add("speculativeAuthenticate", saslStartReply.Documents[0].ToBsonDocument());
  143. }
  144. /* set buildInfoResult to 3.4 to force authenticator to use Query Message Wire Protocol because MockConnection
  145. * does not support OP_MSG */
  146. connection.Description = new ConnectionDescription(
  147. __description.ConnectionId, new IsMasterResult(isMasterResult), new BuildInfoResult(new BsonDocument("version", "3.4")));
  148. BsonDocument isMasterCommand = null;
  149. if (useSpeculativeAuthenticate)
  150. {
  151. // Call CustomizeIsMasterCommand so that the authenticator thinks its started to speculatively
  152. // authenticate
  153. isMasterCommand = subject.CustomizeInitialIsMasterCommand(new BsonDocument { { "isMaster", 1 } });
  154. }
  155. else
  156. {
  157. connection.EnqueueReplyMessage(saslStartReply);
  158. }
  159. connection.EnqueueReplyMessage(saslContinueReply);
  160. if (useLongAuthentication)
  161. {
  162. connection.EnqueueReplyMessage(saslLastStepReply);
  163. }
  164. var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1;
  165. Exception exception;
  166. if (async)
  167. {
  168. exception = Record.Exception(
  169. () => subject.AuthenticateAsync(connection, connection.Description, CancellationToken.None)
  170. .GetAwaiter().GetResult());
  171. }
  172. else
  173. {
  174. exception = Record.Exception(
  175. () => subject.Authenticate(connection, connection.Description, CancellationToken.None));
  176. }
  177. exception.Should().BeNull();
  178. var expectedSentMessageCount = 3 - (useLongAuthentication ? 0 : 1) - (useSpeculativeAuthenticate ? 1 : 0);
  179. SpinWait.SpinUntil(
  180. () => connection.GetSentMessages().Count >= expectedSentMessageCount,
  181. TimeSpan.FromSeconds(5)
  182. ).Should().BeTrue();
  183. var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());
  184. sentMessages.Count.Should().Be(expectedSentMessageCount);
  185. var actualRequestIds = sentMessages.Select(m => m["requestId"].AsInt32).ToList();
  186. for (var i = 0; i != actualRequestIds.Count; ++i)
  187. {
  188. actualRequestIds[i].Should().BeInRange(expectedRequestId + i, expectedRequestId + 10 + i);
  189. }
  190. var expectedMessages = new List<BsonDocument>();
  191. var saslStartMessage = BsonDocument.Parse(
  192. @"{ opcode : 'query'," +
  193. $" requestId : {actualRequestIds[0]}, " +
  194. @" database : 'source',
  195. collection : '$cmd',
  196. batchSize : -1,
  197. slaveOk : true,
  198. query : { saslStart : 1,
  199. mechanism : 'SCRAM-SHA-1'," +
  200. $" payload : new BinData(0, 'biwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM')" +
  201. @" options : { skipEmptyExchange: true }}}");
  202. if (!useSpeculativeAuthenticate)
  203. {
  204. expectedMessages.Add(saslStartMessage);
  205. }
  206. var saslContinueMessage = BsonDocument.Parse(
  207. @"{ opcode : 'query'," +
  208. $" requestId : {(useSpeculativeAuthenticate ? actualRequestIds[0] : actualRequestIds[1])}," +
  209. @" database : 'source',
  210. collection : '$cmd',
  211. batchSize : -1,
  212. slaveOk : true,
  213. query : { saslContinue : 1,
  214. conversationId : 1, " +
  215. $" payload : new BinData(0, 'Yz1iaXdzLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMSG8rVmdrN3F2VU9LVXd1V0xJV2c0bC85U3JhR01IRUUscD1NQzJUOEJ2Ym1XUmNrRHc4b1dsNUlWZ2h3Q1k9')}}}}");
  216. expectedMessages.Add(saslContinueMessage);
  217. if (useLongAuthentication)
  218. {
  219. var saslOptionalFinalMessage = BsonDocument.Parse(
  220. @"{opcode : 'query'," +
  221. $" requestId : {(useSpeculativeAuthenticate ? actualRequestIds[1] : actualRequestIds[2])}," +
  222. @" database : 'source',
  223. collection : '$cmd',
  224. batchSize : -1,
  225. slaveOk : true,
  226. query : { saslContinue : 1,
  227. conversationId : 1, " +
  228. $" payload : new BinData(0, '')}}}}");
  229. expectedMessages.Add(saslOptionalFinalMessage);
  230. }
  231. sentMessages.Should().Equal(expectedMessages);
  232. if (useSpeculativeAuthenticate)
  233. {
  234. isMasterCommand.Should().Contain("speculativeAuthenticate");
  235. var speculativeAuthenticateDocument = isMasterCommand["speculativeAuthenticate"].AsBsonDocument;
  236. var expectedSpeculativeAuthenticateDocument =
  237. saslStartMessage["query"].AsBsonDocument.Add("db", __credential.Source);
  238. speculativeAuthenticateDocument.Should().Be(expectedSpeculativeAuthenticateDocument);
  239. }
  240. }
  241. [Theory]
  242. [ParameterAttributeData]
  243. public void Authenticate_should_use_cache(
  244. [Values(false, true)] bool async)
  245. {
  246. var randomStringGenerator = new ConstantRandomStringGenerator("fyko+d2lbbFgONRv9qkxdawL");
  247. var subject = new ScramSha1Authenticator(__credential, randomStringGenerator);
  248. var saslStartReply = MessageHelper.BuildReply<RawBsonDocument>(
  249. RawBsonDocumentHelper.FromJson(
  250. "{conversationId: 1, payload: BinData(0,'cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw'), done: false, ok: 1}"));
  251. var saslContinueReply = MessageHelper.BuildReply<RawBsonDocument>(
  252. RawBsonDocumentHelper.FromJson(
  253. "{conversationId: 1, payload: BinData(0,'dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9'), done: true, ok: 1}"));
  254. var connection = new MockConnection(__serverId);
  255. connection.EnqueueReplyMessage(saslStartReply);
  256. connection.EnqueueReplyMessage(saslContinueReply);
  257. if (async)
  258. {
  259. subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter()
  260. .GetResult();
  261. }
  262. else
  263. {
  264. subject.Authenticate(connection, __description, CancellationToken.None);
  265. }
  266. SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 2, TimeSpan.FromSeconds(5)).Should()
  267. .BeTrue();
  268. subject._cache().Should().NotBe(null);
  269. subject._cache()._cacheKey().Should().NotBe(null);
  270. subject._cache()._cachedEntry().Should().NotBe(null);
  271. }
  272. }
  273. }