NIFI-655:

- Initial commit of the LDAP based identity providers.
- Fixed issue when attempting to log into a NiFi that does not support new account requests.
This commit is contained in:
Matt Gilman 2015-11-11 19:40:40 -05:00
parent 0281e2773f
commit cfee612a78
14 changed files with 532 additions and 28 deletions

View File

@ -172,6 +172,11 @@ language governing permissions and limitations under the License. -->
<artifactId>nifi-flume-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ldap-iaa-providers-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dbcp-service-nar</artifactId>

View File

@ -1071,7 +1071,7 @@ nf.Canvas = (function () {
deferred.resolve();
}).fail(function (xhr, status, error) {
// there is no anonymous access and we don't know this user - open the login page which handles login/registration/etc
if (xhr.status === 401) {
if (xhr.status === 401 || xhr.status === 403) {
window.location = '/nifi/login';
} else {
deferred.reject(xhr, status, error);

View File

@ -329,6 +329,29 @@ nf.Login = (function () {
logout();
}
// no token granted, user needs to login with their credentials
needsLogin = true;
}).always(function () {
deferred.resolve();
});
} else if (xhr.status === 403) {
// attempt to get a token for the current user without passing login credentials
token.done(function () {
showMessage = true;
// the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate
$('#login-message-title').text('Access Denied');
if ($.trim(xhr.responseText) === '') {
$('#login-message').text('Unable to authorize you to use this NiFi and anonymous access is disabled.');
} else {
$('#login-message').text(xhr.responseText);
}
}).fail(function (tokenXhr) {
if (tokenXhr.status === 400) {
// no credentials supplied so 400 must be due to an invalid/expired token
logout();
}
// no token granted, user needs to login with their credentials
needsLogin = true;
}).always(function () {

View File

@ -0,0 +1,32 @@
<?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-ldap-iaa-providers-bundle</artifactId>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-ldap-iaa-providers-nar</artifactId>
<packaging>nar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ldap-iaa-providers</artifactId>
</dependency>
</dependencies>
<name>nifi-ldap-iaa-providers-nar</name>
</project>

View File

@ -0,0 +1,56 @@
<?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-ldap-iaa-providers-bundle</artifactId>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-ldap-iaa-providers</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<name>nifi-ldap-iaa-providers</name>
</project>

View File

@ -0,0 +1,80 @@
/*
* 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.ldap;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authentication.LoginCredentials;
import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
import org.apache.nifi.authentication.exception.IdentityAccessException;
import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.authorization.exception.ProviderDestructionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
/**
* Abstract LDAP based implementation of a login identity provider.
*/
public abstract class AbstractLdapProvider implements LoginIdentityProvider {
private static final Logger logger = LoggerFactory.getLogger(AbstractLdapProvider.class);
private AbstractLdapAuthenticationProvider provider;
@Override
public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
}
@Override
public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
System.out.println(Thread.currentThread().getContextClassLoader());
provider = getLdapAuthenticationProvider(configurationContext);
}
protected abstract AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException;
@Override
public final void authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
if (provider == null) {
throw new IdentityAccessException("The LDAP authentication provider is not initialized.");
}
try {
final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
provider.authenticate(token);
} catch (final AuthenticationServiceException ase) {
logger.error(ase.getMessage());
if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, ase);
}
throw new IdentityAccessException("Unable to query the configured directory server. See the logs for additional details.", ase);
} catch (final BadCredentialsException bce) {
throw new InvalidLoginCredentialsException(bce.getMessage(), bce);
}
}
@Override
public final void preDestruction() throws ProviderDestructionException {
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.ldap;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
/**
* Active Directory based implementation of a login identity provider.
*/
public class ActiveDirectoryProvider extends AbstractLdapProvider {
@Override
protected AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
final String domain = configurationContext.getProperty("Domain");
if (StringUtils.isBlank(domain)) {
throw new ProviderCreationException("The Active Directory Domain must be specified.");
}
final String url = configurationContext.getProperty("Url");
if (StringUtils.isBlank(url)) {
throw new ProviderCreationException("The Active Directory Url must be specified.");
}
final String rootDn = configurationContext.getProperty("Root DN");
return new ActiveDirectoryLdapAuthenticationProvider(domain, url, StringUtils.isBlank(rootDn) ? null : rootDn);
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.ldap;
/**
*
*/
public enum LdapAuthenticationStrategy {
ANONYMOUS,
SIMPLE,
DIGEST_MD5,
TLS
}

View File

@ -0,0 +1,151 @@
/*
* 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.ldap;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
import org.apache.nifi.authorization.exception.ProviderCreationException;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.SslContextFactory.ClientAuth;
import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy;
import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy;
import org.springframework.ldap.core.support.DigestMd5DirContextAuthenticationStrategy;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch;
/**
* LDAP based implementation of a login identity provider.
*/
public class LdapProvider extends AbstractLdapProvider {
@Override
protected AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
final LdapContextSource context = new LdapContextSource();
final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy");
final LdapAuthenticationStrategy authenticationStrategy;
try {
authenticationStrategy = LdapAuthenticationStrategy.valueOf(rawAuthenticationStrategy);
} catch (final IllegalArgumentException iae) {
throw new ProviderCreationException(String.format("Unrecgonized authentication strategy '%s'", rawAuthenticationStrategy));
}
switch (authenticationStrategy) {
case ANONYMOUS:
context.setAnonymousReadOnly(true);
break;
default:
final String userDn = configurationContext.getProperty("Bind DN");
final String password = configurationContext.getProperty("Bind Password");
context.setUserDn(userDn);
context.setPassword(password);
switch (authenticationStrategy) {
case SIMPLE:
context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
break;
case DIGEST_MD5:
context.setAuthenticationStrategy(new DigestMd5DirContextAuthenticationStrategy());
break;
case TLS:
final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
// shutdown gracefully
final String rawShutdownGracefully = configurationContext.getProperty("TLS - Shutdown Gracefully");
if (StringUtils.isNotBlank(rawShutdownGracefully)) {
final boolean shutdownGracefully = Boolean.TRUE.toString().equalsIgnoreCase(rawShutdownGracefully);
tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully);
}
final String rawKeystore = configurationContext.getProperty("TLS - Keystore");
final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password");
final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type");
final String rawTruststore = configurationContext.getProperty("TLS - Truststore");
final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
try {
final SSLContext sslContext;
if (StringUtils.isBlank(rawKeystore)) {
sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, "TLS");
} else {
if (StringUtils.isBlank(rawTruststore)) {
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, "TLS");
} else {
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, ClientAuth.REQUIRED, "TLS");
}
}
tlsAuthenticationStrategy.setSslSocketFactory(sslContext.getSocketFactory());
} catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) {
throw new ProviderCreationException(e.getMessage(), e);
}
context.setAuthenticationStrategy(tlsAuthenticationStrategy);
break;
}
break;
}
final String url = configurationContext.getProperty("Url");
if (StringUtils.isBlank(url)) {
throw new ProviderCreationException("LDAP identity provider 'Url' must be specified.");
}
// connection
context.setUrl(url);
final String userSearchBase = configurationContext.getProperty("User Search Base");
final String userSearchFilter = configurationContext.getProperty("User Search Filter");
if (StringUtils.isBlank(userSearchBase) || StringUtils.isBlank(userSearchFilter)) {
throw new ProviderCreationException("LDAP identity provider 'User Search Base' and 'User Search Filter' must be specified.");
}
// query
final LdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, context);
// bind vs password?
final BindAuthenticator authenticator = new BindAuthenticator(context);
authenticator.setUserSearch(userSearch);
try {
// handling initializing beans
context.afterPropertiesSet();
authenticator.afterPropertiesSet();
} catch (final Exception e) {
throw new ProviderCreationException(e.getMessage(), e);
}
// create the underlying provider
return new LdapAuthenticationProvider(authenticator);
}
}

