You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2018/09/22 02:11:33 UTC
[34/51] [partial] nifi-registry git commit: NIFIREG-201 Refactoring
project structure to better isolate extensions
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
new file mode 100644
index 0000000..4736fe2
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
@@ -0,0 +1,746 @@
+/*
+ * 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.registry.security.authorization.file;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.properties.util.IdentityMapping;
+import org.apache.nifi.registry.properties.util.IdentityMappingUtil;
+import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext;
+import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider;
+import org.apache.nifi.registry.security.authorization.Group;
+import org.apache.nifi.registry.security.authorization.User;
+import org.apache.nifi.registry.security.authorization.UserAndGroups;
+import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext;
+import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Groups;
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Tenants;
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Users;
+import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
+import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.apache.nifi.registry.util.FileUtils;
+import org.apache.nifi.registry.util.PropertyValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+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.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(FileUserGroupProvider.class);
+
+ private static final String TENANTS_XSD = "/tenants.xsd";
+ private static final String JAXB_TENANTS_PATH = "org.apache.nifi.registry.security.authorization.file.tenants.generated";
+
+ private static final JAXBContext JAXB_TENANTS_CONTEXT = initializeJaxbContext(JAXB_TENANTS_PATH);
+
+ /**
+ * Load the JAXBContext.
+ */
+ private static JAXBContext initializeJaxbContext(final String contextPath) {
+ try {
+ return JAXBContext.newInstance(contextPath, FileAuthorizer.class.getClassLoader());
+ //return JAXBContext.newInstance(contextPath);
+ } catch (JAXBException e) {
+ throw new RuntimeException("Unable to create JAXBContext: " + e);
+ }
+ }
+
+ private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+ private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
+
+ private static final String USER_ELEMENT = "user";
+ private static final String GROUP_USER_ELEMENT = "groupUser";
+ private static final String GROUP_ELEMENT = "group";
+ private static final String IDENTIFIER_ATTR = "identifier";
+ private static final String IDENTITY_ATTR = "identity";
+ private static final String NAME_ATTR = "name";
+
+ static final String PROP_INITIAL_USER_IDENTITY_PREFIX = "Initial User Identity ";
+ static final String PROP_TENANTS_FILE = "Users File";
+ static final Pattern INITIAL_USER_IDENTITY_PATTERN = Pattern.compile(PROP_INITIAL_USER_IDENTITY_PREFIX + "\\S+");
+
+ private Schema usersSchema;
+ private Schema tenantsSchema;
+ private NiFiRegistryProperties properties;
+ private File tenantsFile;
+ private File restoreTenantsFile;
+ private Set<String> initialUserIdentities;
+ private List<IdentityMapping> identityMappings;
+
+ private final AtomicReference<UserGroupHolder> userGroupHolder = new AtomicReference<>();
+
+ @Override
+ public void initialize(UserGroupProviderInitializationContext initializationContext) throws SecurityProviderCreationException {
+ try {
+ final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ tenantsSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(TENANTS_XSD));
+ //usersSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD));
+ } catch (Exception e) {
+ throw new SecurityProviderCreationException(e);
+ }
+ }
+
+ @Override
+ public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+ try {
+ final PropertyValue tenantsPath = configurationContext.getProperty(PROP_TENANTS_FILE);
+ if (StringUtils.isBlank(tenantsPath.getValue())) {
+ throw new SecurityProviderCreationException("The users file must be specified.");
+ }
+
+ // get the tenants file and ensure it exists
+ tenantsFile = new File(tenantsPath.getValue());
+ if (!tenantsFile.exists()) {
+ logger.info("Creating new users file at {}", new Object[] {tenantsFile.getAbsolutePath()});
+ saveTenants(new Tenants());
+ }
+
+ final File tenantsFileDirectory = tenantsFile.getAbsoluteFile().getParentFile();
+
+ // extract the identity mappings from nifi-registry.properties if any are provided
+ identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
+
+ // extract any nifi identities
+ initialUserIdentities = new HashSet<>();
+ for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
+ Matcher matcher = INITIAL_USER_IDENTITY_PATTERN.matcher(entry.getKey());
+ if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
+ initialUserIdentities.add(IdentityMappingUtil.mapIdentity(entry.getValue(), identityMappings));
+ }
+ }
+
+ load();
+
+ // if we've copied the authorizations file to a restore directory synchronize it
+ if (restoreTenantsFile != null) {
+ FileUtils.copyFile(tenantsFile, restoreTenantsFile, false, false, logger);
+ }
+
+ logger.info(String.format("Users/Groups file loaded at %s", new Date().toString()));
+ } catch (IOException | SecurityProviderCreationException | JAXBException | IllegalStateException | SAXException e) {
+ throw new SecurityProviderCreationException(e);
+ }
+ }
+
+ @Override
+ public Set<User> getUsers() throws AuthorizationAccessException {
+ return userGroupHolder.get().getAllUsers();
+ }
+
+ @Override
+ public synchronized User addUser(User user) throws AuthorizationAccessException {
+ if (user == null) {
+ throw new IllegalArgumentException("User cannot be null");
+ }
+
+ final org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser = createJAXBUser(user);
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final Tenants tenants = holder.getTenants();
+ tenants.getUsers().getUser().add(jaxbUser);
+
+ saveAndRefreshHolder(tenants);
+
+ return userGroupHolder.get().getUsersById().get(user.getIdentifier());
+ }
+
+ @Override
+ public User getUser(String identifier) throws AuthorizationAccessException {
+ if (identifier == null) {
+ return null;
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ return holder.getUsersById().get(identifier);
+ }
+
+ @Override
+ public synchronized User updateUser(User user) throws AuthorizationAccessException {
+ if (user == null) {
+ throw new IllegalArgumentException("User cannot be null");
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final Tenants tenants = holder.getTenants();
+
+ final List<org.apache.nifi.registry.security.authorization.file.tenants.generated.User> users = tenants.getUsers().getUser();
+
+ // fine the User that needs to be updated
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.User updateUser = null;
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser : users) {
+ if (user.getIdentifier().equals(jaxbUser.getIdentifier())) {
+ updateUser = jaxbUser;
+ break;
+ }
+ }
+
+ // if user wasn't found return null, otherwise update the user and save changes
+ if (updateUser == null) {
+ return null;
+ } else {
+ updateUser.setIdentity(user.getIdentity());
+ saveAndRefreshHolder(tenants);
+
+ return userGroupHolder.get().getUsersById().get(user.getIdentifier());
+ }
+ }
+
+ @Override
+ public User getUserByIdentity(String identity) throws AuthorizationAccessException {
+ if (identity == null) {
+ return null;
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ return holder.getUsersByIdentity().get(identity);
+ }
+
+ @Override
+ public synchronized User deleteUser(User user) throws AuthorizationAccessException {
+ if (user == null) {
+ throw new IllegalArgumentException("User cannot be null");
+ }
+
+ return deleteUser(user.getIdentifier());
+ }
+
+ @Override
+ public synchronized User deleteUser(String userIdentifier) throws AuthorizationAccessException {
+ if (userIdentifier == null) {
+ throw new IllegalArgumentException("User identifier cannot be null");
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final User deletedUser = holder.getUsersById().get(userIdentifier);
+ if (deletedUser == null) {
+ return null;
+ }
+
+ // for each group iterate over the user references and remove the user reference if it matches the user being deleted
+ final Tenants tenants = holder.getTenants();
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) {
+ Iterator<org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User> groupUserIter = group.getUser().iterator();
+ while (groupUserIter.hasNext()) {
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User groupUser = groupUserIter.next();
+ if (groupUser.getIdentifier().equals(userIdentifier)) {
+ groupUserIter.remove();
+ break;
+ }
+ }
+ }
+
+ // remove the actual user
+ Iterator<org.apache.nifi.registry.security.authorization.file.tenants.generated.User> iter = tenants.getUsers().getUser().iterator();
+ while (iter.hasNext()) {
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser = iter.next();
+ if (userIdentifier.equals(jaxbUser.getIdentifier())) {
+ iter.remove();
+ break;
+ }
+ }
+
+ saveAndRefreshHolder(tenants);
+ return deletedUser;
+ }
+
+ @Override
+ public Set<Group> getGroups() throws AuthorizationAccessException {
+ return userGroupHolder.get().getAllGroups();
+ }
+
+ @Override
+ public synchronized Group addGroup(Group group) throws AuthorizationAccessException {
+ if (group == null) {
+ throw new IllegalArgumentException("Group cannot be null");
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final Tenants tenants = holder.getTenants();
+
+ // create a new JAXB Group based on the incoming Group
+ final org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup =
+ new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group();
+ jaxbGroup.setIdentifier(group.getIdentifier());
+ jaxbGroup.setName(group.getName());
+
+ // add each user to the group
+ for (String groupUser : group.getUsers()) {
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User jaxbGroupUser =
+ new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User();
+ jaxbGroupUser.setIdentifier(groupUser);
+ jaxbGroup.getUser().add(jaxbGroupUser);
+ }
+
+ tenants.getGroups().getGroup().add(jaxbGroup);
+ saveAndRefreshHolder(tenants);
+
+ return userGroupHolder.get().getGroupsById().get(group.getIdentifier());
+ }
+
+ @Override
+ public Group getGroup(String identifier) throws AuthorizationAccessException {
+ if (identifier == null) {
+ return null;
+ }
+ return userGroupHolder.get().getGroupsById().get(identifier);
+ }
+
+ @Override
+ public UserAndGroups getUserAndGroups(final String identity) throws AuthorizationAccessException {
+ final UserGroupHolder holder = userGroupHolder.get();
+ final User user = holder.getUser(identity);
+ final Set<Group> groups = holder.getGroups(identity);
+
+ return new UserAndGroups() {
+ @Override
+ public User getUser() {
+ return user;
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return groups;
+ }
+ };
+ }
+
+ @Override
+ public synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
+ if (group == null) {
+ throw new IllegalArgumentException("Group cannot be null");
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final Tenants tenants = holder.getTenants();
+
+ // find the group that needs to be update
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group updateGroup = null;
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup : tenants.getGroups().getGroup()) {
+ if (jaxbGroup.getIdentifier().equals(group.getIdentifier())) {
+ updateGroup = jaxbGroup;
+ break;
+ }
+ }
+
+ // if the group wasn't found return null, otherwise update the group and save changes
+ if (updateGroup == null) {
+ return null;
+ }
+
+ // reset the list of users and add each user to the group
+ updateGroup.getUser().clear();
+ for (String groupUser : group.getUsers()) {
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User jaxbGroupUser =
+ new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User();
+ jaxbGroupUser.setIdentifier(groupUser);
+ updateGroup.getUser().add(jaxbGroupUser);
+ }
+
+ updateGroup.setName(group.getName());
+ saveAndRefreshHolder(tenants);
+
+ return userGroupHolder.get().getGroupsById().get(group.getIdentifier());
+ }
+
+ @Override
+ public synchronized Group deleteGroup(Group group) throws AuthorizationAccessException {
+ if (group == null) {
+ throw new IllegalArgumentException("Group cannot be null");
+ }
+
+ return deleteGroup(group.getIdentifier());
+ }
+
+ @Override
+ public synchronized Group deleteGroup(String groupIdentifier) throws AuthorizationAccessException {
+ if (groupIdentifier == null) {
+ throw new IllegalArgumentException("Group identifier cannot be null");
+ }
+
+ final UserGroupHolder holder = userGroupHolder.get();
+ final Group deletedGroup = holder.getGroupsById().get(groupIdentifier);
+ if (deletedGroup == null) {
+ return null;
+ }
+
+ // now remove the actual group from the top-level list of groups
+ final Tenants tenants = holder.getTenants();
+ Iterator<org.apache.nifi.registry.security.authorization.file.tenants.generated.Group> iter = tenants.getGroups().getGroup().iterator();
+ while (iter.hasNext()) {
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup = iter.next();
+ if (groupIdentifier.equals(jaxbGroup.getIdentifier())) {
+ iter.remove();
+ break;
+ }
+ }
+
+ saveAndRefreshHolder(tenants);
+ return deletedGroup;
+ }
+
+ UserGroupHolder getUserGroupHolder() {
+ return userGroupHolder.get();
+ }
+
+ @AuthorizerContext
+ public void setNiFiProperties(NiFiRegistryProperties properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public synchronized void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+ final UsersAndGroups usersAndGroups = parseUsersAndGroups(fingerprint);
+ usersAndGroups.getUsers().forEach(user -> addUser(user));
+ usersAndGroups.getGroups().forEach(group -> addGroup(group));
+ }
+
+ @Override
+ public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException {
+ try {
+ // ensure we understand the proposed fingerprint
+ parseUsersAndGroups(proposedFingerprint);
+ } catch (final AuthorizationAccessException e) {
+ throw new UninheritableAuthorizationsException("Unable to parse the proposed fingerprint: " + e);
+ }
+
+ final UserGroupHolder usersAndGroups = userGroupHolder.get();
+
+ // ensure we are in a proper state to inherit the fingerprint
+ if (!usersAndGroups.getAllUsers().isEmpty() || !usersAndGroups.getAllGroups().isEmpty()) {
+ throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current users and groups is not empty.");
+ }
+ }
+
+ @Override
+ public String getFingerprint() throws AuthorizationAccessException {
+ final UserGroupHolder usersAndGroups = userGroupHolder.get();
+
+ final List<User> users = new ArrayList<>(usersAndGroups.getAllUsers());
+ Collections.sort(users, Comparator.comparing(User::getIdentifier));
+
+ final List<Group> groups = new ArrayList<>(usersAndGroups.getAllGroups());
+ Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
+
+ XMLStreamWriter writer = null;
+ final StringWriter out = new StringWriter();
+ try {
+ writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+ writer.writeStartDocument();
+ writer.writeStartElement("tenants");
+
+ for (User user : users) {
+ writeUser(writer, user);
+ }
+ for (Group group : groups) {
+ writeGroup(writer, group);
+ }
+
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.flush();
+ } catch (XMLStreamException e) {
+ throw new AuthorizationAccessException("Unable to generate fingerprint", e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (XMLStreamException e) {
+ // nothing to do here
+ }
+ }
+ }
+
+ return out.toString();
+ }
+
+ private UsersAndGroups parseUsersAndGroups(final String fingerprint) {
+ final List<User> users = new ArrayList<>();
+ final List<Group> groups = new ArrayList<>();
+
+ final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
+ try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
+ final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
+ final Document document = docBuilder.parse(in);
+ final Element rootElement = document.getDocumentElement();
+
+ // parse all the users and add them to the current user group provider
+ NodeList userNodes = rootElement.getElementsByTagName(USER_ELEMENT);
+ for (int i=0; i < userNodes.getLength(); i++) {
+ Node userNode = userNodes.item(i);
+ users.add(parseUser((Element) userNode));
+ }
+
+ // parse all the groups and add them to the current user group provider
+ NodeList groupNodes = rootElement.getElementsByTagName(GROUP_ELEMENT);
+ for (int i=0; i < groupNodes.getLength(); i++) {
+ Node groupNode = groupNodes.item(i);
+ groups.add(parseGroup((Element) groupNode));
+ }
+ } catch (SAXException | ParserConfigurationException | IOException e) {
+ throw new AuthorizationAccessException("Unable to parse fingerprint", e);
+ }
+
+ return new UsersAndGroups(users, groups);
+ }
+
+ private User parseUser(final Element element) {
+ final User.Builder builder = new User.Builder()
+ .identifier(element.getAttribute(IDENTIFIER_ATTR))
+ .identity(element.getAttribute(IDENTITY_ATTR));
+
+ return builder.build();
+ }
+
+ private Group parseGroup(final Element element) {
+ final Group.Builder builder = new Group.Builder()
+ .identifier(element.getAttribute(IDENTIFIER_ATTR))
+ .name(element.getAttribute(NAME_ATTR));
+
+ NodeList groupUsers = element.getElementsByTagName(GROUP_USER_ELEMENT);
+ for (int i=0; i < groupUsers.getLength(); i++) {
+ Element groupUserNode = (Element) groupUsers.item(i);
+ builder.addUser(groupUserNode.getAttribute(IDENTIFIER_ATTR));
+ }
+
+ return builder.build();
+ }
+
+ private void writeUser(final XMLStreamWriter writer, final User user) throws XMLStreamException {
+ writer.writeStartElement(USER_ELEMENT);
+ writer.writeAttribute(IDENTIFIER_ATTR, user.getIdentifier());
+ writer.writeAttribute(IDENTITY_ATTR, user.getIdentity());
+ writer.writeEndElement();
+ }
+
+ private void writeGroup(final XMLStreamWriter writer, final Group group) throws XMLStreamException {
+ List<String> users = new ArrayList<>(group.getUsers());
+ Collections.sort(users);
+
+ writer.writeStartElement(GROUP_ELEMENT);
+ writer.writeAttribute(IDENTIFIER_ATTR, group.getIdentifier());
+ writer.writeAttribute(NAME_ATTR, group.getName());
+
+ for (String user : users) {
+ writer.writeStartElement(GROUP_USER_ELEMENT);
+ writer.writeAttribute(IDENTIFIER_ATTR, user);
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+ }
+
+ private org.apache.nifi.registry.security.authorization.file.tenants.generated.User createJAXBUser(User user) {
+ final org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser =
+ new org.apache.nifi.registry.security.authorization.file.tenants.generated.User();
+ jaxbUser.setIdentifier(user.getIdentifier());
+ jaxbUser.setIdentity(user.getIdentity());
+ return jaxbUser;
+ }
+
+ /**
+ * Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up.
+ *
+ * @throws JAXBException Unable to reload the authorized users file
+ * @throws IllegalStateException Unable to sync file with restore
+ * @throws SAXException Unable to unmarshall tenants
+ */
+ private synchronized void load() throws JAXBException, IllegalStateException, SAXException {
+ final Tenants tenants = unmarshallTenants();
+ if (tenants.getUsers() == null) {
+ tenants.setUsers(new Users());
+ }
+ if (tenants.getGroups() == null) {
+ tenants.setGroups(new Groups());
+ }
+
+ final UserGroupHolder userGroupHolder = new UserGroupHolder(tenants);
+ final boolean emptyTenants = userGroupHolder.getAllUsers().isEmpty() && userGroupHolder.getAllGroups().isEmpty();
+
+ if (emptyTenants) {
+
+ populateInitialUsers(tenants);
+
+ // save any changes that were made and repopulate the holder
+ saveAndRefreshHolder(tenants);
+ } else {
+ this.userGroupHolder.set(userGroupHolder);
+ }
+ }
+
+ private void saveTenants(final Tenants tenants) throws JAXBException {
+ final Marshaller marshaller = JAXB_TENANTS_CONTEXT.createMarshaller();
+ marshaller.setSchema(tenantsSchema);
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ marshaller.marshal(tenants, tenantsFile);
+ }
+
+ private Tenants unmarshallTenants() throws JAXBException {
+ final Unmarshaller unmarshaller = JAXB_TENANTS_CONTEXT.createUnmarshaller();
+ unmarshaller.setSchema(tenantsSchema);
+
+ final JAXBElement<Tenants> element = unmarshaller.unmarshal(new StreamSource(tenantsFile), Tenants.class);
+ return element.getValue();
+ }
+
+ private void populateInitialUsers(final Tenants tenants) {
+ for (String initialUserIdentity : initialUserIdentities) {
+ getOrCreateUser(tenants, initialUserIdentity);
+ }
+ }
+
+ /**
+ * Finds the User with the given identity, or creates a new one and adds it to the Tenants.
+ *
+ * @param tenants the Tenants reference
+ * @param userIdentity the user identity to find or create
+ * @return the User from Tenants with the given identity, or a new instance that was added to Tenants
+ */
+ private org.apache.nifi.registry.security.authorization.file.tenants.generated.User getOrCreateUser(final Tenants tenants, final String userIdentity) {
+ if (StringUtils.isBlank(userIdentity)) {
+ return null;
+ }
+
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.User foundUser = null;
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User user : tenants.getUsers().getUser()) {
+ if (user.getIdentity().equals(userIdentity)) {
+ foundUser = user;
+ break;
+ }
+ }
+
+ if (foundUser == null) {
+ final String userIdentifier = IdentifierUtil.getIdentifier(userIdentity);
+ foundUser = new org.apache.nifi.registry.security.authorization.file.tenants.generated.User();
+ foundUser.setIdentifier(userIdentifier);
+ foundUser.setIdentity(userIdentity);
+ tenants.getUsers().getUser().add(foundUser);
+ }
+
+ return foundUser;
+ }
+
+ /**
+ * Finds the Group with the given name, or creates a new one and adds it to Tenants.
+ *
+ * @param tenants the Tenants reference
+ * @param groupName the name of the group to look for
+ * @return the Group from Tenants with the given name, or a new instance that was added to Tenants
+ */
+ private org.apache.nifi.registry.security.authorization.file.tenants.generated.Group getOrCreateGroup(final Tenants tenants, final String groupName) {
+ if (StringUtils.isBlank(groupName)) {
+ return null;
+ }
+
+ org.apache.nifi.registry.security.authorization.file.tenants.generated.Group foundGroup = null;
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) {
+ if (group.getName().equals(groupName)) {
+ foundGroup = group;
+ break;
+ }
+ }
+
+ if (foundGroup == null) {
+ final String newGroupIdentifier = IdentifierUtil.getIdentifier(groupName);
+ foundGroup = new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group();
+ foundGroup.setIdentifier(newGroupIdentifier);
+ foundGroup.setName(groupName);
+ tenants.getGroups().getGroup().add(foundGroup);
+ }
+
+ return foundGroup;
+ }
+
+ /**
+ * Saves the Authorizations instance by marshalling to a file, then re-populates the
+ * in-memory data structures and sets the new holder.
+ *
+ * Synchronized to ensure only one thread writes the file at a time.
+ *
+ * @param tenants the tenants to save and populate from
+ * @throws AuthorizationAccessException if an error occurs saving the authorizations
+ */
+ private synchronized void saveAndRefreshHolder(final Tenants tenants) throws AuthorizationAccessException {
+ try {
+ saveTenants(tenants);
+
+ this.userGroupHolder.set(new UserGroupHolder(tenants));
+ } catch (JAXBException e) {
+ throw new AuthorizationAccessException("Unable to save Authorizations", e);
+ }
+ }
+
+ @Override
+ public void preDestruction() throws SecurityProviderDestructionException {
+ }
+
+ private static class UsersAndGroups {
+ final List<User> users;
+ final List<Group> groups;
+
+ public UsersAndGroups(List<User> users, List<Group> groups) {
+ this.users = users;
+ this.groups = groups;
+ }
+
+ public List<User> getUsers() {
+ return users;
+ }
+
+ public List<Group> getGroups() {
+ return groups;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java
new file mode 100644
index 0000000..91e673f
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java
@@ -0,0 +1,35 @@
+/*
+ * 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.registry.security.authorization.file;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+public final class IdentifierUtil {
+
+ static String getIdentifier(final String seed) {
+ if (StringUtils.isBlank(seed)) {
+ return null;
+ }
+
+ return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+ }
+
+ private IdentifierUtil() {}
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java
new file mode 100644
index 0000000..9828a45
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java
@@ -0,0 +1,241 @@
+/*
+ * 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.registry.security.authorization.file;
+
+
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Groups;
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Tenants;
+import org.apache.nifi.registry.security.authorization.file.tenants.generated.Users;
+import org.apache.nifi.registry.security.authorization.Group;
+import org.apache.nifi.registry.security.authorization.User;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A holder to provide atomic access to user group data structures.
+ */
+public class UserGroupHolder {
+
+ private final Tenants tenants;
+
+ private final Set<User> allUsers;
+ private final Map<String,User> usersById;
+ private final Map<String,User> usersByIdentity;
+
+ private final Set<Group> allGroups;
+ private final Map<String,Group> groupsById;
+ private final Map<String, Set<Group>> groupsByUserIdentity;
+
+ /**
+ * Creates a new holder and populates all convenience data structures.
+ *
+ * @param tenants the current tenants instance
+ */
+ public UserGroupHolder(final Tenants tenants) {
+ this.tenants = tenants;
+
+ // load all users
+ final Users users = tenants.getUsers();
+ final Set<User> allUsers = Collections.unmodifiableSet(createUsers(users));
+
+ // load all groups
+ final Groups groups = tenants.getGroups();
+ final Set<Group> allGroups = Collections.unmodifiableSet(createGroups(groups, users));
+
+ // create a convenience map to retrieve a user by id
+ final Map<String, User> userByIdMap = Collections.unmodifiableMap(createUserByIdMap(allUsers));
+
+ // create a convenience map to retrieve a user by identity
+ final Map<String, User> userByIdentityMap = Collections.unmodifiableMap(createUserByIdentityMap(allUsers));
+
+ // create a convenience map to retrieve a group by id
+ final Map<String, Group> groupByIdMap = Collections.unmodifiableMap(createGroupByIdMap(allGroups));
+
+ // create a convenience map to retrieve the groups for a user identity
+ final Map<String, Set<Group>> groupsByUserIdentityMap = Collections.unmodifiableMap(createGroupsByUserIdentityMap(allGroups, allUsers));
+
+ // set all the holders
+ this.allUsers = allUsers;
+ this.allGroups = allGroups;
+ this.usersById = userByIdMap;
+ this.usersByIdentity = userByIdentityMap;
+ this.groupsById = groupByIdMap;
+ this.groupsByUserIdentity = groupsByUserIdentityMap;
+ }
+
+ /**
+ * Creates a set of Users from the JAXB Users.
+ *
+ * @param users the JAXB Users
+ * @return a set of API Users matching the provided JAXB Users
+ */
+ private Set<User> createUsers(Users users) {
+ Set<User> allUsers = new HashSet<>();
+ if (users == null || users.getUser() == null) {
+ return allUsers;
+ }
+
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User user : users.getUser()) {
+ final User.Builder builder = new User.Builder()
+ .identity(user.getIdentity())
+ .identifier(user.getIdentifier());
+
+ allUsers.add(builder.build());
+ }
+
+ return allUsers;
+ }
+
+ /**
+ * Creates a set of Groups from the JAXB Groups.
+ *
+ * @param groups the JAXB Groups
+ * @return a set of API Groups matching the provided JAXB Groups
+ */
+ private Set<Group> createGroups(Groups groups,
+ Users users) {
+ Set<Group> allGroups = new HashSet<>();
+ if (groups == null || groups.getGroup() == null) {
+ return allGroups;
+ }
+
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group group : groups.getGroup()) {
+ final Group.Builder builder = new Group.Builder()
+ .identifier(group.getIdentifier())
+ .name(group.getName());
+
+ for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User groupUser : group.getUser()) {
+ builder.addUser(groupUser.getIdentifier());
+ }
+
+ allGroups.add(builder.build());
+ }
+
+ return allGroups;
+ }
+
+ /**
+ * Creates a Map from user identifier to User.
+ *
+ * @param users the set of all users
+ * @return the Map from user identifier to User
+ */
+ private Map<String,User> createUserByIdMap(final Set<User> users) {
+ Map<String,User> usersMap = new HashMap<>();
+ for (User user : users) {
+ usersMap.put(user.getIdentifier(), user);
+ }
+ return usersMap;
+ }
+
+ /**
+ * Creates a Map from user identity to User.
+ *
+ * @param users the set of all users
+ * @return the Map from user identity to User
+ */
+ private Map<String,User> createUserByIdentityMap(final Set<User> users) {
+ Map<String,User> usersMap = new HashMap<>();
+ for (User user : users) {
+ usersMap.put(user.getIdentity(), user);
+ }
+ return usersMap;
+ }
+
+ /**
+ * Creates a Map from group identifier to Group.
+ *
+ * @param groups the set of all groups
+ * @return the Map from group identifier to Group
+ */
+ private Map<String,Group> createGroupByIdMap(final Set<Group> groups) {
+ Map<String,Group> groupsMap = new HashMap<>();
+ for (Group group : groups) {
+ groupsMap.put(group.getIdentifier(), group);
+ }
+ return groupsMap;
+ }
+
+ /**
+ * Creates a Map from user identity to the set of Groups for that identity.
+ *
+ * @param groups all groups
+ * @param users all users
+ * @return a Map from User identity to the set of Groups for that identity
+ */
+ private Map<String, Set<Group>> createGroupsByUserIdentityMap(final Set<Group> groups, final Set<User> users) {
+ Map<String, Set<Group>> groupsByUserIdentity = new HashMap<>();
+
+ for (User user : users) {
+ Set<Group> userGroups = new HashSet<>();
+ for (Group group : groups) {
+ for (String groupUser : group.getUsers()) {
+ if (groupUser.equals(user.getIdentifier())) {
+ userGroups.add(group);
+ }
+ }
+ }
+
+ groupsByUserIdentity.put(user.getIdentity(), userGroups);
+ }
+
+ return groupsByUserIdentity;
+ }
+
+ public Tenants getTenants() {
+ return tenants;
+ }
+
+ public Set<User> getAllUsers() {
+ return allUsers;
+ }
+
+ public Map<String, User> getUsersById() {
+ return usersById;
+ }
+
+ public Map<String, User> getUsersByIdentity() {
+ return usersByIdentity;
+ }
+
+ public Set<Group> getAllGroups() {
+ return allGroups;
+ }
+
+ public Map<String, Group> getGroupsById() {
+ return groupsById;
+ }
+
+ public User getUser(String identity) {
+ if (identity == null) {
+ throw new IllegalArgumentException("Identity cannot be null");
+ }
+ return usersByIdentity.get(identity);
+ }
+
+ public Set<Group> getGroups(String userIdentity) {
+ if (userIdentity == null) {
+ throw new IllegalArgumentException("User Identity cannot be null");
+ }
+ return groupsByUserIdentity.get(userIdentity);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
new file mode 100644
index 0000000..d08467e
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
@@ -0,0 +1,300 @@
+/*
+ * 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.registry.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult.Result;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.apache.nifi.registry.security.authorization.AuthorizationAuditor;
+import org.apache.nifi.registry.security.authorization.AuthorizationRequest;
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.Resource;
+import org.apache.nifi.registry.security.authorization.UserContextKeys;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public interface Authorizable {
+
+ /**
+ * The parent for this Authorizable. May be null.
+ *
+ * @return the parent authorizable or null
+ */
+ Authorizable getParentAuthorizable();
+
+ /**
+ * The Resource for this Authorizable.
+ *
+ * @return the resource
+ */
+ Resource getResource();
+
+ /**
+ * The originally requested resource for this Authorizable. Because policies are inherited, if a resource
+ * does not have a policy, this Authorizable may represent a parent resource and this method will return
+ * the originally requested resource.
+ *
+ * @return the originally requested resource
+ */
+ default Resource getRequestedResource() {
+ return getResource();
+ }
+
+ /**
+ * Returns whether the current user is authorized for the specified action on the specified resource. This
+ * method does not imply the user is directly attempting to access the specified resource. If the user is
+ * attempting a direct access use Authorizable.authorize().
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @return is authorized
+ */
+ default boolean isAuthorized(Authorizer authorizer, RequestAction action, NiFiUser user) {
+ return Result.Approved.equals(checkAuthorization(authorizer, action, user).getResult());
+ }
+
+ /**
+ * Returns the result of an authorization request for the specified user for the specified action on the specified
+ * resource. This method does not imply the user is directly attempting to access the specified resource. If the user is
+ * attempting a direct access use Authorizable.authorize().
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ * @return is authorized
+ */
+ default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
+ if (user == null) {
+ return AuthorizationResult.denied("Unknown user.");
+ }
+
+ final Map<String,String> userContext;
+ if (user.getClientAddress() != null && !user.getClientAddress().trim().isEmpty()) {
+ userContext = new HashMap<>();
+ userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
+ } else {
+ userContext = null;
+ }
+
+ final Resource resource = getResource();
+ final Resource requestedResource = getRequestedResource();
+ final AuthorizationRequest request = new AuthorizationRequest.Builder()
+ .identity(user.getIdentity())
+ .groups(user.getGroups())
+ .anonymous(user.isAnonymous())
+ .accessAttempt(false)
+ .action(action)
+ .resource(resource)
+ .requestedResource(requestedResource)
+ .resourceContext(resourceContext)
+ .userContext(userContext)
+ .explanationSupplier(() -> {
+ // build the safe explanation
+ final StringBuilder safeDescription = new StringBuilder("Unable to ");
+
+ if (RequestAction.READ.equals(action)) {
+ safeDescription.append("view ");
+ } else {
+ safeDescription.append("modify "); // covers write or delete
+ }
+ safeDescription.append(resource.getSafeDescription()).append(".");
+
+ return safeDescription.toString();
+ })
+ .build();
+
+ // perform the authorization
+ final AuthorizationResult result = authorizer.authorize(request);
+
+ // verify the results
+ if (Result.ResourceNotFound.equals(result.getResult())) {
+ final Authorizable parent = getParentAuthorizable();
+ if (parent == null) {
+ return AuthorizationResult.denied("No applicable policies could be found.");
+ } else {
+ // create a custom authorizable to override the safe description but still defer to the parent authorizable
+ final Authorizable parentProxy = new Authorizable() {
+ @Override
+ public Authorizable getParentAuthorizable() {
+ return parent.getParentAuthorizable();
+ }
+
+ @Override
+ public Resource getRequestedResource() {
+ return requestedResource;
+ }
+
+ @Override
+ public Resource getResource() {
+ final Resource parentResource = parent.getResource();
+ return new Resource() {
+ @Override
+ public String getIdentifier() {
+ return parentResource.getIdentifier();
+ }
+
+ @Override
+ public String getName() {
+ return parentResource.getName();
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return resource.getSafeDescription();
+ }
+ };
+ }
+ };
+ return parentProxy.checkAuthorization(authorizer, action, user, resourceContext);
+ }
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Returns the result of an authorization request for the specified user for the specified action on the specified
+ * resource. This method does not imply the user is directly attempting to access the specified resource. If the user is
+ * attempting a direct access use Authorizable.authorize().
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ * @return is authorized
+ */
+ default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user) {
+ return checkAuthorization(authorizer, action, user, null);
+ }
+
+ /**
+ * Authorizes the current user for the specified action on the specified resource. This method does imply the user is
+ * directly accessing the specified resource.
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ * @param resourceContext resource context
+ */
+ default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
+ if (user == null) {
+ throw new AccessDeniedException("Unknown user.");
+ }
+
+ final Map<String,String> userContext;
+ if (user.getClientAddress() != null && !user.getClientAddress().trim().isEmpty()) {
+ userContext = new HashMap<>();
+ userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
+ } else {
+ userContext = null;
+ }
+
+ final Resource resource = getResource();
+ final Resource requestedResource = getRequestedResource();
+ final AuthorizationRequest request = new AuthorizationRequest.Builder()
+ .identity(user.getIdentity())
+ .groups(user.getGroups())
+ .anonymous(user.isAnonymous())
+ .accessAttempt(true)
+ .action(action)
+ .resource(resource)
+ .requestedResource(requestedResource)
+ .resourceContext(resourceContext)
+ .userContext(userContext)
+ .explanationSupplier(() -> {
+ // build the safe explanation
+ final StringBuilder safeDescription = new StringBuilder("Unable to ");
+
+ if (RequestAction.READ.equals(action)) {
+ safeDescription.append("view ");
+ } else {
+ safeDescription.append("modify ");
+ }
+ safeDescription.append(resource.getSafeDescription()).append(".");
+
+ return safeDescription.toString();
+ })
+ .build();
+
+ final AuthorizationResult result = authorizer.authorize(request);
+ if (Result.ResourceNotFound.equals(result.getResult())) {
+ final Authorizable parent = getParentAuthorizable();
+ if (parent == null) {
+ final AuthorizationResult failure = AuthorizationResult.denied("No applicable policies could be found.");
+
+ // audit authorization request
+ if (authorizer instanceof AuthorizationAuditor) {
+ ((AuthorizationAuditor) authorizer).auditAccessAttempt(request, failure);
+ }
+
+ // denied
+ throw new AccessDeniedException(failure.getExplanation());
+ } else {
+ // create a custom authorizable to override the safe description but still defer to the parent authorizable
+ final Authorizable parentProxy = new Authorizable() {
+ @Override
+ public Authorizable getParentAuthorizable() {
+ return parent.getParentAuthorizable();
+ }
+
+ @Override
+ public Resource getRequestedResource() {
+ return requestedResource;
+ }
+
+ @Override
+ public Resource getResource() {
+ final Resource parentResource = parent.getResource();
+ return new Resource() {
+ @Override
+ public String getIdentifier() {
+ return parentResource.getIdentifier();
+ }
+
+ @Override
+ public String getName() {
+ return parentResource.getName();
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return resource.getSafeDescription();
+ }
+ };
+ }
+ };
+ parentProxy.authorize(authorizer, action, user, resourceContext);
+ }
+ } else if (Result.Denied.equals(result.getResult())) {
+ throw new AccessDeniedException(result.getExplanation());
+ }
+ }
+
+ /**
+ * Authorizes the current user for the specified action on the specified resource. This method does imply the user is
+ * directly accessing the specified resource.
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ */
+ default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user) throws AccessDeniedException {
+ authorize(authorizer, action, user, null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/InheritingAuthorizable.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/InheritingAuthorizable.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/InheritingAuthorizable.java
new file mode 100644
index 0000000..b029229
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/InheritingAuthorizable.java
@@ -0,0 +1,85 @@
+/*
+ * 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.registry.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.AuthorizationResult.Result;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+
+import java.util.Map;
+
+public interface InheritingAuthorizable extends Authorizable {
+
+ /**
+ * Returns the result of an authorization request for the specified user for the specified action on the specified
+ * resource. This method does not imply the user is directly attempting to access the specified resource. If the user is
+ * attempting a direct access use Authorizable.authorize().
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ * @return is authorized
+ */
+ default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
+ if (user == null) {
+ throw new AccessDeniedException("Unknown user.");
+ }
+
+ final AuthorizationResult resourceResult = Authorizable.super.checkAuthorization(authorizer, action, user, resourceContext);
+
+ // if we're denied from the resource try inheriting
+ if (Result.Denied.equals(resourceResult.getResult()) && getParentAuthorizable() != null) {
+ return getParentAuthorizable().checkAuthorization(authorizer, action, user, resourceContext);
+ } else {
+ return resourceResult;
+ }
+ }
+
+ /**
+ * Authorizes the current user for the specified action on the specified resource. If the current user is
+ * not in the access policy for the specified resource, the parent authorizable resource will be checked, recursively
+ *
+ * @param authorizer authorizer
+ * @param action action
+ * @param user user
+ * @param resourceContext resource context
+ */
+ default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
+ if (user == null) {
+ throw new AccessDeniedException("Unknown user.");
+ }
+
+ try {
+ Authorizable.super.authorize(authorizer, action, user, resourceContext);
+ } catch (final AccessDeniedException resourceDenied) {
+ // if we're denied from the resource try inheriting
+ try {
+ if (getParentAuthorizable() != null) {
+ getParentAuthorizable().authorize(authorizer, action, user, resourceContext);
+ } else {
+ throw resourceDenied;
+ }
+ } catch (final AccessDeniedException policiesDenied) {
+ throw resourceDenied;
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
new file mode 100644
index 0000000..c605d4a
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
@@ -0,0 +1,235 @@
+/*
+ * 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.registry.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.Resource;
+
+import java.util.Objects;
+
+public final class ResourceFactory {
+
+ private final static Resource BUCKETS_RESOURCE = new Resource() {
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Bucket.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Buckets";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "buckets";
+ }
+ };
+
+ private final static Resource PROXY_RESOURCE = new Resource() {
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Proxy.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Proxy User Requests";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "proxy requests on behalf of users";
+ }
+ };
+
+ private final static Resource TENANTS_RESOURCE = new Resource() {
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Tenant.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Tenants";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "users/user groups";
+ }
+ };
+
+ private final static Resource POLICIES_RESOURCE = new Resource() {
+
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Policy.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Access Policies";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "policies";
+ }
+ };
+
+ private final static Resource ACTUATOR_RESOURCE = new Resource() {
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Actuator.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Actuator";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "actuator";
+ }
+ };
+
+ private final static Resource SWAGGER_RESOURCE = new Resource() {
+ @Override
+ public String getIdentifier() {
+ return ResourceType.Swagger.getValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Swagger";
+ }
+
+ @Override
+ public String getSafeDescription() {
+ return "swagger";
+ }
+ };
+
+ /**
+ * Gets the Resource for actuator system management endpoints.
+ *
+ * @return The resource for actuator system management endpoints.
+ */
+ public static Resource getActuatorResource() {
+ return ACTUATOR_RESOURCE;
+ }
+
+ /**
+ * Gets the Resource for swagger UI static resources.
+ *
+ * @return The resource for swagger UI static resources.
+ */
+ public static Resource getSwaggerResource() {
+ return SWAGGER_RESOURCE;
+ }
+
+ /**
+ * Gets the Resource for proxying a user request.
+ *
+ * @return The resource for proxying a user request
+ */
+ public static Resource getProxyResource() {
+ return PROXY_RESOURCE;
+ }
+
+ /**
+ * Gets the Resource for accessing Tenants which includes creating, modifying, and deleting Users and UserGroups.
+ *
+ * @return The Resource for accessing Tenants
+ */
+ public static Resource getTenantsResource() {
+ return TENANTS_RESOURCE;
+ }
+
+ /**
+ * Gets the {@link Resource} for accessing access policies.
+ * @return The policies resource
+ */
+ public static Resource getPoliciesResource() {
+ return POLICIES_RESOURCE;
+ }
+
+ /**
+ * Gets the {@link Resource} for accessing buckets.
+ * @return The buckets resource
+ */
+ public static Resource getBucketsResource() {
+ return BUCKETS_RESOURCE;
+ }
+
+ /**
+ * Gets the {@link Resource} for accessing buckets.
+ * @return The buckets resource
+ */
+ public static Resource getBucketResource(String bucketIdentifier, String bucketName) {
+ return getChildResource(ResourceType.Bucket, bucketIdentifier, bucketName);
+ }
+
+ /**
+ * Get a Resource object for any object that has a base type and an identifier, ie:
+ * /buckets/{uuid}
+ *
+ * @param parentResourceType - Required, the base resource type
+ * @param childIdentifier - Required, the identity of this sub resource
+ * @param name - Optional, the name of the subresource
+ * @return A resource for this object
+ */
+ private static Resource getChildResource(final ResourceType parentResourceType, final String childIdentifier, final String name) {
+ Objects.requireNonNull(parentResourceType, "The base resource type must be specified.");
+ Objects.requireNonNull(childIdentifier, "The child identifier identifier must be specified.");
+
+ return new Resource() {
+ @Override
+ public String getIdentifier() {
+ return String.format("%s/%s", parentResourceType.getValue(), childIdentifier);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getSafeDescription() {
+ final StringBuilder safeDescription = new StringBuilder();
+ switch (parentResourceType) {
+ case Bucket:
+ safeDescription.append("Bucket");
+ break;
+ default:
+ safeDescription.append("Unknown resource type");
+ break;
+ }
+ safeDescription.append(" with ID ");
+ safeDescription.append(childIdentifier);
+ return safeDescription.toString();
+ }
+ };
+
+ }
+
+ /**
+ * Prevent outside instantiation.
+ */
+ private ResourceFactory() {}
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
new file mode 100644
index 0000000..0b77cd2
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
@@ -0,0 +1,87 @@
+/*
+ * 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.registry.security.authorization.resource;
+
+public enum ResourceType {
+ Bucket("/buckets"),
+ Policy("/policies"),
+ Proxy("/proxy"),
+ Tenant("/tenants"),
+ Actuator("/actuator"),
+ Swagger("/swagger");
+
+ final String value;
+
+ private ResourceType(final String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static ResourceType valueOfValue(final String rawValue) {
+ ResourceType type = null;
+
+ for (final ResourceType rt : values()) {
+ if (rt.getValue().equals(rawValue)) {
+ type = rt;
+ break;
+ }
+ }
+
+ if (type == null) {
+ throw new IllegalArgumentException("Unknown resource type value " + rawValue);
+ }
+
+ return type;
+ }
+
+ /**
+ * Map an arbitrary resource path to its base resource type. The base resource type is
+ * what the resource path starts with.
+ *
+ * The resourcePath arg is expected to be a string of the format:
+ *
+ * {ResourceTypeValue}/arbitrary/sub-resource/path
+ *
+ * For example:
+ * /buckets -> ResourceType.Bucket
+ * /buckets/bucketId -> ResourceType.Bucket
+ * /policies/read/buckets -> ResourceType.Policy
+ *
+ * @param resourcePath the path component of a URI (not including the context path)
+ * @return the base resource type
+ */
+ public static ResourceType mapFullResourcePathToResourceType(final String resourcePath) {
+ if (resourcePath == null) {
+ throw new IllegalArgumentException("Resource path must not be null");
+ }
+
+ ResourceType type = null;
+
+ for (final ResourceType rt : values()) {
+ final String rtValue = rt.getValue();
+ if(resourcePath.equals(rtValue) || resourcePath.startsWith(rtValue + "/")) {
+ type = rt;
+ break;
+ }
+ }
+
+ return type;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java
new file mode 100644
index 0000000..47127b6
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.registry.security.authorization.user;
+
+import java.util.Set;
+
+/**
+ * A representation of a NiFi user that has logged into the application
+ */
+public interface NiFiUser {
+
+ /**
+ * @return the unique identity of this user
+ */
+ String getIdentity();
+
+ /**
+ * @return the groups that this user belongs to if this nifi is configured to load user groups, null otherwise.
+ */
+ Set<String> getGroups();
+
+ /**
+ * @return the next user in the proxied entities chain, or <code>null</code> if no more users exist in the chain.
+ */
+ NiFiUser getChain();
+
+ /**
+ * @return <code>true</code> if the user is the unauthenticated Anonymous user
+ */
+ boolean isAnonymous();
+
+ /**
+ * @return the address of the client that made the request which created this user
+ */
+ String getClientAddress();
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java
new file mode 100644
index 0000000..ca6ea2e
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java
@@ -0,0 +1,91 @@
+/*
+ * 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.registry.security.authorization.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * User details for a NiFi user.
+ */
+public class NiFiUserDetails implements UserDetails {
+
+ private final NiFiUser user;
+
+ /**
+ * Creates a new NiFiUserDetails.
+ *
+ * @param user user
+ */
+ public NiFiUserDetails(NiFiUser user) {
+ this.user = user;
+ }
+
+ /**
+ * Get the user for this UserDetails.
+ *
+ * @return user
+ */
+ public NiFiUser getNiFiUser() {
+ return user;
+ }
+
+ /**
+ * Returns the authorities that this NiFi user has.
+ *
+ * @return authorities
+ */
+ @Override
+ public Collection<? extends GrantedAuthority> getAuthorities() {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
+ public String getPassword() {
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getIdentity();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java
new file mode 100644
index 0000000..b5147ea
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java
@@ -0,0 +1,91 @@
+/*
+ * 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.registry.security.authorization.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods for retrieving information about the current application user.
+ *
+ */
+public final class NiFiUserUtils {
+
+ /**
+ * Returns the current NiFiUser or null if the current user is not a NiFiUser.
+ *
+ * @return user
+ */
+ public static NiFiUser getNiFiUser() {
+ NiFiUser user = null;
+
+ // obtain the principal in the current authentication
+ final SecurityContext context = SecurityContextHolder.getContext();
+ final Authentication authentication = context.getAuthentication();
+ if (authentication != null) {
+ Object principal = authentication.getPrincipal();
+ if (principal instanceof NiFiUserDetails) {
+ user = ((NiFiUserDetails) principal).getNiFiUser();
+ }
+ }
+
+ return user;
+ }
+
+ public static String getNiFiUserIdentity() {
+ // get the nifi user to extract the username
+ NiFiUser user = NiFiUserUtils.getNiFiUser();
+ if (user == null) {
+ return "unknown";
+ } else {
+ return user.getIdentity();
+ }
+ }
+
+ /**
+ * Builds the proxy chain for the specified user.
+ *
+ * @param user The current user
+ * @return The proxy chain for that user in List form
+ */
+ public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
+ // calculate the dn chain
+ final List<String> proxyChain = new ArrayList<>();
+
+ // build the dn chain
+ NiFiUser chainedUser = user;
+ while (chainedUser != null) {
+ // add the entry for this user
+ if (chainedUser.isAnonymous()) {
+ // use an empty string to represent an anonymous user in the proxy entities chain
+ proxyChain.add(StringUtils.EMPTY);
+ } else {
+ proxyChain.add(chainedUser.getIdentity());
+ }
+
+ // go to the next user in the chain
+ chainedUser = chainedUser.getChain();
+ }
+
+ return proxyChain;
+ }
+}