/* * 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.nifi.integration.util; import com.sun.jersey.api.client.Client; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.framework.security.util.SslContextFactory; import org.apache.nifi.services.FlowService; import org.apache.nifi.ui.extension.UiExtensionMapping; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.util.WebUtils; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.webapp.WebAppContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletContext; import java.io.File; import java.util.Collections; /** * Creates an embedded server for testing the NiFi REST API. */ public class NiFiTestServer { private static final Logger logger = LoggerFactory.getLogger(NiFiTestServer.class); private Server jetty; private final NiFiProperties properties; private WebAppContext webappContext; public NiFiTestServer(String webappRoot, String contextPath, NiFiProperties properties) { this.properties = properties; createWebAppContext(webappRoot, contextPath); createServer(); } private WebAppContext createWebAppContext(String webappRoot, String contextPath) { webappContext = new WebAppContext(); webappContext.setContextPath(contextPath); webappContext.setWar(webappRoot); webappContext.setLogUrlOnStart(true); webappContext.setTempDirectory(new File("target/jetty")); return webappContext; } private Server createServer() { jetty = new Server(0); createSecureConnector(); jetty.setHandler(webappContext); return jetty; } private void createSecureConnector() { org.eclipse.jetty.util.ssl.SslContextFactory contextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory(); // require client auth when not supporting login or anonymous access if (StringUtils.isBlank(properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER))) { contextFactory.setNeedClientAuth(true); } else { contextFactory.setWantClientAuth(true); } /* below code sets JSSE system properties when values are provided */ // keystore properties if (StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE))) { contextFactory.setKeyStorePath(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE)); } if (StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE))) { contextFactory.setKeyStoreType(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE)); } final String keystorePassword = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD); final String keyPassword = properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD); if (StringUtils.isNotBlank(keystorePassword)) { // if no key password was provided, then assume the keystore password is the same as the key password. final String defaultKeyPassword = (StringUtils.isBlank(keyPassword)) ? keystorePassword : keyPassword; contextFactory.setKeyManagerPassword(keystorePassword); contextFactory.setKeyStorePassword(defaultKeyPassword); } else if (StringUtils.isNotBlank(keyPassword)) { // since no keystore password was provided, there will be no keystore integrity check contextFactory.setKeyStorePassword(keyPassword); } // truststore properties if (StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE))) { contextFactory.setTrustStorePath(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE)); } if (StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE))) { contextFactory.setTrustStoreType(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE)); } if (StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD))) { contextFactory.setTrustStorePassword(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD)); } // add some secure config final HttpConfiguration httpsConfiguration = new HttpConfiguration(); httpsConfiguration.setSecureScheme("https"); httpsConfiguration.setSecurePort(0); httpsConfiguration.addCustomizer(new SecureRequestCustomizer()); // build the connector final ServerConnector https = new ServerConnector(jetty, new SslConnectionFactory(contextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfiguration)); // set host and port https.setPort(0); // add the connector jetty.addConnector(https); } public void startServer() throws Exception { jetty.start(); // ensure the ui extensions are set webappContext.getServletContext().setAttribute("nifi-ui-extensions", new UiExtensionMapping(Collections.EMPTY_MAP)); } public void loadFlow() throws Exception { logger.info("Loading Flow..."); // start and load the flow FlowService flowService = getSpringBean("flowService", FlowService.class); flowService.load(null); logger.info("Flow loaded successfully."); } public void shutdownServer() throws Exception { jetty.stop(); } public int getPort() { if (!jetty.isStarted()) { throw new IllegalStateException("Jetty server not started"); } return ((ServerConnector) jetty.getConnectors()[1]).getLocalPort(); } public String getBaseUrl() { return "https://localhost:" + getPort(); } public Client getClient() { return WebUtils.createClient(null, SslContextFactory.createSslContext(properties)); } /** * Convenience method to provide access to Spring beans accessible from the * web application context. * * @param <T> target cast * @param beanName name of the spring bean * @param clazz class of the spring bean * @return Spring bean with given name and class type * * @throws ClassCastException if the bean found cannot be cast to the given * class type */ public <T> T getSpringBean(String beanName, Class<T> clazz) { ServletContext servletContext = webappContext.getServletHandler().getServletContext(); WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); return clazz.cast(webApplicationContext.getBean(beanName)); } }