/* * 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.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import javax.security.sasl.SaslServerFactory; import org.junit.Ignore; import org.junit.Test; import org.wildfly.security.sasl.WildFlySasl; import org.wildfly.security.sasl.test.BaseTestCase; import org.wildfly.security.sasl.util.AbstractSaslParticipant; /** * Tests for the SASL Server 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 ExternalSaslServerTest extends BaseTestCase { private static final String ADMIN = "admin"; private static final String EXTERNAL = "EXTERNAL"; private static final CallbackHandler CALLBACK_HANDLER_AUTHZ_ADMIN = callbacks -> { for (Callback callback : callbacks) { if (callback instanceof AuthorizeCallback) { final AuthorizeCallback ac = (AuthorizeCallback) callback; ac.setAuthorized(ADMIN.equals(ac.getAuthorizationID())); } else { throw new UnsupportedCallbackException(callback); } } }; @Test public void testMechanismNames() throws Exception { SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.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))); final String[] allMechanisms = new String[] { EXTERNAL }; assertArrayEquals(allMechanisms, factory.getMechanismNames(setProps(WildFlySasl.MECHANISM_QUERY_ALL, Sasl.POLICY_NOPLAINTEXT))); assertArrayEquals(allMechanisms, factory.getMechanismNames(null)); } @Test public void testCreateSaslServerUsingRegistry() throws Exception { assertNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_FORWARD_SECRECY), null)); assertNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOANONYMOUS), null)); assertNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS), null)); SaslServer server = Sasl.createSaslServer(EXTERNAL, "test", "localhost", null, null); assertNotNull(server); assertEquals(ExternalSaslServer.class, server.getClass()); } @Test public void testCreateSaslServerUsingFactory() throws Exception { SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.class); assertNotNull("SaslServerFactory not registered", factory); assertNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_FORWARD_SECRECY), null)); assertNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOANONYMOUS), null)); assertNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS), null)); SaslServer server = factory.createSaslServer(EXTERNAL, "test", "localhost", null, null); assertNotNull(server); assertEquals(ExternalSaslServer.class, server.getClass()); } @Test public void testDontUseQueryAllPolicyInCreateMethod() throws Exception { SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.class); assertNotNull("SaslServerFactory not registered", factory); assertNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_PASS_CREDENTIALS, WildFlySasl.MECHANISM_QUERY_ALL), null)); } @Test @Ignore("ELY-982") public void testCreateSaslServerWithValidPolicy() throws Exception { assertNotNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOACTIVE), null)); assertNotNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NODICTIONARY), null)); assertNotNull(Sasl.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOPLAINTEXT), null)); SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.class); assertNotNull("SaslServerFactory not registered", factory); assertNotNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOACTIVE), null)); assertNotNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOPLAINTEXT), null)); assertNotNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NODICTIONARY), null)); assertNotNull(factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(Sasl.POLICY_NOPLAINTEXT, WildFlySasl.MECHANISM_QUERY_ALL), null)); } @Test public void testAuthnClientData() throws Exception { SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.class); assertNotNull("SaslServerFactory not registered", factory); SaslServer saslServer = factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(), CALLBACK_HANDLER_AUTHZ_ADMIN); assertEquals(EXTERNAL, saslServer.getMechanismName()); assertFalse(saslServer.isComplete()); // The challenge is null if the authentication has succeeded and no more challenge data is to be sent to the client. assertNull(saslServer.evaluateResponse(ADMIN.getBytes(StandardCharsets.UTF_8))); assertTrue(saslServer.isComplete()); assertEquals(ADMIN, saslServer.getAuthorizationID()); try { saslServer.evaluateResponse(AbstractSaslParticipant.NO_BYTES); fail("evaluateResponse() 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 testFailedAuthn() throws Exception { SaslServer saslServer = obtainSaslServerFactory(ExternalSaslServerFactory.class).createSaslServer(EXTERNAL, "test", "localhost", setProps(), CALLBACK_HANDLER_AUTHZ_ADMIN); assertFalse(saslServer.isComplete()); saslServer.evaluateResponse("test".getBytes(StandardCharsets.UTF_8)); } /** * Test failing authn (as we only authenticate "admin") for empty data received from client. */ @Test(expected = SaslException.class) public void testAuthnEmptyData() throws Exception { SaslServer saslServer = obtainSaslServerFactory(ExternalSaslServerFactory.class).createSaslServer(EXTERNAL, "test", "localhost", setProps(), CALLBACK_HANDLER_AUTHZ_ADMIN); assertFalse(saslServer.isComplete()); saslServer.evaluateResponse(AbstractSaslParticipant.NO_BYTES); } @Test public void testWrapUnwrap() throws Exception { SaslServerFactory factory = obtainSaslServerFactory(ExternalSaslServerFactory.class); assertNotNull("SaslServerFactory not registered", factory); SaslServer saslServer = factory.createSaslServer(EXTERNAL, "test", "localhost", setProps(), CALLBACK_HANDLER_AUTHZ_ADMIN); try { saslServer.wrap(new byte[] {}, 0, 0); fail("wrap() invocation should throw IllegalStateException as not yet completed"); } catch (IllegalStateException e) { // expected } try { saslServer.unwrap(new byte[] {}, 0, 0); fail("unwrap() invocation should throw IllegalStateException as not yet completed"); } catch (IllegalStateException e) { // expected } saslServer.evaluateResponse(ADMIN.getBytes(StandardCharsets.UTF_8)); assertTrue(saslServer.isComplete()); try { saslServer.wrap(new byte[] {}, 0, 0); fail("wrap() invocation should throw IllegalStateException as this mechanism supports neither integrity nor privacy"); } catch (IllegalStateException e) { // expected } try { saslServer.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")); } }