Github user bbende commented on a diff in the pull request: https://github.com/apache/nifi-registry/pull/14#discussion_r142427496 --- Diff: nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java --- @@ -0,0 +1,791 @@ +/* + * 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.authorization; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.authorization.generated.Authorizers; +import org.apache.nifi.registry.authorization.generated.Prop; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.provider.StandardProviderFactory; +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.Unmarshaller; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This implementation of AuthorizerFactory in NiFi Registry is based on a combination of + * NiFi's AuthorizerFactory and AuthorizerFactoryBean. + */ +public class StandardAuthorizerFactory implements AuthorizerFactory, UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup{ + + private static final Logger logger = LoggerFactory.getLogger(StandardProviderFactory.class); + + private static final String AUTHORIZERS_XSD = "/authorizers.xsd"; + private static final String JAXB_GENERATED_PATH = "org.apache.nifi.registry.authorization.generated"; + private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); + + /** + * Load the JAXBContext. + */ + private static JAXBContext initializeJaxbContext() { + try { + return JAXBContext.newInstance(JAXB_GENERATED_PATH, StandardAuthorizerFactory.class.getClassLoader()); + } catch (JAXBException e) { + throw new RuntimeException("Unable to create JAXBContext.", e); + } + } + + private final NiFiRegistryProperties properties; + private Authorizer authorizer; + private final Map<String, UserGroupProvider> userGroupProviders = new HashMap<>(); + private final Map<String, AccessPolicyProvider> accessPolicyProviders = new HashMap<>(); + private final Map<String, Authorizer> authorizers = new HashMap<>(); + + public StandardAuthorizerFactory(final NiFiRegistryProperties properties) { + this.properties = properties; + + if (this.properties == null) { + throw new IllegalStateException("NiFiRegistryProperties cannot be null"); + } + } + + /***** UserGroupProviderLookup *****/ + + @Override + public UserGroupProvider getUserGroupProvider(String identifier) { + return userGroupProviders.get(identifier); + } + + /***** AccessPolicyProviderLookup *****/ + + @Override + public AccessPolicyProvider getAccessPolicyProvider(String identifier) { + return accessPolicyProviders.get(identifier); + } + + + /***** AuthorizerLookup *****/ + + @Override + public Authorizer getAuthorizer(String identifier) { + return authorizers.get(identifier); + } + + + /***** AuthorizerFactory *****/ + + @Override + public void initialize() throws AuthorizerFactoryException { +// if (authorizerHolder.get() == null) { +// final File authorizersConfigFile = properties.getAuthorizersConfigurationFile(); +// if (authorizersConfigFile.exists()) { +// try { +// // find the schema +// final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); +// final Schema schema = schemaFactory.newSchema(StandardProviderFactory.class.getResource(AUTHORIZERS_XSD)); +// +// // attempt to unmarshal +// final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); +// unmarshaller.setSchema(schema); +// +// // set the holder for later use +// final JAXBElement<Authorizers> element = unmarshaller.unmarshal(new StreamSource(authorizersConfigFile), Authorizers.class); +// authorizerHolder.set(element.getValue()); +// } catch (SAXException | JAXBException e) { +// throw new AuthorizerFactoryException("Unable to load the authorizer configuration file at: " + authorizersConfigFile.getAbsolutePath(), e); +// } +// } else { +// throw new AuthorizerFactoryException("Unable to find the providers configuration file at " + authorizersConfigFile.getAbsolutePath()); +// } +// } + } + + @Override + public Authorizer getAuthorizer() throws AuthorizerFactoryException { + if (authorizer == null) { + if (properties.getSslPort() == null) { + // use a default authorizer... only allowable when running not securely + authorizer = createDefaultAuthorizer(); + } else { + // look up the authorizer to use + final String authorizerIdentifier = properties.getProperty(NiFiRegistryProperties.SECURITY_USER_AUTHORIZER); + + // ensure the authorizer class name was specified + if (StringUtils.isBlank(authorizerIdentifier)) { + throw new AuthorizerFactoryException("When running securely, the authorizer identifier must be specified in the nifi properties file."); + } else { + + try { + final Authorizers authorizerConfiguration = loadAuthorizersConfiguration(); + + // create each user group provider + for (final org.apache.nifi.registry.authorization.generated.UserGroupProvider userGroupProvider : authorizerConfiguration.getUserGroupProvider()) { + userGroupProviders.put(userGroupProvider.getIdentifier(), createUserGroupProvider(userGroupProvider.getIdentifier(), userGroupProvider.getClazz())); + } + + // configure each user group provider + for (final org.apache.nifi.registry.authorization.generated.UserGroupProvider provider : authorizerConfiguration.getUserGroupProvider()) { + final UserGroupProvider instance = userGroupProviders.get(provider.getIdentifier()); + instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); + } + + // create each access policy provider + for (final org.apache.nifi.registry.authorization.generated.AccessPolicyProvider accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) { + accessPolicyProviders.put(accessPolicyProvider.getIdentifier(), createAccessPolicyProvider(accessPolicyProvider.getIdentifier(), accessPolicyProvider.getClazz())); + } + + // configure each access policy provider + for (final org.apache.nifi.registry.authorization.generated.AccessPolicyProvider provider : authorizerConfiguration.getAccessPolicyProvider()) { + final AccessPolicyProvider instance = accessPolicyProviders.get(provider.getIdentifier()); + instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); + } + + // create each authorizer + for (final org.apache.nifi.registry.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) { + authorizers.put(authorizer.getIdentifier(), createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz(), authorizer.getClasspath())); + } + + // configure each authorizer + for (final org.apache.nifi.registry.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) { + final Authorizer instance = authorizers.get(provider.getIdentifier()); + instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); + } + + // get the authorizer instance + authorizer = getAuthorizer(authorizerIdentifier); + + // ensure it was found + if (authorizer == null) { + throw new AuthorizerFactoryException(String.format("The specified authorizer '%s' could not be found.", authorizerIdentifier)); + } + } catch (Exception e) { + throw new AuthorizerFactoryException("Failed to construct Authorizer.", e); + } + } + } + } + return authorizer; + } + + private Authorizers loadAuthorizersConfiguration() throws Exception { + final File authorizersConfigurationFile = properties.getAuthorizersConfigurationFile(); + + // load the authorizers from the specified file + if (authorizersConfigurationFile.exists()) { + try { + // find the schema + final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + final Schema schema = schemaFactory.newSchema(Authorizers.class.getResource(AUTHORIZERS_XSD)); + + // attempt to unmarshal + final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); + unmarshaller.setSchema(schema); + final JAXBElement<Authorizers> element = unmarshaller.unmarshal(new StreamSource(authorizersConfigurationFile), Authorizers.class); + return element.getValue(); + } catch (SAXException | JAXBException e) { + throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e); + } + } else { + throw new Exception("Unable to find the authorizer configuration file at " + authorizersConfigurationFile.getAbsolutePath()); + } + } + + private AuthorizerConfigurationContext loadAuthorizerConfiguration(final String identifier, final List<Prop> properties) { + final Map<String, String> authorizerProperties = new HashMap<>(); + + for (final Prop property : properties) { + authorizerProperties.put(property.getName(), property.getValue()); + } + return new StandardAuthorizerConfigurationContext(identifier, authorizerProperties); + } + + private UserGroupProvider createUserGroupProvider(final String identifier, final String userGroupProviderClassName) throws Exception { + + final UserGroupProvider instance; + + // attempt to load the class + Class<?> rawUserGroupProviderClass = Class.forName(userGroupProviderClassName); + Class<? extends UserGroupProvider> userGroupProviderClass = rawUserGroupProviderClass.asSubclass(UserGroupProvider.class); + + // otherwise create a new instance + Constructor constructor = userGroupProviderClass.getConstructor(); + instance = (UserGroupProvider) constructor.newInstance(); + + // method injection + performMethodInjection(instance, userGroupProviderClass); + + // field injection + performFieldInjection(instance, userGroupProviderClass); + + // call post construction lifecycle event + instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this)); + + return instance; + } + + private AccessPolicyProvider createAccessPolicyProvider(final String identifier, final String accessPolicyProviderClassName) throws Exception { + final AccessPolicyProvider instance; + + // attempt to load the class + Class<?> rawAccessPolicyProviderClass = Class.forName(accessPolicyProviderClassName); + Class<? extends AccessPolicyProvider> accessPolicyClass = rawAccessPolicyProviderClass.asSubclass(AccessPolicyProvider.class); + + // otherwise create a new instance + Constructor constructor = accessPolicyClass.getConstructor(); + instance = (AccessPolicyProvider) constructor.newInstance(); + + // method injection + performMethodInjection(instance, accessPolicyClass); + + // field injection + performFieldInjection(instance, accessPolicyClass); + + // call post construction lifecycle event + instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this)); + + return instance; + } + + private Authorizer createAuthorizer(final String identifier, final String authorizerClassName, final String classpathResources) throws Exception { + final Authorizer instance; + // attempt to load the class + Class<?> rawAuthorizerClass = Class.forName(authorizerClassName); + Class<? extends Authorizer> authorizerClass = rawAuthorizerClass.asSubclass(Authorizer.class); + + // otherwise create a new instance + Constructor constructor = authorizerClass.getConstructor(); + instance = (Authorizer) constructor.newInstance(); + + // method injection + performMethodInjection(instance, authorizerClass); + + // field injection + performFieldInjection(instance, authorizerClass); + + // call post construction lifecycle event + instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this)); + +// if (StringUtils.isNotEmpty(classpathResources)) { --- End diff -- Should be fine to leave this out for now, but we may need to revisit this if/when we add support for Ranger... this was done so someone can configure additional libraries to be on the classpath of the authorizer, specifically for Ranger to know where the Hadoop client JARs were if wanting to send audit logs to HDFS.
---