NIFI-655:

- Update admin guide with documentation for username/password authentication.
- Setting default anonymous roles to none.
- Making account status messages to users more clear.
- Deleting user keys when an admin revokes/deletes an account.
- Updating authentication filter to error back whenever authentication fails.
This commit is contained in:
Matt Gilman 2015-11-25 14:17:23 -05:00
parent 1312bde498
commit c073253366
25 changed files with 390 additions and 441 deletions

View File

@ -362,7 +362,7 @@ language governing permissions and limitations under the License. -->
<nifi.security.user.login.identity.provider></nifi.security.user.login.identity.provider>
<nifi.security.x509.principal.extractor />
<nifi.security.support.new.account.requests />
<nifi.security.anonymous.authorities>ROLE_MONITOR,ROLE_DFM,ROLE_ADMIN,ROLE_PROVENANCE,ROLE_NIFI</nifi.security.anonymous.authorities>
<nifi.security.anonymous.authorities></nifi.security.anonymous.authorities>
<nifi.security.ocsp.responder.url />
<nifi.security.ocsp.responder.certificate />

View File

@ -530,7 +530,13 @@ public class NiFiProperties extends Properties {
final String rawAnonymousAuthorities = getProperty(SECURITY_ANONYMOUS_AUTHORITIES);
if (!StringUtils.isEmpty(rawAnonymousAuthorities)) {
authorities = new HashSet<>(Arrays.asList(rawAnonymousAuthorities.split(",")));
authorities = new HashSet<>();
// parse the raw authorities and trim them
final List<String> authoritiesList = Arrays.asList(rawAnonymousAuthorities.split(","));
for (final String authority : authoritiesList) {
authorities.add(authority.trim());
}
} else {
authorities = Collections.EMPTY_SET;
}

View File

@ -146,10 +146,13 @@ NiFi provides several different configuration options for security purposes. The
attempt to connect will be provided access as the 'Anonymous' user.
|`nifi.security.truststoreType` | The type of the Truststore. Must be either `PKCS12` or `JKS`.
|`nifi.security.truststorePasswd` | The password for the Truststore.
|`nifi.security.needClientAuth` | Specifies whether or not connecting clients must authenticate themselves. If the Truststore properties are not set,
this must be `false`. Otherwise, a value of `true` indicates that users will be authenticated and must have
certificates that are trusted by the Truststore loaded into their web browsers. A value of `false` indicates
that all users should be given access as the 'Anonymous' user.
|`nifi.security.needClientAuth` | Specifies whether or not connecting clients must authenticate themselves. Specifically this property is used
by the NiFi cluster protocol. If the Truststore properties are not set, this must be `false`. Otherwise, a value
of `true` indicates that nodes in the cluster will be authenticated and must have certificates that are trusted
by the Truststores.
|`nifi.security.anonymous.authorities` | Specifies the roles that should be granted to users that connect over HTTPS anonymously. All users will
be given this level access, however if they have been granted a particular level of access by an administrator
it will take precedence if they access NiFi using a client certificate or once they have logged in.
|==================================================================================================================================================
Once the above properties have been configured, we can enable the User Interface to be accessed over HTTPS instead of HTTP. This is accomplished
@ -159,14 +162,99 @@ be accessible from all network interfaces, a value of `0.0.0.0` should be used.
NOTE: It is important when enabling HTTPS that the `nifi.web.http.port` property be unset.
Similar to `nifi.security.needClientAuth`, the web server can be configured to require certificate based client authentication for users accessing
the User Interface. In order to do this it must be configured to not support username/password authentication (see below) and not grant access to
anonymous users (see `nifi.security.anonymous.authorities` above). Either of these options will configure the web server to WANT certificate based client
authentication. This will allow it to support users with certificates and those without that may be logging in with their credentials or those accessing
anonymously. If username/password authentication and anonymous access are not configured, the web server will REQUIRE certificate based client authentication.
Now that the User Interface has been secured, we can easily secure Site-to-Site connections and inner-cluster communications, as well. This is
accomplished by setting the `nifi.remote.input.secure` and `nifi.cluster.protocol.is.secure` properties, respectively, to `true`.
User Authentication
-------------------
NiFi supports user authentication via client certificates or via username/password. Username/password authentication is performed by a 'Login Identity
Provider'. The Login Identity Provider is a pluggable mechanism for authenticating users via their username/password. Which Login Identity Provider
to use is configured in two properties in the _nifi.properties_ file.
The `nifi.login.identity.provider.configuration.file` property specifies the configuration file for Login Identity Providers.
The `nifi.security.user.login.identity.provider` property indicates which of the configured Login Identity Provider should be
used. If this property is not configured, NiFi will not support username/password authentication and will require client
certificates for authenticating users over HTTPS. By default, this property is not configured meaning that username/password must be
explicity enabled.
NiFi does not perform user authentication over HTTP. Using HTTP all users will be granted all roles.
Below is an example and description of configuring a Login Identity Provider that integrates with a Directory Server to authenticate users.
----
<provider>
<identifier>ldap-provider</identifier>
<class>org.apache.nifi.ldap.LdapProvider</class>
<property name="Authentication Strategy">START_TLS</property>
<property name="Manager DN"></property>
<property name="Manager Password"></property>
<property name="TLS - Keystore"></property>
<property name="TLS - Keystore Password"></property>
<property name="TLS - Keystore Type"></property>
<property name="TLS - Truststore"></property>
<property name="TLS - Truststore Password"></property>
<property name="TLS - Truststore Type"></property>
<property name="TLS - Client Auth"></property>
<property name="TLS - Protocol"></property>
<property name="TLS - Shutdown Gracefully"></property>
<property name="Referral Strategy">FOLLOW</property>
<property name="Connect Timeout">10 secs</property>
<property name="Read Timeout">10 secs</property>
<property name="Url"></property>
<property name="User Search Base"></property>
<property name="User Search Filter"></property>
<property name="Authentication Expiration">12 hours</property>
</provider>
----
With this configuration, username/password authentication can be enabled by referencing this provider in _nifi.properties_.
----
nifi.security.user.login.identity.provider=ldap-provider
----
[options="header,footer"]
|==================================================================================================================================================
| Property Name | Description
|`Authentication Strategy` | How the connection to the LDAP server is authenticated. Possible values are ANONYMOUS, SIMPLE, or START_TLS.
|`Manager DN` | The DN of the manager that is used to bind to the LDAP server to search for users.
|`Manager Password` | The password of the manager that is used to bind to the LDAP server to search for users.
|`TLS - Keystore` | Path to the Keystore that is used when connecting to LDAP using START_TLS.
|`TLS - Keystore Password` | Password for the Keystore that is used when connecting to LDAP using START_TLS.
|`TLS - Keystore Type` | Type of the Keystore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12).
|`TLS - Truststore` | Path to the Truststore that is used when connecting to LDAP using START_TLS.
|`TLS - Truststore Password` | Password for the Truststore that is used when connecting to LDAP using START_TLS.
|`TLS - Truststore Type` | Type of the Truststore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12).
|`TLS - Client Auth` | Client authentication policy when connecting to LDAP using START_TLS. Possible values are REQUIRED, WANT, NONE.
|`TLS - Protocol` | Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS, TLSv1.1, TLSv1.2, etc).
|`TLS - Shutdown Gracefully` | Specifies whether the TLS should be shut down gracefully before the target context is closed. Defaults to false.
|`Referral Strategy` | Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW.
|`Connect Timeout` | Duration of connect timeout. (i.e. 10 secs).
|`Read Timeout` | Duration of read timeout. (i.e. 10 secs).
|`Url` | Url of the LDAP servier (i.e. ldap://<hostname>:<port>).
|`User Search Base` | Base DN for searching for users (i.e. CN=Users,DC=example,DC=com).
|`User Search Filter` | Filter for searching for users against the 'User Search Base'. (i.e. sAMAccountName={0}). The user specified name is inserted into '{0}'.
|`Authentication Expiration` | The duration of how long the user authentication is valid for. If the user never logs out, they will be required to log back in following this duration.
|==================================================================================================================================================
Controlling Levels of Access
----------------------------
Once NiFi is configured to run securely as discussed in the previous section, it is necessary
Once NiFi is configured to run securely and an authentication mechanism is configured, it is necessary
to configure who will have access to the system and what types of access those people will have.
NiFi controls this through the user of an 'Authority Provider.' The Authority Provider is a pluggable
mechanism for providing authorizations to different users. Which Authority Provider to use is configured
@ -510,7 +598,9 @@ The first section of the _nifi.properties_ file is for the Core Properties. Thes
|nifi.flowservice.writedelay.interval|When many changes are made to the flow.xml, this property specifies how long to wait before writing out the changes, so as to batch the changes into a single write. The default value is 500 ms.
|nifi.administrative.yield.duration|If a component allows an unexpected exception to escape, it is considered a bug. As a result, the framework will pause (or administratively yield) the component for this amount of time. This is done so that the component does not use up massive amounts of system resources, since it is known to have problems in the existing state. The default value is 30 sec.
|nifi.bored.yield.duration|When a component has no work to do (i.e., is "bored"), this is the amount of time it will wait before checking to see if it has new data to work on. This way, it does not use up CPU resources by checking for new work too often. When setting this property, be aware that it could add extra latency for components that do not constantly have work to do, as once they go into this "bored" state, they will wait this amount of time before checking for more work. The default value is 10 millis.
|nifi.authority.provider.configuration.file*|This is the location of the file that specifies how user access is authenticated. The default value is ./conf/authority-providers.xml.
|nifi.authority.provider.configuration.file*|This is the location of the file that specifies how user access is authorized. The default value is ./conf/authority-providers.xml.
|nifi.login.identity.provider.configuration.file*|This is the location of the file that specifies how username/password authentication is performed. This file is
only consider if `nifi.security.user.login.identity.provider` configured with a provider identifier. The default value is ./conf/login-identity-providers.xml.
|nifi.templates.directory*|This is the location of the directory where flow templates are saved. The default value is ./conf/templates.l
|nifi.ui.banner.text|This is banner text that may be configured to display at the top of the User Interface. It is blank by default.
|nifi.ui.autorefresh.interval|The interval at which the User Interface auto-refreshes. The default value is 30 sec.
@ -698,11 +788,16 @@ Security Configuration section of this Administrator's Guide.
|nifi.security.truststore*|The full path and name of the truststore. It is blank by default.
|nifi.security.truststoreType|The truststore type. It is blank by default.
|nifi.security.truststorePasswd|The truststore password. It is blank by default.
|nifi.security.needClientAuth|This indicates whether client authentication is required. It is blank by default.
|nifi.security.needClientAuth|This indicates whether client authentication in the cluster protocol. It is blank by default.
|nifi.security.user.credential.cache.duration|The length of time to cache user credentials. The default value is 24 hours.
|nifi.security.user.authority.provider|This indicates what type of authority provider to use. The default value is file-provider, which refers to the file
configured in the core property nifi.authority.provider.configuration.file. Another authority provider may be used, such as when the NiFi instance is part of a cluster. But the default value of file-provider is fine for a standalone instance of NiFi.
configured in the core property `nifi.authority.provider.configuration.file`. Another authority provider may be used, such as when the NiFi instance is part of a cluster. But the default value of file-provider is fine for a standalone instance of NiFi.
|nifi.security.user.login.identity.provider|This indicates what type of login identity provider to use. The default value is blank, can be set to the identifier from a provider
in the file specified in `nifi.login.identity.provider.configuration.file`. Setting this property will trigger NiFi to support username/password authentication.
|nifi.security.support.new.account.requests|This indicates whether a secure NiFi is configured to allow users to request access. It is blank by default.
|nifi.security.anonymous.authorities|This indicates what roles to grant to anonymous users accessing NiFi over HTTPS. It is blank by default, but could be
set to any combination of ROLE_MONITOR, ROLE_DFM, ROLE_ADMIN, ROLE_PROVENANCE, ROLE_NIFI. Leaving this property blank will require that users accessing NiFi
over HTTPS be authenticated either using a client certificate or their credentials against the configured log identity provider.
|nifi.security.ocsp.responder.url|This is the URL for the Online Certificate Status Protocol (OCSP) responder if one is being used. It is blank by default.
|nifi.security.ocsp.responder.certificate|This is the location of the OCSP responder certificate if one is being used. It is blank by default.
|====

View File

@ -1,154 +0,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.
*/
package org.apache.nifi.admin;
import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.lang3.StringUtils;
import org.h2.jdbcx.JdbcConnectionPool;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
/**
*
*/
public class KeyDataSourceFactoryBean implements FactoryBean {
private static final Logger logger = LoggerFactory.getLogger(KeyDataSourceFactoryBean.class);
private static final String NF_USERNAME_PASSWORD = "nf";
private static final int MAX_CONNECTIONS = 5;
// database file name
private static final String KEY_DATABASE_FILE_NAME = "nifi-key";
// ----------
// keys table
// ----------
private static final String CREATE_KEY_TABLE = "CREATE TABLE KEY ("
+ "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
+ "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
+ "KEY VARCHAR2(100) NOT NULL"
+ ")";
private JdbcConnectionPool connectionPool;
private NiFiProperties properties;
@Override
public Object getObject() throws Exception {
if (connectionPool == null) {
// locate the repository directory
String repositoryDirectoryPath = properties.getProperty(NiFiProperties.REPOSITORY_DATABASE_DIRECTORY);
// ensure the repository directory is specified
if (repositoryDirectoryPath == null) {
throw new NullPointerException("Database directory must be specified.");
}
// create a handle to the repository directory
File repositoryDirectory = new File(repositoryDirectoryPath);
// get a handle to the database file
File databaseFile = new File(repositoryDirectory, KEY_DATABASE_FILE_NAME);
// format the database url
String databaseUrl = "jdbc:h2:" + databaseFile + ";AUTOCOMMIT=OFF;DB_CLOSE_ON_EXIT=FALSE;LOCK_MODE=3";
String databaseUrlAppend = properties.getProperty(NiFiProperties.H2_URL_APPEND);
if (StringUtils.isNotBlank(databaseUrlAppend)) {
databaseUrl += databaseUrlAppend;
}
// create the pool
connectionPool = JdbcConnectionPool.create(databaseUrl, NF_USERNAME_PASSWORD, NF_USERNAME_PASSWORD);
connectionPool.setMaxConnections(MAX_CONNECTIONS);
Connection connection = null;
ResultSet rs = null;
Statement statement = null;
try {
// get a connection
connection = connectionPool.getConnection();
connection.setAutoCommit(false);
// create a statement for initializing the database
statement = connection.createStatement();
// determine if the tables need to be created
rs = connection.getMetaData().getTables(null, null, "KEY", null);
if (!rs.next()) {
logger.info("Database not built for repository: " + databaseUrl + ". Building now...");
RepositoryUtils.closeQuietly(rs);
// action table
statement.execute(CREATE_KEY_TABLE);
} else {
logger.info("Existing database found and connected to at: " + databaseUrl);
}
// commit any changes
connection.commit();
} catch (SQLException sqle) {
RepositoryUtils.rollback(connection, logger);
throw sqle;
} finally {
RepositoryUtils.closeQuietly(rs);
RepositoryUtils.closeQuietly(statement);
RepositoryUtils.closeQuietly(connection);
}
}
return connectionPool;
}
@Override
public Class getObjectType() {
return JdbcConnectionPool.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
/**
* Disposes resources.
*/
public void shutdown() {
// shutdown the connection pool
if (connectionPool != null) {
try {
connectionPool.dispose();
} catch (Exception e) {
logger.warn("Unable to dispose of connection pool: " + e.getMessage());
if (logger.isDebugEnabled()) {
logger.warn(StringUtils.EMPTY, e);
}
}
}
}
}

View File

@ -88,6 +88,15 @@ public class UserDataSourceFactoryBean implements FactoryBean {
private static final String RESIZE_IDENTITY_COLUMN = "ALTER TABLE USER MODIFY IDENTITY VARCHAR(4096)";
private static final String RESIZE_USER_NAME_COLUMN = "ALTER TABLE USER MODIFY USER_NAME VARCHAR(4096)";
// ----------
// keys table
// ----------
private static final String CREATE_KEY_TABLE = "CREATE TABLE KEY ("
+ "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
+ "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
+ "KEY VARCHAR2(100) NOT NULL"
+ ")";
private JdbcConnectionPool connectionPool;
private NiFiProperties properties;
@ -112,7 +121,8 @@ public class UserDataSourceFactoryBean implements FactoryBean {
if (rawAnonymousAuthorities.size() != anonymousAuthorities.size()) {
final Set<String> validAuthorities = Authority.convertAuthorities(anonymousAuthorities);
rawAnonymousAuthorities.removeAll(validAuthorities);
throw new IllegalStateException("Invalid authorities specified: " + StringUtils.join(rawAnonymousAuthorities, ", "));
throw new IllegalStateException(String.format("Invalid authorities specified for anonymous access: [%s]. Valid values are: [%s].",
StringUtils.join(rawAnonymousAuthorities, ", "), StringUtils.join(Authority.values(), ", ")));
}
// create a handle to the repository directory
@ -169,6 +179,14 @@ public class UserDataSourceFactoryBean implements FactoryBean {
statement.execute(String.format(INSERT_ANONYMOUS_AUTHORITY, authority.name()));
}
RepositoryUtils.closeQuietly(rs);
// determine if the key table need to be created
rs = connection.getMetaData().getTables(null, null, "KEY", null);
if (!rs.next()) {
statement.execute(CREATE_KEY_TABLE);
}
// commit any changes
connection.commit();
} catch (SQLException sqle) {

View File

@ -46,4 +46,11 @@ public interface KeyDAO {
* @return The key
*/
Key createKey(String identity);
/**
* Deletes all keys for the specified user identity.
*
* @param identity The user identity
*/
void deleteKeys(String identity);
}

View File

@ -46,6 +46,9 @@ public class StandardKeyDAO implements KeyDAO {
+ "?, ?"
+ ")";
private static final String DELETE_KEYS = "DELETE FROM KEY "
+ "WHERE IDENTITY = ?";
private final Connection connection;
public StandardKeyDAO(Connection connection) {
@ -151,4 +154,26 @@ public class StandardKeyDAO implements KeyDAO {
RepositoryUtils.closeQuietly(statement);
}
}
@Override
public void deleteKeys(String identity) {
// ensure there are some authorities to create
PreparedStatement statement = null;
try {
// add each authority for the specified user
statement = connection.prepareStatement(DELETE_KEYS);
statement.setString(1, identity);
// insert the authorities
int count = statement.executeUpdate();
System.out.println();
} catch (SQLException sqle) {
throw new DataAccessException(sqle);
} catch (DataAccessException dae) {
throw dae;
} finally {
RepositoryUtils.closeQuietly(statement);
}
}
}

View File

@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.nifi.authorization.Authority;
import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.key.Key;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.user.NiFiUserGroup;
@ -47,14 +48,12 @@ public interface UserService {
/**
* @param dnChain user dn chain
* @param attributes attributes for authorization request
* @return Determines if the users in the dnChain are authorized to download
* content with the specified attributes
* @return Determines if the users in the dnChain are authorized to download content with the specified attributes
*/
DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes);
/**
* Updates a user group using the specified group comprised of the specified
* users. Returns all the users that are currently in the specified group.
* Updates a user group using the specified group comprised of the specified users. Returns all the users that are currently in the specified group.
*
* @param group group
* @param userIds users
@ -154,4 +153,28 @@ public interface UserService {
* @throws AdministrationException ae
*/
NiFiUser getUserByDn(String dn);
/**
* Gets a key for the specified user identity. Returns null if the user has not had a key issued
*
* @param id The key id
* @return The key or null
*/
Key getKey(int id);
/**
* Gets a key for the specified user identity. If a key does not exist, one will be created.
*
* @param identity The user identity
* @return The key
* @throws AdministrationException if it failed to get/create the key
*/
Key getOrCreateKey(String identity);
/**
* Deletes keys for the specified identity.
*
* @param identity The user identity
*/
void deleteKey(String identity);
}

View File

@ -14,29 +14,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.admin.service;
package org.apache.nifi.admin.service.action;
import org.apache.nifi.key.Key;
import org.apache.nifi.admin.dao.DAOFactory;
import org.apache.nifi.admin.dao.DataAccessException;
import org.apache.nifi.admin.dao.KeyDAO;
import org.apache.nifi.authorization.AuthorityProvider;
/**
* Supports retrieving and issues keys for signing user tokens.
*
*/
public interface KeyService {
public class DeleteKeysAction implements AdministrationAction<Void> {
private final String identity;
/**
* Gets a key for the specified user identity. Returns null if the user has not had a key issued
* Creates a new transactions for deleting keys for specified user.
*
* @param id The key id
* @return The key or null
* @param identity user identity
*/
Key getKey(int id);
public DeleteKeysAction(String identity) {
this.identity = identity;
}
/**
* Gets a key for the specified user identity. If a key does not exist, one will be created.
*
* @param identity The user identity
* @return The key
* @throws AdministrationException if it failed to get/create the key
*/
Key getOrCreateKey(String identity);
@Override
public Void execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) throws DataAccessException {
final KeyDAO keyDao = daoFactory.getKeyDAO();
keyDao.deleteKeys(identity);
return null;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.nifi.admin.service.action;
import org.apache.nifi.admin.dao.AuthorityDAO;
import org.apache.nifi.admin.dao.DAOFactory;
import org.apache.nifi.admin.dao.DataAccessException;
import org.apache.nifi.admin.dao.KeyDAO;
import org.apache.nifi.admin.dao.UserDAO;
import org.apache.nifi.admin.service.AccountNotFoundException;
import org.apache.nifi.authorization.AuthorityProvider;
@ -59,6 +60,10 @@ public class DeleteUserAction implements AdministrationAction<Void> {
throw new IllegalStateException(String.format("An active user cannot be removed. Revoke user access before attempting to remove."));
}
// remove the user's keys
final KeyDAO keyDao = daoFactory.getKeyDAO();
keyDao.deleteKeys(user.getIdentity());
// remove the user and their authorities
authorityDAO.deleteAuthorities(userId);
userDAO.deleteUser(userId);

View File

@ -18,6 +18,7 @@ package org.apache.nifi.admin.service.action;
import org.apache.nifi.admin.dao.DAOFactory;
import org.apache.nifi.admin.dao.DataAccessException;
import org.apache.nifi.admin.dao.KeyDAO;
import org.apache.nifi.admin.dao.UserDAO;
import org.apache.nifi.admin.service.AccountNotFoundException;
import org.apache.nifi.admin.service.AdministrationException;
@ -61,6 +62,10 @@ public class DisableUserAction implements AdministrationAction<NiFiUser> {
// update the user locally
userDao.updateUser(user);
// remove the user's keys
KeyDAO keyDao = daoFactory.getKeyDAO();
keyDao.deleteKeys(user.getIdentity());
try {
// revoke the user in the authority provider
authorityProvider.revokeUser(user.getIdentity());

View File

@ -16,14 +16,17 @@
*/
package org.apache.nifi.admin.service.action;
import java.util.Set;
import org.apache.nifi.admin.dao.DAOFactory;
import org.apache.nifi.admin.dao.DataAccessException;
import org.apache.nifi.admin.dao.KeyDAO;
import org.apache.nifi.admin.dao.UserDAO;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.authorization.AuthorityProvider;
import org.apache.nifi.authorization.exception.AuthorityAccessException;
import org.apache.nifi.authorization.exception.UnknownIdentityException;
import org.apache.nifi.user.AccountStatus;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.user.NiFiUserGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,14 +46,20 @@ public class DisableUserGroupAction implements AdministrationAction<NiFiUserGrou
@Override
public NiFiUserGroup execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) throws DataAccessException {
final NiFiUserGroup userGroup = new NiFiUserGroup();
final UserDAO userDao = daoFactory.getUserDAO();
final Set<NiFiUser> users = userDao.findUsersForGroup(group);
// delete the keys for each user
final KeyDAO keyDao = daoFactory.getKeyDAO();
for (final NiFiUser user : users) {
keyDao.deleteKeys(user.getIdentity());
}
// update the user group locally
userDao.updateGroupStatus(group, AccountStatus.DISABLED);
// populate the group details
final NiFiUserGroup userGroup = new NiFiUserGroup();
userGroup.setGroup(group);
userGroup.setUsers(userDao.findUsersForGroup(group));

View File

@ -1,126 +0,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.
*/
package org.apache.nifi.admin.service.impl;
import org.apache.nifi.admin.dao.DataAccessException;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.KeyService;
import org.apache.nifi.admin.service.action.GetKeyByIdAction;
import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
import org.apache.nifi.admin.service.transaction.Transaction;
import org.apache.nifi.admin.service.transaction.TransactionBuilder;
import org.apache.nifi.admin.service.transaction.TransactionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.nifi.key.Key;
/**
*
*/
public class StandardKeyService implements KeyService {
private static final Logger logger = LoggerFactory.getLogger(StandardKeyService.class);
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
private TransactionBuilder transactionBuilder;
@Override
public Key getKey(int id) {
Transaction transaction = null;
Key key = null;
readLock.lock();
try {
// start the transaction
transaction = transactionBuilder.start();
// get the key
GetKeyByIdAction addActions = new GetKeyByIdAction(id);
key = transaction.execute(addActions);
// commit the transaction
transaction.commit();
} catch (TransactionException | DataAccessException te) {
rollback(transaction);
throw new AdministrationException(te);
} catch (Throwable t) {
rollback(transaction);
throw t;
} finally {
closeQuietly(transaction);
readLock.unlock();
}
return key;
}
@Override
public Key getOrCreateKey(String identity) {
Transaction transaction = null;
Key key = null;
writeLock.lock();
try {
// start the transaction
transaction = transactionBuilder.start();
// seed the accounts
GetOrCreateKeyAction addActions = new GetOrCreateKeyAction(identity);
key = transaction.execute(addActions);
// commit the transaction
transaction.commit();
} catch (TransactionException | DataAccessException te) {
rollback(transaction);
throw new AdministrationException(te);
} catch (Throwable t) {
rollback(transaction);
throw t;
} finally {
closeQuietly(transaction);
writeLock.unlock();
}
return key;
}
private void rollback(Transaction transaction) {
if (transaction != null) {
transaction.rollback();
}
}
private void closeQuietly(final Transaction transaction) {
if (transaction != null) {
try {
transaction.close();
} catch (final IOException ioe) {
}
}
}
public void setTransactionBuilder(TransactionBuilder transactionBuilder) {
this.transactionBuilder = transactionBuilder;
}
}

View File

@ -31,11 +31,14 @@ import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.admin.service.action.AuthorizeDownloadAction;
import org.apache.nifi.admin.service.action.AuthorizeUserAction;
import org.apache.nifi.admin.service.action.DeleteKeysAction;
import org.apache.nifi.admin.service.action.DeleteUserAction;
import org.apache.nifi.admin.service.action.DisableUserAction;
import org.apache.nifi.admin.service.action.DisableUserGroupAction;
import org.apache.nifi.admin.service.action.FindUserByDnAction;
import org.apache.nifi.admin.service.action.FindUserByIdAction;
import org.apache.nifi.admin.service.action.GetKeyByIdAction;
import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
import org.apache.nifi.admin.service.action.GetUserGroupAction;
import org.apache.nifi.admin.service.action.GetUsersAction;
import org.apache.nifi.admin.service.action.HasPendingUserAccounts;
@ -52,6 +55,7 @@ import org.apache.nifi.admin.service.transaction.TransactionBuilder;
import org.apache.nifi.admin.service.transaction.TransactionException;
import org.apache.nifi.authorization.Authority;
import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.key.Key;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.user.NiFiUserGroup;
import org.apache.nifi.util.FormatUtils;
@ -404,10 +408,8 @@ public class StandardUserService implements UserService {
}
/**
* Invalidates the user with the specified id. This is done to ensure a user
* account will need to be re-validated in case an error occurs while
* modifying a user account. This method should only be invoked from within
* a write lock.
* Invalidates the user with the specified id. This is done to ensure a user account will need to be re-validated in case an error occurs while modifying a user account. This method should only be
* invoked from within a write lock.
*
* @param id user account identifier
*/
@ -616,6 +618,93 @@ public class StandardUserService implements UserService {
}
}
@Override
public Key getKey(int id) {
Transaction transaction = null;
Key key = null;
readLock.lock();
try {
// start the transaction
transaction = transactionBuilder.start();
// get the key
GetKeyByIdAction addActions = new GetKeyByIdAction(id);
key = transaction.execute(addActions);
// commit the transaction
transaction.commit();
} catch (TransactionException | DataAccessException te) {
rollback(transaction);
throw new AdministrationException(te);
} catch (Throwable t) {
rollback(transaction);
throw t;
} finally {
closeQuietly(transaction);
readLock.unlock();
}
return key;
}
@Override
public Key getOrCreateKey(String identity) {
Transaction transaction = null;
Key key = null;
writeLock.lock();
try {
// start the transaction
transaction = transactionBuilder.start();
// get or create a key
GetOrCreateKeyAction addActions = new GetOrCreateKeyAction(identity);
key = transaction.execute(addActions);
// commit the transaction
transaction.commit();
} catch (TransactionException | DataAccessException te) {
rollback(transaction);
throw new AdministrationException(te);
} catch (Throwable t) {
rollback(transaction);
throw t;
} finally {
closeQuietly(transaction);
writeLock.unlock();
}
return key;
}
@Override
public void deleteKey(String identity) {
Transaction transaction = null;
writeLock.lock();
try {
// start the transaction
transaction = transactionBuilder.start();
// delete the keys
DeleteKeysAction deleteKeys = new DeleteKeysAction(identity);
transaction.execute(deleteKeys);
// commit the transaction
transaction.commit();
} catch (TransactionException | DataAccessException te) {
rollback(transaction);
throw new AdministrationException(te);
} catch (Throwable t) {
rollback(transaction);
throw t;
} finally {
closeQuietly(transaction);
writeLock.unlock();
}
}
private void rollback(final Transaction transaction) {
if (transaction != null) {
transaction.rollback();

View File

@ -37,11 +37,6 @@
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- initialize the data source -->
<bean id="keyDataSource" class="org.apache.nifi.admin.KeyDataSourceFactoryBean" destroy-method="shutdown">
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- initialize the user transaction builder -->
<bean id="userTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
<property name="authorityProvider" ref="authorityProvider"/>
@ -54,12 +49,6 @@
<property name="dataSource" ref="auditDataSource"/>
</bean>
<!-- initialize the key transaction builder -->
<bean id="keyTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
<property name="authorityProvider" ref="authorityProvider"/>
<property name="dataSource" ref="keyDataSource"/>
</bean>
<!-- administration service -->
<bean id="userService" class="org.apache.nifi.admin.service.impl.StandardUserService" init-method="seedUserAccounts">
<property name="transactionBuilder" ref="userTransactionBuilder"/>
@ -70,9 +59,4 @@
<bean id="auditService" class="org.apache.nifi.admin.service.impl.StandardAuditService">
<property name="transactionBuilder" ref="auditTransactionBuilder"/>
</bean>
<!-- key service -->
<bean id="keyService" class="org.apache.nifi.admin.service.impl.StandardKeyService">
<property name="transactionBuilder" ref="keyTransactionBuilder"/>
</bean>
</beans>

View File

@ -26,9 +26,11 @@ import org.apache.nifi.authorization.exception.AuthorityAccessException;
import org.apache.nifi.user.AccountStatus;
import org.apache.nifi.user.NiFiUser;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.dao.KeyDAO;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -45,6 +47,7 @@ public class DisableUserActionTest {
private DAOFactory daoFactory;
private UserDAO userDao;
private KeyDAO keyDao;
private AuthorityProvider authorityProvider;
@Before
@ -91,9 +94,14 @@ public class DisableUserActionTest {
}
}).when(userDao).updateUser(Mockito.any(NiFiUser.class));
// mock the dao factory
keyDao = Mockito.mock(KeyDAO.class);
Mockito.doNothing().when(keyDao).deleteKeys(Matchers.anyString());
// mock the dao factory
daoFactory = Mockito.mock(DAOFactory.class);
Mockito.when(daoFactory.getUserDAO()).thenReturn(userDao);
Mockito.when(daoFactory.getKeyDAO()).thenReturn(keyDao);
// mock the authority provider
authorityProvider = Mockito.mock(AuthorityProvider.class);

View File

@ -127,6 +127,7 @@ nifi.security.user.credential.cache.duration=${nifi.security.user.credential.cac
nifi.security.user.authority.provider=${nifi.security.user.authority.provider}
nifi.security.user.login.identity.provider=${nifi.security.user.login.identity.provider}
nifi.security.support.new.account.requests=${nifi.security.support.new.account.requests}
# Valid Authorities include: ROLE_MONITOR,ROLE_DFM,ROLE_ADMIN,ROLE_PROVENANCE,ROLE_NIFI
nifi.security.anonymous.authorities=${nifi.security.anonymous.authorities}
nifi.security.ocsp.responder.url=${nifi.security.ocsp.responder.url}
nifi.security.ocsp.responder.certificate=${nifi.security.ocsp.responder.certificate}

View File

@ -21,7 +21,6 @@ import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtService;
import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
@ -74,9 +73,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe().disable()
.exceptionHandling()
.authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties))
.and()
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()

View File

@ -70,6 +70,7 @@ import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
@ -201,12 +202,16 @@ public class AccessResource extends ApplicationResource {
// without a certificate, this is not a proxied request
final List<String> chain = Arrays.asList(principal);
// check authorization for this user
checkAuthorization(chain);
// ensure the proxy chain is authorized
final UserDetails userDetails = checkAuthorization(chain);
// no issues with authorization
// no issues with authorization... verify authorities
accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
accessStatus.setMessage("Account is active and authorized");
if (userDetails.getAuthorities().isEmpty()) {
accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
} else {
accessStatus.setMessage("Your account is active and you are already logged in.");
}
} catch (JwtException e) {
throw new InvalidAuthenticationException(e.getMessage(), e);
}
@ -227,18 +232,27 @@ public class AccessResource extends ApplicationResource {
accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
// ensure the proxy chain is authorized
checkAuthorization(proxyChain);
final UserDetails userDetails = checkAuthorization(proxyChain);
// no issues with authorization
// no issues with authorization... verify authorities
accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
accessStatus.setMessage("Account is active and authorized");
if (userDetails.getAuthorities().isEmpty()) {
accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
} else {
accessStatus.setMessage("Your account is active and you are already logged in.");
}
} catch (final IllegalArgumentException iae) {
throw new InvalidAuthenticationException(iae.getMessage(), iae);
}
}
} catch (final UsernameNotFoundException unfe) {
accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
if (properties.getSupportNewAccountRequests()) {
accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
} else {
accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
accessStatus.setMessage("This NiFi does not support new account requests.");
}
} catch (final AccountStatusException ase) {
accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
accessStatus.setMessage(ase.getMessage());
@ -266,8 +280,8 @@ public class AccessResource extends ApplicationResource {
* @param proxyChain the proxy chain
* @throws AuthenticationException if the proxy chain is not authorized
*/
private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
private UserDetails checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
return userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
}
/**

View File

@ -1,71 +0,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.
*/
package org.apache.nifi.web.security;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* This is our own implementation of org.springframework.security.web.AuthenticationEntryPoint that allows us to send the response to the client exactly how we want to and log the results.
*/
public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
private final NiFiProperties properties;
public NiFiAuthenticationEntryPoint(NiFiProperties properties) {
this.properties = properties;
}
/**
* Always returns a 403 error code to the client.
*
* @param request request
* @param response response
* @param ae ae
* @throws java.io.IOException ex
* @throws javax.servlet.ServletException ex
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
// if the content type is not set, mark as access denied
if (StringUtils.isBlank(response.getContentType())) {
// write the response message
PrintWriter out = response.getWriter();
response.setContentType("text/plain");
// return authorized if the request is secure and this nifi supports new account requests
if (request.isSecure() && properties.getSupportNewAccountRequests()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
out.println("Not authorized.");
} else {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.println("Access is denied.");
}
}
}
}

View File

@ -63,10 +63,11 @@ public abstract class NiFiAuthenticationFilter implements Filter {
}
if (requiresAuthentication((HttpServletRequest) request)) {
authenticate((HttpServletRequest) request, (HttpServletResponse) response);
authenticate((HttpServletRequest) request, (HttpServletResponse) response, chain);
} else {
chain.doFilter(request, response);
}
chain.doFilter(request, response);
}
private boolean requiresAuthentication(final HttpServletRequest request) {
@ -84,7 +85,7 @@ public abstract class NiFiAuthenticationFilter implements Filter {
return user != null && NiFiUser.ANONYMOUS_USER_IDENTITY.equals(user.getIdentity());
}
private void authenticate(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
try {
final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response);
if (authenticated != null) {
@ -93,11 +94,21 @@ public abstract class NiFiAuthenticationFilter implements Filter {
ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(),
request.getRequestURL().toString(), request.getRemoteAddr()));
// attempt to authorize the user
final Authentication authorized = authenticationManager.authenticate(authenticated);
successfulAuthorization(request, response, authorized);
}
// continue
chain.doFilter(request, response);
} catch (final InvalidAuthenticationException iae) {
// invalid authentication - always error out
unsuccessfulAuthorization(request, response, iae);
} catch (final AuthenticationException ae) {
if (!isAnonymousUser()) {
// other authentication exceptions... if we are already the anonymous user, allow through otherwise error out
if (isAnonymousUser()) {
chain.doFilter(request, response);
} else {
unsuccessfulAuthorization(request, response, ae);
}
}

View File

@ -29,28 +29,29 @@ import io.jsonwebtoken.SigningKeyResolverAdapter;
import io.jsonwebtoken.UnsupportedJwtException;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.KeyService;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.key.Key;
/**
*
*/
public class JwtService {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
private static final String KEY_ID_CLAIM = "kid";
private static final String USERNAME_CLAIM = "preferred_username";
private final KeyService keyService;
private final UserService userService;
public JwtService(final KeyService keyService) {
this.keyService = keyService;
public JwtService(final UserService userService) {
this.userService = userService;
}
public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException {
@ -91,7 +92,7 @@ public class JwtService {
// Get the key based on the key id in the claims
final Integer keyId = claims.get(KEY_ID_CLAIM, Integer.class);
final Key key = keyService.getKey(keyId);
final Key key = userService.getKey(keyId);
// Ensure we were able to find a key that was previously issued by this key service for this user
if (key == null || key.getKey() == null) {
@ -103,7 +104,7 @@ public class JwtService {
}).parseClaimsJws(base64EncodedToken);
} catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
// TODO: Exercise all exceptions to ensure none leak key material to logs
final String errorMessage = "There was an error validating the JWT";
final String errorMessage = "Unable to validate the access token.";
throw new JwtException(errorMessage, e);
}
}
@ -137,13 +138,12 @@ public class JwtService {
try {
// Get/create the key for this user
final Key key = keyService.getOrCreateKey(identity);
final Key key = userService.getOrCreateKey(identity);
final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8);
logger.trace("Generating JWT for " + authenticationToken);
// TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
// Build the token
return Jwts.builder().setSubject(identity)
.setIssuer(authenticationToken.getIssuer())

View File

@ -47,7 +47,7 @@
<!-- jwt service -->
<bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService">
<constructor-arg ref="keyService"/>
<constructor-arg ref="userService"/>
</bean>
<!-- login identity provider -->

View File

@ -20,7 +20,7 @@ import io.jsonwebtoken.JwtException;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.KeyService;
import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.key.Key;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.codehaus.jettison.json.JSONObject;
@ -131,7 +131,7 @@ public class JwtServiceTest {
private static final String HMAC_SECRET = "test_hmac_shared_secret";
private KeyService mockKeyService;
private UserService mockUserService;
// Class under test
private JwtService jwtService;
@ -177,10 +177,10 @@ public class JwtServiceTest {
key.setIdentity(DEFAULT_IDENTITY);
key.setKey(HMAC_SECRET);
mockKeyService = Mockito.mock(KeyService.class);
when(mockKeyService.getKey(anyInt())).thenReturn(key);
when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
jwtService = new JwtService(mockKeyService);
mockUserService = Mockito.mock(UserService.class);
when(mockUserService.getKey(anyInt())).thenReturn(key);
when(mockUserService.getOrCreateKey(anyString())).thenReturn(key);
jwtService = new JwtService(mockUserService);
}
@After
@ -431,7 +431,7 @@ public class JwtServiceTest {
logger.debug("Generating token for " + loginAuthenticationToken);
// Set up the bad key service
KeyService missingKeyService = Mockito.mock(KeyService.class);
UserService missingKeyService = Mockito.mock(UserService.class);
when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a "
+ "key for that user"));
jwtService = new JwtService(missingKeyService);

View File

@ -262,13 +262,13 @@ nf.Login = (function () {
} else if (accessStatus.status === 'NOT_ACTIVE') {
showMessage = true;
$('#login-message-title').text('Access Denied');
$('#login-message-title').text('Unable to log in');
$('#login-message').text(accessStatus.message);
} else if (accessStatus.status === 'ACTIVE') {
showMessage = true;
$('#login-message-title').text('Success');
$('#login-message').text('Your account is active and you are already logged in.');
$('#login-message').text(accessStatus.message);
}
// if login is required, verify its supported