public void Authenticate_should_throw_an_AuthenticationException_when_authentication_fails(
            [Values("MongoConnectionException", "MongoNotPrimaryException")] string exceptionName,
            [Values(false, true)] bool async)
        {
            var subject = new ScramSha256Authenticator(__credential, serverApi: null);

            var responseException = CoreExceptionHelper.CreateException(exceptionName);
            var connection        = new MockConnection(__serverId);

            connection.EnqueueCommandResponseMessage(responseException);
            connection.Description = __descriptionCommandWireProtocol;

            Exception exception;

            if (async)
            {
                exception = Record.Exception(() => subject.AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None).GetAwaiter().GetResult());
            }
            else
            {
                exception = Record.Exception(() => subject.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None));
            }

            exception.Should().BeOfType <MongoAuthenticationException>();
        }
        public void Authenticate_should_throw_when_server_provides_invalid_r_value(
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject                       = new ScramSha256Authenticator(__credential, randomStringGenerator);
            var poisonedSaslStart             = PoisonSaslMessage(message: __clientRequest1, poison: "bluePill");
            var poisonedSaslStartReply        = CreateSaslStartReply(poisonedSaslStart, _serverNonce, _serverSalt, _iterationCount);
            var poisonedSaslStartReplyMessage = MessageHelper.BuildReply(RawBsonDocumentHelper.FromJson(
                                                                             @"{conversationId: 1, " +
                                                                             $" payload: BinData(0,\"{ToUtf8Base64(poisonedSaslStartReply)}\")," +
                                                                             @" done: false,
                   ok: 1}"));

            var connection = new MockConnection(__serverId);

            connection.EnqueueReplyMessage(poisonedSaslStartReplyMessage);

            var act = async
                ? () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult()
                : (Action)(() => subject.Authenticate(connection, __description, CancellationToken.None));

            var exception = Record.Exception(act);

            exception.Should().BeOfType <MongoAuthenticationException>();
        }
Example #3
0
        public void Authenticate_should_send_serverApi_with_command_wire_protocol_if_serverApi_is_provided(
            [Values(false, true)] bool useServerApi,
            [Values(false, true)] bool async)
        {
            var serverApi             = useServerApi ? new ServerApi(ServerApiVersion.V1, true, true) : null;
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi);

            var connection        = new MockConnection(__serverId);
            var saslStartReply    = RawBsonDocumentHelper.FromJson($"{{ conversationId : 1, payload : BinData(0,'{ToUtf8Base64(__serverResponse1)}'), done : false, ok : 1 }}");
            var saslContinueReply = RawBsonDocumentHelper.FromJson($"{{ conversationId : 1, payload : BinData(0,'{ToUtf8Base64(__serverResponse2)}'), done : true, ok : 1}}");

            if (useServerApi)
            {
                connection.EnqueueCommandResponseMessage(MessageHelper.BuildCommandResponse(saslStartReply));
                connection.EnqueueCommandResponseMessage(MessageHelper.BuildCommandResponse(saslContinueReply));
            }
            else
            {
                connection.EnqueueReplyMessage(MessageHelper.BuildReply(saslStartReply));
                connection.EnqueueReplyMessage(MessageHelper.BuildReply(saslContinueReply));
            }

            connection.Description = __descriptionQueryWireProtocol;

            if (async)
            {
                subject.AuthenticateAsync(connection, __descriptionQueryWireProtocol, CancellationToken.None).GetAwaiter().GetResult();
            }
            else
            {
                subject.Authenticate(connection, __descriptionQueryWireProtocol, CancellationToken.None);
            }

            SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 2, TimeSpan.FromSeconds(5)).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(2);

            var actualRequestId0 = sentMessages[0]["requestId"].AsInt32;
            var actualRequestId1 = sentMessages[1]["requestId"].AsInt32;

            if (useServerApi)
            {
                sentMessages[0].Should().Be($"{{opcode : \"opmsg\", requestId : {actualRequestId0}, responseTo : 0, sections : [{{ payloadType : 0, document : {{ saslStart : 1, mechanism : \"SCRAM-SHA-256\", payload : new BinData(0, \"biwsbj11c2VyLHI9ck9wck5HZndFYmVSV2diTkVrcU8=\"), options : {{ skipEmptyExchange : true }}, \"$db\" : \"source\", apiVersion : \"1\", apiStrict : true, apiDeprecationErrors : true }} }}]}}");
                sentMessages[1].Should().Be($"{{opcode : \"opmsg\", requestId : {actualRequestId1}, responseTo : 0, sections : [{{ payloadType : 0, document : {{ saslContinue : 1, conversationId : 1, payload : new BinData(0, \"Yz1iaXdzLHI9ck9wck5HZndFYmVSV2diTkVrcU8laHZZRHBXVWEyUmFUQ0FmdXhGSWxqKWhObEYkazAscD1kSHpiWmFwV0lrNGpVaE4rVXRlOXl0YWc5empmTUhnc3FtbWl6N0FuZFZRPQ==\"), \"$db\" : \"source\", apiVersion : \"1\", apiStrict : true, apiDeprecationErrors : true }} }}]}}");
            }
            else
            {
                sentMessages[0].Should().Be($"{{ opcode : \"query\", requestId : {actualRequestId0}, database : \"source\", collection : \"$cmd\", batchSize : -1, slaveOk : true, query : {{ saslStart : 1, mechanism : \"SCRAM-SHA-256\", payload : new BinData(0, \"{ToUtf8Base64(__clientRequest1)}\"), options : {{ \"skipEmptyExchange\" : true }} }} }}");
                sentMessages[1].Should().Be($"{{ opcode : \"query\", requestId : {actualRequestId1}, database : \"source\", collection : \"$cmd\", batchSize : -1, slaveOk : true, query : {{ saslContinue : 1, conversationId : 1, payload : new BinData(0, \"{ToUtf8Base64(__clientRequest2)}\") }} }}");
            }
        }
