NIFI-1553:

- Implementing a file based authorizer.
- Providing an example authorizations files.
- Address comments from PR.
- This closes #330
This commit is contained in:
Matt Gilman 2016-04-07 16:11:07 -04:00
parent 3f4ac3156c
commit 5de40ccec3
13 changed files with 735 additions and 6 deletions

View File

@ -40,8 +40,8 @@ public class AuthorizationRequest {
this.resource = builder.resource;
this.identity = builder.identity;
this.action = builder.action;
this.context = Collections.unmodifiableMap(builder.context);
this.eventAttributes = Collections.unmodifiableMap(builder.eventAttributes);
this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context);
this.eventAttributes = builder.context == null ? null : Collections.unmodifiableMap(builder.eventAttributes);
}
/**

View File

@ -21,7 +21,7 @@ package org.apache.nifi.authorization;
*/
public class AuthorizationResult {
private enum Result {
public enum Result {
Approved,
Denied,
ResourceNotFound

View File

@ -16,6 +16,8 @@
*/
package org.apache.nifi.authorization;
import org.apache.nifi.components.PropertyValue;
import java.util.Map;
/**
@ -44,5 +46,5 @@ public interface AuthorizerConfigurationContext {
* PropertyDescriptor. This method does not substitute default
* PropertyDescriptor values, so the value returned will be null if not set
*/
String getProperty(String property);
PropertyValue getProperty(String property);
}

View File

@ -116,5 +116,9 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-expression-language</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -16,6 +16,9 @@
*/
package org.apache.nifi.authorization;
import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
import org.apache.nifi.components.PropertyValue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -44,8 +47,8 @@ public class StandardAuthorizerConfigurationContext implements AuthorizerConfigu
}
@Override
public String getProperty(String property) {
return properties.get(property);
public PropertyValue getProperty(String property) {
return new StandardPropertyValue(properties.get(property), null);
}
}

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-file-authorizer</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>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<packageName>org.apache.nifi.authorization.generated</packageName>
</configuration>
</execution>
</executions>
<configuration>
<outputDirectory>${project.build.directory}/generated-sources/jaxb</outputDirectory>
</configuration>
</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>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-authorization</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-expression-language</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,279 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.authorization;
import 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.ProviderCreationException;
import org.apache.nifi.authorization.generated.Authorization;
import org.apache.nifi.authorization.generated.Resource;
import org.apache.nifi.authorization.generated.Resources;
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.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.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
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.concurrent.atomic.AtomicReference;
/**
* Provides identity checks and grants authorities.
*/
public class FileAuthorizer implements Authorizer {
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.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
/**
* Load the JAXBContext.
*/
private static JAXBContext initializeJaxbContext() {
try {
return JAXBContext.newInstance(JAXB_GENERATED_PATH, FileAuthorizer.class.getClassLoader());
} catch (JAXBException e) {
throw new RuntimeException("Unable to create JAXBContext.");
}
}
private NiFiProperties properties;
private File authorizationsFile;
private File restoreAuthorizationsFile;
private SynchronousFileWatcher fileWatcher;
private ScheduledExecutorService fileWatcherExecutorService;
private final AtomicReference<Map<String, Map<String, Set<RequestAction>>>> authorizations = new AtomicReference<>();
@Override
public void initialize(final AuthorizerInitializationContext initializationContext) throws ProviderCreationException {
}
@Override
public void onConfigured(final AuthorizerConfigurationContext configurationContext) throws ProviderCreationException {
try {
final PropertyValue authorizationsPath = configurationContext.getProperty("Authorizations File");
if (StringUtils.isBlank(authorizationsPath.getValue())) {
throw new ProviderCreationException("The authorizations file must be specified.");
}
// get the authorizations file and ensure it exists
authorizationsFile = new File(authorizationsPath.getValue());
if (!authorizationsFile.exists()) {
throw new ProviderCreationException("The authorizations file must exist.");
}
final File authorizationsFileDirectory = authorizationsFile.getAbsoluteFile().getParentFile();
// the restore directory is optional and may be null
final File restoreDirectory = properties.getRestoreDirectory();
if (restoreDirectory != null) {
// sanity check that restore directory is a directory, creating it if necessary
FileUtils.ensureDirectoryExistAndCanAccess(restoreDirectory);
// check that restore directory is not the same as the primary directory
if (authorizationsFileDirectory.getAbsolutePath().equals(restoreDirectory.getAbsolutePath())) {
throw new ProviderCreationException(String.format("Authorizations file directory '%s' is the same as restore directory '%s' ",
authorizationsFileDirectory.getAbsolutePath(), restoreDirectory.getAbsolutePath()));
}
// the restore copy will have same file name, but reside in a different directory
restoreAuthorizationsFile = new File(restoreDirectory, authorizationsFile.getName());
try {
// sync the primary copy with the restore copy
FileUtils.syncWithRestore(authorizationsFile, restoreAuthorizationsFile, logger);
} catch (final IOException | IllegalStateException ioe) {
throw new ProviderCreationException(ioe);
}
}
final PropertyValue rawReloadInterval = configurationContext.getProperty("Reload Interval");
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;
}
// reload the authorizations
reload();
// 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 | ProviderCreationException | SAXException | JAXBException | IllegalStateException e) {
throw new ProviderCreationException(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();
// get the authorizations for the requested resources
final Map<String, Set<RequestAction>> resourceAuthorizations = currentAuthorizations.get(requestedResource.getIdentifier());
// ensure the resource has authorizations
if (resourceAuthorizations == null) {
return AuthorizationResult.resourceNotFound();
}
// get the user authorizations
final Set<RequestAction> userAuthorizations = resourceAuthorizations.get(request.getIdentity());
// ensure the user has authorizations
if (userAuthorizations == null) {
return AuthorizationResult.denied();
}
// ensure the appropriate response
if (userAuthorizations.contains(request.getAction())) {
return AuthorizationResult.approved();
} else {
return AuthorizationResult.denied();
}
}
/**
* Reloads the authorized users file.
*
* @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
*/
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));
// 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();
// new authorizations
final Map<String, Map<String, Set<RequestAction>>> newAuthorizations = new HashMap<>();
// load the new authorizations
for (final Resource authorizedResource : resources.getResource()) {
final String identifier = authorizedResource.getIdentifier();
// ensure the entry exists
if (!newAuthorizations.containsKey(identifier)) {
newAuthorizations.put(identifier, new HashMap<String, Set<RequestAction>>());
}
// go through each authorization
for (final Authorization authorization : authorizedResource.getAuthorization()) {
final String identity = authorization.getIdentity();
// get the authorizations for this resource
final Map<String, Set<RequestAction>> resourceAuthorizations = newAuthorizations.get(identifier);
// ensure the entry exists
if (!resourceAuthorizations.containsKey(identity)) {
resourceAuthorizations.put(identity, EnumSet.noneOf(RequestAction.class));
}
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);
}
if (authorizationCode.contains(WRITE_CODE)) {
authorizedActions.add(RequestAction.WRITE);
}
}
}
// set the new authorizations
authorizations.set(newAuthorizations);
// if we've copied a the authorizations file to a restore directory synchronize it
if (restoreAuthorizationsFile != null) {
FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger);
}
logger.info(String.format("Authorizations file loaded at %s", new Date().toString()));
}
@AuthorizerContext
public void setNiFiProperties(NiFiProperties properties) {
this.properties = properties;
}
@Override
public void preDestruction() {
if (fileWatcherExecutorService != null) {
fileWatcherExecutorService.shutdown();
}
}
}

