| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.qpid.protonj2.engine.impl.sasl; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| import static org.junit.jupiter.api.Assertions.fail; |
| |
| import java.security.Principal; |
| |
| import javax.security.sasl.SaslException; |
| |
| import org.apache.qpid.protonj2.engine.Connection; |
| import org.apache.qpid.protonj2.engine.Engine; |
| import org.apache.qpid.protonj2.engine.EngineFactory; |
| import org.apache.qpid.protonj2.engine.exceptions.EngineFailedException; |
| import org.apache.qpid.protonj2.engine.impl.ProtonEngineTestSupport; |
| import org.apache.qpid.protonj2.engine.sasl.client.SaslAuthenticator; |
| import org.apache.qpid.protonj2.engine.sasl.client.SaslCredentialsProvider; |
| import org.apache.qpid.protonj2.test.driver.ProtonTestConnector; |
| import org.apache.qpid.protonj2.types.security.SaslCode; |
| import org.junit.jupiter.api.Test; |
| import org.junit.jupiter.api.Timeout; |
| |
| /** |
| * Test proton engine from the perspective of a SASL client |
| */ |
| @Timeout(20) |
| public class ProtonSaslClientTest extends ProtonEngineTestSupport { |
| |
| @Test |
| public void testSaslAnonymousConnection() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| peer.expectSASLAnonymousConnect(); |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(null, null)); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testDriverThrowsIfServerStateRequestedAfterClientStateActivated() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| peer.expectSASLAnonymousConnect(); |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(null, null)); |
| |
| assertThrows(IllegalStateException.class, () -> engine.saslDriver().server()); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testSaslAnonymousConnectionWhenPlainAlsoOfferedButNoCredentialsGiven() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| peer.expectSASLHeader().respondWithSASLHeader(); |
| peer.remoteSaslMechanisms().withMechanisms("PLAIN", "ANONYMOUS").queue(); |
| peer.expectSaslInit().withMechanism("ANONYMOUS"); |
| peer.remoteSaslOutcome().withCode(SaslCode.OK.byteValue()).queue(); |
| peer.expectAMQPHeader().respondWithAMQPHeader(); |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(null, null)); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testSaslPlainConnection() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| // Expect a PLAIN connection |
| String user = "user"; |
| String pass = "qwerty123456"; |
| |
| peer.expectSASLPlainConnect(user, pass); |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(user, pass)); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testSaslPlainConnectionWhenUnknownMechanismsOfferedBeforeIt() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| // Expect a PLAIN connection |
| String user = "user"; |
| String pass = "qwerty123456"; |
| |
| peer.expectSASLHeader().respondWithSASLHeader(); |
| peer.remoteSaslMechanisms().withMechanisms("UNKNOWN", "PLAIN", "ANONYMOUS").queue(); |
| peer.expectSaslInit().withMechanism("PLAIN").withInitialResponse(peer.saslPlainInitialResponse(user, pass)); |
| peer.remoteSaslOutcome().withCode(SaslCode.OK.byteValue()).queue(); |
| peer.expectAMQPHeader().respondWithAMQPHeader(); |
| |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(user, pass)); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testDefaultClientSaslMismatchBetweenClientAndServer() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| peer.expectSASLHeader().respondWithSASLHeader(); |
| peer.remoteSaslMechanisms().withMechanisms("PLAIN").queue(); |
| |
| // Default client only know about ANONYMOUS |
| engine.saslDriver().client(); |
| |
| Connection connection = engine.start().open(); |
| |
| try { |
| connection.close(); |
| fail("Engine should have failed"); |
| } catch (EngineFailedException efe) { |
| // Expected as engine failed but was not shutdown |
| } |
| |
| peer.waitForScriptToComplete(); |
| |
| assertTrue(engine.isFailed()); |
| assertNotNull(failure); |
| } |
| |
| @Test |
| public void testSaslXOauth2Connection() throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| // Expect a XOAUTH2 connection |
| String user = "user"; |
| String pass = "eyB1c2VyPSJ1c2VyIiB9"; |
| |
| peer.expectSaslXOauth2Connect(user, pass); |
| peer.expectOpen().respond(); |
| peer.expectClose().respond(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator(user, pass)); |
| |
| Connection connection = engine.start().open(); |
| |
| connection.close(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNull(failure); |
| } |
| |
| @Test |
| public void testSaslFailureCodesFailEngine() throws Exception { |
| doSaslFailureCodesTestImpl(SaslCode.AUTH); |
| doSaslFailureCodesTestImpl(SaslCode.SYS); |
| doSaslFailureCodesTestImpl(SaslCode.SYS_PERM); |
| doSaslFailureCodesTestImpl(SaslCode.SYS_TEMP); |
| } |
| |
| private void doSaslFailureCodesTestImpl(SaslCode saslFailureCode) throws Exception { |
| Engine engine = EngineFactory.PROTON.createEngine(); |
| engine.errorHandler(result -> failure = result.failureCause()); |
| ProtonTestConnector peer = createTestPeer(engine); |
| |
| peer.expectSASLHeader().respondWithSASLHeader(); |
| peer.remoteSaslMechanisms().withMechanisms("PLAIN", "ANONYMOUS").queue(); |
| peer.expectSaslInit().withMechanism("PLAIN"); |
| peer.remoteSaslOutcome().withCode(saslFailureCode.byteValue()).queue(); |
| |
| engine.saslDriver().client().setListener(createSaslPlainAuthenticator("user", "pass")); |
| |
| engine.start().open(); |
| |
| peer.waitForScriptToComplete(); |
| |
| assertNotNull(failure); |
| assertFalse(engine.isShutdown()); |
| assertTrue(engine.isFailed()); |
| assertEquals(failure, engine.failureCause()); |
| assertTrue(failure instanceof SaslException); |
| } |
| |
| private SaslAuthenticator createSaslPlainAuthenticator(String user, String password) { |
| SaslCredentialsProvider credentials = new SaslCredentialsProvider() { |
| |
| @Override |
| public String vhost() { |
| return null; |
| } |
| |
| @Override |
| public String username() { |
| return user; |
| } |
| |
| @Override |
| public String password() { |
| return password; |
| } |
| |
| @Override |
| public Principal localPrincipal() { |
| return null; |
| } |
| }; |
| |
| return new SaslAuthenticator(credentials); |
| } |
| } |