Example #4
0
 private static void Authenticate(
     ScramSha256Authenticator authenticator,
     IConnection connection,
     ConnectionDescription description,
     bool async)
 {
     if (async)
     {
         authenticator.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
     }
     authenticator.Authenticate(connection, __description, CancellationToken.None);
 }
 private static void Authenticate(
     ScramSha256Authenticator authenticator,
     IConnection connection,
     bool async)
 {
     if (async)
     {
         authenticator
         .AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None)
         .GetAwaiter()
         .GetResult();
     }
     else
     {
         authenticator.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None);
     }
 }
Example #6
0
        public void Authenticate_should_work_regardless_of_culture(
            [Values("da-DK", "en-US")] string name,
            [Values(false, true)] bool async)
        {
            SetCultureAndResetAfterTest(name, () =>
            {
                var randomStringGenerator = new ConstantRandomStringGenerator("a");

                // ScramSha1Authenticator will have exactly the same code paths
                var subject        = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null);
                var mockConnection = new MockConnection();

                var payload1                   = $"r=aa,s={_serverSalt},i=1";
                var serverResponse1            = $"{{ ok : 1, payload : BinData(0,\"{ToUtf8Base64(payload1)}\"), done : true, conversationId : 1 }}";
                var serverResponseRawDocument1 = RawBsonDocumentHelper.FromJson(serverResponse1);
                var serverResponseMessage1     = MessageHelper.BuildCommandResponse(serverResponseRawDocument1);

                var payload2                   = $"v=v1wZS02d7kZVSzuKoB7TuI+jIpSsKvnQUkU9Oqj2t+w=";
                var serverResponse2            = $"{{ ok : 1, payload : BinData(0,\"{ToUtf8Base64(payload2)}\"), done : true }}";
                var serverResponseRawDocument2 = RawBsonDocumentHelper.FromJson(serverResponse2);
                var serverResponseMessage2     = MessageHelper.BuildCommandResponse(serverResponseRawDocument2);

                mockConnection.EnqueueCommandResponseMessage(serverResponseMessage1);
                mockConnection.EnqueueCommandResponseMessage(serverResponseMessage2);

                mockConnection.Description = __descriptionCommandWireProtocol;

                Authenticate(subject, mockConnection, async);
            });

            void SetCultureAndResetAfterTest(string cultureName, Action test)
            {
                var originalCulture = Thread.CurrentThread.CurrentCulture;

                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);

                try
                {
                    test();
                }
                finally
                {
                    Thread.CurrentThread.CurrentCulture = originalCulture;
                }
            }
        }
        public void Authenticate_should_throw_an_AuthenticationException_when_authentication_fails(
            [Values(false, true)] bool async)
        {
            var subject = new ScramSha256Authenticator(__credential);

            var reply      = MessageHelper.BuildNoDocumentsReturnedReply <RawBsonDocument>();
            var connection = new MockConnection(__serverId);

            connection.EnqueueReplyMessage(reply);

            var act = async
                ? () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult()
                : (Action)(() => subject.Authenticate(connection, __description, CancellationToken.None));

            var exception = Record.Exception(act);

            exception.Should().BeOfType <MongoAuthenticationException>();
        }
