/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * 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 org.wildfly.security.sasl.external; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslClientFactory; import javax.security.sasl.SaslException; import org.junit.Ignore; import org.junit.Test; import org.wildfly.security.sasl.WildFlySasl; import org.wildfly.security.sasl.test.BaseTestCase; /** * Tests for the SASL Client for EXTERNAL mechanism ( * <a href="https://tools.ietf.org/html/rfc4422#appendix-A">https://tools.ietf.org/html/rfc4422#appendix-A</a>). * * @author Josef Cacek */ public class ExternalSaslClientTest extends BaseTestCase { private static final byte[] BYTES_EMPTY = new byte[0]; private static final String ADMIN = "admin"; private static final String EXTERNAL = "EXTERNAL"; private static final String[] MECHANISMS_EXTERNAL_ONLY = new String[] { EXTERNAL }; private static final String[] MECHANISMS_WITH_EXTERNAL = new String[] { EXTERNAL, "TEST" }; private static final String[] MECHANISMS_WITHOUT_EXTERNAL = new String[] { "TEST" }; @Test public void testMechanismNames() throws Exception { SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); assertNotNull("SaslServerFactory not registered", factory); final String[] empty = new String[] {}; assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_FORWARD_SECRECY))); assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_NOACTIVE))); assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_NOANONYMOUS))); assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_NODICTIONARY))); assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_NOPLAINTEXT))); assertArrayEquals(empty, factory.getMechanismNames(setProps(Sasl.POLICY_PASS_CREDENTIALS))); assertArrayEquals(MECHANISMS_EXTERNAL_ONLY, factory.getMechanismNames(setProps(WildFlySasl.MECHANISM_QUERY_ALL, Sasl.POLICY_NOPLAINTEXT))); assertArrayEquals(MECHANISMS_EXTERNAL_ONLY, factory.getMechanismNames(null)); } @Test public void testCreateSaslClientUsingRegistry() throws Exception { assertNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_FORWARD_SECRECY), null)); assertNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOANONYMOUS), null)); assertNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS), null)); SaslClient saslClient = Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(), null); assertNotNull(saslClient); assertEquals(ExternalSaslClient.class, saslClient.getClass()); saslClient = Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(), null); assertNotNull(saslClient); assertEquals(ExternalSaslClient.class, saslClient.getClass()); assertNull(Sasl.createSaslClient(MECHANISMS_WITHOUT_EXTERNAL, null, "test", "localhost", setProps(), null)); assertNotNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", null, null)); } @Test public void testCreateSaslClientUsingFactory() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); assertNotNull("SaslClientFactory not registered", factory); assertNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_FORWARD_SECRECY), null)); assertNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOANONYMOUS), null)); assertNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS), null)); final SaslClient saslClient = factory.createSaslClient(MECHANISMS_WITH_EXTERNAL, null, "test", "localhost", setProps(), null); assertNotNull(saslClient); assertEquals(ExternalSaslClient.class, saslClient.getClass()); assertNotNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", null, null)); } @Test public void testDontUseQueryAllPolicyInCreateMethod() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); assertNotNull("SaslClientFactory not registered", factory); assertNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS, WildFlySasl.MECHANISM_QUERY_ALL), null)); } @Test @Ignore("ELY-982") public void testCreateSaslClientWithValidFiltering() throws Exception { assertNotNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOACTIVE), null)); assertNotNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NODICTIONARY), null)); assertNotNull(Sasl.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOPLAINTEXT), null)); final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); assertNotNull("SaslClientFactory not registered", factory); assertNotNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOACTIVE), null)); assertNotNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NODICTIONARY), null)); assertNotNull(factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(Sasl.POLICY_NOPLAINTEXT), null)); } @Test public void testServerChallengeProvideAuthzId() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); final SaslClient saslClient = factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, ADMIN, "test", "localhost", setProps(), null); assertEquals(EXTERNAL, saslClient.getMechanismName()); assertTrue(saslClient.hasInitialResponse()); assertFalse(saslClient.isComplete()); // The challenge is null if the authentication has succeeded and no more challenge data is to be sent to the client. assertArrayEquals(ADMIN.getBytes(StandardCharsets.UTF_8), saslClient.evaluateChallenge(BYTES_EMPTY)); assertTrue(saslClient.isComplete()); try { saslClient.evaluateChallenge(BYTES_EMPTY); fail("evaluateChallenge() invocation should fail when the authentication is already completed"); } catch (SaslException e) { // expected } } @Test public void testServerChallengeEmptyAuthzId() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); final SaslClient saslClient = factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, null, "test", "localhost", setProps(), null); assertEquals(EXTERNAL, saslClient.getMechanismName()); assertTrue(saslClient.hasInitialResponse()); assertFalse(saslClient.isComplete()); // The challenge is null if the authentication has succeeded and no more challenge data is to be sent to the client. assertArrayEquals(BYTES_EMPTY, saslClient.evaluateChallenge(BYTES_EMPTY)); assertTrue(saslClient.isComplete()); try { saslClient.evaluateChallenge(BYTES_EMPTY); fail("evaluateChallenge() invocation should fail when the authentication is already completed"); } catch (SaslException e) { // expected } } /** * Test failing (as we only authenticate "admin") authn for unsupported data "test" from client. */ @Test(expected = SaslException.class) public void testWrongServerChallenge() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); final SaslClient saslClient = factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, ADMIN, "test", "localhost", setProps(), null); assertFalse(saslClient.isComplete()); saslClient.evaluateChallenge("test".getBytes(StandardCharsets.UTF_8)); } @Test public void testWrapUnwrap() throws Exception { final SaslClientFactory factory = obtainSaslClientFactory(ExternalSaslClientFactory.class); final SaslClient saslClient = factory.createSaslClient(MECHANISMS_EXTERNAL_ONLY, ADMIN, "test", "localhost", setProps(), null); try { saslClient.wrap(new byte[] {}, 0, 0); fail("wrap() invocation should throw IllegalStateException as not yet completed"); } catch (IllegalStateException e) { // expected } try { saslClient.unwrap(new byte[] {}, 0, 0); fail("unwrap() invocation should throw IllegalStateException as not yet completed"); } catch (IllegalStateException e) { // expected } saslClient.evaluateChallenge(BYTES_EMPTY); assertTrue(saslClient.isComplete()); try { saslClient.wrap(new byte[] {}, 0, 0); fail("wrap() invocation should throw IllegalStateException as this mechanism supports neither integrity nor privacy"); } catch (IllegalStateException e) { // expected } try { saslClient.unwrap(new byte[] {}, 0, 0); fail("wrap() invocation should throw IllegalStateException as this mechanism supports neither integrity nor privacy"); } catch (IllegalStateException e) { // expected } } private Map<String, ?> setProps(String... propNames) { return Stream.of(propNames).collect(Collectors.toMap(Function.identity(), s -> "true")); } }