org.codice.ddf.cxf.SecureCxfClientFactoryTest.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.cxf.SecureCxfClientFactoryTest.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.cxf;

import static org.codice.ddf.security.common.jaxrs.RestSecurity.AUTH_HEADER;
import static org.codice.ddf.security.common.jaxrs.RestSecurity.SAML_HEADER_PREFIX;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import ddf.security.SecurityConstants;
import ddf.security.Subject;
import ddf.security.assertion.SecurityAssertion;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.UUID;
import javax.net.ssl.X509KeyManager;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.jaxrs.client.Client;
import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DelegatingSubject;
import org.codice.ddf.configuration.PropertyResolver;
import org.codice.ddf.cxf.client.SecureCxfClientFactory;
import org.codice.ddf.cxf.client.impl.ClientKeyInfo;
import org.codice.ddf.cxf.client.impl.SecureCxfClientFactoryImpl;
import org.codice.ddf.cxf.client.impl.SecureCxfClientFactoryImpl.AliasSelectorKeyManager;
import org.codice.ddf.cxf.paos.PaosInInterceptor;
import org.codice.ddf.cxf.paos.PaosOutInterceptor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.w3c.dom.Element;

public class SecureCxfClientFactoryTest {

    private static final String INSECURE_ENDPOINT = "http://some.url.com/query";

    private static final String SECURE_ENDPOINT = "https://some.url.com/query";

    File systemKeystoreFile = null;

    File systemTruststoreFile = null;

    String password = "changeit";

    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Before
    public void setup() throws IOException {
        systemKeystoreFile = temporaryFolder.newFile("serverKeystore.jks");
        FileOutputStream systemKeyOutStream = new FileOutputStream(systemKeystoreFile);
        InputStream systemKeyStream = SecureCxfClientFactoryTest.class.getResourceAsStream("/serverKeystore.jks");
        IOUtils.copy(systemKeyStream, systemKeyOutStream);

        systemTruststoreFile = temporaryFolder.newFile("serverTruststore.jks");
        FileOutputStream systemTrustOutStream = new FileOutputStream(systemTruststoreFile);
        InputStream systemTrustStream = SecureCxfClientFactoryTest.class
                .getResourceAsStream("/serverTruststore.jks");
        IOUtils.copy(systemTrustStream, systemTrustOutStream);

        System.setProperty(SecurityConstants.KEYSTORE_TYPE, "jks");
        System.setProperty(SecurityConstants.TRUSTSTORE_TYPE, "jks");
        System.setProperty("ddf.home", "");
        System.setProperty(SecurityConstants.KEYSTORE_PATH, systemKeystoreFile.getAbsolutePath());
        System.setProperty(SecurityConstants.TRUSTSTORE_PATH, systemTruststoreFile.getAbsolutePath());
        System.setProperty(SecurityConstants.KEYSTORE_PASSWORD, password);
        System.setProperty(SecurityConstants.TRUSTSTORE_PASSWORD, password);
    }

    @Test
    public void testConstructorNegativeCases() {
        // negative tests
        SecureCxfClientFactory<IDummy> secureCxfClientFactory;
        boolean invalid = false;
        try { // test empty string for url
            secureCxfClientFactory = new SecureCxfClientFactoryImpl<>("", IDummy.class);
        } catch (IllegalArgumentException e) {
            invalid = true;
        }
        assertThat(invalid, is(true));
        invalid = false;
        try { // null for url
            secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(null, IDummy.class);
        } catch (IllegalArgumentException e) {
            invalid = true;
        }
        assertThat(invalid, is(true));
        invalid = false;
        try { // null for url and class
            secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(null, null);
        } catch (IllegalArgumentException e) {
            invalid = true;
        }
        assertThat(invalid, is(true));
        invalid = false;
        try { // null for class
            secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(INSECURE_ENDPOINT, null);
        } catch (IllegalArgumentException e) {
            invalid = true;
        }
        assertThat(invalid, is(true));
        invalid = false;
        try {
            secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(null, null, null, null, false, false, 0, 0,
                    new ClientKeyInfo("alias", "keystore"), "TLSv1.1");
        } catch (IllegalArgumentException e) {
            invalid = true;
        }
        assertThat(invalid, is(true));
    }

    @Test
    public void testInsecureWebClient() {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(INSECURE_ENDPOINT,
                IDummy.class);
        WebClient client = secureCxfClientFactory.getWebClient();

        assertThat(hasEcpEnabled(client), is(false));
        assertThat(client.getBaseURI().toASCIIString().equals(INSECURE_ENDPOINT), is(true));
    }

    @Test
    public void testInsecureWebClientForSubject() throws Exception {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(INSECURE_ENDPOINT,
                IDummy.class);
        Subject subject = setupMockSubject();
        WebClient client = secureCxfClientFactory.getWebClientForSubject(subject);

        assertThat(hasEcpEnabled(client), is(false));
        assertThat(client.getBaseURI().toASCIIString().equals(INSECURE_ENDPOINT), is(true));
        assertThat(client.getHeaders().get(AUTH_HEADER), is(nullValue()));
    }

    @Test
    public void testSecureClient() {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT,
                IDummy.class);
        IDummy client = secureCxfClientFactory.getClient();

