Java tutorial
/* * 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.authorization; import java.io.File; import java.io.IOException; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; /** * Provides identity checks and grants authorities. */ 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<>(); @Override public void initialize(final AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException { } @Override public void onConfigured(final AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException { try { 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"); if (StringUtils.isNotBlank(rawDefaultAuthorities)) { final Set<String> invalidDefaultAuthorities = new HashSet<>(); // validate the specified authorities final String[] rawDefaultAuthorityList = rawDefaultAuthorities.split(","); for (String rawAuthority : rawDefaultAuthorityList) { rawAuthority = rawAuthority.trim(); final Authority authority = Authority.valueOfAuthority(rawAuthority); if (authority == null) { invalidDefaultAuthorities.add(rawAuthority); } else { defaultAuthorities.add(rawAuthority); } } // report any unrecognized authorities if (!invalidDefaultAuthorities.isEmpty()) { logger.warn(String.format( "The following default role(s) '%s' were not recognized. Possible values: %s.", StringUtils.join(invalidDefaultAuthorities, ", "), StringUtils.join(Authority.getRawAuthorities(), ", "))); } } } catch (IOException | ProviderCreationException | SAXException | JAXBException e) { throw new ProviderCreationException(e); } } @Override public void preDestruction() { } private boolean hasDefaultRoles() { return !defaultAuthorities.isEmpty(); } @Override public boolean doesDnExist(String dn) throws AuthorityAccessException { if (hasDefaultRoles()) { return true; } final User user = getUser(dn); return user != null; } @Override public synchronized Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException { final Set<Authority> authorities = EnumSet.noneOf(Authority.class); // get the user final User user = getUser(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); // 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())); } } return authorities; } @Override 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 User user, final Set<Authority> authorities) { // clear the existing rules user.getRole().clear(); // set the new roles final ObjectFactory objFactory = new ObjectFactory(); for (final Authority authority : authorities) { final Role role = objFactory.createRole(); role.setName(authority.toString()); // add the new role user.getRole().add(role); } } @Override public synchronized void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException { final User user = getUser(dn); // ensure the user doesn't already exist if (user != null) { throw new IdentityAlreadyExistsException(String.format("User DN already exists: %s", dn)); } // create the new user final ObjectFactory objFactory = new ObjectFactory(); final User newUser = objFactory.createUser(); // 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); } } // add the user users.getUser().add(newUser); try { // save the file save(); } catch (Exception e) { throw new AuthorityAccessException(e.getMessage(), e); } } @Override public synchronized Set<String> getUsers(Authority authority) throws AuthorityAccessException { final Set<String> userSet = new HashSet<>(); 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 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(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 { // 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(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(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 { // 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); } } /** * Grants access to download content regardless of FlowFile attributes. */ @Override public DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException { 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; } }