/*
* 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.accesscontrol;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import org.apache.commons.io.FileUtils;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.integration.util.NiFiTestServer;
import org.apache.nifi.integration.util.NiFiTestUser;
import org.apache.nifi.integration.util.SourceTestProcessor;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarClassLoaders;
import org.apache.nifi.nar.SystemBundle;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
import org.apache.nifi.web.api.dto.AccessStatusDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
import org.apache.nifi.web.api.entity.AccessStatusEntity;
import org.apache.nifi.web.api.entity.ProcessorEntity;
import org.apache.nifi.web.util.WebUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* Access token endpoint test.
*/
public class ITAccessTokenEndpoint {
private static final String CLIENT_ID = "token-endpoint-id";
private static final String CONTEXT_PATH = "/nifi-api";
private static String flowXmlPath;
private static NiFiTestServer SERVER;
private static NiFiTestUser TOKEN_USER;
private static String BASE_URL;
@BeforeClass
public static void setup() throws Exception {
// configure the location of the nifi properties
File nifiPropertiesFile = new File("src/test/resources/access-control/nifi.properties");
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, nifiPropertiesFile.getAbsolutePath());
NiFiProperties props = NiFiProperties.createBasicNiFiProperties(null, null);
flowXmlPath = props.getProperty(NiFiProperties.FLOW_CONFIGURATION_FILE);
// delete the database directory to avoid issues with re-registration in testRequestAccessUsingToken
FileUtils.deleteDirectory(props.getDatabaseRepositoryPath().toFile());
// load extensions
final Bundle systemBundle = SystemBundle.create(props);
NarClassLoaders.getInstance().init(props.getFrameworkWorkingDirectory(), props.getExtensionsWorkingDirectory());
ExtensionManager.discoverExtensions(systemBundle, NarClassLoaders.getInstance().getBundles());
// start the server
SERVER = new NiFiTestServer("src/main/webapp", CONTEXT_PATH, props);
SERVER.startServer();
SERVER.loadFlow();
// get the base url
BASE_URL = SERVER.getBaseUrl() + CONTEXT_PATH;
// create the user
final Client client = WebUtils.createClient(null, createTrustContext(props));
TOKEN_USER = new NiFiTestUser(client, null);
}
private static SSLContext createTrustContext(final NiFiProperties props) throws Exception {
return SslContextFactory.createTrustSslContext(props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE),
props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray(),
props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), "TLS");
}
// -----------
// LOGIN CONIG
// -----------
/**
* Test getting access configuration.
*
* @throws Exception ex
*/
@Test
public void testGetAccessConfig() throws Exception {
String url = BASE_URL + "/access/config";
ClientResponse response = TOKEN_USER.testGet(url);
// ensure the request is successful
Assert.assertEquals(200, response.getStatus());
// extract the process group
AccessConfigurationEntity accessConfigEntity = response.getEntity(AccessConfigurationEntity.class);
// ensure there is content
Assert.assertNotNull(accessConfigEntity);
// extract the process group dto
AccessConfigurationDTO accessConfig = accessConfigEntity.getConfig();
// verify config
Assert.assertTrue(accessConfig.getSupportsLogin());
}
/**
* Obtains a token and creates a processor using it.
*
* @throws Exception ex
*/
@Test
public void testCreateProcessorUsingToken() throws Exception {
String url = BASE_URL + "/access/token";
ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "whatever");
// ensure the request is successful
Assert.assertEquals(201, response.getStatus());
// get the token
String token = response.getEntity(String.class);
// attempt to create a processor with it
createProcessor(token);
}
private ProcessorDTO createProcessor(final String token) throws Exception {
String url = BASE_URL + "/process-groups/root/processors";
// authorization header
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
// create the processor
ProcessorDTO processor = new ProcessorDTO();
processor.setName("Copy");
processor.setType(SourceTestProcessor.class.getName());
// create the revision
final RevisionDTO revision = new RevisionDTO();
revision.setClientId(CLIENT_ID);
revision.setVersion(0l);
// create the entity body
ProcessorEntity entity = new ProcessorEntity();
entity.setRevision(revision);
entity.setComponent(processor);
// perform the request
ClientResponse response = TOKEN_USER.testPostWithHeaders(url, entity, headers);
// ensure the request is successful
Assert.assertEquals(201, response.getStatus());
// get the entity body
entity = response.getEntity(ProcessorEntity.class);
// verify creation
processor = entity.getComponent();
Assert.assertEquals("Copy", processor.getName());
Assert.assertEquals("org.apache.nifi.integration.util.SourceTestProcessor", processor.getType());
return processor;
}
/**
* Verifies the response when bad credentials are specified.
*
* @throws Exception ex
*/
@Test
public void testInvalidCredentials() throws Exception {
String url = BASE_URL + "/access/token";
ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "not a real password");
// ensure the request is successful
Assert.assertEquals(400, response.getStatus());
}
/**
* Verifies the response when the user is known.
*
* @throws Exception ex
*/
@Test
public void testUnknownUser() throws Exception {
String url = BASE_URL + "/access/token";
ClientResponse response = TOKEN_USER.testCreateToken(url, "not a real user", "not a real password");
// ensure the request is successful
Assert.assertEquals(400, response.getStatus());
}
/**
* Request access using access token.
*
* @throws Exception ex
*/
@Test
public void testRequestAccessUsingToken() throws Exception {
String accessStatusUrl = BASE_URL + "/access";
String accessTokenUrl = BASE_URL + "/access/token";
ClientResponse response = TOKEN_USER.testGet(accessStatusUrl);
// ensure the request is successful
Assert.assertEquals(200, response.getStatus());
AccessStatusEntity accessStatusEntity = response.getEntity(AccessStatusEntity.class);
AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus();
// verify unknown
Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
response = TOKEN_USER.testCreateToken(accessTokenUrl, "unregistered-user@nifi", "password");
// ensure the request is successful
Assert.assertEquals(201, response.getStatus());
// get the token
String token = response.getEntity(String.class);
// authorization header
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
// check the status with the token
response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
// ensure the request is successful
Assert.assertEquals(200, response.getStatus());
accessStatusEntity = response.getEntity(AccessStatusEntity.class);
accessStatus = accessStatusEntity.getAccessStatus();
// verify unregistered
Assert.assertEquals("ACTIVE", accessStatus.getStatus());
}
@AfterClass
public static void cleanup() throws Exception {
// shutdown the server
SERVER.shutdownServer();
SERVER = null;
// look for the flow.xml
File flow = new File(flowXmlPath);
if (flow.exists()) {
flow.delete();
}
}
}