/* * Copyright (c) 2008-2014 MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mongodb.connection; import com.mongodb.MongoCredential; import com.mongodb.MongoSecurityException; import com.mongodb.ServerAddress; import com.mongodb.async.FutureResultCallback; import org.bson.io.BsonInput; import org.junit.Before; import org.junit.Test; import java.util.List; import java.util.concurrent.ExecutionException; import static com.mongodb.connection.MessageHelper.buildSuccessfulReply; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class ScramSha1SaslAuthenticatorTest { private TestInternalConnection connection; private MongoCredential credential; private ScramSha1Authenticator subject; private ConnectionDescription connectionDescription; @Before public void before() { this.connection = new TestInternalConnection(new ServerId(new ClusterId(), new ServerAddress("localhost", 27017))); connectionDescription = new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress())); this.credential = MongoCredential.createScramSha1Credential("user", "database", "pencil".toCharArray()); ScramSha1Authenticator.RandomStringGenerator randomStringGenerator = new ScramSha1Authenticator.RandomStringGenerator() { @Override public String generate(final int length) { return "fyko+d2lbbFgONRv9qkxdawL"; } }; this.subject = new ScramSha1Authenticator(this.credential, randomStringGenerator); } @Test public void testAuthenticateThrowsWhenServerProvidesAnInvalidRValue() { enqueueInvalidRValueReply(); try { this.subject.authenticate(connection, connectionDescription); fail(); } catch (MongoSecurityException e) { // all good } } @Test public void testAuthenticateThrowsWhenServerProvidesAnInvalidRValueAsync() throws InterruptedException { enqueueInvalidRValueReply(); try { FutureResultCallback<Void> futureCallback = new FutureResultCallback<Void>(); this.subject.authenticateAsync(connection, connectionDescription, futureCallback); futureCallback.get(); fail(); } catch (Throwable t) { if (!(t instanceof MongoSecurityException)) { fail(); } } } private void enqueueInvalidRValueReply() { ResponseBuffers invalidRValueReply = buildSuccessfulReply("{conversationId: 1, " + "payload: BinData(0,cj1meWtvLWQybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0t" + "Vd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw), " + "done: false, " + "ok: 1}"); this.connection.enqueueReply(invalidRValueReply); } @Test public void testAuthenticateThrowsWhenServerProvidesInvalidServerSignature() { enqueueInvalidServerSignature(); try { this.subject.authenticate(connection, connectionDescription); fail(); } catch (MongoSecurityException e) { // all good } } @Test public void testAuthenticateThrowsWhenServerProvidesInvalidServerSignatureAsync() throws InterruptedException { enqueueInvalidServerSignature(); try { FutureResultCallback<Void> futureCallback = new FutureResultCallback<Void>(); this.subject.authenticateAsync(connection, connectionDescription, futureCallback); futureCallback.get(); fail(); } catch (Throwable t) { if (!(t instanceof MongoSecurityException)) { fail(); } } } private void enqueueInvalidServerSignature() { ResponseBuffers firstReply = buildSuccessfulReply("{conversationId: 1, " + "payload: BinData(0,cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3" + "VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw), " + "done: false, " + "ok: 1}"); ResponseBuffers invalidServerSignatureReply = buildSuccessfulReply("{conversationId: 1, " + "payload: BinData(0,dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTBh), " + "done: false, " + "ok: 1}"); this.connection.enqueueReply(firstReply); this.connection.enqueueReply(invalidServerSignatureReply); } @Test public void testSuccessfulAuthentication() { enqueueSuccessfulReplies(); this.subject.authenticate(connection, connectionDescription); validateMessages(); } @Test public void testSuccessfulAuthenticationAsync() throws ExecutionException, InterruptedException { enqueueSuccessfulReplies(); FutureResultCallback<Void> futureCallback = new FutureResultCallback<Void>(); this.subject.authenticateAsync(connection, connectionDescription, futureCallback); futureCallback.get(); validateMessages(); } private void validateMessages() { List<BsonInput> sent = connection.getSent(); String firstCommand = MessageHelper.decodeCommandAsJson(sent.get(0)); String expectedFirstCommand = "{ \"saslStart\" : 1, " + "\"mechanism\" : \"SCRAM-SHA-1\", " + "\"payload\" : { \"$binary\" : \"biwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM\", " + "\"$type\" : \"00\" } }"; String secondCommand = MessageHelper.decodeCommandAsJson(sent.get(1)); String expectedSecondCommand = "{ \"saslContinue\" : 1, " + "\"conversationId\" : 1, " + "\"payload\" : { \"$binary\" : \"Yz1iaXdzLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMSG8rVmdrN3F2VU9LVXd" + "1V0xJV2c0bC85U3JhR01IRUUscD1NQzJUOEJ2Ym1XUmNrRHc4b1dsNUlWZ2h3Q1k9\", \"$type\" : \"00\" } }"; String thirdCommand = MessageHelper.decodeCommandAsJson(sent.get(2)); String expectedThirdCommand = "{ \"saslContinue\" : 1, " + "\"conversationId\" : 1, " + "\"payload\" : { \"$binary\" : \"dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9\", \"$type\" : \"00\" } }"; assertEquals(expectedFirstCommand, firstCommand); assertEquals(expectedSecondCommand, secondCommand); assertEquals(expectedThirdCommand, thirdCommand); } private void enqueueSuccessfulReplies() { ResponseBuffers firstReply = buildSuccessfulReply("{conversationId: 1, " + "payload: BinData(0," + "cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTE" + "lXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw), " + "done: false, " + "ok: 1}"); ResponseBuffers secondReply = buildSuccessfulReply("{conversationId: 1, " + "payload: BinData(0,dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9), " + "done: false, " + "ok: 1}"); ResponseBuffers thirdReply = buildSuccessfulReply("{conversationId: 1, " + "done: true, " + "ok: 1}"); connection.enqueueReply(firstReply); connection.enqueueReply(secondReply); connection.enqueueReply(thirdReply); } }