        assertThat(hasEcpEnabled(client), is(true));
    }

    @Test
    public void testSecureWebClient() {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT,
                IDummy.class);
        WebClient client = secureCxfClientFactory.getWebClient();

        assertThat(hasEcpEnabled(client), is(true));
        assertThat(client.getBaseURI().toASCIIString().equals(SECURE_ENDPOINT), is(true));
    }

    @Test
    public void testSecureClientForSubject() throws Exception {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT,
                IDummy.class);
        Subject subject = setupMockSubject();
        IDummy client = secureCxfClientFactory.getClientForSubject(subject);

        assertThat(hasEcpEnabled(client), is(true));
        assertThat(WebClient.client(client).getHeaders().get(AUTH_HEADER).get(0), startsWith(SAML_HEADER_PREFIX));
    }

    @Test
    public void testSecureWebClientForSubject() throws Exception {
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT,
                IDummy.class);
        Subject subject = setupMockSubject();
        WebClient client = secureCxfClientFactory.getWebClientForSubject(subject);

        assertThat(hasEcpEnabled(client), is(true));
        assertThat(client.getBaseURI().toASCIIString().equals(SECURE_ENDPOINT), is(true));
        assertThat(client.getHeaders().get(AUTH_HEADER).get(0), startsWith(SAML_HEADER_PREFIX));
    }

    @Test
    public void validateConduit() {
        IDummy clientForSubject = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT, IDummy.class, null, null, true,
                true).getClient();
        HTTPConduit httpConduit = WebClient.getConfig(WebClient.client(clientForSubject)).getHttpConduit();
        assertThat(httpConduit.getTlsClientParameters().isDisableCNCheck(), is(true));
    }

    @Test
    public void testHttpsClientWithSystemProperty() {
        PropertyResolver mockPropertyResolver = mock(PropertyResolver.class);
        when(mockPropertyResolver.getResolvedString()).thenReturn(SECURE_ENDPOINT);
        SecureCxfClientFactory<IDummy> secureCxfClientFactory = new SecureCxfClientFactoryImpl<>(SECURE_ENDPOINT,
                IDummy.class, null, null, false, false, mockPropertyResolver);
        Client unsecuredClient = WebClient.client(secureCxfClientFactory.getClient());
        assertThat(unsecuredClient.getBaseURI().toASCIIString(), is(SECURE_ENDPOINT));
        verify(mockPropertyResolver).getResolvedString();
    }

    @Test
    public void testKeyInfo() {
        String alias = "alias";
        String keystorePath = "/path/to/keystore";

        ClientKeyInfo keyInfo = new ClientKeyInfo(alias, keystorePath);
        assertThat(keyInfo.getAlias(), is(alias));
        assertThat(keyInfo.getKeystorePath(), is(keystorePath));
    }

    @Test
    public void testAliasSelectorKeyManager() {
        X509KeyManager keyManager = mock(X509KeyManager.class);
        String alias = "testAlias";
        String[] aliases = new String[] { alias };
        when(keyManager.chooseClientAlias(any(), any(), any())).thenReturn(alias);
        when(keyManager.getClientAliases(any(), any())).thenReturn(aliases);

        AliasSelectorKeyManager aliasSelectorKeyManager = new AliasSelectorKeyManager(keyManager, alias);
        String chosenAlias = aliasSelectorKeyManager.chooseClientAlias(new String[] { "x509" }, null, null);
        assertThat(chosenAlias, is(alias));
    }

    private boolean hasEcpEnabled(Object client) {
        ClientConfiguration clientConfig = WebClient.getConfig(client);
        return clientConfig.getOutInterceptors().stream().anyMatch(i -> i instanceof PaosOutInterceptor)
                && clientConfig.getInInterceptors().stream().anyMatch(i -> i instanceof PaosInInterceptor);
    }

    private DummySubject getSubject() {
        return new DummySubject(new DefaultSecurityManager(), new SimplePrincipalCollection());
    }

    private Subject setupMockSubject() throws Exception {
        Subject mockSubject = mock(Subject.class);
        PrincipalCollection mockPrincipals = mock(PrincipalCollection.class);
        SecurityAssertion mockSecurityAssertion = mock(SecurityAssertion.class);
        SecurityToken mockToken = mock(SecurityToken.class);

        when(mockSubject.getPrincipals()).thenReturn(mockPrincipals);
        when(mockPrincipals.asList()).thenReturn(Arrays.asList(mockSecurityAssertion));
        when(mockSecurityAssertion.getSecurityToken()).thenReturn(mockToken);
        when(mockToken.getToken()).thenReturn(getAssertionElement());

        return mockSubject;
    }

    private Element getAssertionElement() throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        dbf.setValidating(false);
        dbf.setIgnoringComments(false);
        dbf.setIgnoringElementContentWhitespace(true);
        dbf.setNamespaceAware(true);

        DocumentBuilder db = dbf.newDocumentBuilder();
        db.setEntityResolver(new DOMUtils.NullResolver());

        return db.parse(SecureCxfClientFactoryTest.class.getResourceAsStream("/SAMLAssertion.xml"))
                .getDocumentElement();
    }

    private interface IDummy {
        @GET
        public Response ok();
    }

    private class IDummyImpl implements IDummy {

        @Override
        public Response ok() {
            return Response.ok().build();
        }
    }

    private class DummySubject extends DelegatingSubject implements Subject {

        public DummySubject(org.apache.shiro.mgt.SecurityManager manager, PrincipalCollection principals) {
            super(principals, true, null, new SimpleSession(UUID.randomUUID().toString()), manager);
        }

        @Override
        public boolean isGuest() {
            return false;
        }

        @Override
        public String getName() {
            return "Dummy Subject";
        }
    }
}