Example #8
0
        public void Authenticate_should_use_cache(
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null);

            var saslStartResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                           @"{conversationId: 1," +
                                                                           $" payload: BinData(0,\"{ToUtf8Base64(__serverResponse1)}\")," +
                                                                           @" done: false,
                   ok: 1}"));
            var saslContinueResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                              @"{conversationId: 1," +
                                                                              $" payload: BinData(0,\"{ToUtf8Base64(__serverResponse2)}\")," +
                                                                              @" done: true,
                   ok: 1}"));

            var connection = new MockConnection(__serverId);

            connection.EnqueueCommandResponseMessage(saslStartResponse);
            connection.EnqueueCommandResponseMessage(saslContinueResponse);
            connection.Description = __descriptionCommandWireProtocol;

            if (async)
            {
                subject.AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None)
                .GetAwaiter()
                .GetResult();
            }
            else
            {
                subject.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None);
            }

            SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 2, TimeSpan.FromSeconds(5))
            .Should()
            .BeTrue();

            subject._cache().Should().NotBe(null);
            subject._cache()._cacheKey().Should().NotBe(null);
            subject._cache()._cachedEntry().Should().NotBe(null);
        }
        public void Authenticate_should_send_serverApi_with_command_wire_protocol(
            [Values(false, true)] bool useServerApi,
            [Values(false, true)] bool async)
        {
            var serverApi             = useServerApi ? new ServerApi(ServerApiVersion.V1, true, true) : null;
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);

            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi);

            var connection        = new MockConnection(__serverId);
            var saslStartResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson($"{{ conversationId : 1, payload : BinData(0,'{ToUtf8Base64(__serverResponse1)}'), done : false, ok : 1 }}"));

            connection.EnqueueCommandResponseMessage(saslStartResponse);
            var saslContinueResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson($"{{ conversationId : 1, payload : BinData(0,'{ToUtf8Base64(__serverResponse2)}'), done : true, ok : 1}}"));

            connection.EnqueueCommandResponseMessage(saslContinueResponse);
            connection.Description = __descriptionCommandWireProtocol;

            if (async)
            {
                subject.AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None).GetAwaiter().GetResult();
            }
            else
            {
                subject.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None);
            }

            SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 2, TimeSpan.FromSeconds(5)).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(2);

            var actualRequestId0        = sentMessages[0]["requestId"].AsInt32;
            var actualRequestId1        = sentMessages[1]["requestId"].AsInt32;
            var expectedServerApiString = useServerApi ? ", apiVersion : \"1\", apiStrict : true, apiDeprecationErrors : true" : "";

            sentMessages[0].Should().Be($"{{ opcode : \"opmsg\", requestId : {actualRequestId0}, responseTo : 0, sections : [ {{ payloadType : 0, document : {{ saslStart : 1, mechanism : \"SCRAM-SHA-256\", payload : new BinData(0, \"{ToUtf8Base64(__clientRequest1)}\"), options : {{ \"skipEmptyExchange\" : true }}, $db : \"source\"{expectedServerApiString} }} }} ] }}");
            sentMessages[1].Should().Be($"{{ opcode : \"opmsg\", requestId : {actualRequestId1}, responseTo : 0, sections : [ {{ payloadType : 0, document : {{ saslContinue : 1, conversationId : 1, payload : new BinData(0, \"{ToUtf8Base64(__clientRequest2)}\"), $db : \"source\"{expectedServerApiString} }} }} ] }}");
        }
        public void Authenticate_should_throw_when_server_provides_invalid_serverSignature(
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null);

            var saslStartReply            = CreateSaslStartReply(__clientRequest1, _serverNonce, _serverSalt, _iterationCount);
            var poisonedSaslContinueReply = PoisonSaslMessage(message: __serverResponse2, poison: "redApple");
            var saslStartResponseMessage  = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                                   @"{conversationId: 1, " +
                                                                                   $" payload: BinData(0,\"{ToUtf8Base64(saslStartReply)}\")," +
                                                                                   @" done: false,
                   ok: 1}"));
            var poisonedSaslContinueResponseMessage = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                                             @"{conversationId: 1, " +
                                                                                             $" payload: BinData(0,\"{ToUtf8Base64(poisonedSaslContinueReply)}\")," +
                                                                                             @" done: true,
                   ok: 1}"));

            var connection = new MockConnection(__serverId);

            connection.EnqueueCommandResponseMessage(saslStartResponseMessage);
            connection.EnqueueCommandResponseMessage(poisonedSaslContinueResponseMessage);
            connection.Description = __descriptionCommandWireProtocol;

            Action act;

            if (async)
            {
                act = () => subject.AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None).GetAwaiter().GetResult();
            }
            else
            {
                act = () => subject.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None);
            }

            var exception = Record.Exception(act);

            exception.Should().BeOfType <MongoAuthenticationException>();
        }
        public void Authenticate_should_not_throw_when_authentication_succeeds(
            [Values(false, true)] bool useLongAuthentication,
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator);

            var saslStartReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                @"{conversationId: 1," +
                                                                                $" payload: BinData(0,\"{ToUtf8Base64(__serverResponse1)}\")," +
                                                                                @" done: false,
                   ok: 1}"));
            var saslContinueReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                   @"{conversationId: 1," +
                                                                                   $" payload: BinData(0,\"{ToUtf8Base64(__serverResponse2)}\")," +
                                                                                   $" done: {new BsonBoolean(!useLongAuthentication)}," +
                                                                                   @"   ok: 1}"));
            var saslLastStepReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                   @"{conversationId: 1," +
                                                                                   $" payload: BinData(0,\"{ToUtf8Base64(__serverOptionalFinalResponse)}\")," +
                                                                                   @" done: true,
                   ok: 1}"));

            var connection = new MockConnection(__serverId);

            connection.EnqueueReplyMessage(saslStartReply);
            connection.EnqueueReplyMessage(saslContinueReply);
            if (useLongAuthentication)
            {
                connection.EnqueueReplyMessage(saslLastStepReply);
            }

            var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1;

            Action act;

            if (async)
            {
                act = () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
            }
            else
            {
                act = () => subject.Authenticate(connection, __description, CancellationToken.None);
            }

            var exception = Record.Exception(act);

            exception.Should().BeNull();
            var expectedSentMessageCount = useLongAuthentication ? 3 : 2;

            SpinWait.SpinUntil(
                () => connection.GetSentMessages().Count >= expectedSentMessageCount,
                TimeSpan.FromSeconds(5)
                ).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(expectedSentMessageCount);

            var actualRequestIds = sentMessages.Select(m => m["requestId"].AsInt32).ToList();

            for (var i = 0; i != actualRequestIds.Count; ++i)
            {
                actualRequestIds[i].Should().BeInRange(expectedRequestId + i, expectedRequestId + 10 + i);
            }

            sentMessages[0].Should().Be(
                @"{opcode: ""query""," +
                $" requestId: {actualRequestIds[0]}," +
                @" database: ""source"",
                   collection: ""$cmd"",
                   batchSize: -1,
                   slaveOk: true,
                   query: {saslStart: 1,
                           mechanism: ""SCRAM-SHA-256""," +
                $"         payload: new BinData(0, \"{ToUtf8Base64(__clientRequest1)}\")" +
                @"         options: { skipEmptyExchange: true }}}");
            sentMessages[1].Should().Be(
                @"{opcode: ""query""," +
                $" requestId: {actualRequestIds[1]}," +
                @" database: ""source"",
                   collection: ""$cmd"",
                   batchSize: -1,
                   slaveOk: true,
                   query: {saslContinue: 1,
                           conversationId: 1, " +
                $"         payload: new BinData(0, \"{ToUtf8Base64(__clientRequest2)}\")}}}}");
            if (useLongAuthentication)
            {
                sentMessages[2].Should().Be(
                    @"{opcode: ""query""," +
                    $" requestId: {actualRequestIds[2]}," +
                    @" database: ""source"",
                       collection: ""$cmd"",
                       batchSize: -1,
                       slaveOk: true,
                       query: {saslContinue: 1,
                               conversationId: 1, " +
                    $"         payload: new BinData(0, \"{ToUtf8Base64(__clientOptionalFinalRequest)}\")}}}}");
            }
        }
        public void Authenticate_should_not_throw_when_authentication_succeeds(
            [Values(false, true)] bool useSpeculativeAuthenticate,
            [Values(false, true)] bool useLongAuthentication,
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null);

            var saslStartReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                @"{ conversationId : 1," +
                                                                                $"  payload : BinData(0,'{ToUtf8Base64(__serverResponse1)}')," +
                                                                                @"  done : false,
                    ok : 1 }"));
            var saslContinueReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                   @"{ conversationId : 1," +
                                                                                   $"  payload : BinData(0,'{ToUtf8Base64(__serverResponse2)}')," +
                                                                                   $"  done : {new BsonBoolean(!useLongAuthentication)}," +
                                                                                   @"  ok : 1 }"));
            var saslLastStepReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                   @"{ conversationId : 1," +
                                                                                   $"  payload : BinData(0,'{ToUtf8Base64(__serverOptionalFinalResponse)}')," +
                                                                                   @"  done : true,
                    ok : 1 }"));

            var connection     = new MockConnection(__serverId);
            var isMasterResult = (BsonDocument)__descriptionQueryWireProtocol.IsMasterResult.Wrapped.Clone();

            if (useSpeculativeAuthenticate)
            {
                isMasterResult.Add("speculativeAuthenticate", saslStartReply.Documents[0].ToBsonDocument());
            }

            /* set buildInfoResult to 3.4 to force authenticator to use Query Message Wire Protocol because MockConnection
             * does not support OP_MSG */
            connection.Description = new ConnectionDescription(
                __descriptionQueryWireProtocol.ConnectionId,
                new IsMasterResult(isMasterResult),
                new BuildInfoResult(new BsonDocument("version", "3.4")));

            BsonDocument isMasterCommand = null;

            if (useSpeculativeAuthenticate)
            {
                // We must call CustomizeIsMasterCommand so that the authenticator thinks its started to speculatively
                // authenticate
                isMasterCommand = subject.CustomizeInitialIsMasterCommand(new BsonDocument {
                    { "isMaster", 1 }
                });
            }
            else
            {
                connection.EnqueueReplyMessage(saslStartReply);
            }

            connection.EnqueueReplyMessage(saslContinueReply);
            if (useLongAuthentication)
            {
                connection.EnqueueReplyMessage(saslLastStepReply);
            }

            var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1;

            Exception exception;

            if (async)
            {
                exception = Record.Exception(
                    () => subject.AuthenticateAsync(connection, connection.Description, CancellationToken.None)
                    .GetAwaiter().GetResult());
            }
            else
            {
                exception = Record.Exception(
                    () => subject.Authenticate(connection, connection.Description, CancellationToken.None));
            }

            exception.Should().BeNull();
            var expectedSentMessageCount = 3 - (useLongAuthentication ? 0 : 1) - (useSpeculativeAuthenticate ? 1 : 0);

            SpinWait.SpinUntil(
                () => connection.GetSentMessages().Count >= expectedSentMessageCount,
                TimeSpan.FromSeconds(5)
                ).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(expectedSentMessageCount);

            var actualRequestIds = sentMessages.Select(m => m["requestId"].AsInt32).ToList();

            for (var i = 0; i != actualRequestIds.Count; ++i)
            {
                actualRequestIds[i].Should().BeInRange(expectedRequestId + i, expectedRequestId + 10 + i);
            }

            var expectedMessages = new List <BsonDocument>();

            var saslStartMessage = BsonDocument.Parse(
                @"{ opcode : 'query'," +
                $"  requestId : {actualRequestIds[0]}," +
                @"  database : 'source',
                    collection : '$cmd',
                    batchSize : -1,
                    slaveOk : true,
                    query : { saslStart : 1,
                              mechanism : 'SCRAM-SHA-256'," +
                $"            payload : new BinData(0, '{ToUtf8Base64(__clientRequest1)}')" +
                @"            options : { skipEmptyExchange: true }}}");

            if (!useSpeculativeAuthenticate)
            {
                expectedMessages.Add(saslStartMessage);
            }

            var saslContinueMessage = BsonDocument.Parse(
                @"{ opcode : 'query'," +
                $"  requestId : {(useSpeculativeAuthenticate ? actualRequestIds[0] : actualRequestIds[1])}," +
                @"  database : 'source',
                    collection : '$cmd',
                    batchSize : -1,
                    slaveOk : true,
                    query : { saslContinue : 1,
                              conversationId : 1, " +
                $"            payload : new BinData(0, \"{ToUtf8Base64(__clientRequest2)}\")}}}}");

            expectedMessages.Add(saslContinueMessage);

            if (useLongAuthentication)
            {
                var saslOptionalFinalMessage = BsonDocument.Parse(
                    @"{ opcode : 'query'," +
                    $"  requestId : {(useSpeculativeAuthenticate ? actualRequestIds[1] : actualRequestIds[2])}," +
                    @"  database : 'source',
                        collection : '$cmd',
                        batchSize : -1,
                        slaveOk : true,
                        query : { saslContinue : 1,
                                  conversationId : 1, " +
                    $"            payload : new BinData(0, '{ToUtf8Base64(__clientOptionalFinalRequest)}')}}}}");
                expectedMessages.Add(saslOptionalFinalMessage);
            }

            sentMessages.Should().Equal(expectedMessages);
            if (useSpeculativeAuthenticate)
            {
                isMasterCommand.Should().Contain("speculativeAuthenticate");
                var speculativeAuthenticateDocument         = isMasterCommand["speculativeAuthenticate"].AsBsonDocument;
                var expectedSpeculativeAuthenticateDocument =
                    saslStartMessage["query"].AsBsonDocument.Add("db", __credential.Source);
                speculativeAuthenticateDocument.Should().Be(expectedSpeculativeAuthenticateDocument);
            }
        }
        public void Authenticate_should_not_throw_when_authentication_succeeds(
            [Values(false, true)] bool useSpeculativeAuthenticate,
            [Values(false, true)] bool useLongAuthentication,
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null);

            var saslStartResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                           @"{ conversationId : 1," +
                                                                           $"  payload : BinData(0,'{ToUtf8Base64(__serverResponse1)}')," +
                                                                           @"  done : false,
                    ok : 1 }"));
            var saslContinueResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                              @"{ conversationId : 1," +
                                                                              $"  payload : BinData(0,'{ToUtf8Base64(__serverResponse2)}')," +
                                                                              $"  done : {new BsonBoolean(!useLongAuthentication)}," +
                                                                              @"  ok : 1 }"));
            var saslLastStepResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(
                                                                              @"{ conversationId : 1," +
                                                                              $"  payload : BinData(0,'{ToUtf8Base64(__serverOptionalFinalResponse)}')," +
                                                                              @"  done : true,
                    ok : 1 }"));

            var connection  = new MockConnection(__serverId);
            var helloResult = (BsonDocument)__descriptionCommandWireProtocol.HelloResult.Wrapped.Clone();

            if (useSpeculativeAuthenticate)
            {
                helloResult.Add("speculativeAuthenticate", ((Type0CommandMessageSection <RawBsonDocument>)saslStartResponse.WrappedMessage.Sections[0]).Document);
            }

            connection.Description = new ConnectionDescription(__descriptionCommandWireProtocol.ConnectionId, new HelloResult(helloResult));

            BsonDocument helloCommand = null;

            if (useSpeculativeAuthenticate)
            {
                // We must call CustomizeInitialHelloCommand so that the authenticator thinks its started to speculatively
                // authenticate
                helloCommand = subject.CustomizeInitialHelloCommand(new BsonDocument {
                    { OppressiveLanguageConstants.LegacyHelloCommandName, 1 }
                });
            }
            else
            {
                connection.EnqueueCommandResponseMessage(saslStartResponse);
            }

            connection.EnqueueCommandResponseMessage(saslContinueResponse);
            if (useLongAuthentication)
            {
                connection.EnqueueCommandResponseMessage(saslLastStepResponse);
            }

            var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1;

            Exception exception;

            if (async)
            {
                exception = Record.Exception(
                    () => subject.AuthenticateAsync(connection, connection.Description, CancellationToken.None)
                    .GetAwaiter().GetResult());
            }
            else
            {
                exception = Record.Exception(
                    () => subject.Authenticate(connection, connection.Description, CancellationToken.None));
            }

            exception.Should().BeNull();
            var expectedSentMessageCount = 3 - (useLongAuthentication ? 0 : 1) - (useSpeculativeAuthenticate ? 1 : 0);

            SpinWait.SpinUntil(
                () => connection.GetSentMessages().Count >= expectedSentMessageCount,
                TimeSpan.FromSeconds(5)
                ).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(expectedSentMessageCount);

            var actualRequestIds = sentMessages.Select(m => m["requestId"].AsInt32).ToList();

            for (var i = 0; i != actualRequestIds.Count; ++i)
            {
                actualRequestIds[i].Should().BeInRange(expectedRequestId + i, expectedRequestId + 10 + i);
            }

            var expectedMessages = new List <BsonDocument>();

            var saslStartMessage = BsonDocument.Parse(@$ "
            {{
                opcode : 'opmsg',
                requestId : {actualRequestIds[0]},
Example #14
0
        public void Authenticate_should_not_throw_when_authentication_succeeds(
            [Values(false, true)] bool async)
        {
            var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce);
            var subject = new ScramSha256Authenticator(__credential, randomStringGenerator);

            var saslStartReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                @"{conversationId: 1," +
                                                                                $" payload: BinData(0,\"{ToUtf8Base64(_serverResponse1)}\")," +
                                                                                @" done: false, 
                   ok: 1}"));
            var saslContinueReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson(
                                                                                   @"{conversationId: 1," +
                                                                                   $" payload: BinData(0,\"{ToUtf8Base64(_serverReponse2)}\")," +
                                                                                   @" done: true, 
                   ok: 1}"));

            var connection = new MockConnection(__serverId);

            connection.EnqueueReplyMessage(saslStartReply);
            connection.EnqueueReplyMessage(saslContinueReply);

            var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1;

            Action act;

            if (async)
            {
                act = () => subject.AuthenticateAsync(connection, __description, CancellationToken.None).GetAwaiter().GetResult();
            }
            else
            {
                act = () => subject.Authenticate(connection, __description, CancellationToken.None);
            }

            var exception = Record.Exception(act);

            exception.Should().BeNull();
            SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 2, TimeSpan.FromSeconds(5)).Should().BeTrue();

            var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages());

            sentMessages.Count.Should().Be(2);

            var actualRequestId0 = sentMessages[0]["requestId"].AsInt32;
            var actualRequestId1 = sentMessages[1]["requestId"].AsInt32;

            actualRequestId0.Should().BeInRange(expectedRequestId, expectedRequestId + 10);
            actualRequestId1.Should().BeInRange(actualRequestId0 + 1, actualRequestId0 + 11);

            sentMessages[0].Should().Be(
                @"{opcode: ""query""," +
                $" requestId: {actualRequestId0}," +
                @" database: ""source"", 
                   collection: ""$cmd"", 
                   batchSize: -1, 
                   slaveOk: true, 
                   query: {saslStart: 1, 
                           mechanism: ""SCRAM-SHA-256""," +
                $"         payload: new BinData(0, \"{ToUtf8Base64(_clientRequest1)}\")}}}}");
            sentMessages[1].Should().Be(
                @"{opcode: ""query""," +
                $" requestId: {actualRequestId1}," +
                @" database: ""source"", 
                   collection: ""$cmd"", 
                   batchSize: -1, 
                   slaveOk: true, 
                   query: {saslContinue: 1, 
                           conversationId: 1, " +
                $"         payload: new BinData(0, \"{ToUtf8Base64(_clientRequest2)}\")}}}}");
        }