| /* |
| * 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.AccessPolicy; |
| import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; |
| import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; |
| import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider; |
| import org.apache.nifi.registry.security.authorization.RequestAction; |
| import org.apache.nifi.registry.security.authorization.User; |
| import org.apache.nifi.registry.security.authorization.UserGroupProvider; |
| import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; |
| 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.generated.Authorizations; |
| import org.apache.nifi.registry.security.authorization.file.generated.Policies; |
| import org.apache.nifi.registry.security.authorization.file.generated.Policy; |
| import org.apache.nifi.registry.security.exception.SecurityProviderCreationException; |
| import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException; |
| 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 FileAccessPolicyProvider implements ConfigurableAccessPolicyProvider { |
| |
| private static final Logger logger = LoggerFactory.getLogger(FileAccessPolicyProvider.class); |
| |
| private static final String AUTHORIZATIONS_XSD = "/authorizations.xsd"; |
| private static final String JAXB_AUTHORIZATIONS_PATH = "org.apache.nifi.registry.security.authorization.file.generated"; |
| |
| private static final JAXBContext JAXB_AUTHORIZATIONS_CONTEXT = initializeJaxbContext(JAXB_AUTHORIZATIONS_PATH); |
| |
| /** |
| * Load the JAXBContext. |
| */ |
| private static JAXBContext initializeJaxbContext(final String contextPath) { |
| try { |
| return JAXBContext.newInstance(contextPath, FileAuthorizer.class.getClassLoader()); |
| } catch (JAXBException e) { |
| throw new RuntimeException("Unable to create JAXBContext."); |
| } |
| } |
| |
| private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); |
| private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance(); |
| |
| private static final String POLICY_ELEMENT = "policy"; |
| private static final String POLICY_USER_ELEMENT = "policyUser"; |
| private static final String POLICY_GROUP_ELEMENT = "policyGroup"; |
| private static final String IDENTIFIER_ATTR = "identifier"; |
| private static final String RESOURCE_ATTR = "resource"; |
| private static final String ACTIONS_ATTR = "actions"; |
| |
| /* These codes must match the enumeration values set in authorizations.xsd */ |
| static final String READ_CODE = "R"; |
| static final String WRITE_CODE = "W"; |
| static final String DELETE_CODE = "D"; |
| |
| /* TODO - move this somewhere into nifi-registry-security-framework so it can be applied to any ConfigurableAccessPolicyProvider |
| * (and also gets us away from requiring magic strings here) */ |
| private static final ResourceActionPair[] INITIAL_ADMIN_ACCESS_POLICIES = { |
| new ResourceActionPair("/tenants", READ_CODE), |
| new ResourceActionPair("/tenants", WRITE_CODE), |
| new ResourceActionPair("/tenants", DELETE_CODE), |
| new ResourceActionPair("/policies", READ_CODE), |
| new ResourceActionPair("/policies", WRITE_CODE), |
| new ResourceActionPair("/policies", DELETE_CODE), |
| new ResourceActionPair("/buckets", READ_CODE), |
| new ResourceActionPair("/buckets", WRITE_CODE), |
| new ResourceActionPair("/buckets", DELETE_CODE), |
| new ResourceActionPair("/actuator", READ_CODE), |
| new ResourceActionPair("/actuator", WRITE_CODE), |
| new ResourceActionPair("/actuator", DELETE_CODE), |
| new ResourceActionPair("/proxy", WRITE_CODE) |
| }; |
| |
| /* TODO - move this somewhere into nifi-registry-security-framework so it can be applied to any ConfigurableAccessPolicyProvider |
| * (and also gets us away from requiring magic strings here) */ |
| private static final ResourceActionPair[] NIFI_ACCESS_POLICIES = { |
| new ResourceActionPair("/buckets", READ_CODE), |
| new ResourceActionPair("/proxy", WRITE_CODE) |
| }; |
| |
| static final String PROP_NIFI_IDENTITY_PREFIX = "NiFi Identity "; |
| static final String PROP_USER_GROUP_PROVIDER = "User Group Provider"; |
| static final String PROP_AUTHORIZATIONS_FILE = "Authorizations File"; |
| static final String PROP_INITIAL_ADMIN_IDENTITY = "Initial Admin Identity"; |
| static final Pattern NIFI_IDENTITY_PATTERN = Pattern.compile(PROP_NIFI_IDENTITY_PREFIX + "\\S+"); |
| |
| private Schema authorizationsSchema; |
| private NiFiRegistryProperties properties; |
| private File authorizationsFile; |
| private String initialAdminIdentity; |
| private Set<String> nifiIdentities; |
| private List<IdentityMapping> identityMappings; |
| |
| private UserGroupProvider userGroupProvider; |
| private UserGroupProviderLookup userGroupProviderLookup; |
| private final AtomicReference<AuthorizationsHolder> authorizationsHolder = new AtomicReference<>(); |
| |
| @Override |
| public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws SecurityProviderCreationException { |
| userGroupProviderLookup = initializationContext.getUserGroupProviderLookup(); |
| |
| try { |
| final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| authorizationsSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(AUTHORIZATIONS_XSD)); |
| } catch (Exception e) { |
| throw new SecurityProviderCreationException(e); |
| } |
| } |
| |
| @Override |
| public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException { |
| try { |
| final PropertyValue userGroupProviderIdentifier = configurationContext.getProperty(PROP_USER_GROUP_PROVIDER); |
| if (!userGroupProviderIdentifier.isSet()) { |
| throw new SecurityProviderCreationException("The user group provider must be specified."); |
| } |
| |
| userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderIdentifier.getValue()); |
| if (userGroupProvider == null) { |
| throw new SecurityProviderCreationException("Unable to locate user group provider with identifier " + userGroupProviderIdentifier.getValue()); |
| } |
| |
| final PropertyValue authorizationsPath = configurationContext.getProperty(PROP_AUTHORIZATIONS_FILE); |
| if (StringUtils.isBlank(authorizationsPath.getValue())) { |
| throw new SecurityProviderCreationException("The authorizations file must be specified."); |
| } |
| |
| // get the authorizations file and ensure it exists |
| authorizationsFile = new File(authorizationsPath.getValue()); |
| if (!authorizationsFile.exists()) { |
| logger.info("Creating new authorizations file at {}", new Object[] {authorizationsFile.getAbsolutePath()}); |
| saveAuthorizations(new Authorizations()); |
| } |
| |
| // extract the identity mappings from nifi-registry.properties if any are provided |
| identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties)); |
| |
| // get the value of the initial admin identity |
| final PropertyValue initialAdminIdentityProp = configurationContext.getProperty(PROP_INITIAL_ADMIN_IDENTITY); |
| initialAdminIdentity = initialAdminIdentityProp.isSet() ? IdentityMappingUtil.mapIdentity(initialAdminIdentityProp.getValue(), identityMappings) : null; |
| |
| // extract any nifi identities |
| nifiIdentities = new HashSet<>(); |
| for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) { |
| Matcher matcher = NIFI_IDENTITY_PATTERN.matcher(entry.getKey()); |
| if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) { |
| nifiIdentities.add(IdentityMappingUtil.mapIdentity(entry.getValue(), identityMappings)); |
| } |
| } |
| |
| // load the authorizations |
| load(); |
| |
| logger.info(String.format("Authorizations file loaded at %s", new Date().toString())); |
| } catch (SecurityProviderCreationException | JAXBException | IllegalStateException | SAXException e) { |
| throw new SecurityProviderCreationException(e); |
| } |
| } |
| |
| @Override |
| public UserGroupProvider getUserGroupProvider() { |
| return userGroupProvider; |
| } |
| |
| @Override |
| public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException { |
| return authorizationsHolder.get().getAllPolicies(); |
| } |
| |
| @Override |
| public synchronized AccessPolicy addAccessPolicy(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 AuthorizationsHolder holder = authorizationsHolder.get(); |
| final Authorizations authorizations = holder.getAuthorizations(); |
| authorizations.getPolicies().getPolicy().add(policy); |
| |
| saveAndRefreshHolder(authorizations); |
| |
| return authorizationsHolder.get().getPoliciesById().get(accessPolicy.getIdentifier()); |
| } |
| |
| @Override |
| public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { |
| if (identifier == null) { |
| return null; |
| } |
| |
| final AuthorizationsHolder holder = authorizationsHolder.get(); |
| return holder.getPoliciesById().get(identifier); |
| } |
| |
| @Override |
| public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { |
| return authorizationsHolder.get().getAccessPolicy(resourceIdentifier, action); |
| } |
| |
| @Override |
| public synchronized AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException { |
| if (accessPolicy == null) { |
| throw new IllegalArgumentException("AccessPolicy cannot be null"); |
| } |
| |
| final AuthorizationsHolder holder = this.authorizationsHolder.get(); |
| final Authorizations authorizations = holder.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 |
| transferUsersAndGroups(accessPolicy, updatePolicy); |
| saveAndRefreshHolder(authorizations); |
| |
| return this.authorizationsHolder.get().getPoliciesById().get(accessPolicy.getIdentifier()); |
| } |
| |
| @Override |
| public synchronized AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException { |
| if (accessPolicy == null) { |
| throw new IllegalArgumentException("AccessPolicy cannot be null"); |
| } |
| |
| return deleteAccessPolicy(accessPolicy.getIdentifier()); |
| } |
| |
| @Override |
| public synchronized AccessPolicy deleteAccessPolicy(String accessPolicyIdentifer) throws AuthorizationAccessException { |
| if (accessPolicyIdentifer == null) { |
| throw new IllegalArgumentException("Access policy identifier cannot be null"); |
| } |
| |
| final AuthorizationsHolder holder = this.authorizationsHolder.get(); |
| AccessPolicy deletedPolicy = holder.getPoliciesById().get(accessPolicyIdentifer); |
| if (deletedPolicy == null) { |
| return null; |
| } |
| |
| // find the matching Policy and remove it |
| final Authorizations authorizations = holder.getAuthorizations(); |
| Iterator<Policy> policyIter = authorizations.getPolicies().getPolicy().iterator(); |
| while (policyIter.hasNext()) { |
| final Policy policy = policyIter.next(); |
| if (policy.getIdentifier().equals(accessPolicyIdentifer)) { |
| policyIter.remove(); |
| break; |
| } |
| } |
| |
| saveAndRefreshHolder(authorizations); |
| return deletedPolicy; |
| } |
| |
| AuthorizationsHolder getAuthorizationsHolder() { |
| return authorizationsHolder.get(); |
| } |
| |
| @AuthorizerContext |
| public void setNiFiProperties(NiFiRegistryProperties properties) { |
| this.properties = properties; |
| } |
| |
| @Override |
| public synchronized void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { |
| parsePolicies(fingerprint).forEach(policy -> addAccessPolicy(policy)); |
| } |
| |
| @Override |
| public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { |
| try { |
| // ensure we can understand the proposed fingerprint |
| parsePolicies(proposedFingerprint); |
| } catch (final AuthorizationAccessException e) { |
| throw new UninheritableAuthorizationsException("Unable to parse the proposed fingerprint: " + e); |
| } |
| |
| // ensure we are in a proper state to inherit the fingerprint |
| if (!getAccessPolicies().isEmpty()) { |
| throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current access policies is not empty."); |
| } |
| } |
| |
| @Override |
| public String getFingerprint() throws AuthorizationAccessException { |
| final List<AccessPolicy> policies = new ArrayList<>(getAccessPolicies()); |
| Collections.sort(policies, Comparator.comparing(AccessPolicy::getIdentifier)); |
| |
| XMLStreamWriter writer = null; |
| final StringWriter out = new StringWriter(); |
| try { |
| writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out); |
| writer.writeStartDocument(); |
| writer.writeStartElement("accessPolicies"); |
| |
| for (AccessPolicy policy : policies) { |
| writePolicy(writer, policy); |
| } |
| |
| 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 List<AccessPolicy> parsePolicies(final String fingerprint) { |
| final List<AccessPolicy> policies = 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 policies and add them to the current access policy provider |
| NodeList policyNodes = rootElement.getElementsByTagName(POLICY_ELEMENT); |
| for (int i = 0; i < policyNodes.getLength(); i++) { |
| Node policyNode = policyNodes.item(i); |
| policies.add(parsePolicy((Element) policyNode)); |
| } |
| } catch (SAXException | ParserConfigurationException | IOException e) { |
| throw new AuthorizationAccessException("Unable to parse fingerprint", e); |
| } |
| |
| return policies; |
| } |
| |
| private AccessPolicy parsePolicy(final Element element) { |
| final AccessPolicy.Builder builder = new AccessPolicy.Builder() |
| .identifier(element.getAttribute(IDENTIFIER_ATTR)) |
| .resource(element.getAttribute(RESOURCE_ATTR)); |
| |
| final String actions = element.getAttribute(ACTIONS_ATTR); |
| if (actions.equals(RequestAction.READ.name())) { |
| builder.action(RequestAction.READ); |
| } else if (actions.equals(RequestAction.WRITE.name())) { |
| builder.action(RequestAction.WRITE); |
| } else if (actions.equals(RequestAction.DELETE.name())) { |
| builder.action(RequestAction.DELETE); |
| } else { |
| throw new IllegalStateException("Unknown Policy Action: " + actions); |
| } |
| |
| NodeList policyUsers = element.getElementsByTagName(POLICY_USER_ELEMENT); |
| for (int i=0; i < policyUsers.getLength(); i++) { |
| Element policyUserNode = (Element) policyUsers.item(i); |
| builder.addUser(policyUserNode.getAttribute(IDENTIFIER_ATTR)); |
| } |
| |
| NodeList policyGroups = element.getElementsByTagName(POLICY_GROUP_ELEMENT); |
| for (int i=0; i < policyGroups.getLength(); i++) { |
| Element policyGroupNode = (Element) policyGroups.item(i); |
| builder.addGroup(policyGroupNode.getAttribute(IDENTIFIER_ATTR)); |
| } |
| |
| return builder.build(); |
| } |
| |
| private void writePolicy(final XMLStreamWriter writer, final AccessPolicy policy) throws XMLStreamException { |
| // sort the users for the policy |
| List<String> policyUsers = new ArrayList<>(policy.getUsers()); |
| Collections.sort(policyUsers); |
| |
| // sort the groups for this policy |
| List<String> policyGroups = new ArrayList<>(policy.getGroups()); |
| Collections.sort(policyGroups); |
| |
| writer.writeStartElement(POLICY_ELEMENT); |
| writer.writeAttribute(IDENTIFIER_ATTR, policy.getIdentifier()); |
| writer.writeAttribute(RESOURCE_ATTR, policy.getResource()); |
| writer.writeAttribute(ACTIONS_ATTR, policy.getAction().name()); |
| |
| for (String policyUser : policyUsers) { |
| writer.writeStartElement(POLICY_USER_ELEMENT); |
| writer.writeAttribute(IDENTIFIER_ATTR, policyUser); |
| writer.writeEndElement(); |
| } |
| |
| for (String policyGroup : policyGroups) { |
| writer.writeStartElement(POLICY_GROUP_ELEMENT); |
| writer.writeAttribute(IDENTIFIER_ATTR, policyGroup); |
| writer.writeEndElement(); |
| } |
| |
| writer.writeEndElement(); |
| } |
| |
| /** |
| * Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up. |
| * |
| * @throws JAXBException Unable to reload the authorized users file |
| */ |
| private synchronized void load() throws JAXBException, SAXException { |
| // attempt to unmarshal |
| final Authorizations authorizations = unmarshallAuthorizations(); |
| if (authorizations.getPolicies() == null) { |
| authorizations.setPolicies(new Policies()); |
| } |
| |
| final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations); |
| final boolean emptyAuthorizations = authorizationsHolder.getAllPolicies().isEmpty(); |
| final boolean hasInitialAdminIdentity = (initialAdminIdentity != null && !StringUtils.isBlank(initialAdminIdentity)); |
| final boolean hasNiFiIdentities = (nifiIdentities != null && !nifiIdentities.isEmpty()); |
| |
| // if we are starting fresh then we might need to populate an initial admin |
| if (emptyAuthorizations) { |
| if (hasInitialAdminIdentity) { |
| logger.info("Populating authorizations for Initial Admin: " + initialAdminIdentity); |
| populateInitialAdmin(authorizations); |
| } |
| |
| if (hasNiFiIdentities) { |
| logger.info("Populating proxy authorizations for NiFi clients: [{}]", StringUtils.join(nifiIdentities, ";")); |
| populateNiFiIdentities(authorizations); |
| } |
| |
| saveAndRefreshHolder(authorizations); |
| } else { |
| this.authorizationsHolder.set(authorizationsHolder); |
| } |
| } |
| |
| private void saveAuthorizations(final Authorizations authorizations) throws JAXBException { |
| final Marshaller marshaller = JAXB_AUTHORIZATIONS_CONTEXT.createMarshaller(); |
| marshaller.setSchema(authorizationsSchema); |
| marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); |
| marshaller.marshal(authorizations, authorizationsFile); |
| } |
| |
| private Authorizations unmarshallAuthorizations() throws JAXBException { |
| final Unmarshaller unmarshaller = JAXB_AUTHORIZATIONS_CONTEXT.createUnmarshaller(); |
| unmarshaller.setSchema(authorizationsSchema); |
| |
| final JAXBElement<Authorizations> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Authorizations.class); |
| return element.getValue(); |
| } |
| |
| /** |
| * Creates the initial admin user and sets policies managing buckets, users, and policies. |
| * |
| * TODO - move this somewhere into nifi-registry-security-framework so it can be applied to any ConfigurableAccessPolicyProvider |
| */ |
| private void populateInitialAdmin(final Authorizations authorizations) { |
| final User initialAdmin = userGroupProvider.getUserByIdentity(initialAdminIdentity); |
| if (initialAdmin == null) { |
| throw new SecurityProviderCreationException("Unable to locate initial admin " + initialAdminIdentity + " to seed policies"); |
| } |
| |
| for (ResourceActionPair resourceAction : INITIAL_ADMIN_ACCESS_POLICIES) { |
| addUserToAccessPolicy(authorizations, resourceAction.resource, initialAdmin.getIdentifier(), resourceAction.actionCode); |
| } |
| } |
| |
| /** |
| * Creates a user for each NiFi client and gives each one write permission to /proxy. |
| * |
| * @param authorizations the overall authorizations |
| */ |
| private void populateNiFiIdentities(Authorizations authorizations) { |
| for (String nifiIdentity : nifiIdentities) { |
| final User nifiUser = userGroupProvider.getUserByIdentity(nifiIdentity); |
| if (nifiUser == null) { |
| throw new SecurityProviderCreationException("Unable to locate node " + nifiIdentity + " to seed policies."); |
| } |
| |
| // grant access to the resources needed for initial nifi-proxy identities |
| for (ResourceActionPair resourceAction : NIFI_ACCESS_POLICIES) { |
| addUserToAccessPolicy(authorizations, resourceAction.resource, nifiUser.getIdentifier(), resourceAction.actionCode); |
| } |
| } |
| } |
| |
| |
| /** |
| * Creates and adds an access policy for the given resource, identity, and actions to the specified authorizations. |
| * |
| * @param authorizations the Authorizations instance to add the policy to |
| * @param resource the resource for the policy |
| * @param userIdentifier the identifier for the user to add to the policy |
| * @param action the action for the policy |
| */ |
| private void addUserToAccessPolicy(final Authorizations authorizations, final String resource, final String userIdentifier, final String action) { |
| // first try to find an existing policy for the given resource and action |
| Policy foundPolicy = null; |
| for (Policy policy : authorizations.getPolicies().getPolicy()) { |
| if (policy.getResource().equals(resource) && policy.getAction().equals(action)) { |
| foundPolicy = policy; |
| break; |
| } |
| } |
| |
| if (foundPolicy == null) { |
| // if we didn't find an existing policy create a new one |
| final String uuidSeed = resource + action; |
| |
| final AccessPolicy.Builder builder = new AccessPolicy.Builder() |
| .identifierGenerateFromSeed(uuidSeed) |
| .resource(resource) |
| .addUser(userIdentifier); |
| |
| if (action.equals(READ_CODE)) { |
| builder.action(RequestAction.READ); |
| } else if (action.equals(WRITE_CODE)) { |
| builder.action(RequestAction.WRITE); |
| } else if (action.equals(DELETE_CODE)) { |
| builder.action(RequestAction.DELETE); |
| } else { |
| throw new IllegalStateException("Unknown Policy Action: " + action); |
| } |
| |
| final AccessPolicy accessPolicy = builder.build(); |
| final Policy jaxbPolicy = createJAXBPolicy(accessPolicy); |
| authorizations.getPolicies().getPolicy().add(jaxbPolicy); |
| } else { |
| // otherwise add the user to the existing policy |
| Policy.User policyUser = new Policy.User(); |
| policyUser.setIdentifier(userIdentifier); |
| foundPolicy.getUser().add(policyUser); |
| } |
| } |
| |
| private Policy createJAXBPolicy(final AccessPolicy accessPolicy) { |
| final Policy policy = new Policy(); |
| policy.setIdentifier(accessPolicy.getIdentifier()); |
| policy.setResource(accessPolicy.getResource()); |
| |
| switch (accessPolicy.getAction()) { |
| case READ: |
| policy.setAction(READ_CODE); |
| break; |
| case WRITE: |
| policy.setAction(WRITE_CODE); |
| break; |
| case DELETE: |
| policy.setAction(DELETE_CODE); |
| break; |
| default: |
| break; |
| } |
| |
| transferUsersAndGroups(accessPolicy, policy); |
| return policy; |
| } |
| |
| /** |
| * 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 transferUsersAndGroups(AccessPolicy accessPolicy, Policy policy) { |
| // 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); |
| } |
| } |
| |
| /** |
| * Adds the given user identifier to the policy if it doesn't already exist. |
| * |
| * @param userIdentifier a user identifier |
| * @param policy a policy to add the user to |
| */ |
| private void addUserToPolicy(final String userIdentifier, final Policy policy) { |
| // determine if the user already exists in the policy |
| boolean userExists = false; |
| for (Policy.User policyUser : policy.getUser()) { |
| if (policyUser.getIdentifier().equals(userIdentifier)) { |
| userExists = true; |
| break; |
| } |
| } |
| |
| // add the user to the policy if doesn't already exist |
| if (!userExists) { |
| Policy.User policyUser = new Policy.User(); |
| policyUser.setIdentifier(userIdentifier); |
| policy.getUser().add(policyUser); |
| } |
| } |
| |
| /** |
| * Adds the given group identifier to the policy if it doesn't already exist. |
| * |
| * @param groupIdentifier a group identifier |
| * @param policy a policy to add the user to |
| */ |
| private void addGroupToPolicy(final String groupIdentifier, final Policy policy) { |
| // determine if the group already exists in the policy |
| boolean groupExists = false; |
| for (Policy.Group policyGroup : policy.getGroup()) { |
| if (policyGroup.getIdentifier().equals(groupIdentifier)) { |
| groupExists = true; |
| break; |
| } |
| } |
| |
| // add the group to the policy if doesn't already exist |
| if (!groupExists) { |
| Policy.Group policyGroup = new Policy.Group(); |
| policyGroup.setIdentifier(groupIdentifier); |
| policy.getGroup().add(policyGroup); |
| } |
| } |
| |
| /** |
| * Finds the Policy matching the resource and action, or creates a new one and adds it to the list of policies. |
| * |
| * @param policies the policies to search through |
| * @param seedIdentity the seedIdentity to use when creating identifiers for new policies |
| * @param resource the resource for the policy |
| * @param action the action string for the police (R or RW) |
| * @return the matching policy or a new policy |
| */ |
| private Policy getOrCreatePolicy(final List<Policy> policies, final String seedIdentity, final String resource, final String action) { |
| Policy foundPolicy = null; |
| |
| // try to find a policy with the same resource and actions |
| for (Policy policy : policies) { |
| if (policy.getResource().equals(resource) && policy.getAction().equals(action)) { |
| foundPolicy = policy; |
| break; |
| } |
| } |
| |
| // if a matching policy wasn't found then create one |
| if (foundPolicy == null) { |
| final String uuidSeed = resource + action + seedIdentity; |
| final String policyIdentifier = IdentifierUtil.getIdentifier(uuidSeed); |
| |
| foundPolicy = new Policy(); |
| foundPolicy.setIdentifier(policyIdentifier); |
| foundPolicy.setResource(resource); |
| foundPolicy.setAction(action); |
| |
| policies.add(foundPolicy); |
| } |
| |
| return foundPolicy; |
| } |
| |
| /** |
| * 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 authorizations the authorizations to save and populate from |
| * @throws AuthorizationAccessException if an error occurs saving the authorizations |
| */ |
| private synchronized void saveAndRefreshHolder(final Authorizations authorizations) throws AuthorizationAccessException { |
| try { |
| saveAuthorizations(authorizations); |
| |
| this.authorizationsHolder.set(new AuthorizationsHolder(authorizations)); |
| } catch (JAXBException e) { |
| throw new AuthorizationAccessException("Unable to save Authorizations", e); |
| } |
| } |
| |
| @Override |
| public void preDestruction() throws SecurityProviderDestructionException { |
| } |
| |
| private static class ResourceActionPair { |
| public String resource; |
| public String actionCode; |
| public ResourceActionPair(String resource, String actionCode) { |
| this.resource = resource; |
| this.actionCode = actionCode; |
| } |
| } |
| } |