http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java index a9236ff..ca99e09 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java @@ -20,51 +20,49 @@ import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.annotation.AuthorizerContext; import org.apache.nifi.authorization.exception.AuthorizationAccessException; import org.apache.nifi.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.authorization.file.generated.Authorization; -import org.apache.nifi.authorization.file.generated.Resources; -import org.apache.nifi.authorization.file.generated.Resource; +import org.apache.nifi.authorization.file.generated.Authorizations; +import org.apache.nifi.authorization.file.generated.Groups; +import org.apache.nifi.authorization.file.generated.Policies; +import org.apache.nifi.authorization.file.generated.Policy; +import org.apache.nifi.authorization.file.generated.Users; import org.apache.nifi.components.PropertyValue; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.util.file.FileUtils; -import org.apache.nifi.util.file.monitor.MD5SumMonitor; -import org.apache.nifi.util.file.monitor.SynchronousFileWatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -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.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Date; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; +import java.util.Iterator; +import java.util.List; import java.util.Set; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; +import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; /** * Provides identity checks and grants authorities. */ -public class FileAuthorizer implements Authorizer { +public class FileAuthorizer extends AbstractPolicyBasedAuthorizer { private static final Logger logger = LoggerFactory.getLogger(FileAuthorizer.class); - private static final String READ_CODE = "R"; - private static final String WRITE_CODE = "W"; private static final String USERS_XSD = "/authorizations.xsd"; private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authorization.file.generated"; private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); + static final String READ_CODE = "R"; + static final String WRITE_CODE = "W"; + /** * Load the JAXBContext. */ @@ -76,16 +74,23 @@ public class FileAuthorizer implements Authorizer { } } + private Schema schema; + private SchemaFactory schemaFactory; private NiFiProperties properties; private File authorizationsFile; private File restoreAuthorizationsFile; - private SynchronousFileWatcher fileWatcher; - private ScheduledExecutorService fileWatcherExecutorService; + private String rootGroupId; - private final AtomicReference<Map<String, Map<String, Set<RequestAction>>>> authorizations = new AtomicReference<>(); + private final AtomicReference<AuthorizationsHolder> authorizationsHolder = new AtomicReference<>(); @Override public void initialize(final AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + try { + schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD)); + } catch (Exception e) { + throw new AuthorizerCreationException(e); + } } @Override @@ -127,153 +132,549 @@ public class FileAuthorizer implements Authorizer { } } - final PropertyValue rawReloadInterval = configurationContext.getProperty("Reload Interval"); + final PropertyValue initialAdminIdentityProp = configurationContext.getProperty("Initial Admin Identity"); + final String initialAdminIdentity = initialAdminIdentityProp == null ? null : initialAdminIdentityProp.getValue(); - long reloadInterval; - try { - reloadInterval = rawReloadInterval.asTimePeriod(TimeUnit.MILLISECONDS); - } catch (final Exception iae) { - logger.info(String.format("Unable to interpret reload interval '%s'. Using default of 30 seconds.", rawReloadInterval)); - reloadInterval = 30000L; + // load the authorizations + load(initialAdminIdentity); + + // if we've copied the authorizations file to a restore directory synchronize it + if (restoreAuthorizationsFile != null) { + FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger); } - // reload the authorizations - reload(); + logger.info(String.format("Authorizations file loaded at %s", new Date().toString())); - // watch the file for modifications - fileWatcher = new SynchronousFileWatcher(authorizationsFile.toPath(), new MD5SumMonitor()); - fileWatcherExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(final Runnable r) { - return new Thread(r, "Authorization File Reload Thread"); - } - }); - fileWatcherExecutorService.scheduleWithFixedDelay(new Runnable() { - @Override - public void run() { - try { - if (fileWatcher.checkAndReset()) { - reload(); - } - } catch (final Exception e) { - logger.warn("Unable to reload Authorizations file do to: " + e, e); - } - } - }, reloadInterval, reloadInterval, TimeUnit.MILLISECONDS); - } catch (IOException | AuthorizerCreationException | SAXException | JAXBException | IllegalStateException e) { + this.rootGroupId = configurationContext.getRootGroupId(); + + } catch (IOException | AuthorizerCreationException | JAXBException | IllegalStateException e) { throw new AuthorizerCreationException(e); } - } - @Override - public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { - // get the current authorizations - final Map<String, Map<String, Set<RequestAction>>> currentAuthorizations = authorizations.get(); - - // get the requested resource - final org.apache.nifi.authorization.Resource requestedResource = request.getResource(); + /** + * Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up. + * + * @throws JAXBException Unable to reload the authorized users file + * @throws IOException Unable to sync file with restore + * @throws IllegalStateException Unable to sync file with restore + */ + private synchronized void load(final String initialAdminIdentity) throws JAXBException, IOException, IllegalStateException { + // attempt to unmarshal + final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); + unmarshaller.setSchema(schema); + final JAXBElement<Authorizations> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Authorizations.class); - // get the authorizations for the requested resources - final Map<String, Set<RequestAction>> resourceAuthorizations = currentAuthorizations.get(requestedResource.getIdentifier()); + final Authorizations authorizations = element.getValue(); - // ensure the resource has authorizations - if (resourceAuthorizations == null) { - return AuthorizationResult.resourceNotFound(); + if (authorizations.getUsers() == null) { + authorizations.setUsers(new Users()); + } + if (authorizations.getGroups() == null) { + authorizations.setGroups(new Groups()); + } + if (authorizations.getPolicies() == null) { + authorizations.setPolicies(new Policies()); } - // get the user authorizations - final Set<RequestAction> userAuthorizations = resourceAuthorizations.get(request.getIdentity()); + final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations); + final boolean hasInitialAdminIdentity = (initialAdminIdentity != null && !StringUtils.isBlank(initialAdminIdentity)); - // ensure the user has authorizations - if (userAuthorizations == null) { - return AuthorizationResult.denied(); + // if an initial admin was provided and there are no users or policies then automatically create the admin user & policies + if (hasInitialAdminIdentity && authorizationsHolder.getAllUsers().isEmpty() && authorizationsHolder.getAllPolicies().isEmpty()) { + populateInitialAdmin(authorizations, initialAdminIdentity); + saveAndRefreshHolder(authorizations); + } else { + this.authorizationsHolder.set(authorizationsHolder); } + } - // ensure the appropriate response - if (userAuthorizations.contains(request.getAction())) { - return AuthorizationResult.approved(); - } else { - return AuthorizationResult.denied(); + /** + * Creates the initial admin user and policies for access the flow and managing users and policies. + * + * @param adminIdentity the identity of the admin user + */ + private void populateInitialAdmin(final Authorizations authorizations, final String adminIdentity) { + // generate an identifier and add a User with the given identifier and identity + final UUID adminIdentifier = UUID.nameUUIDFromBytes(adminIdentity.getBytes(StandardCharsets.UTF_8)); + final User adminUser = new User.Builder().identifier(adminIdentifier.toString()).identity(adminIdentity).build(); + + final org.apache.nifi.authorization.file.generated.User jaxbAdminUser = createJAXBUser(adminUser); + authorizations.getUsers().getUser().add(jaxbAdminUser); + + // grant the user read access to the /flow resource + final AccessPolicy flowPolicy = createInitialAdminPolicy("/flow", adminUser.getIdentifier(), RequestAction.READ); + final Policy jaxbFlowPolicy = createJAXBPolicy(flowPolicy); + authorizations.getPolicies().getPolicy().add(jaxbFlowPolicy); + + // grant the user read/write access to the /users resource + final AccessPolicy usersPolicy = createInitialAdminPolicy("/users", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE); + final Policy jaxbUsersPolicy = createJAXBPolicy(usersPolicy); + authorizations.getPolicies().getPolicy().add(jaxbUsersPolicy); + + // grant the user read/write access to the /groups resource + final AccessPolicy groupsPolicy = createInitialAdminPolicy("/groups", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE); + final Policy jaxbGroupsPolicy = createJAXBPolicy(groupsPolicy); + authorizations.getPolicies().getPolicy().add(jaxbGroupsPolicy); + + // grant the user read/write access to the /policies resource + final AccessPolicy policiesPolicy = createInitialAdminPolicy("/policies", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE); + final Policy jaxbPoliciesPolicy = createJAXBPolicy(policiesPolicy); + authorizations.getPolicies().getPolicy().add(jaxbPoliciesPolicy); + } + + /** + * Creates an AccessPolicy based on the given parameters, generating an identifier from the resource and admin identity. + * + * @param resource the resource for the policy + * @param adminIdentity the identity of the admin user to add to the policy + * @param actions the actions for the policy + * @return the AccessPolicy based on the given parameters + */ + private AccessPolicy createInitialAdminPolicy(final String resource, final String adminIdentity, final RequestAction ... actions) { + final String uuidSeed = resource + adminIdentity; + final UUID flowPolicyIdentifier = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8)); + + final AccessPolicy.Builder builder = new AccessPolicy.Builder() + .identifier(flowPolicyIdentifier.toString()) + .resource(resource) + .addUser(adminIdentity); + + for (RequestAction action : actions) { + builder.addAction(action); } + + return builder.build(); } /** - * Reloads the authorized users file. + * Saves the Authorizations instance by marshalling to a file, then re-populates the + * in-memory data structures and sets the new holder. * - * @throws SAXException Unable to reload the authorized users file - * @throws JAXBException Unable to reload the authorized users file - * @throws IOException Unable to sync file with restore - * @throws IllegalStateException Unable to sync file with restore + * Synchronized to ensure only one thread writes the file at a time. + * + * @param authorizations the authorizations to save and populate from + * @throws AuthorizationAccessException if an error occurs saving the authorizations */ - private void reload() throws SAXException, JAXBException, IOException, IllegalStateException { - // find the schema - final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - final Schema schema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD)); + private synchronized void saveAndRefreshHolder(final Authorizations authorizations) throws AuthorizationAccessException { + try { + final Marshaller marshaller = JAXB_CONTEXT.createMarshaller(); + marshaller.setSchema(schema); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(authorizations, authorizationsFile); - // attempt to unmarshal - final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); - unmarshaller.setSchema(schema); - final JAXBElement<Resources> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Resources.class); - final Resources resources = element.getValue(); + final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations); + this.authorizationsHolder.set(authorizationsHolder); + } catch (JAXBException e) { + throw new AuthorizationAccessException("Unable to save Authorizations", e); + } + } - // new authorizations - final Map<String, Map<String, Set<RequestAction>>> newAuthorizations = new HashMap<>(); + @AuthorizerContext + public void setNiFiProperties(NiFiProperties properties) { + this.properties = properties; + } + + @Override + public void preDestruction() { + + } + + // ------------------ Groups ------------------ + + @Override + public synchronized Group addGroup(Group group) throws AuthorizationAccessException { + if (group == null) { + throw new IllegalArgumentException("Group cannot be null"); + } + + // create a new JAXB Group based on the incoming Group + final org.apache.nifi.authorization.file.generated.Group jaxbGroup = new org.apache.nifi.authorization.file.generated.Group(); + jaxbGroup.setIdentifier(group.getIdentifier()); + jaxbGroup.setName(group.getName()); - // load the new authorizations - for (final Resource authorizedResource : resources.getResource()) { - final String identifier = authorizedResource.getIdentifier(); + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + authorizations.getGroups().getGroup().add(jaxbGroup); + saveAndRefreshHolder(authorizations); - // ensure the entry exists - if (!newAuthorizations.containsKey(identifier)) { - newAuthorizations.put(identifier, new HashMap<String, Set<RequestAction>>()); + final AuthorizationsHolder holder = this.authorizationsHolder.get(); + return holder.getGroupsById().get(group.getIdentifier()); + } + + @Override + public Group getGroup(String identifier) throws AuthorizationAccessException { + if (identifier == null) { + return null; + } + return authorizationsHolder.get().getGroupsById().get(identifier); + } + + @Override + public synchronized Group updateGroup(Group group) throws AuthorizationAccessException { + if (group == null) { + throw new IllegalArgumentException("Group cannot be null"); + } + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + final List<org.apache.nifi.authorization.file.generated.Group> groups = authorizations.getGroups().getGroup(); + + // find the group that needs to be update + org.apache.nifi.authorization.file.generated.Group updateGroup = null; + for (org.apache.nifi.authorization.file.generated.Group jaxbGroup : groups) { + 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; + } - // go through each authorization - for (final Authorization authorization : authorizedResource.getAuthorization()) { - final String identity = authorization.getIdentity(); + updateGroup.setName(group.getName()); + saveAndRefreshHolder(authorizations); - // get the authorizations for this resource - final Map<String, Set<RequestAction>> resourceAuthorizations = newAuthorizations.get(identifier); + final AuthorizationsHolder holder = this.authorizationsHolder.get(); + return holder.getGroupsById().get(group.getIdentifier()); + } - // ensure the entry exists - if (!resourceAuthorizations.containsKey(identity)) { - resourceAuthorizations.put(identity, EnumSet.noneOf(RequestAction.class)); + @Override + public synchronized Group deleteGroup(Group group) throws AuthorizationAccessException { + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + final List<org.apache.nifi.authorization.file.generated.Group> groups = authorizations.getGroups().getGroup(); + + // for each user iterate over the group references and remove the group reference if it matches the group being deleted + for (org.apache.nifi.authorization.file.generated.User user : authorizations.getUsers().getUser()) { + Iterator<org.apache.nifi.authorization.file.generated.User.Group> userGroupIter = user.getGroup().iterator(); + while (userGroupIter.hasNext()) { + org.apache.nifi.authorization.file.generated.User.Group userGroup = userGroupIter.next(); + if (userGroup.getIdentifier().equals(group.getIdentifier())) { + userGroupIter.remove(); + break; } + } + } - final Set<RequestAction> authorizedActions = resourceAuthorizations.get(identity); - final String authorizationCode = authorization.getAction(); - - // updated the actions for this identity - if (authorizationCode.contains(READ_CODE)) { - authorizedActions.add(RequestAction.READ); + // for each policy iterate over the group reference and remove the group reference if it matches the group being deleted + for (Policy policy : authorizations.getPolicies().getPolicy()) { + Iterator<Policy.Group> policyGroupIter = policy.getGroup().iterator(); + while (policyGroupIter.hasNext()) { + Policy.Group policyGroup = policyGroupIter.next(); + if (policyGroup.getIdentifier().equals(group.getIdentifier())) { + policyGroupIter.remove(); + break; } - if (authorizationCode.contains(WRITE_CODE)) { - authorizedActions.add(RequestAction.WRITE); + } + } + + // now remove the actual group from the top-level list of groups + boolean removedGroup = false; + Iterator<org.apache.nifi.authorization.file.generated.Group> iter = groups.iterator(); + while (iter.hasNext()) { + org.apache.nifi.authorization.file.generated.Group jaxbGroup = iter.next(); + if (group.getIdentifier().equals(jaxbGroup.getIdentifier())) { + iter.remove(); + removedGroup = true; + break; + } + } + + if (removedGroup) { + saveAndRefreshHolder(authorizations); + return group; + } else { + return null; + } + } + + @Override + public Set<Group> getGroups() throws AuthorizationAccessException { + return authorizationsHolder.get().getAllGroups(); + } + + // ------------------ Users ------------------ + + @Override + public synchronized User addUser(final User user) throws AuthorizationAccessException { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + + final org.apache.nifi.authorization.file.generated.User jaxbUser = createJAXBUser(user); + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + authorizations.getUsers().getUser().add(jaxbUser); + + saveAndRefreshHolder(authorizations); + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getUsersById().get(user.getIdentifier()); + } + + private org.apache.nifi.authorization.file.generated.User createJAXBUser(User user) { + final org.apache.nifi.authorization.file.generated.User jaxbUser = new org.apache.nifi.authorization.file.generated.User(); + jaxbUser.setIdentifier(user.getIdentifier()); + jaxbUser.setIdentity(user.getIdentity()); + + for (String groupIdentifier : user.getGroups()) { + org.apache.nifi.authorization.file.generated.User.Group group = new org.apache.nifi.authorization.file.generated.User.Group(); + group.setIdentifier(groupIdentifier); + jaxbUser.getGroup().add(group); + } + return jaxbUser; + } + + @Override + public User getUser(final String identifier) throws AuthorizationAccessException { + if (identifier == null) { + return null; + } + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getUsersById().get(identifier); + } + + @Override + public User getUserByIdentity(final String identity) throws AuthorizationAccessException { + if (identity == null) { + return null; + } + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getUsersByIdentity().get(identity); + } + + @Override + public synchronized User updateUser(final User user) throws AuthorizationAccessException { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + final List<org.apache.nifi.authorization.file.generated.User> users = authorizations.getUsers().getUser(); + + // fine the User that needs to be updated + org.apache.nifi.authorization.file.generated.User updateUser = null; + for (org.apache.nifi.authorization.file.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()); + + updateUser.getGroup().clear(); + for (String groupIdentifier : user.getGroups()) { + org.apache.nifi.authorization.file.generated.User.Group group = new org.apache.nifi.authorization.file.generated.User.Group(); + group.setIdentifier(groupIdentifier); + updateUser.getGroup().add(group); + } + + saveAndRefreshHolder(authorizations); + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getUsersById().get(user.getIdentifier()); + } + } + + @Override + public synchronized User deleteUser(final User user) throws AuthorizationAccessException { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + final List<org.apache.nifi.authorization.file.generated.User> users = authorizations.getUsers().getUser(); + + // remove any references to the user being deleted from policies + for (Policy policy : authorizations.getPolicies().getPolicy()) { + Iterator<Policy.User> policyUserIter = policy.getUser().iterator(); + while (policyUserIter.hasNext()) { + Policy.User policyUser = policyUserIter.next(); + if (policyUser.getIdentifier().equals(user.getIdentifier())) { + policyUserIter.remove(); + break; } } } - // set the new authorizations - authorizations.set(newAuthorizations); + // remove the actual user if it exists + boolean removedUser = false; + Iterator<org.apache.nifi.authorization.file.generated.User> iter = users.iterator(); + while (iter.hasNext()) { + org.apache.nifi.authorization.file.generated.User jaxbUser = iter.next(); + if (user.getIdentifier().equals(jaxbUser.getIdentifier())) { + iter.remove(); + removedUser = true; + break; + } + } - // if we've copied a the authorizations file to a restore directory synchronize it - if (restoreAuthorizationsFile != null) { - FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger); + if (removedUser) { + saveAndRefreshHolder(authorizations); + return user; + } else { + return null; } + } - logger.info(String.format("Authorizations file loaded at %s", new Date().toString())); + @Override + public Set<User> getUsers() throws AuthorizationAccessException { + return authorizationsHolder.get().getAllUsers(); } - @AuthorizerContext - public void setNiFiProperties(NiFiProperties properties) { - this.properties = properties; + // ------------------ AccessPolicies ------------------ + + @Override + public synchronized AccessPolicy addAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException { + if (accessPolicy == null) { + throw new IllegalArgumentException("AccessPolicy cannot be null"); + } + + // create the new JAXB Policy + final Policy policy = createJAXBPolicy(accessPolicy); + + // add the new Policy to the top-level list of policies + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + authorizations.getPolicies().getPolicy().add(policy); + + saveAndRefreshHolder(authorizations); + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getPoliciesById().get(accessPolicy.getIdentifier()); + } + + private Policy createJAXBPolicy(final AccessPolicy accessPolicy) { + final Policy policy = new Policy(); + policy.setIdentifier(accessPolicy.getIdentifier()); + transferState(accessPolicy, policy); + return policy; } @Override - public void preDestruction() { - if (fileWatcherExecutorService != null) { - fileWatcherExecutorService.shutdown(); + public AccessPolicy getAccessPolicy(final String identifier) throws AuthorizationAccessException { + if (identifier == null) { + return null; + } + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getPoliciesById().get(identifier); + } + + @Override + public synchronized AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException { + if (accessPolicy == null) { + throw new IllegalArgumentException("AccessPolicy cannot be null"); + } + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + + // try to find an existing Authorization that matches the policy id + Policy updatePolicy = null; + for (Policy policy : authorizations.getPolicies().getPolicy()) { + if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) { + updatePolicy = policy; + break; + } + } + + // no matching Policy so return null + if (updatePolicy == null) { + return null; + } + + // update the Policy, save, reload, and return + transferState(accessPolicy, updatePolicy); + saveAndRefreshHolder(authorizations); + + final AuthorizationsHolder holder = authorizationsHolder.get(); + return holder.getPoliciesById().get(accessPolicy.getIdentifier()); + } + + @Override + public synchronized AccessPolicy deleteAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException { + if (accessPolicy == null) { + throw new IllegalArgumentException("AccessPolicy cannot be null"); + } + + final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations(); + + // find the matching Policy and remove it + boolean deletedPolicy = false; + Iterator<Policy> policyIter = authorizations.getPolicies().getPolicy().iterator(); + while (policyIter.hasNext()) { + final Policy policy = policyIter.next(); + if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) { + policyIter.remove(); + deletedPolicy = true; + break; + } + } + + // never found a matching Policy so return null + if (!deletedPolicy) { + return null; + } + + saveAndRefreshHolder(authorizations); + return accessPolicy; + } + + @Override + public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException { + return authorizationsHolder.get().getAllPolicies(); + } + + @Override + public UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException { + return authorizationsHolder.get(); + } + + /** + * Sets the given Policy to the state of the provided AccessPolicy. Users and Groups will be cleared and + * set to match the AccessPolicy, the resource and action will be set to match the AccessPolicy. + * + * Does not set the identifier. + * + * @param accessPolicy the AccessPolicy to transfer state from + * @param policy the Policy to transfer state to + */ + private void transferState(AccessPolicy accessPolicy, Policy policy) { + policy.setResource(accessPolicy.getResource()); + + // add users to the policy + policy.getUser().clear(); + for (String userIdentifier : accessPolicy.getUsers()) { + Policy.User policyUser = new Policy.User(); + policyUser.setIdentifier(userIdentifier); + policy.getUser().add(policyUser); + } + + // add groups to the policy + policy.getGroup().clear(); + for (String groupIdentifier : accessPolicy.getGroups()) { + Policy.Group policyGroup = new Policy.Group(); + policyGroup.setIdentifier(groupIdentifier); + policy.getGroup().add(policyGroup); + } + + // add the action to the policy + boolean containsRead = accessPolicy.getActions().contains(RequestAction.READ); + boolean containsWrite = accessPolicy.getActions().contains(RequestAction.WRITE); + + if (containsRead && containsWrite) { + policy.setAction(READ_CODE + WRITE_CODE); + } else if (containsRead) { + policy.setAction(READ_CODE); + } else { + policy.setAction(WRITE_CODE); } } + }
http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/authorizations.xsd ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/authorizations.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/authorizations.xsd index f659b27..f3e220d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/authorizations.xsd +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/authorizations.xsd @@ -14,9 +14,10 @@ limitations under the License. --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <!-- authorization --> - <xs:complexType name="Authorization"> - <xs:attribute name="identity"> + + <!-- group --> + <xs:complexType name="Group"> + <xs:attribute name="identifier"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:minLength value="1"/> @@ -24,20 +25,91 @@ </xs:restriction> </xs:simpleType> </xs:attribute> - <xs:attribute name="action"> + <xs:attribute name="name"> <xs:simpleType> <xs:restriction base="xs:string"> - <xs:enumeration value="R"/> - <xs:enumeration value="RW"/> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <!-- groups --> + <xs:complexType name="Groups"> + <xs:sequence> + <xs:element name="group" type="Group" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- user --> + <xs:complexType name="User"> + <xs:sequence> + <xs:element name="group" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="identity"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> - <!-- resource --> - <xs:complexType name="Resource"> + <!-- users --> + <xs:complexType name="Users"> <xs:sequence> - <xs:element name="authorization" type="Authorization" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- authorization --> + <xs:complexType name="Policy"> + <xs:sequence> + <xs:element name="group" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="user" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> </xs:sequence> <xs:attribute name="identifier"> <xs:simpleType> @@ -47,14 +119,40 @@ </xs:restriction> </xs:simpleType> </xs:attribute> + <xs:attribute name="resource"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="action"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="R"/> + <xs:enumeration value="RW"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> </xs:complexType> <!-- resources --> - <xs:element name="resources"> + <xs:complexType name="Policies"> + <xs:sequence> + <xs:element name="policy" type="Policy" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- top-level authorizations element --> + <xs:element name="authorizations"> <xs:complexType> <xs:sequence> - <xs:element name="resource" type="Resource" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="groups" type="Groups" minOccurs="0" maxOccurs="1" /> + <xs:element name="users" type="Users" minOccurs="0" maxOccurs="1" /> + <xs:element name="policies" type="Policies" minOccurs="0" maxOccurs="1" /> </xs:sequence> </xs:complexType> </xs:element> + </xs:schema> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/users.xsd ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/users.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/users.xsd new file mode 100644 index 0000000..4ee1e17 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/xsd/users.xsd @@ -0,0 +1,64 @@ +<?xml version="1.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. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <!-- role --> + <xs:complexType name="Role"> + <xs:attribute name="name"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="ROLE_MONITOR"/> + <xs:enumeration value="ROLE_PROVENANCE"/> + <xs:enumeration value="ROLE_DFM"/> + <xs:enumeration value="ROLE_ADMIN"/> + <xs:enumeration value="ROLE_PROXY"/> + <xs:enumeration value="ROLE_NIFI"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <!-- 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"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <!-- users --> + <xs:element name="users"> + <xs:complexType> + <xs:sequence> + <xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java index bfb064a..0822b60 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java @@ -31,9 +31,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -42,25 +45,57 @@ public class FileAuthorizerTest { private static final String EMPTY_AUTHORIZATIONS_CONCISE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" - + "<resources/>"; + + "<authorizations/>"; private static final String EMPTY_AUTHORIZATIONS = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" - + "<resources>" - + "</resources>"; + + "<authorizations>" + + "</authorizations>"; private static final String BAD_SCHEMA_AUTHORIZATIONS = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" - + "<resource>" - + "</resource>"; + + "<authorization>" + + "</authorization>"; + + private static final String SIMPLE_AUTHORIZATION_BY_USER = + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + + "<authorizations>" + + " <users>" + + " <user identifier=\"user-1\" identity=\"user-1\"/>" + + " <user identifier=\"user-2\" identity=\"user-2\"/>" + + " </users>" + + " <policies>" + + " <policy identifier=\"policy-1\" resource=\"/flow\" action=\"R\">" + + " <user identifier=\"user-1\" />" + + " </policy>" + + " </policies>" + + "</authorizations>"; private static final String AUTHORIZATIONS = - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" - + "<resources>" - + "<resource identifier=\"/flow\">" - + "<authorization identity=\"user-1\" action=\"R\"/>" - + "</resource>" - + "</resources>"; + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + + "<authorizations>" + + " <groups>" + + " <group identifier=\"group-1\" name=\"group-1\" />" + + " <group identifier=\"group-2\" name=\"group-2\" />" + + " </groups>" + + " <users>" + + " <user identifier=\"user-1\" identity=\"user-1\">" + + " <group identifier=\"group-1\" />" + + " <group identifier=\"group-2\" />" + + " </user>\n" + + " <user identifier=\"user-2\" identity=\"user-2\" />" + + " </users>" + + " <policies>" + + " <policy identifier=\"policy-1\" resource=\"/flow\" action=\"RW\">" + + " <group identifier=\"group-1\" />" + + " <group identifier=\"group-2\" />" + + " <user identifier=\"user-1\" />" + + " </policy>" + + " <policy identifier=\"policy-2\" resource=\"/flow\" action=\"RW\">" + + " <user identifier=\"user-2\" />" + + " </policy>" + + " </policies>" + + "</authorizations>"; private static final String UPDATED_AUTHORIZATIONS = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" @@ -104,7 +139,39 @@ public class FileAuthorizerTest { } @Test - public void testPostConstructionWhenRestoreDoesNotExist() throws Exception { + public void testOnConfiguredWhenInitialAdminProvided() throws Exception { + final String adminIdentity = "admin-user"; + + when(configurationContext.getProperty(Mockito.eq("Initial Admin Identity"))) + .thenReturn(new StandardPropertyValue(adminIdentity, null)); + + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE); + authorizer.onConfigured(configurationContext); + + final Set<User> users = authorizer.getUsers(); + assertEquals(1, users.size()); + + final User adminUser = users.iterator().next(); + assertEquals(adminIdentity, adminUser.getIdentity()); + + final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); + assertEquals(4, policies.size()); + } + + @Test + public void testOnConfiguredWhenInitialAdminNotProvided() throws Exception { + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE); + authorizer.onConfigured(configurationContext); + + final Set<User> users = authorizer.getUsers(); + assertEquals(0, users.size()); + + final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); + assertEquals(0, policies.size()); + } + + @Test + public void testOnConfiguredWhenRestoreDoesNotExist() throws Exception { writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE); authorizer.onConfigured(configurationContext); @@ -112,78 +179,622 @@ public class FileAuthorizerTest { } @Test(expected = AuthorizerCreationException.class) - public void testPostConstructionWhenPrimaryDoesNotExist() throws Exception { + public void testOnConfiguredWhenPrimaryDoesNotExist() throws Exception { writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE); authorizer.onConfigured(configurationContext); } @Test(expected = AuthorizerCreationException.class) - public void testPostConstructionWhenPrimaryDifferentThanRestore() throws Exception { + public void testOnConfiguredWhenPrimaryDifferentThanRestore() throws Exception { writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS); writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE); authorizer.onConfigured(configurationContext); } @Test(expected = AuthorizerCreationException.class) - public void testBadSchema() throws Exception { + public void testOnConfiguredWithBadSchema() throws Exception { writeAuthorizationsFile(primary, BAD_SCHEMA_AUTHORIZATIONS); authorizer.onConfigured(configurationContext); } @Test public void testAuthorizedUserAction() throws Exception { - writeAuthorizationsFile(primary, AUTHORIZATIONS); + writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER); authorizer.onConfigured(configurationContext); - final AuthorizationRequest request = new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction - .READ).build(); + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getFlowResource()) + .identity("user-1") + .anonymous(false) + .accessAttempt(true) + .action(RequestAction.READ) + .build(); + final AuthorizationResult result = authorizer.authorize(request); assertTrue(Result.Approved.equals(result.getResult())); } @Test public void testUnauthorizedUser() throws Exception { - writeAuthorizationsFile(primary, AUTHORIZATIONS); + writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER); authorizer.onConfigured(configurationContext); - final AuthorizationRequest request = - new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-2").anonymous(false).accessAttempt(true).action(RequestAction.READ).build(); + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getFlowResource()) + .identity("user-2") + .anonymous(false) + .accessAttempt(true) + .action(RequestAction.READ) + .build(); + final AuthorizationResult result = authorizer.authorize(request); assertFalse(Result.Approved.equals(result.getResult())); } @Test public void testUnauthorizedAction() throws Exception { - writeAuthorizationsFile(primary, AUTHORIZATIONS); + writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER); authorizer.onConfigured(configurationContext); - final AuthorizationRequest request = - new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction.WRITE).build(); + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getFlowResource()) + .identity("user-1") + .anonymous(false) + .accessAttempt(true) + .action(RequestAction.WRITE) + .build(); + final AuthorizationResult result = authorizer.authorize(request); assertFalse(Result.Approved.equals(result.getResult())); } @Test - public void testReloadAuthorizations() throws Exception { + public void testGetAllUsersGroupsPolicies() throws Exception { writeAuthorizationsFile(primary, AUTHORIZATIONS); - when(configurationContext.getProperty(Mockito.eq("Reload Interval"))).thenReturn(new StandardPropertyValue("1 sec", null)); authorizer.onConfigured(configurationContext); - // ensure the user currently does not have write access - final AuthorizationRequest request = - new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction.WRITE).build(); - AuthorizationResult result = authorizer.authorize(request); - assertFalse(Result.Approved.equals(result.getResult())); + final Set<Group> groups = authorizer.getGroups(); + assertEquals(2, groups.size()); - // add write access for the user - writeAuthorizationsFile(primary, UPDATED_AUTHORIZATIONS); + boolean foundGroup1 = false; + boolean foundGroup2 = false; - // wait at least one second for the file to be stale - Thread.sleep(4000L); + for (Group group : groups) { + if (group.getIdentifier().equals("group-1") && group.getName().equals("group-1") + && group.getUsers().size() == 1 && group.getUsers().contains("user-1")) { + foundGroup1 = true; + } else if (group.getIdentifier().equals("group-2") && group.getName().equals("group-2") + && group.getUsers().size() == 1 && group.getUsers().contains("user-1")) { + foundGroup2 = true; + } + } - // ensure the user does have write access now using the same request - result = authorizer.authorize(request); - assertTrue(Result.Approved.equals(result.getResult())); + assertTrue(foundGroup1); + assertTrue(foundGroup2); + + final Set<User> users = authorizer.getUsers(); + assertEquals(2, users.size()); + + boolean foundUser1 = false; + boolean foundUser2 = false; + + for (User user : users) { + if (user.getIdentifier().equals("user-1") && user.getIdentity().equals("user-1") + && user.getGroups().size() == 2 && user.getGroups().contains("group-1") + && user.getGroups().contains("group-2")) { + foundUser1 = true; + } else if (user.getIdentifier().equals("user-2") && user.getIdentity().equals("user-2") + && user.getGroups().size() == 0) { + foundUser2 = true; + } + } + + assertTrue(foundUser1); + assertTrue(foundUser2); + + final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); + assertEquals(2, policies.size()); + + boolean foundPolicy1 = false; + boolean foundPolicy2 = false; + + for (AccessPolicy policy : policies) { + if (policy.getIdentifier().equals("policy-1") + && policy.getResource().equals("/flow") + && policy.getActions().size() == 2 + && policy.getActions().contains(RequestAction.READ) + && policy.getActions().contains(RequestAction.WRITE) + && policy.getGroups().size() == 2 + && policy.getGroups().contains("group-1") + && policy.getGroups().contains("group-2") + && policy.getUsers().size() == 1 + && policy.getUsers().contains("user-1")) { + foundPolicy1 = true; + } else if (policy.getIdentifier().equals("policy-2") + && policy.getResource().equals("/flow") + && policy.getActions().size() == 2 + && policy.getActions().contains(RequestAction.READ) + && policy.getActions().contains(RequestAction.WRITE) + && policy.getGroups().size() == 0 + && policy.getUsers().size() == 1 + && policy.getUsers().contains("user-2")) { + foundPolicy2 = true; + } + } + + assertTrue(foundPolicy1); + assertTrue(foundPolicy2); + } + + // --------------- User Tests ------------------------ + + @Test + public void testAddUser() throws Exception { + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(0, authorizer.getUsers().size()); + + final User user = new User.Builder() + .identifier("user-1") + .identity("user-identity-1") + .addGroup("group1") + .addGroup("group2") + .build(); + + final User addedUser = authorizer.addUser(user); + assertNotNull(addedUser); + assertEquals(user.getIdentifier(), addedUser.getIdentifier()); + assertEquals(user.getIdentity(), addedUser.getIdentity()); + assertEquals(2, addedUser.getGroups().size()); + assertTrue(addedUser.getGroups().contains("group1")); + assertTrue(addedUser.getGroups().contains("group2")); + + final Set<User> users = authorizer.getUsers(); + assertEquals(1, users.size()); + } + + @Test + public void testGetUserByIdentifierWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final String identifier = "user-1"; + final User user = authorizer.getUser(identifier); + assertNotNull(user); + assertEquals(identifier, user.getIdentifier()); + } + + @Test + public void testGetUserByIdentifierWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final String identifier = "user-X"; + final User user = authorizer.getUser(identifier); + assertNull(user); + } + + @Test + public void testGetUserByIdentityWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final String identity = "user-1"; + final User user = authorizer.getUserByIdentity(identity); + assertNotNull(user); + assertEquals(identity, user.getIdentifier()); + } + + @Test + public void testGetUserByIdentityWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final String identity = "user-X"; + final User user = authorizer.getUserByIdentity(identity); + assertNull(user); + } + + @Test + public void testDeleteUser() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + // retrieve user-1 and verify it exsits + final User user = authorizer.getUser("user-1"); + assertEquals("user-1", user.getIdentifier()); + + final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1"); + assertTrue(policy1.getUsers().contains("user-1")); + + // delete user-1 + final User deletedUser = authorizer.deleteUser(user); + assertNotNull(deletedUser); + assertEquals("user-1", deletedUser.getIdentifier()); + + // should be one less user + assertEquals(1, authorizer.getUsers().size()); + assertNull(authorizer.getUser(user.getIdentifier())); + + // verify policy-1 no longer has a reference to user-1 + final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1"); + assertFalse(updatedPolicy1.getUsers().contains("user-1")); + } + + @Test + public void testDeleteUserWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + //user that doesn't exist + final User user = new User.Builder().identifier("user-X").identity("user-identity-X").build(); + + // should return null and still have 2 users because nothing was deleted + final User deletedUser = authorizer.deleteUser(user); + assertNull(deletedUser); + assertEquals(2, authorizer.getUsers().size()); + } + + @Test + public void testUpdateUserWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final User user = new User.Builder() + .identifier("user-1") + .identity("new-identity") + .addGroup("new-group") + .build(); + + final User updatedUser = authorizer.updateUser(user); + assertNotNull(updatedUser); + assertEquals(user.getIdentifier(), updatedUser.getIdentifier()); + assertEquals(user.getIdentity(), updatedUser.getIdentity()); + assertEquals(1, updatedUser.getGroups().size()); + assertTrue(updatedUser.getGroups().contains("new-group")); + } + + @Test + public void testUpdateUserWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getUsers().size()); + + final User user = new User.Builder() + .identifier("user-X") + .identity("new-identity") + .addGroup("new-group") + .build(); + + final User updatedUser = authorizer.updateUser(user); + assertNull(updatedUser); + } + + // --------------- Group Tests ------------------------ + + @Test + public void testAddGroup() throws Exception { + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(0, authorizer.getGroups().size()); + + final Group group = new Group.Builder() + .identifier("group-id-1") + .name("group-name-1") + .addUser("user1") // should be ignored + .build(); + + final Group addedGroup = authorizer.addGroup(group); + assertNotNull(addedGroup); + assertEquals(group.getIdentifier(), addedGroup.getIdentifier()); + assertEquals(group.getName(), addedGroup.getName()); + assertEquals(0, addedGroup.getUsers().size()); + + final Set<Group> groups = authorizer.getGroups(); + assertEquals(1, groups.size()); + } + + @Test + public void testGetGroupByIdentifierWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + final String identifier = "group-1"; + final Group group = authorizer.getGroup(identifier); + assertNotNull(group); + assertEquals(identifier, group.getIdentifier()); + } + + @Test + public void testGetGroupByIdentifierWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + final String identifier = "group-X"; + final Group group = authorizer.getGroup(identifier); + assertNull(group); + } + + @Test + public void testDeleteGroupWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + // retrieve user-1 and verify its in group-1 + final User user1 = authorizer.getUser("user-1"); + assertNotNull(user1); + assertEquals(2, user1.getGroups().size()); + assertTrue(user1.getGroups().contains("group-1")); + + final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1"); + assertTrue(policy1.getGroups().contains("group-1")); + + // retrieve group-1 + final Group group = authorizer.getGroup("group-1"); + assertEquals("group-1", group.getIdentifier()); + + // delete group-1 + final Group deletedGroup = authorizer.deleteGroup(group); + assertNotNull(deletedGroup); + assertEquals("group-1", deletedGroup.getIdentifier()); + + // verify there is one less overall group + assertEquals(1, authorizer.getGroups().size()); + + // verify we can no longer retrieve group-1 by identifier + assertNull(authorizer.getGroup(group.getIdentifier())); + + // verify user-1 is no longer in group-1 + final User updatedUser1 = authorizer.getUser("user-1"); + assertNotNull(updatedUser1); + assertEquals(1, updatedUser1.getGroups().size()); + assertFalse(updatedUser1.getGroups().contains("group-1")); + + // verify group-1 is no longer in policy-1 + final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1"); + assertFalse(updatedPolicy1.getGroups().contains("group-1")); + } + + @Test + public void testDeleteGroupWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + final Group group = new Group.Builder() + .identifier("group-id-X") + .name("group-name-X") + .build(); + + final Group deletedGroup = authorizer.deleteGroup(group); + assertNull(deletedGroup); + assertEquals(2, authorizer.getGroups().size()); + } + + @Test + public void testUpdateGroupWhenFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + final Group group = new Group.Builder() + .identifier("group-1") + .name("new-name") + .build(); + + final Group updatedGroup = authorizer.updateGroup(group); + assertEquals(group.getIdentifier(), updatedGroup.getIdentifier()); + assertEquals(group.getName(), updatedGroup.getName()); + } + + @Test + public void testUpdateGroupWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getGroups().size()); + + final Group group = new Group.Builder() + .identifier("group-X") + .name("group-X") + .build(); + + final Group updatedGroup = authorizer.updateGroup(group); + assertNull(updatedGroup); + assertEquals(2, authorizer.getGroups().size()); + } + + // --------------- AccessPolicy Tests ------------------------ + + @Test + public void testAddAccessPolicy() throws Exception { + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(0, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy1 = new AccessPolicy.Builder() + .identifier("policy-1") + .resource("resource-1") + .addUser("user-1") + .addGroup("group-1") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy returnedPolicy1 = authorizer.addAccessPolicy(policy1); + assertNotNull(returnedPolicy1); + assertEquals(policy1.getIdentifier(), returnedPolicy1.getIdentifier()); + assertEquals(policy1.getResource(), returnedPolicy1.getResource()); + assertEquals(policy1.getUsers(), returnedPolicy1.getUsers()); + assertEquals(policy1.getGroups(), returnedPolicy1.getGroups()); + assertEquals(policy1.getActions(), returnedPolicy1.getActions()); + + assertEquals(1, authorizer.getAccessPolicies().size()); + + // second policy for the same resource + final AccessPolicy policy2 = new AccessPolicy.Builder() + .identifier("policy-2") + .resource("resource-1") + .addUser("user-1") + .addGroup("group-1") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy returnedPolicy2 = authorizer.addAccessPolicy(policy2); + assertNotNull(returnedPolicy2); + assertEquals(2, authorizer.getAccessPolicies().size()); + } + + @Test + public void testAddAccessPolicyWithEmptyUsersAndGroups() throws Exception { + writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(0, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy1 = new AccessPolicy.Builder() + .identifier("policy-1") + .resource("resource-1") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy returnedPolicy1 = authorizer.addAccessPolicy(policy1); + assertNotNull(returnedPolicy1); + assertEquals(policy1.getIdentifier(), returnedPolicy1.getIdentifier()); + assertEquals(policy1.getResource(), returnedPolicy1.getResource()); + assertEquals(policy1.getUsers(), returnedPolicy1.getUsers()); + assertEquals(policy1.getGroups(), returnedPolicy1.getGroups()); + assertEquals(policy1.getActions(), returnedPolicy1.getActions()); + + assertEquals(1, authorizer.getAccessPolicies().size()); + } + + @Test + public void testGetAccessPolicy() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = authorizer.getAccessPolicy("policy-1"); + assertNotNull(policy); + assertEquals("policy-1", policy.getIdentifier()); + assertEquals("/flow", policy.getResource()); + + assertEquals(2, policy.getActions().size()); + assertTrue(policy.getActions().contains(RequestAction.WRITE)); + assertTrue(policy.getActions().contains(RequestAction.READ)); + + assertEquals(1, policy.getUsers().size()); + assertTrue(policy.getUsers().contains("user-1")); + + assertEquals(2, policy.getGroups().size()); + assertTrue(policy.getGroups().contains("group-1")); + assertTrue(policy.getGroups().contains("group-2")); + } + + @Test + public void testGetAccessPolicyWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = authorizer.getAccessPolicy("policy-X"); + assertNull(policy); + } + + @Test + public void testUpdateAccessPolicy() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = new AccessPolicy.Builder() + .identifier("policy-1") + .resource("resource-A") + .addUser("user-A") + .addGroup("group-A") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy updateAccessPolicy = authorizer.updateAccessPolicy(policy); + assertNotNull(updateAccessPolicy); + assertEquals("policy-1", updateAccessPolicy.getIdentifier()); + assertEquals("resource-A", updateAccessPolicy.getResource()); + + assertEquals(1, updateAccessPolicy.getUsers().size()); + assertTrue(updateAccessPolicy.getUsers().contains("user-A")); + + assertEquals(1, updateAccessPolicy.getGroups().size()); + assertTrue(updateAccessPolicy.getGroups().contains("group-A")); + + assertEquals(1, updateAccessPolicy.getActions().size()); + assertTrue(updateAccessPolicy.getActions().contains(RequestAction.READ)); + } + + @Test + public void testUpdateAccessPolicyWhenResourceNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = new AccessPolicy.Builder() + .identifier("policy-XXX") + .resource("resource-A") + .addUser("user-A") + .addGroup("group-A") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy updateAccessPolicy = authorizer.updateAccessPolicy(policy); + assertNull(updateAccessPolicy); + } + + @Test + public void testDeleteAccessPolicy() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = new AccessPolicy.Builder() + .identifier("policy-1") + .resource("resource-A") + .addUser("user-A") + .addGroup("group-A") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy deletedAccessPolicy = authorizer.deleteAccessPolicy(policy); + assertNotNull(deletedAccessPolicy); + assertEquals(policy.getIdentifier(), deletedAccessPolicy.getIdentifier()); + + // should have one less policy, and get by policy id should return null + assertEquals(1, authorizer.getAccessPolicies().size()); + assertNull(authorizer.getAccessPolicy(policy.getIdentifier())); + } + + @Test + public void testDeleteAccessPolicyWhenNotFound() throws Exception { + writeAuthorizationsFile(primary, AUTHORIZATIONS); + authorizer.onConfigured(configurationContext); + assertEquals(2, authorizer.getAccessPolicies().size()); + + final AccessPolicy policy = new AccessPolicy.Builder() + .identifier("policy-XXX") + .resource("resource-A") + .addUser("user-A") + .addGroup("group-A") + .addAction(RequestAction.READ) + .build(); + + final AccessPolicy deletedAccessPolicy = authorizer.deleteAccessPolicy(policy); + assertNull(deletedAccessPolicy); } private static void writeAuthorizationsFile(final File file, final String content) throws Exception { http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/pom.xml index 918c6cf..f1a6c28 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/pom.xml @@ -21,40 +21,7 @@ <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>nifi-framework-authorization</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>current</id> - <goals> - <goal>xjc</goal> - </goals> - <configuration> - <packageName>org.apache.nifi.authorization.generated</packageName> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - <configuration> - <excludes>**/authorization/generated/*.java,</excludes> - </configuration> - </plugin> - </plugins> - </build> + <dependencies> <dependency> <groupId>org.apache.nifi</groupId> @@ -65,22 +32,10 @@ <artifactId>nifi-expression-language</artifactId> </dependency> <dependency> - <groupId>org.apache.nifi</groupId> - <artifactId>nifi-properties</artifactId> - </dependency> - <dependency> - <groupId>org.apache.nifi</groupId> - <artifactId>nifi-nar-utils</artifactId> - </dependency> - <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> </dependency> <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-beans</artifactId> - </dependency> - <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency>
