NIFI-655:

- Removing registration support.
- Removing file based implementation.
This commit is contained in:
Matt Gilman 2015-11-09 15:00:33 -05:00
parent efa1939fc5
commit f250560474
27 changed files with 382 additions and 1309 deletions

View File

@ -17,7 +17,6 @@
package org.apache.nifi.authentication;
import org.apache.nifi.authentication.exception.IdentityAccessException;
import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.authorization.exception.ProviderDestructionException;
@ -26,25 +25,12 @@ import org.apache.nifi.authorization.exception.ProviderDestructionException;
*/
public interface LoginIdentityProvider {
/**
* Returns whether this provider supports user registration.
*
* @return whether user registration is supported
*/
boolean supportsRegistration();
/**
* Invoked to register the user with the specified login credentials.
*
* @param credentials the login credentials
*/
void register(LoginCredentials credentials) throws IdentityAlreadyExistsException, IdentityAccessException;
/**
* Authenticates the specified login credentials.
*
* @param credentials the credentials
* @return whether the user was authenticated
* @return was able to check the user credentials and returns whether the user was authenticated
* @throws IdentityAccessException Unable to register the user due to an issue accessing the underlying storage
*/
boolean authenticate(LoginCredentials credentials) throws IdentityAccessException;
@ -52,6 +38,7 @@ public interface LoginIdentityProvider {
* Called immediately after instance creation for implementers to perform additional setup
*
* @param initializationContext in which to initialize
* @throws ProviderCreationException Unable to initialize
*/
void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException;

View File

@ -1,33 +0,0 @@
/*
* 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.authentication.exception;
/**
* Represents the case when the identity could not be registered for some reason.
* Like the credentials did not meet the minimum requirements
*/
public class IdentityRegistrationException extends RuntimeException {
public IdentityRegistrationException(String message, Throwable cause) {
super(message, cause);
}
public IdentityRegistrationException(String message) {
super(message);
}
}

View File

@ -35,10 +35,6 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-file-authorization-provider</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-file-identity-provider</artifactId>
</dependency>
<!-- mark these nifi artifacts as provided since it is included in the lib -->
<dependency>

View File

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework</artifactId>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-authorized-users</artifactId>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/xsd</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<packageName>org.apache.nifi.user.generated</packageName>
</configuration>
</execution>
</executions>
<configuration>
<generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<excludes>**/user/generated/*.java</excludes>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,445 +0,0 @@
/*
* 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.authorized.users;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.nifi.authorization.exception.AuthorityAccessException;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.authorization.exception.UnknownIdentityException;
import org.apache.nifi.user.generated.LoginUser;
import org.apache.nifi.user.generated.NiFiUser;
import org.apache.nifi.user.generated.User;
import org.apache.nifi.user.generated.Users;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* Access to the configured Authorized Users.
*/
public final class AuthorizedUsers {
private static final Logger logger = LoggerFactory.getLogger(AuthorizedUsers.class);
private static final String USERS_XSD = "/users.xsd";
private static final String JAXB_GENERATED_PATH = "org.apache.nifi.user.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
private static final Map<String, AuthorizedUsers> instances = new HashMap<>();
private File usersFile;
private File restoreFile;
/**
* Load the JAXBContext.
*/
private static JAXBContext initializeJaxbContext() {
try {
return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorizedUsers.class.getClassLoader());
} catch (JAXBException e) {
throw new RuntimeException("Unable to create JAXBContext.");
}
}
private AuthorizedUsers(final File usersFile, final NiFiProperties properties) throws IOException, IllegalStateException {
this.usersFile = usersFile;
// the restore directory is optional and may be null
final File restoreDirectory = properties.getRestoreDirectory();
if (restoreDirectory != null) {
// sanity check that restore directory is a directory, creating it if necessary
FileUtils.ensureDirectoryExistAndCanAccess(restoreDirectory);
// check that restore directory is not the same as the primary directory
final File usersFileDirectory = usersFile.getParentFile();
if (usersFileDirectory.getAbsolutePath().equals(restoreDirectory.getAbsolutePath())) {
throw new IllegalStateException(String.format("Directory of users file '%s' is the same as restore directory '%s' ",
usersFileDirectory.getAbsolutePath(), restoreDirectory.getAbsolutePath()));
}
// the restore copy will have same file name, but reside in a different directory
restoreFile = new File(restoreDirectory, usersFile.getName());
// sync the primary copy with the restore copy
try {
FileUtils.syncWithRestore(usersFile, restoreFile, logger);
} catch (final IOException | IllegalStateException ioe) {
throw new ProviderCreationException(ioe);
}
}
}
public static AuthorizedUsers getInstance(final String usersFilePath, final NiFiProperties properties) throws IOException, IllegalStateException {
final File usersFile = new File(usersFilePath);
// see if an authorizedUsers has already been created using this filename
AuthorizedUsers authorizedUsers = instances.get(usersFile.getName());
if (authorizedUsers == null) {
// create a new authorizedUsers
authorizedUsers = new AuthorizedUsers(usersFile, properties);
// store it for later
instances.put(usersFile.getName(), authorizedUsers);
} else {
// ensure the file paths are the same, the restore capability cannot support different files with the same name
if (!authorizedUsers.usersFile.equals(usersFile)) {
throw new IllegalStateException(String.format("A users file with this name has already been initialized. The name must be unique given the constraints of "
+ "the restore directory. The paths in question are '%s' and '%s'", authorizedUsers.usersFile.getAbsolutePath(), usersFile.getAbsolutePath()));
}
}
return authorizedUsers;
}
/**
* Gets the user identity.
*
* @param user The user
* @return The user identity
*/
public String getUserIdentity(final NiFiUser user) {
if (user instanceof User) {
return ((User) user).getDn();
} else {
return ((LoginUser) user).getUsername();
}
}
/**
* Gets all users from configured file.
*
* @return The Users
*/
public synchronized Users getUsers() {
try {
// ensure the directory exists and it can be created
if (!usersFile.exists() && !usersFile.mkdirs()) {
throw new IllegalStateException("The users file does not exist and could not be created.");
}
// find the schema
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(AuthorizedUsers.class.getResource(USERS_XSD));
// attempt to unmarshal
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(schema);
final JAXBElement<Users> element = unmarshaller.unmarshal(new StreamSource(usersFile), Users.class);
return element.getValue();
} catch (SAXException | JAXBException e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
/**
* Determines if a user exists through the specified HasUser.
*
* @param finder The finder
* @return Whether the user exists
*/
public synchronized boolean hasUser(final HasUser finder) {
// load the users
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the desired user
return finder.hasUser(nifiUsers);
}
/**
* Gets the desired user.
*
* @param finder The finder
* @return The NiFiUser
* @throws UnknownIdentityException If the desired user could not be found
*/
public synchronized NiFiUser getUser(final FindUser finder) {
// load the users
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the desired user
return finder.findUser(nifiUsers);
}
/**
* Gets the desired users.
*
* @param finder The finder
* @return The NiFiUsers
* @throws UnknownIdentityException If the users could not be found
*/
public synchronized List<NiFiUser> getUsers(final FindUsers finder) {
// load the users
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the desired user
return finder.findUsers(nifiUsers);
}
/**
* Creates the user via the specified CreateUser.
*
* @param creator The creator
*/
public synchronized void createUser(final CreateUser creator) {
// add the user
final Users users = getUsers();
// create the user
final NiFiUser newUser = creator.createUser();
if (newUser instanceof User) {
users.getUser().add((User) newUser);
} else {
users.getLoginUser().add((LoginUser) newUser);
}
// save the users
saveUsers(users);
}
/**
* Creates or Updates a user identified by the finder. If the user exists, it's updated otherwise it's created.
*
* @param finder The finder
* @param creator The creator
* @param updater The updater
*/
public synchronized void createOrUpdateUser(final FindUser finder, final CreateUser creator, final UpdateUser updater) {
try {
updateUser(finder, updater);
} catch (final UnknownIdentityException uie) {
createUser(creator);
}
}
/**
* Updates the user identified by the finder.
*
* @param finder The finder
* @param updater The updater
*/
public synchronized void updateUser(final FindUser finder, final UpdateUser updater) {
// update the user
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the user to update
final NiFiUser user = finder.findUser(nifiUsers);
// update the user
updater.updateUser(user);
// save the users
saveUsers(users);
}
/**
* Updates the users identified by the finder.
*
* @param finder The finder
* @param updater The updater
*/
public synchronized void updateUsers(final FindUsers finder, final UpdateUsers updater) {
// update the user
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
final List<NiFiUser> userToUpdate = finder.findUsers(nifiUsers);
// update the user
updater.updateUsers(userToUpdate);
// save the users
saveUsers(users);
}
/**
* Removes the user identified by the finder.
*
* @param finder The finder
*/
public synchronized void removeUser(final FindUser finder) {
// load the users
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the desired user
final NiFiUser user = finder.findUser(nifiUsers);
if (user instanceof User) {
users.getUser().remove((User) user);
} else {
users.getLoginUser().remove((LoginUser) user);
}
// save the users
saveUsers(users);
}
/**
* Removes the users identified by the finder.
*
* @param finder The finder
*/
public synchronized void removeUsers(final FindUsers finder) {
// load the users
final Users users = getUsers();
// combine the user lists
final List<NiFiUser> nifiUsers = new ArrayList<>();
nifiUsers.addAll(users.getUser());
nifiUsers.addAll(users.getLoginUser());
// find the desired user
final List<NiFiUser> usersToRemove = finder.findUsers(nifiUsers);
for (final NiFiUser user : usersToRemove) {
if (user instanceof User) {
users.getUser().remove((User) user);
} else {
users.getLoginUser().remove((LoginUser) user);
}
}
// save the users
saveUsers(users);
}
private synchronized void saveUsers(final Users users) {
try {
final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// save users to restore directory before primary directory
if (restoreFile != null) {
marshaller.marshal(users, restoreFile);
}
// save users to primary directory
marshaller.marshal(users, usersFile);
} catch (JAXBException e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
public static interface HasUser {
/**
* Determines if a user exists. Returns whether this user exists and will never through an UnknownIdentityException.
*
* @param users the users
* @return whether the desired user exists
*/
boolean hasUser(List<NiFiUser> users);
}
public static interface FindUser {
/**
* Finds the desired user. If the user cannot be found throws an UnknownIdentityException. Never returns null.
*
* @param users the users
* @return the desired user
* @throws UnknownIdentityException if the user cannot be found
*/
NiFiUser findUser(List<NiFiUser> users) throws UnknownIdentityException;
}
public static interface FindUsers {
/**
* Finds the specified users.
*
* @param users the userss
* @return the desired users
* @throws UnknownIdentityException if the users cannot be found
*/
List<NiFiUser> findUsers(List<NiFiUser> users) throws UnknownIdentityException;
}
public static interface CreateUser {
/**
* Creates the user to add.
*
* @return the users to add
*/
NiFiUser createUser();
}
public static interface UpdateUser {
/**
* Updates the specified user.
*
* @param user the user
*/
void updateUser(NiFiUser user);
}
public static interface UpdateUsers {
/**
* Updates the specified users.
*
* @param users the users to update
*/
void updateUsers(List<NiFiUser> users);
}
}

View File

@ -26,7 +26,6 @@ import javax.xml.bind.annotation.XmlType;
public class LoginConfigurationDTO {
private Boolean supportsLogin;
private Boolean supportsRegistration;
/**
* @return Indicates whether or not this NiFi supports user login.
@ -42,19 +41,4 @@ public class LoginConfigurationDTO {
public void setSupportsLogin(Boolean supportsLogin) {
this.supportsLogin = supportsLogin;
}
/**
* @return If this NiFi supports login, indicates whether or not registration is supported.
*/
@ApiModelProperty(
value = "If this NiFi supports login, indicates whether or not registration is supported.",
readOnly = true
)
public Boolean getSupportsRegistration() {
return supportsRegistration;
}
public void setSupportsRegistration(Boolean supportsRegistration) {
this.supportsRegistration = supportsRegistration;
}
}

View File

@ -21,19 +21,52 @@
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-file-authorization-provider</artifactId>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/xsd</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<packageName>org.apache.nifi.user.generated</packageName>
</configuration>
</execution>
</executions>
<configuration>
<generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<excludes>**/user/generated/*.java</excludes>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-authorized-users</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>

View File

@ -16,34 +16,38 @@
*/
package org.apache.nifi.authorization;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.nifi.authorization.annotation.AuthorityProviderContext;
import org.apache.nifi.authorization.exception.AuthorityAccessException;
import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.authorization.exception.UnknownIdentityException;
import org.apache.nifi.util.file.FileUtils;
import org.apache.nifi.user.generated.ObjectFactory;
import org.apache.nifi.user.generated.Role;
import org.apache.nifi.user.generated.User;
import org.apache.nifi.user.generated.Users;
import org.apache.nifi.util.NiFiProperties;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorized.users.AuthorizedUsers;
import org.apache.nifi.authorized.users.AuthorizedUsers.CreateUser;
import org.apache.nifi.authorized.users.AuthorizedUsers.FindUser;
import org.apache.nifi.authorized.users.AuthorizedUsers.FindUsers;
import org.apache.nifi.authorized.users.AuthorizedUsers.HasUser;
import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUser;
import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUsers;
import org.apache.nifi.user.generated.LoginUser;
import org.apache.nifi.user.generated.NiFiUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* Provides identity checks and grants authorities.
@ -51,26 +55,84 @@ import org.slf4j.LoggerFactory;
public class FileAuthorizationProvider implements AuthorityProvider {
private static final Logger logger = LoggerFactory.getLogger(FileAuthorizationProvider.class);
private static final String USERS_XSD = "/users.xsd";
private static final String JAXB_GENERATED_PATH = "org.apache.nifi.user.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
/**
* Load the JAXBContext.
*/
private static JAXBContext initializeJaxbContext() {
try {
return JAXBContext.newInstance(JAXB_GENERATED_PATH, FileAuthorizationProvider.class.getClassLoader());
} catch (JAXBException e) {
throw new RuntimeException("Unable to create JAXBContext.");
}
}
private NiFiProperties properties;
private File usersFile;
private File restoreUsersFile;
private Users users;
private final Set<String> defaultAuthorities = new HashSet<>();
private AuthorizedUsers authorizedUsers;
@Override
public void initialize(final AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException {
}
@Override
public void onConfigured(final AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException {
final String usersFilePath = configurationContext.getProperty("Authorized Users File");
if (usersFilePath == null || usersFilePath.trim().isEmpty()) {
throw new ProviderCreationException("The authorized users file must be specified.");
}
try {
// initialize the authorized users
authorizedUsers = AuthorizedUsers.getInstance(usersFilePath, properties);
final String usersFilePath = configurationContext.getProperty("Authorized Users File");
if (usersFilePath == null || usersFilePath.trim().isEmpty()) {
throw new ProviderCreationException("The authorized users file must be specified.");
}
// the users file instance will never be null because a default is used
usersFile = new File(usersFilePath);
final File usersFileDirectory = usersFile.getParentFile();
// the restore directory is optional and may be null
final File restoreDirectory = properties.getRestoreDirectory();
if (restoreDirectory != null) {
// sanity check that restore directory is a directory, creating it if necessary
FileUtils.ensureDirectoryExistAndCanAccess(restoreDirectory);
// check that restore directory is not the same as the primary directory
if (usersFileDirectory.getAbsolutePath().equals(restoreDirectory.getAbsolutePath())) {
throw new ProviderCreationException(String.format("Authorized User's directory '%s' is the same as restore directory '%s' ",
usersFileDirectory.getAbsolutePath(), restoreDirectory.getAbsolutePath()));
}
// the restore copy will have same file name, but reside in a different directory
restoreUsersFile = new File(restoreDirectory, usersFile.getName());
// sync the primary copy with the restore copy
try {
FileUtils.syncWithRestore(usersFile, restoreUsersFile, logger);
} catch (final IOException | IllegalStateException ioe) {
throw new ProviderCreationException(ioe);
}
}
// load the users from the specified file
if (usersFile.exists()) {
// find the schema
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(FileAuthorizationProvider.class.getResource(USERS_XSD));
// attempt to unmarshal
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(schema);
final JAXBElement<Users> element = unmarshaller.unmarshal(new StreamSource(usersFile), Users.class);
users = element.getValue();
} else {
final ObjectFactory objFactory = new ObjectFactory();
users = objFactory.createUsers();
}
// attempt to load a default roles
final String rawDefaultAuthorities = configurationContext.getProperty("Default User Roles");
@ -95,9 +157,10 @@ public class FileAuthorizationProvider implements AuthorityProvider {
StringUtils.join(invalidDefaultAuthorities, ", "), StringUtils.join(Authority.getRawAuthorities(), ", ")));
}
}
} catch (IOException | IllegalStateException | ProviderCreationException e) {
} catch (IOException | ProviderCreationException | SAXException | JAXBException e) {
throw new ProviderCreationException(e);
}
}
@Override
@ -109,64 +172,67 @@ public class FileAuthorizationProvider implements AuthorityProvider {
}
@Override
public boolean doesDnExist(final String dn) throws AuthorityAccessException {
public boolean doesDnExist(String dn) throws AuthorityAccessException {
if (hasDefaultRoles()) {
return true;
}
return authorizedUsers.hasUser(new HasUserByIdentity(dn));
final User user = getUser(dn);
return user != null;
}
@Override
public Set<Authority> getAuthorities(final String dn) throws UnknownIdentityException, AuthorityAccessException {
public synchronized Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException {
final Set<Authority> authorities = EnumSet.noneOf(Authority.class);
// get the user
final NiFiUser user = authorizedUsers.getUser(new FindUser() {
@Override
public NiFiUser findUser(List<NiFiUser> users) {
final FindUser byDn = new FindUserByIdentity(dn);
NiFiUser user = byDn.findUser(users);
final User user = getUser(dn);
// if the user is not found, add them and locate them
if (user == null) {
if (hasDefaultRoles()) {
logger.debug(String.format("User identity not found: %s. Creating new user with default roles.", dn));
// ensure the user was located
if (user == null) {
if (hasDefaultRoles()) {
logger.debug(String.format("User DN not found: %s. Creating new user with default roles.", dn));
// create the user (which will automatically add any default authorities)
addUser(dn, null);
// create the user (which will automatically add any default authorities)
addUser(dn, null);
// find the user that was just added
user = byDn.findUser(users);
} else {
throw new UnknownIdentityException(String.format("User identity not found: %s.", dn));
}
}
return user;
// get the authorities for the newly created user
authorities.addAll(getAuthorities(dn));
} else {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
} else {
// create the authorities that this user has
for (final Role role : user.getRole()) {
authorities.add(Authority.valueOfAuthority(role.getName()));
}
});
// create the authorities that this user has
for (final Role role : user.getRole()) {
authorities.add(Authority.valueOfAuthority(role.getName()));
}
return authorities;
}
@Override
public void setAuthorities(final String dn, final Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException {
authorizedUsers.updateUser(new FindUserByIdentity(dn), new UpdateUser() {
@Override
public void updateUser(NiFiUser user) {
// add the user authorities
setUserAuthorities(user, authorities);
}
});
public synchronized void setAuthorities(String dn, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException {
// get the user
final User user = getUser(dn);
// ensure the user was located
if (user == null) {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
// add the user authorities
setUserAuthorities(user, authorities);
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
private void setUserAuthorities(final NiFiUser user, final Set<Authority> authorities) {
private void setUserAuthorities(final User user, final Set<Authority> authorities) {
// clear the existing rules
user.getRole().clear();
@ -182,145 +248,186 @@ public class FileAuthorizationProvider implements AuthorityProvider {
}
@Override
public void addUser(final String dn, final String group) throws IdentityAlreadyExistsException, AuthorityAccessException {
authorizedUsers.createOrUpdateUser(new FindUser() {
@Override
public NiFiUser findUser(final List<NiFiUser> users) throws UnknownIdentityException {
// attempt to get the user and ensure it was located
NiFiUser desiredUser = null;
for (final NiFiUser user : users) {
if (dn.equalsIgnoreCase(authorizedUsers.getUserIdentity(user))) {
desiredUser = user;
break;
}
}
public synchronized void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException {
final User user = getUser(dn);
// user does not exist, will create
if (desiredUser == null) {
throw new UnknownIdentityException("This exception will trigger the creator to be invoked.");
}
// ensure the user doesn't already exist
if (user != null) {
throw new IdentityAlreadyExistsException(String.format("User DN already exists: %s", dn));
}
// user exists, verify its still pending
if (LoginUser.class.isAssignableFrom(desiredUser.getClass())) {
if (((LoginUser) desiredUser).isPending()) {
return desiredUser;
}
}
// create the new user
final ObjectFactory objFactory = new ObjectFactory();
final User newUser = objFactory.createUser();
// user exists and account is valid... no good
throw new IdentityAlreadyExistsException(String.format("User identity already exists: %s", dn));
// set the user properties
newUser.setDn(dn);
newUser.setGroup(group);
// add default roles if appropriate
if (hasDefaultRoles()) {
for (final String authority : defaultAuthorities) {
Role role = objFactory.createRole();
role.setName(authority);
// add the role
newUser.getRole().add(role);
}
}, new CreateUser() {
@Override
public NiFiUser createUser() {
// only support adding PreAuthenticated User's via this API - LoginUser's are added
// via the LoginIdentityProvider
final ObjectFactory objFactory = new ObjectFactory();
final User newUser = objFactory.createUser();
}
// set the user properties
newUser.setDn(dn);
newUser.setGroup(group);
// add the user
users.getUser().add(newUser);
// add default roles if appropriate
if (hasDefaultRoles()) {
for (final String authority : defaultAuthorities) {
Role role = objFactory.createRole();
role.setName(authority);
// add the role
newUser.getRole().add(role);
}
}
return newUser;
}
}, new UpdateUser() {
@Override
public void updateUser(final NiFiUser user) {
// only support updating Login Users's via this API - need to mark the account as non pending
LoginUser loginUser = (LoginUser) user;
loginUser.setPending(false);
}
});
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
@Override
public Set<String> getUsers(final Authority authority) throws AuthorityAccessException {
final List<NiFiUser> matchingUsers = authorizedUsers.getUsers(new FindUsers() {
@Override
public List<NiFiUser> findUsers(List<NiFiUser> users) throws UnknownIdentityException {
final List<NiFiUser> matchingUsers = new ArrayList<>();
for (final NiFiUser user : users) {
for (final Role role : user.getRole()) {
if (role.getName().equals(authority.toString())) {
matchingUsers.add(user);
}
}
}
return matchingUsers;
}
});
public synchronized Set<String> getUsers(Authority authority) throws AuthorityAccessException {
final Set<String> userSet = new HashSet<>();
for (final NiFiUser user : matchingUsers) {
userSet.add(authorizedUsers.getUserIdentity(user));
for (final User user : users.getUser()) {
for (final Role role : user.getRole()) {
if (role.getName().equals(authority.toString())) {
userSet.add(user.getDn());
}
}
}
return userSet;
}
@Override
public void revokeUser(final String dn) throws UnknownIdentityException, AuthorityAccessException {
authorizedUsers.removeUser(new FindUserByIdentity(dn));
public synchronized void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
// get the user
final User user = getUser(dn);
// ensure the user was located
if (user == null) {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
// remove the specified user
users.getUser().remove(user);
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
@Override
public void setUsersGroup(final Set<String> dns, final String group) throws UnknownIdentityException, AuthorityAccessException {
authorizedUsers.updateUsers(new FindUsersByIdentity(dns), new UpdateUsers() {
@Override
public void updateUsers(List<NiFiUser> users) {
// update each user group
for (final NiFiUser user : users) {
user.setGroup(group);
}
public void setUsersGroup(Set<String> dns, String group) throws UnknownIdentityException, AuthorityAccessException {
final Collection<User> groupedUsers = new HashSet<>();
// get the specified users
for (final String dn : dns) {
// get the user
final User user = getUser(dn);
// ensure the user was located
if (user == null) {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
});
groupedUsers.add(user);
}
// update each user group
for (final User user : groupedUsers) {
user.setGroup(group);
}
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
@Override
public void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
authorizedUsers.updateUser(new FindUserByIdentity(dn), new UpdateUser() {
@Override
public void updateUser(NiFiUser user) {
// remove the users group
user.setGroup(null);
}
});
// get the user
final User user = getUser(dn);
// ensure the user was located
if (user == null) {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
// remove the users group
user.setGroup(null);
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
@Override
public void ungroup(final String group) throws AuthorityAccessException {
authorizedUsers.updateUsers(new FindUsersByGroup(group), new UpdateUsers() {
@Override
public void updateUsers(List<NiFiUser> users) {
// update each user group
for (final NiFiUser user : users) {
user.setGroup(null);
}
}
});
public void ungroup(String group) throws AuthorityAccessException {
// get the user group
final Collection<User> userGroup = getUserGroup(group);
// ensure the user group was located
if (userGroup == null) {
return;
}
// update each user group
for (final User user : userGroup) {
user.setGroup(null);
}
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
@Override
public String getGroupForUser(final String dn) throws UnknownIdentityException, AuthorityAccessException {
final NiFiUser user = authorizedUsers.getUser(new FindUserByIdentity(dn));
public String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
// get the user
final User user = getUser(dn);
// ensure the user was located
if (user == null) {
throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
}
return user.getGroup();
}
@Override
public void revokeGroup(String group) throws UnknownIdentityException, AuthorityAccessException {
authorizedUsers.removeUsers(new FindUsersByGroup(group));
// get the user group
final Collection<User> userGroup = getUserGroup(group);
// ensure the user group was located
if (userGroup == null) {
throw new UnknownIdentityException(String.format("User group not found: %s.", group));
}
// remove each user in the group
for (final User user : userGroup) {
users.getUser().remove(user);
}
try {
// save the file
save();
} catch (Exception e) {
throw new AuthorityAccessException(e.getMessage(), e);
}
}
/**
@ -331,143 +438,59 @@ public class FileAuthorizationProvider implements AuthorityProvider {
return DownloadAuthorization.approved();
}
private User getUser(String dn) throws UnknownIdentityException {
// ensure the DN was specified
if (dn == null) {
throw new UnknownIdentityException("User DN not specified.");
}
// attempt to get the user and ensure it was located
User desiredUser = null;
for (final User user : users.getUser()) {
if (dn.equalsIgnoreCase(user.getDn())) {
desiredUser = user;
break;
}
}
return desiredUser;
}
private Collection<User> getUserGroup(String group) throws UnknownIdentityException {
// ensure the DN was specified
if (group == null) {
throw new UnknownIdentityException("User group not specified.");
}
// get all users with this group
Collection<User> userGroup = null;
for (final User user : users.getUser()) {
if (group.equals(user.getGroup())) {
if (userGroup == null) {
userGroup = new HashSet<>();
}
userGroup.add(user);
}
}
return userGroup;
}
private void save() throws Exception {
final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// save users to restore directory before primary directory
if (restoreUsersFile != null) {
marshaller.marshal(users, restoreUsersFile);
}
// save users to primary directory
marshaller.marshal(users, usersFile);
}
@AuthorityProviderContext
public void setNiFiProperties(NiFiProperties properties) {
this.properties = properties;
}
private boolean isPendingLoginUser(final NiFiUser user) {
if (LoginUser.class.isAssignableFrom(user.getClass())) {
return ((LoginUser) user).isPending();
}
return false;
}
public class HasUserByIdentity implements HasUser {
private final String identity;
public HasUserByIdentity(String identity) {
// ensure the identity was specified
if (identity == null) {
throw new UnknownIdentityException("User identity not specified.");
}
this.identity = identity;
}
@Override
public boolean hasUser(List<NiFiUser> users) {
// attempt to get the user and ensure it was located
NiFiUser desiredUser = null;
for (final NiFiUser user : users) {
if (identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user)) && !isPendingLoginUser(user)) {
desiredUser = user;
break;
}
}
return desiredUser != null;
}
}
public class FindUserByIdentity implements FindUser {
private final String identity;
public FindUserByIdentity(String identity) {
// ensure the identity was specified
if (identity == null) {
throw new UnknownIdentityException("User identity not specified.");
}
this.identity = identity;
}
@Override
public NiFiUser findUser(List<NiFiUser> users) {
// attempt to get the user and ensure it was located
NiFiUser desiredUser = null;
for (final NiFiUser user : users) {
if (identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user)) && !isPendingLoginUser(user)) {
desiredUser = user;
break;
}
}
if (desiredUser == null) {
throw new UnknownIdentityException(String.format("User identity not found: %s.", identity));
}
return desiredUser;
}
}
public class FindUsersByGroup implements FindUsers {
private final String group;
public FindUsersByGroup(String group) {
// ensure the group was specified
if (group == null) {
throw new UnknownIdentityException("User group not specified.");
}
this.group = group;
}
@Override
public List<NiFiUser> findUsers(List<NiFiUser> users) throws UnknownIdentityException {
// get all users with this group
List<NiFiUser> userGroup = new ArrayList<>();
for (final NiFiUser user : users) {
if (group.equals(user.getGroup()) && !isPendingLoginUser(user)) {
userGroup.add(user);
}
}
// ensure the user group was located
if (userGroup.isEmpty()) {
throw new UnknownIdentityException(String.format("User group not found: %s.", group));
}
return userGroup;
}
}
public class FindUsersByIdentity implements FindUsers {
private final Set<String> identities;
public FindUsersByIdentity(Set<String> identities) {
// ensure the group was specified
if (identities == null) {
throw new UnknownIdentityException("User identities not specified.");
}
this.identities = identities;
}
@Override
public List<NiFiUser> findUsers(List<NiFiUser> users) throws UnknownIdentityException {
final Set<String> copy = new HashSet<>(identities);
// get all users with this group
List<NiFiUser> userList = new ArrayList<>();
for (final NiFiUser user : users) {
final String userIdentity = authorizedUsers.getUserIdentity(user);
if (copy.contains(userIdentity) && !isPendingLoginUser(user)) {
copy.remove(userIdentity);
userList.add(user);
}
}
if (!copy.isEmpty()) {
throw new UnknownIdentityException("Unable to find users with identities: " + StringUtils.join(copy, ", "));
}
return userList;
}
}
}

View File

@ -30,10 +30,19 @@
</xs:attribute>
</xs:complexType>
<xs:complexType name="NiFiUser">
<!-- user -->
<xs:complexType name="User">
<xs:sequence>
<xs:element name="role" type="Role" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="dn">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="group">
<xs:simpleType>
<xs:restriction base="xs:string">
@ -44,53 +53,11 @@
</xs:attribute>
</xs:complexType>
<!-- preauthenticated user -->
<xs:complexType name="User">
<xs:complexContent>
<xs:extension base="NiFiUser">
<xs:attribute name="dn" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- login user -->
<xs:complexType name="LoginUser">
<xs:complexContent>
<xs:extension base="NiFiUser">
<xs:attribute name="username" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="password" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="pending" type="xs:boolean" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- users -->
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="loginUser" type="LoginUser" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework</artifactId>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-file-identity-provider</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-authorized-users</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,15 +0,0 @@
# 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.
org.apache.nifi.authentication.FileLoginIdentityProvider

View File

@ -19,9 +19,4 @@
must be specified in the nifi.properties file.
-->
<loginIdentityProviders>
<provider>
<identifier>file-provider</identifier>
<class>org.apache.nifi.authentication.FileLoginIdentityProvider</class>
<property name="Authenticated Users File">./conf/authorized-users.xml</property>
</provider>
</loginIdentityProviders>

View File

@ -25,7 +25,6 @@ import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
import org.apache.nifi.web.security.RegistrationStatusFilter;
import org.apache.nifi.web.security.login.LoginAuthenticationFilter;
import org.apache.nifi.web.security.login.RegistrationFilter;
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtService;
import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
@ -88,13 +87,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
if (loginIdentityProvider != null) {
// verify the configured login authenticator supports user login registration
if (loginIdentityProvider.supportsRegistration()) {
http.addFilterBefore(buildRegistrationFilter("/registration"), UsernamePasswordAuthenticationFilter.class);
}
}
// login authentication for /token - exchanges for JWT for subsequent API usage
http.addFilterBefore(buildLoginFilter("/token"), UsernamePasswordAuthenticationFilter.class);
@ -139,14 +131,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
return loginFilter;
}
private Filter buildRegistrationFilter(final String url) {
final RegistrationFilter registrationFilter = new RegistrationFilter(url);
registrationFilter.setJwtService(jwtService);
registrationFilter.setLoginIdentityProvider(loginIdentityProvider);
registrationFilter.setUserService(userService);
return registrationFilter;
}
private Filter buildRegistrationStatusFilter(final String url) {
final RegistrationStatusFilter registrationStatusFilter = new RegistrationStatusFilter(url);
registrationStatusFilter.setCertificateExtractor(certificateExtractor);

View File

@ -2354,14 +2354,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public LoginConfigurationDTO getLoginConfiguration() {
final LoginConfigurationDTO loginConfiguration = new LoginConfigurationDTO();
// specify whether login/registration should be supported
if (loginIdentityProvider == null) {
loginConfiguration.setSupportsLogin(false);
loginConfiguration.setSupportsRegistration(false);
} else {
loginConfiguration.setSupportsLogin(true);
loginConfiguration.setSupportsRegistration(loginIdentityProvider.supportsRegistration());
}
// specify whether login should be supported
loginConfiguration.setSupportsLogin(loginIdentityProvider != null);
return loginConfiguration;
}

View File

@ -688,7 +688,6 @@ public class ControllerResource extends ApplicationResource {
// only support login/registration when running securely
loginConfig.setSupportsLogin(loginConfig.getSupportsLogin() && httpServletRequest.isSecure());
loginConfig.setSupportsRegistration(loginConfig.getSupportsRegistration() && httpServletRequest.isSecure());
// create the revision
final RevisionDTO revision = new RevisionDTO();

View File

@ -1,163 +0,0 @@
/*
* 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.web.security.login;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.admin.service.AccountDisabledException;
import org.apache.nifi.admin.service.AccountNotFoundException;
import org.apache.nifi.admin.service.AccountPendingException;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.authentication.LoginCredentials;
import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.authentication.exception.IdentityAccessException;
import org.apache.nifi.authentication.exception.IdentityRegistrationException;
import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
import org.apache.nifi.util.StringUtils;
import org.apache.nifi.web.security.jwt.JwtService;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
/**
* Exchanges a successful login with the configured provider for a ID token for accessing the API.
*/
public class RegistrationFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger logger = LoggerFactory.getLogger(RegistrationFilter.class);
private LoginIdentityProvider loginIdentityProvider;
private JwtService jwtService;
private UserService userService;
public RegistrationFilter(final String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
// do not continue filter chain... simply exchanging authentication for token
setContinueChainBeforeSuccessfulAuthentication(false);
}
@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
// only suppport registration when running securely
if (!request.isSecure()) {
return null;
}
// look for the credentials in the request
final LoginCredentials credentials = getLoginCredentials(request);
// if the credentials were not part of the request, attempt to log in with the certificate in the request
if (credentials == null) {
throw new UsernameNotFoundException("User login credentials not found in request.");
} else {
try {
// attempt to register the user
loginIdentityProvider.register(credentials);
} catch (final IdentityAlreadyExistsException iaee) {
// if the identity already exists, try to create the nifi account request
} catch (final IdentityRegistrationException ire) {
// the credentials are not acceptable for some reason
throw new BadCredentialsException(ire.getMessage(), ire);
} catch (final IdentityAccessException iae) {
throw new AuthenticationServiceException(iae.getMessage(), iae);
}
try {
// see if the account already exists so we're able to return the current status
userService.checkAuthorization(credentials.getUsername());
// account exists and is valid
throw new AccountStatusException(String.format("An account for %s already exists.", credentials.getUsername())) {
};
} catch (AdministrationException ase) {
throw new AuthenticationServiceException(ase.getMessage(), ase);
} catch (AccountDisabledException | AccountPendingException e) {
throw new AccountStatusException(e.getMessage(), e) {
};
} catch (AccountNotFoundException anfe) {
// create the pending user account
userService.createPendingUserAccount(credentials.getUsername(), request.getParameter("justification"));
// create the login token
return new LoginAuthenticationToken(credentials);
}
}
}
private LoginCredentials getLoginCredentials(HttpServletRequest request) {
final String username = request.getParameter("username");
final String password = request.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
return null;
} else {
return new LoginCredentials(username, password);
}
}
@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
throws IOException, ServletException {
// generate JWT for response
jwtService.addToken(response, authentication);
}
@Override
protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
response.setContentType("text/plain");
final PrintWriter out = response.getWriter();
out.println(failed.getMessage());
// set the appropriate response status
if (failed instanceof UsernameNotFoundException || failed instanceof BadCredentialsException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
} else if (failed instanceof AccountStatusException) {
// account exists (maybe valid, pending, revoked)
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
public void setJwtService(JwtService jwtService) {
this.jwtService = jwtService;
}
public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
this.loginIdentityProvider = loginIdentityProvider;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}

View File

@ -258,20 +258,6 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
private LoginIdentityProvider withNarLoader(final LoginIdentityProvider baseProvider) {
return new LoginIdentityProvider() {
@Override
public boolean supportsRegistration() {
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
return baseProvider.supportsRegistration();
}
}
@Override
public void register(LoginCredentials credentials) {
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
baseProvider.register(credentials);
}
}
@Override
public boolean authenticate(LoginCredentials credentials) {
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {

View File

@ -43,7 +43,6 @@
<div id="login-contents-container">
<jsp:include page="/WEB-INF/partials/login/login-message.jsp"/>
<jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
<jsp:include page="/WEB-INF/partials/login/user-registration-form.jsp"/>
<jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
<jsp:include page="/WEB-INF/partials/login/login-submission.jsp"/>
</div>

View File

@ -27,10 +27,6 @@
<div class="setting-name">Password</div>
<div class="setting-field">
<input type="password" id="password"/>
<div id="create-account-message" class="hidden">
<div style="font-style: italic;">Don't have an account?</div>
<div><span id="create-account-link" class="link">Create one</span> to request access.</div>
</div>
</div>
</div>
</div>

View File

@ -30,10 +30,6 @@
<div class="setting-field">
<textarea cols="30" rows="4" id="nifi-registration-justification" maxlength="500" class="setting-input"></textarea>
</div>
<div id="login-to-account-message" class="hidden">
<div style="font-style: italic;">Already have an account?</div>
<div style="margin-top: 2px;"><span id="login-to-account-link" class="link">Log in</span></div>
</div>
<div style="text-align: right; color: #666; margin-top: 2px; float: right;">
<span id="remaining-characters"></span>&nbsp;characters remaining
</div>

View File

@ -1,34 +0,0 @@
<%--
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.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="user-registration-container" class="hidden">
<div class="login-title">Create Account</div>
<div class="setting">
<div class="setting-name">Username</div>
<div class="setting-field">
<input type="text" id="registration-username"/>
</div>
</div>
<div class="setting">
<div class="setting-name">Password</div>
<div class="setting-field">
<input type="password" id="registration-password" style="margin-bottom: 5px;"/>
<br/>
<input type="password" id="registration-password-confirmation" placeholder="Confirm password"/>
</div>
</div>
</div>

View File

@ -56,18 +56,6 @@ body.login-body input, body.login-body textarea {
width: 400px;
}
#create-account-message {
margin-top: 2px;
}
#create-account-link {
text-decoration: underline;
}
/*
User Registration
*/
/*
NiFi Registration
*/
@ -88,15 +76,6 @@ body.login-body input, body.login-body textarea {
height: 200px;
}
#login-to-account-message {
float: left;
margin-top: 2px;
}
#login-to-account-link {
text-decoration: underline;
}
/*
Submission
*/

View File

@ -141,21 +141,15 @@ nf.CanvasHeader = (function () {
nf.Shell.showPage(config.urls.helpDocument);
});
// show the login link if supported and user is currently anonymous
var isAnonymous = $('#current-user').text() === nf.Canvas.ANONYMOUS_USER_TEXT;
if (supportsLogin === true && isAnonymous) {
// login link
$('#login-link').click(function () {
nf.Shell.showPage('login', false);
});
} else {
// hide the login link if the user is already logged in
if ($('#current-user').text() !== nf.Canvas.ANONYMOUS_USER_TEXT) {
$('#login-link-container').css('display', 'none');
}
// if login is not supported, don't show the current user
if (supportsLogin !== true) {
$('#current-user-container').css('display', 'none');
}
// login link
$('#login-link').click(function () {
nf.Shell.showPage('login', false);
});
// logout link
$('#logout-link').click(function () {

View File

@ -1087,13 +1087,6 @@ nf.Canvas = (function () {
dataType: 'json'
});
// get the login config
var loginXhr = $.ajax({
type: 'GET',
url: config.urls.loginConfig,
dataType: 'json'
});
// create the deferred cluster request
var isClusteredRequest = $.Deferred(function (deferred) {
$.ajax({
@ -1113,9 +1106,8 @@ nf.Canvas = (function () {
}).promise();
// ensure the config requests are loaded
$.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) {
$.when(configXhr, userXhr).done(function (configResult) {
var configResponse = configResult[0];
var loginResponse = loginResult[0];
// calculate the canvas offset
var canvasContainer = $('#canvas-container');
@ -1123,7 +1115,6 @@ nf.Canvas = (function () {
// get the config details
var configDetails = configResponse.config;
var loginDetails = loginResponse.config;
// when both request complete, load the application
isClusteredRequest.done(function () {
@ -1143,7 +1134,7 @@ nf.Canvas = (function () {
nf.ContextMenu.init();
nf.CanvasToolbar.init();
nf.CanvasToolbox.init();
nf.CanvasHeader.init(loginDetails.supportsLogin);
nf.CanvasHeader.init();
nf.GraphControl.init();
nf.Search.init();
nf.Settings.init();

View File

@ -40,20 +40,7 @@ nf.Login = (function () {
$('#login-message-container').show();
};
var initializeLogin = function (supportsRegistration) {
// if this nifi supports registration, render the registration form
if (supportsRegistration === true) {
initializeUserRegistration();
initializeNiFiRegistration();
// show the create account message
$('#create-account-message').show();
// toggle between login and signup
$('#create-account-link').on('click', function () {
showUserRegistration();
});
}
var initializeLogin = function () {
};
var showLogin = function () {
@ -82,7 +69,6 @@ nf.Login = (function () {
$('div.nifi-submit-justification').hide();
$('#user-registration-container').show();
$('#login-to-account-message').show();
$('#login-submission-button').text('Create');
};
@ -416,7 +402,7 @@ nf.Login = (function () {
if (showMessage === true) {
initializeMessage();
} else if (needsLogin === true) {
initializeLogin(loginConfig.supportsRegistration);
initializeLogin();
showLogin();
} else if (needsNiFiRegistration === true) {
initializeNiFiRegistration();

View File

@ -40,8 +40,6 @@
<module>nifi-web</module>
<module>nifi-resources</module>
<module>nifi-documentation</module>
<module>nifi-authorized-users</module>
<module>nifi-file-identity-provider</module>
</modules>
<dependencies>
<dependency>

View File

@ -43,11 +43,6 @@
<artifactId>nifi-file-authorization-provider</artifactId>
<version>0.3.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-file-identity-provider</artifactId>
<version>0.3.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-cluster-authorization-provider</artifactId>
@ -63,11 +58,6 @@
<artifactId>nifi-runtime</artifactId>
<version>0.3.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-authorized-users</artifactId>
<version>0.3.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-client-dto</artifactId>