View File

@ -0,0 +1,16 @@
# 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.ldap.LdapProvider
org.apache.nifi.ldap.ActiveDirectoryProvider

View File

@ -0,0 +1,38 @@
<?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-nar-bundles</artifactId>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
<packaging>pom</packaging>
<modules>
<module>nifi-ldap-iaa-providers</module>
<module>nifi-ldap-iaa-providers-nar</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ldap-iaa-providers</artifactId>
<version>0.3.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -46,6 +46,7 @@
<module>nifi-image-bundle</module>
<module>nifi-avro-bundle</module>
<module>nifi-couchbase-bundle</module>
<module>nifi-ldap-iaa-providers-bundle</module>
</modules>
<dependencyManagement>
<dependencies>

49
pom.xml
View File

@ -1,14 +1,14 @@
<?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. -->
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@ -91,7 +91,7 @@
<jetty.version>9.2.11.v20150529</jetty.version>
<lucene.version>4.10.4</lucene.version>
<spring.version>4.1.6.RELEASE</spring.version>
<spring.security.version>4.0.2.RELEASE</spring.security.version>
<spring.security.version>4.0.3.RELEASE</spring.security.version>
<jersey.version>1.19</jersey.version>
<hadoop.version>2.6.0</hadoop.version>
<yammer.metrics.version>2.2.0</yammer.metrics.version>
@ -464,6 +464,29 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring.security.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
@ -912,6 +935,12 @@
<version>0.3.1-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ldap-iaa-providers-nar</artifactId>
<version>0.3.1-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>