View File

@ -0,0 +1,15 @@
# 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.
org.apache.nifi.authorization.FileAuthorizer

View File

@ -0,0 +1,61 @@
<?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">
<!-- authorization -->
<xs:complexType name="Authorization">
<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:attribute name="action">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="R"/>
<xs:enumeration value="W"/>
<xs:enumeration value="RW"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- resource -->
<xs:complexType name="Resource">
<xs:sequence>
<xs:element name="authorization" type="Authorization" minOccurs="0" maxOccurs="unbounded"/>
</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:complexType>
<!-- resources -->
<xs:element name="resources">
<xs:complexType>
<xs:sequence>
<xs:element name="resource" type="Resource" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,198 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.authorization;
import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
import org.apache.nifi.authorization.AuthorizationResult.Result;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.authorization.resource.ResourceFactory;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class FileAuthorizerTest {
private static final String EMPTY_AUTHORIZATIONS_CONCISE =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources/>";
private static final String EMPTY_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources>"
+ "</resources>";
private static final String BAD_SCHEMA_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resource>"
+ "</resource>";
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>";
private static final String UPDATED_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources>"
+ "<resource identifier=\"/flow\">"
+ "<authorization identity=\"user-1\" action=\"RW\"/>"
+ "</resource>"
+ "</resources>";
private FileAuthorizer authorizer;
private File primary;
private File restore;
private AuthorizerConfigurationContext configurationContext;
@Before
public void setup() throws IOException {
// primary authorizations
primary = new File("target/primary/authorizations.xml");
FileUtils.ensureDirectoryExistAndCanAccess(primary.getParentFile());
// restore authorizations
restore = new File("target/restore/authorizations.xml");
FileUtils.ensureDirectoryExistAndCanAccess(restore.getParentFile());
final NiFiProperties properties = mock(NiFiProperties.class);
when(properties.getRestoreDirectory()).thenReturn(restore.getParentFile());
configurationContext = mock(AuthorizerConfigurationContext.class);
when(configurationContext.getProperty(Mockito.eq("Authorizations File"))).thenReturn(new StandardPropertyValue(primary.getPath(), null));
authorizer = new FileAuthorizer();
authorizer.setNiFiProperties(properties);
authorizer.initialize(null);
}
@After
public void cleanup() throws Exception {
deleteFile(primary);
deleteFile(restore);
}
@Test
public void testPostConstructionWhenRestoreDoesNotExist() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
assertEquals(primary.length(), restore.length());
}
@Test(expected = ProviderCreationException.class)
public void testPostConstructionWhenPrimaryDoesNotExist() throws Exception {
writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
}
@Test(expected = ProviderCreationException.class)
public void testPostConstructionWhenPrimaryDifferentThanRestore() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
}
@Test(expected = ProviderCreationException.class)
public void testBadSchema() throws Exception {
writeAuthorizationsFile(primary, BAD_SCHEMA_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
}
@Test
public void testAuthorizedUserAction() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request = new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").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);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request = new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-2").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);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request = new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").action(RequestAction.WRITE).build();
final AuthorizationResult result = authorizer.authorize(request);
assertFalse(Result.Approved.equals(result.getResult()));
}
@Test
public void testReloadAuthorizations() 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").action(RequestAction.WRITE).build();
AuthorizationResult result = authorizer.authorize(request);
assertFalse(Result.Approved.equals(result.getResult()));
// add write access for the user
writeAuthorizationsFile(primary, UPDATED_AUTHORIZATIONS);
// wait at least one second for the file to be stale
Thread.sleep(4000L);
// ensure the user does have write access now using the same request
result = authorizer.authorize(request);
assertTrue(Result.Approved.equals(result.getResult()));
}
private static void writeAuthorizationsFile(final File file, final String content) throws Exception {
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
try (final FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
}
}
private static boolean deleteFile(final File file) {
if (file.isDirectory()) {
FileUtils.deleteFilesInDir(file, null, null, true, true);
}
return FileUtils.deleteFile(file, null, 10);
}
}

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
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.
-->
<!--
This file lists all authorizations for this NiFi instance. Refer to the properties file and authorizers.xml for configuration details.
Available resources:
/flow - READ - allows user/entity to load the UI and see the flow structure
- WRITE - NA
/resource - READ - allows user/entity to retrieve the available resources
- WRITE - NA
/system - READ - allows user/entity to retrieve system level diagnostics (CPU load, disk utilization, etc)
- WRITE - NA
/controller - READ - allows user/entity to retrieve configuration details for the controller (controller bulletins, thread pool, reporting tasks, etc)
- WRITE - allows user/entity to modify configuration details for the controller
/provenance - READ - allows user/entity to perform provenance requests. results will be filtered based on access to provenance data per component
- WRITE - NA
/token - READ - NA
- WRITE - allows user/entity to create a token for access the REST API
/site-to-site - READ - allows user/entity to retrieve configuration details for performing site to site data transfers with this NiFi
- WRITE - NA
/proxy - READ - NA
- WRITE - allows user/entity to create a proxy request on behalf of another user
/process-groups/{id} - READ - allows user/entity to retrieve configuration details for the process group and all descendant components without explicit access policies
- WRITE - allows user/entity to create/update/delete configuration details for the process group and all descendant components without explicit access policies
/processors/{id} - READ - allows user/entity to retrieve configuration details for the processor overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the processor overriding any inherited authorizations from an ancestor process group
/input-ports/{id} - READ - allows user/entity to retrieve configuration details for the input port overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the input port overriding any inherited authorizations from an ancestor process group
/output-ports/{id} - READ - allows user/entity to retrieve configuration details for the output port overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the output port overriding any inherited authorizations from an ancestor process group
/labels/{id} - READ - allows user/entity to retrieve configuration details for the label overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group
/connections/{id} - READ - allows user/entity to retrieve configuration details for the connection overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group
/remote-process-groups/{id} - READ - allows user/entity to retrieve configuration details for the remote process group overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the remote process group overriding any inherited authorizations from an ancestor process group
/templates/{id} - READ - allows user/entity to retrieve configuration details for the template overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to create/update/delete the template overriding any inherited authorizations from an ancestor process group
/controller-services/{id} - READ - allows user/entity to retrieve configuration details for the controller service overriding any inherited authorizations from an ancestor process group
- WRITE - allows user/entity to update/delete the controller service overriding any inherited authorizations from an ancestor process group
/reporting-tasks/{id} - READ - allows user/entity to retrieve configuration details for the reporting tasks overriding any inherited authorizations from the controller
- WRITE - allows user/entity to create/update/delete the reporting tasks overriding any inherited authorizations from the controller
/{type}/{id}/provenance - READ - allows user/entity to view provenance data from the underlying component
- WRITE - NA
-->
<resources>
<!--
<resource identifier="/flow">
<authorization identity="user-identity-1" action="R"></authorization>
<authorization identity="user-identity-2" action="W"></authorization>
<authorization identity="user-identity-3" action="RW"></authorization>
</resource>
-->
</resources>

View File

@ -37,6 +37,7 @@
<module>nifi-cluster-authorization-provider</module>
<module>nifi-user-actions</module>
<module>nifi-framework-authorization</module>
<module>nifi-file-authorizer</module>
<module>nifi-administration</module>
<module>nifi-web</module>
<module>nifi-resources</module>

View File

@ -88,6 +88,11 @@
<artifactId>nifi-framework-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-authorization</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-user-actions</artifactId>