NIFI-655: - Refactoring key service to expose the key id. - Handling client side expiration better. - Removing specialized active directory provider and abstract ldap provider.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/c94d0271 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/c94d0271 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/c94d0271 Branch: refs/heads/NIFI-655 Commit: c94d0271d990df6d6cd9a1dfee7b1706f451483c Parents: 7d04dfe Author: Matt Gilman <[email protected]> Authored: Wed Nov 18 14:01:45 2015 -0500 Committer: Matt Gilman <[email protected]> Committed: Wed Nov 18 14:01:45 2015 -0500 ---------------------------------------------------------------------- .../nifi/security/util/CertificateUtils.java | 31 ++--- .../java/org/apache/nifi/admin/dao/KeyDAO.java | 18 ++- .../nifi/admin/dao/impl/StandardKeyDAO.java | 67 ++++++++-- .../apache/nifi/admin/service/KeyService.java | 8 +- .../nifi/admin/service/action/GetKeyAction.java | 41 ------- .../admin/service/action/GetKeyByIdAction.java | 42 +++++++ .../service/action/GetKeyByIdentityAction.java | 42 +++++++ .../service/action/GetOrCreateKeyAction.java | 7 +- .../admin/service/impl/StandardKeyService.java | 19 ++- .../src/main/java/org/apache/nifi/key/Key.java | 69 +++++++++++ .../org/apache/nifi/web/api/AccessResource.java | 82 ++++++------- .../InvalidAuthenticationExceptionMapper.java | 44 +++++++ .../src/main/resources/nifi-web-api-context.xml | 1 + .../InvalidAuthenticationException.java | 35 ++++++ .../web/security/NiFiAuthenticationFilter.java | 5 +- .../security/jwt/JwtAuthenticationFilter.java | 9 +- .../nifi/web/security/jwt/JwtService.java | 20 ++- .../security/x509/X509AuthenticationFilter.java | 4 +- .../nifi/web/security/jwt/JwtServiceTest.java | 11 +- .../webapp/js/nf/canvas/nf-canvas-header.js | 2 +- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 14 +-- .../src/main/webapp/js/nf/login/nf-login.js | 12 +- .../src/main/webapp/js/nf/nf-common.js | 112 ++++++++++------- .../src/main/webapp/js/nf/nf-dialog.js | 23 ++-- .../src/main/webapp/js/nf/nf-storage.js | 75 ++++++++---- .../apache/nifi/ldap/AbstractLdapProvider.java | 106 ---------------- .../nifi/ldap/ActiveDirectoryProvider.java | 51 -------- .../java/org/apache/nifi/ldap/LdapProvider.java | 122 ++++++++++++++----- ...he.nifi.authentication.LoginIdentityProvider | 3 +- 29 files changed, 627 insertions(+), 448 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java index 5126933..ea3a6c6 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java @@ -87,30 +87,21 @@ public final class CertificateUtils { */ public static String extractUsername(String dn) { String username = dn; - String cn = ""; // ensure the dn is specified if (StringUtils.isNotBlank(dn)) { - - // attempt to locate the cn - if (dn.startsWith("CN=")) { - cn = StringUtils.substringBetween(dn, "CN=", ","); - } else if (dn.startsWith("/CN=")) { - cn = StringUtils.substringBetween(dn, "CN=", "/"); - } else if (dn.startsWith("C=") || dn.startsWith("/C=")) { - cn = StringUtils.substringAfter(dn, "CN="); - } else if (dn.startsWith("/") && StringUtils.contains(dn, "CN=")) { - cn = StringUtils.substringAfter(dn, "CN="); - } - - // attempt to get the username from the cn - if (StringUtils.isNotBlank(cn)) { - if (cn.endsWith(")")) { - username = StringUtils.substringBetween(cn, "(", ")"); - } else if (cn.contains(" ")) { - username = StringUtils.substringAfterLast(cn, " "); + // determine the separate + final String separator = StringUtils.indexOfIgnoreCase(dn, "/cn=") > 0 ? "/" : ","; + + // attempt to locate the cd + final String cnPattern = "cn="; + final int cnIndex = StringUtils.indexOfIgnoreCase(dn, cnPattern); + if (cnIndex >= 0) { + int separatorIndex = StringUtils.indexOf(dn, separator, cnIndex); + if (separatorIndex > 0) { + username = StringUtils.substring(dn, cnIndex + cnPattern.length(), separatorIndex); } else { - username = cn; + username = StringUtils.substring(dn, cnIndex + cnPattern.length()); } } } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java index 0bc6e99..2a24e0b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java @@ -16,18 +16,28 @@ */ package org.apache.nifi.admin.dao; +import org.apache.nifi.key.Key; + /** * Key data access. */ public interface KeyDAO { /** - * Gets the key for the specified user identity. Returns null if no key exists for the user identity. + * Gets the key for the specified user identity. Returns null if no key exists for the key id. * - * @param identity The user identity + * @param id The key id + * @return The key or null + */ + Key findKeyById(int id); + + /** + * Gets the latest key for the specified identity. Returns null if no key exists for the user identity. + * + * @param identity The identity * @return The key or null */ - String getKey(String identity); + Key findLatestKeyByIdentity(String identity); /** * Creates a key for the specified user identity. @@ -35,5 +45,5 @@ public interface KeyDAO { * @param identity The user identity * @return The key */ - String createKey(String identity); + Key createKey(String identity); } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java index 994e95b..f4bdc1d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java @@ -20,17 +20,23 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.UUID; import org.apache.nifi.admin.RepositoryUtils; import org.apache.nifi.admin.dao.DataAccessException; import org.apache.nifi.admin.dao.KeyDAO; +import org.apache.nifi.key.Key; /** * */ public class StandardKeyDAO implements KeyDAO { - private static final String SELECT_KEY_FOR_USER = "SELECT KEY " + private static final String SELECT_KEY_FOR_USER_BY_ID = "SELECT ID, IDENTITY, KEY " + + "FROM KEY " + + "WHERE ID = ?"; + + private static final String SELECT_KEY_FOR_USER_BY_IDENTITY = "SELECT ID, IDENTITY, KEY " + "FROM KEY " + "WHERE IDENTITY = ?"; @@ -47,18 +53,49 @@ public class StandardKeyDAO implements KeyDAO { } @Override - public String getKey(String identity) { + public Key findKeyById(int id) { + Key key = null; + + PreparedStatement statement = null; + ResultSet rs = null; + try { + // add each authority for the specified user + statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_ID); + statement.setInt(1, id); + + // execute the query + rs = statement.executeQuery(); + + // if the key was found, add it + if (rs.next()) { + key = new Key(); + key.setId(rs.getInt("ID")); + key.setIdentity(rs.getString("IDENTITY")); + key.setKey(rs.getString("KEY")); + } + } catch (SQLException sqle) { + throw new DataAccessException(sqle); + } finally { + RepositoryUtils.closeQuietly(rs); + RepositoryUtils.closeQuietly(statement); + } + + return key; + } + + @Override + public Key findLatestKeyByIdentity(String identity) { if (identity == null) { throw new IllegalArgumentException("Specified identity cannot be null."); } - String key = null; + Key key = null; PreparedStatement statement = null; ResultSet rs = null; try { // add each authority for the specified user - statement = connection.prepareStatement(SELECT_KEY_FOR_USER); + statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_IDENTITY); statement.setString(1, identity); // execute the query @@ -66,7 +103,10 @@ public class StandardKeyDAO implements KeyDAO { // if the key was found, add it if (rs.next()) { - key = rs.getString("KEY"); + key = new Key(); + key.setId(rs.getInt("ID")); + key.setIdentity(rs.getString("IDENTITY")); + key.setKey(rs.getString("KEY")); } } catch (SQLException sqle) { throw new DataAccessException(sqle); @@ -79,20 +119,27 @@ public class StandardKeyDAO implements KeyDAO { } @Override - public String createKey(final String identity) { + public Key createKey(final String identity) { PreparedStatement statement = null; ResultSet rs = null; try { - final String key = UUID.randomUUID().toString(); + final String keyValue = UUID.randomUUID().toString(); // add each authority for the specified user - statement = connection.prepareStatement(INSERT_KEY); + statement = connection.prepareStatement(INSERT_KEY, Statement.RETURN_GENERATED_KEYS); statement.setString(1, identity); - statement.setString(2, key); + statement.setString(2, keyValue); // insert the key int updateCount = statement.executeUpdate(); - if (updateCount == 1) { + rs = statement.getGeneratedKeys(); + + // verify the results + if (updateCount == 1 && rs.next()) { + final Key key = new Key(); + key.setId(rs.getInt(1)); + key.setIdentity(identity); + key.setKey(keyValue); return key; } else { throw new DataAccessException("Unable to add key for user."); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java index 9346625..ae64c41 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java @@ -16,6 +16,8 @@ */ package org.apache.nifi.admin.service; +import org.apache.nifi.key.Key; + /** * Supports retrieving and issues keys for signing user tokens. */ @@ -24,10 +26,10 @@ public interface KeyService { /** * Gets a key for the specified user identity. Returns null if the user has not had a key issued * - * @param identity The user identity + * @param id The key id * @return The key or null */ - String getKey(String identity); + Key getKey(int id); /** * Gets a key for the specified user identity. If a key does not exist, one will be created. @@ -36,5 +38,5 @@ public interface KeyService { * @return The key * @throws AdministrationException if it failed to get/create the key */ - String getOrCreateKey(String identity); + Key getOrCreateKey(String identity); } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java deleted file mode 100644 index f12b1ef..0000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java +++ /dev/null @@ -1,41 +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.action; - -import org.apache.nifi.admin.dao.DAOFactory; -import org.apache.nifi.authorization.AuthorityProvider; - -import org.apache.nifi.admin.dao.KeyDAO; - -/** - * Gets a key for the specified user identity. - */ -public class GetKeyAction implements AdministrationAction<String> { - - private final String identity; - - public GetKeyAction(String identity) { - this.identity = identity; - } - - @Override - public String execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) { - final KeyDAO keyDao = daoFactory.getKeyDAO(); - return keyDao.getKey(identity); - } - -} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java new file mode 100644 index 0000000..8763b9d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java @@ -0,0 +1,42 @@ +/* + * 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.action; + +import org.apache.nifi.admin.dao.DAOFactory; +import org.apache.nifi.authorization.AuthorityProvider; + +import org.apache.nifi.admin.dao.KeyDAO; +import org.apache.nifi.key.Key; + +/** + * Gets a key for the specified key id. + */ +public class GetKeyByIdAction implements AdministrationAction<Key> { + + private final int id; + + public GetKeyByIdAction(int id) { + this.id = id; + } + + @Override + public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) { + final KeyDAO keyDao = daoFactory.getKeyDAO(); + return keyDao.findKeyById(id); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java new file mode 100644 index 0000000..9bcb0b3 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java @@ -0,0 +1,42 @@ +/* + * 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.action; + +import org.apache.nifi.admin.dao.DAOFactory; +import org.apache.nifi.authorization.AuthorityProvider; + +import org.apache.nifi.admin.dao.KeyDAO; +import org.apache.nifi.key.Key; + +/** + * Gets a key for the specified key id. + */ +public class GetKeyByIdentityAction implements AdministrationAction<Key> { + + private final String identity; + + public GetKeyByIdentityAction(String identity) { + this.identity = identity; + } + + @Override + public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) { + final KeyDAO keyDao = daoFactory.getKeyDAO(); + return keyDao.findLatestKeyByIdentity(identity); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java index 209cbcd..bb85b6f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java @@ -20,11 +20,12 @@ import org.apache.nifi.admin.dao.DAOFactory; import org.apache.nifi.authorization.AuthorityProvider; import org.apache.nifi.admin.dao.KeyDAO; +import org.apache.nifi.key.Key; /** * Gets a key for the specified user identity. */ -public class GetOrCreateKeyAction implements AdministrationAction<String> { +public class GetOrCreateKeyAction implements AdministrationAction<Key> { private final String identity; @@ -33,10 +34,10 @@ public class GetOrCreateKeyAction implements AdministrationAction<String> { } @Override - public String execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) { + public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) { final KeyDAO keyDao = daoFactory.getKeyDAO(); - String key = keyDao.getKey(identity); + Key key = keyDao.findLatestKeyByIdentity(identity); if (key == null) { key = keyDao.createKey(identity); } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java index 7dff9d8..ca0a124 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java @@ -19,7 +19,7 @@ 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.GetKeyAction; +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; @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.apache.nifi.key.Key; /** * @@ -44,19 +45,17 @@ public class StandardKeyService implements KeyService { private TransactionBuilder transactionBuilder; @Override - public String getKey(String identity) { - // TODO: Change this service to look up by "key ID" instead of identity - // TODO: Change the return type to a Key POJO to support key rotation + public Key getKey(int id) { Transaction transaction = null; - String key = null; + Key key = null; readLock.lock(); try { // start the transaction transaction = transactionBuilder.start(); - // seed the accounts - GetKeyAction addActions = new GetKeyAction(identity); + // get the key + GetKeyByIdAction addActions = new GetKeyByIdAction(id); key = transaction.execute(addActions); // commit the transaction @@ -76,11 +75,9 @@ public class StandardKeyService implements KeyService { } @Override - public String getOrCreateKey(String identity) { - // TODO: Change this service to look up by "key ID" instead of identity - // TODO: Change the return type to a Key POJO to support key rotation + public Key getOrCreateKey(String identity) { Transaction transaction = null; - String key = null; + Key key = null; writeLock.lock(); try { http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java new file mode 100644 index 0000000..b7158c2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java @@ -0,0 +1,69 @@ +/* + * 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.key; + +import java.io.Serializable; + +/** + * An signing key for a NiFi user. + */ +public class Key implements Serializable { + + private int id; + private String identity; + private String key; + + /** + * The key id. + * + * @return the id + */ + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + /** + * The identity of the user this key is associated with. + * + * @return the identity + */ + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + /** + * The signing key. + * + * @return the signing key + */ + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java index 2e1c44e..27d7d29 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java @@ -55,6 +55,7 @@ import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.entity.AccessStatusEntity; import org.apache.nifi.web.api.entity.AccessConfigurationEntity; import org.apache.nifi.web.api.request.ClientIdParameter; +import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.UntrustedProxyException; import org.apache.nifi.web.security.jwt.JwtService; @@ -185,55 +186,52 @@ public class AccessResource extends ApplicationResource { accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name()); accessStatus.setMessage("No credentials supplied, unknown user."); } else { - // Extract the Base64 encoded token from the Authorization header - final String token = StringUtils.substringAfterLast(authorization, " "); - try { + // Extract the Base64 encoded token from the Authorization header + final String token = StringUtils.substringAfterLast(authorization, " "); final String principal = jwtService.getAuthenticationFromToken(token); - // ensure we have something we can work with (certificate or credentials) - if (principal == null) { - throw new IllegalArgumentException("The specific token is not valid."); - } else { - // set the user identity - accessStatus.setIdentity(principal); - accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + // set the user identity + accessStatus.setIdentity(principal); + accessStatus.setUsername(CertificateUtils.extractUsername(principal)); - // without a certificate, this is not a proxied request - final List<String> chain = Arrays.asList(principal); + // without a certificate, this is not a proxied request + final List<String> chain = Arrays.asList(principal); - // check authorization for this user - checkAuthorization(chain); + // check authorization for this user + checkAuthorization(chain); - // no issues with authorization - accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setMessage("Account is active and authorized"); - } + // no issues with authorization + accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); + accessStatus.setMessage("Account is active and authorized"); } catch (JwtException e) { - // TODO: Handle the exception from a failed JWT verification - throw new AccessDeniedException("The JWT could not be verified", e); + throw new InvalidAuthenticationException(e.getMessage(), e); } } } else { - final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates); - - // get the proxy chain and ensure its populated - final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity()); - if (proxyChain.isEmpty()) { - logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity())); - throw new IllegalArgumentException("Unable to determine the user from the incoming request."); - } + try { + final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates); + + // get the proxy chain and ensure its populated + final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity()); + if (proxyChain.isEmpty()) { + logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity())); + throw new IllegalArgumentException("Unable to determine the user from the incoming request."); + } - // ensure the proxy chain is authorized - checkAuthorization(proxyChain); + // set the user identity + accessStatus.setIdentity(proxyChain.get(0)); + accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0))); - // set the user identity - accessStatus.setIdentity(proxyChain.get(0)); - accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0))); + // ensure the proxy chain is authorized + checkAuthorization(proxyChain); - // no issues with authorization - accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setMessage("Account is active and authorized"); + // no issues with authorization + accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); + accessStatus.setMessage("Account is active and authorized"); + } catch (final IllegalArgumentException iae) { + throw new InvalidAuthenticationException(iae.getMessage(), iae); + } } } catch (final UsernameNotFoundException unfe) { accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name()); @@ -323,20 +321,20 @@ public class AccessResource extends ApplicationResource { final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS); final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES); - + long expiration = authenticationResponse.getExpiration(); if (expiration > maxExpiration) { expiration = maxExpiration; - - logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration, + + logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration, authenticationResponse.getExpiration(), authenticationResponse.getIdentity())); } else if (expiration < minExpiration) { expiration = minExpiration; - - logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration, + + logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration, authenticationResponse.getExpiration(), authenticationResponse.getIdentity())); } - + // create the authentication token // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getName()); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java new file mode 100644 index 0000000..14d5139 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java @@ -0,0 +1,44 @@ +/* + * 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.api.config; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.web.security.InvalidAuthenticationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps access denied exceptions into a client response. + */ +@Provider +public class InvalidAuthenticationExceptionMapper implements ExceptionMapper<InvalidAuthenticationException> { + + private static final Logger logger = LoggerFactory.getLogger(InvalidAuthenticationExceptionMapper.class); + + @Override + public Response toResponse(InvalidAuthenticationException exception) { + if (logger.isDebugEnabled()) { + logger.debug(StringUtils.EMPTY, exception); + } + + return Response.status(Response.Status.UNAUTHORIZED).entity(exception.getMessage()).type("text/plain").build(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index 73929d8..9f3d2f5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -255,6 +255,7 @@ <!-- exception mapping --> <bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/> + <bean class="org.apache.nifi.web.api.config.InvalidAuthenticationExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.AuthenticationCredentialsNotFoundExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.AccountNotFoundExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/> http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java new file mode 100644 index 0000000..1065152 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java @@ -0,0 +1,35 @@ +/* + * 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 org.springframework.security.core.AuthenticationException; + +/** + * Thrown if the authentication of a given request is invalid. For instance, + * an expired certificate or token. + */ +public class InvalidAuthenticationException extends AuthenticationException { + + public InvalidAuthenticationException(String msg) { + super(msg); + } + + public InvalidAuthenticationException(String msg, Throwable t) { + super(msg, t); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java index ec34ace..f09d610 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java @@ -36,7 +36,6 @@ import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AccountStatusException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; @@ -134,8 +133,8 @@ public abstract class NiFiAuthenticationFilter implements Filter { response.setStatus(HttpServletResponse.SC_FORBIDDEN); out.println("Access is denied."); } - } else if (ae instanceof BadCredentialsException) { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } else if (ae instanceof InvalidAuthenticationException) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); out.println(ae.getMessage()); } else if (ae instanceof AccountStatusException) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java index dea5bba..20675fb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; +import org.apache.nifi.web.security.InvalidAuthenticationException; /** */ @@ -49,7 +50,6 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter { // TODO: Refactor request header extraction logic to shared utility as it is duplicated in AccessResource // get the principal out of the user token - // look for an authorization token final String authorization = request.getHeader(AUTHORIZATION); // if there is no authorization header, we don't know the user @@ -61,9 +61,6 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter { try { final String jwtPrincipal = jwtService.getAuthenticationFromToken(token); - if (jwtPrincipal == null) { - return null; - } if (isNewAccountRequest(request)) { return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request))); @@ -71,9 +68,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter { return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal)); } } catch (JwtException e) { - // TODO: Is this the correct way to handle an unverified token? - logger.error("Could not verify JWT", e); - return null; + throw new InvalidAuthenticationException(e.getMessage(), e); } } } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java index f006e5b..9635354 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.Calendar; +import org.apache.nifi.key.Key; /** * @@ -88,17 +89,16 @@ public class JwtService { public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { final String identity = claims.getSubject(); - // TODO: Currently the kid field is identical to identity, but will be a unique key ID when key rotation is implemented - final String keyId = claims.get(KEY_ID_CLAIM, String.class); - // The key is unique per identity and should be retrieved from the key service - final String key = keyService.getKey(keyId); + // 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); // Ensure we were able to find a key that was previously issued by this key service for this user - if (key == null) { + if (key == null || key.getKey() == null) { throw new UnsupportedJwtException("Unable to determine signing key for " + identity + " [kid: " + keyId + "]"); } - return key.getBytes(StandardCharsets.UTF_8); + return key.getKey().getBytes(StandardCharsets.UTF_8); } }).parseClaimsJws(base64EncodedToken); } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) { @@ -137,21 +137,19 @@ public class JwtService { try { // Get/create the key for this user - final String key = keyService.getOrCreateKey(identity); - final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + final Key key = keyService.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 - // TODO: Change kid field to key ID when KeyService is refactored - // Build the token return Jwts.builder().setSubject(identity) .setIssuer(authenticationToken.getIssuer()) .setAudience(authenticationToken.getIssuer()) .claim(USERNAME_CLAIM, username) - .claim(KEY_ID_CLAIM, identity) + .claim(KEY_ID_CLAIM, key.getId()) .setExpiration(expiration.getTime()) .setIssuedAt(Calendar.getInstance().getTime()) .signWith(SIGNATURE_ALGORITHM, keyBytes).compact(); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java index e626f74..dd7d47e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java @@ -21,6 +21,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.nifi.authentication.AuthenticationResponse; +import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.NiFiAuthenticationFilter; import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken; @@ -28,7 +29,6 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; import org.apache.nifi.web.security.user.NewAccountRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.BadCredentialsException; /** * Custom X509 filter that will inspect the HTTP headers for a proxied user before extracting the user details from the client certificate. @@ -58,7 +58,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter { try { authenticationResponse = certificateIdentityProvider.authenticate(certificates); } catch (final IllegalArgumentException iae) { - throw new BadCredentialsException(iae.getMessage(), iae); + throw new InvalidAuthenticationException(iae.getMessage(), iae); } final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity()); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java index c9107b9..ea3e122 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java @@ -22,10 +22,12 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.LinkedHashMap; import java.util.Map; +import org.apache.nifi.key.Key; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.when; /** @@ -117,9 +119,14 @@ public class JwtServiceTest { @Before public void setUp() throws Exception { + final Key key = new Key(); + key.setId(0); + key.setIdentity(HMAC_SECRET); + key.setKey(HMAC_SECRET); + mockKeyService = Mockito.mock(KeyService.class); - when(mockKeyService.getKey(anyString())).thenReturn(HMAC_SECRET); - when(mockKeyService.getOrCreateKey(anyString())).thenReturn(HMAC_SECRET); + when(mockKeyService.getKey(anyInt())).thenReturn(key); + when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key); jwtService = new JwtService(mockKeyService); } http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js index eab5297..7d63534 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js @@ -142,7 +142,7 @@ nf.CanvasHeader = (function () { }); // show the login link if supported and user is currently anonymous - var isAnonymous = $('#current-user').text() === nf.Canvas.ANONYMOUS_USER_TEXT; + var isAnonymous = $('#current-user').text() === nf.Common.ANONYMOUS_USER_TEXT; if (supportsLogin === true && isAnonymous) { // login link $('#login-link').click(function () { http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index 09a8212..c9498fe 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -936,7 +936,6 @@ nf.Canvas = (function () { }; return { - ANONYMOUS_USER_TEXT: 'Anonymous user', CANVAS_OFFSET: 0, /** * Determines if the current broswer supports SVG. @@ -1056,17 +1055,8 @@ nf.Canvas = (function () { $('#logout-link-container').show(); } } else { - // alert user's of anonymous access - $('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, { - content: 'You are accessing with limited authority. Log in or request an account to access with additional authority granted to you by an administrator.', - position: { - my: 'top right', - at: 'bottom left' - } - })); - - // render the anonymous user text - $('#current-user').text(nf.Canvas.ANONYMOUS_USER_TEXT).show(); + // set the anonymous user label + nf.Common.setAnonymousUserLabel(); } deferred.resolve(); }).fail(function (xhr, status, error) { http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js index 89db6ce..bd39a44 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js @@ -96,8 +96,10 @@ nf.Login = (function () { 'password': $('#password').val() } }).done(function (jwt) { - // store the jwt and reload the page - nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt)); + // get the payload and store the token with the appropirate expiration + var token = nf.Common.getJwtPayload(jwt); + var expiration = parseInt(token['exp'], 10) * nf.Common.MILLIS_PER_SECOND; + nf.Storage.setItem('jwt', jwt, expiration); // check to see if they actually have access now $.ajax({ @@ -112,8 +114,7 @@ nf.Login = (function () { nf.Common.scheduleTokenRefresh(); // show the user - var user = nf.Common.getJwtSubject(jwt); - $('#nifi-user-submit-justification').text(user); + $('#nifi-user-submit-justification').text(token['preferred_username']); // show the registration form initializeNiFiRegistration(); @@ -133,8 +134,7 @@ nf.Login = (function () { nf.Common.scheduleTokenRefresh(); // show the user - var user = nf.Common.getJwtSubject(jwt); - $('#nifi-user-submit-justification').text(user); + $('#nifi-user-submit-justification').text(token['preferred_username']); if (xhr.status === 401) { initializeNiFiRegistration(); http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 3be6b91..9202819 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -54,9 +54,17 @@ $(document).ready(function () { // include jwt when possible $.ajaxSetup({ 'beforeSend': function(xhr) { + var hadToken = nf.Storage.hasItem('jwt'); + + // get the token to include in all requests var token = nf.Storage.getItem('jwt'); - if (token) { + if (token !== null) { xhr.setRequestHeader('Authorization', 'Bearer ' + token); + } else { + // if the current user was logged in with a token and the token just expired, reload + if (hadToken === true) { + return false; + } } } }); @@ -83,6 +91,8 @@ nf.Common = (function () { var tokenRefreshInterval = null; return { + ANONYMOUS_USER_TEXT: 'Anonymous user', + config: { sensitiveText: 'Sensitive value set', tooltipConfig: { @@ -100,9 +110,6 @@ nf.Common = (function () { at: 'top right', my: 'bottom left' } - }, - urls: { - token: '../nifi-api/access/token' } }, @@ -148,7 +155,7 @@ nf.Common = (function () { } // set the interval to one hour - var interval = nf.Common.MILLIS_PER_HOUR; + var interval = 10 * nf.Common.MILLIS_PER_MINUTE; var checkExpiration = function () { var expiration = nf.Storage.getItemExpiration('jwt'); @@ -161,13 +168,16 @@ nf.Common = (function () { // get the time remainging plus a little bonus time to reload the token var timeRemaining = expirationDate.valueOf() - now.valueOf() - nf.Common.MILLIS_PER_MINUTE; if (timeRemaining < interval) { - // if the token will expire before the next interval minus some bonus time, refresh now - $.ajax({ - type: 'POST', - url: nf.Common.config.urls.token - }).done(function (jwt) { - nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt)); - }); + if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT && !$('#anonymous-user-alert').is(':visible')) { + // if the token will expire before the next interval minus some bonus time, notify the user to re-login + $('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, { + content: 'Your session will expire soon. Please log in again to avoid being automatically logged out.', + position: { + my: 'top right', + at: 'bottom left' + } + })); + } } } }; @@ -180,56 +190,46 @@ nf.Common = (function () { }, /** - * Extracts the subject from the specified jwt. If the jwt is not as expected - * an empty string is returned. - * - * @param {string} jwt - * @returns {string} + * Sets the anonymous user label. */ - getJwtSubject: function (jwt) { - if (nf.Common.isDefinedAndNotNull(jwt)) { - var segments = jwt.split(/\./); - if (segments.length !== 3) { - return ''; - } - - var rawPayload = $.base64.atob(segments[1]); - var payload = JSON.parse(rawPayload); - - if (nf.Common.isDefinedAndNotNull(payload['preferred_username'])) { - return payload['preferred_username']; - } else { - ''; - } + setAnonymousUserLabel: function () { + var anonymousUserAlert = $('#anonymous-user-alert'); + if (anonymousUserAlert.data('qtip')) { + anonymousUserAlert.qtip('api').destroy(true); } + + // alert user's of anonymous access + anonymousUserAlert.show().qtip($.extend({}, nf.Common.config.tooltipConfig, { + content: 'You are accessing with limited authority. Log in or request an account to access with additional authority granted to you by an administrator.', + position: { + my: 'top right', + at: 'bottom left' + } + })); - return ''; + // render the anonymous user text + $('#current-user').text(nf.Common.ANONYMOUS_USER_TEXT).show(); }, /** - * Extracts the expiration from the specified jwt. If the jwt is not as expected - * a null value is returned. + * Extracts the subject from the specified jwt. If the jwt is not as expected + * an empty string is returned. * * @param {string} jwt - * @returns {integer} + * @returns {string} */ - getJwtExpiration: function (jwt) { + getJwtPayload: function (jwt) { if (nf.Common.isDefinedAndNotNull(jwt)) { var segments = jwt.split(/\./); if (segments.length !== 3) { - return null; + return ''; } var rawPayload = $.base64.atob(segments[1]); var payload = JSON.parse(rawPayload); - if (nf.Common.isDefinedAndNotNull(payload['exp'])) { - try { - // jwt exp is in seconds - return parseInt(payload['exp'], 10) * nf.Common.MILLIS_PER_SECOND; - } catch (e) { - return null; - } + if (nf.Common.isDefinedAndNotNull(payload)) { + return payload; } else { return null; } @@ -313,6 +313,28 @@ nf.Common = (function () { * @argument {string} error The error */ handleAjaxError: function (xhr, status, error) { + if (status === 'canceled') { + if ($('#splash').is(':visible')) { + $('#message-title').text('Session Expired'); + $('#message-content').text('Your session has expired. Please reload to log in again.'); + + // show the error pane + $('#message-pane').show(); + + // close the canvas + nf.Common.closeCanvas(); + } else { + nf.Dialog.showOkDialog({ + dialogContent: 'Your session has expired. Please press Ok to log in again.', + overlayBackground: false, + okHandler: function () { + window.location = '/nifi'; + } + }); + } + return; + } + // if an error occurs while the splash screen is visible close the canvas show the error message if ($('#splash').is(':visible')) { if (xhr.status === 401) { http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js index 2d1183f..d305e4d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js @@ -23,15 +23,6 @@ $(document).ready(function () { // configure the ok dialog $('#nf-ok-dialog').modal({ - buttons: [{ - buttonText: 'Ok', - handler: { - click: function () { - // close the dialog - $('#nf-ok-dialog').modal('hide'); - } - } - }], handler: { close: function () { // clear the content @@ -77,6 +68,20 @@ nf.Dialog = (function () { var content = $('<p></p>').append(options.dialogContent); $('#nf-ok-dialog-content').append(content); + // update the button model + $('#nf-ok-dialog').modal('setButtonModel', [{ + buttonText: 'Ok', + handler: { + click: function () { + // close the dialog + $('#nf-ok-dialog').modal('hide'); + if (typeof options.okHandler === 'function') { + options.okHandler.call(this); + } + } + } + }]); + // show the dialog $('#nf-ok-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show'); }, http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js index cdb2e9e..7b0c3f6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js @@ -42,32 +42,18 @@ nf.Storage = (function () { }; /** - * If the item at key is not expired, the value of field is returned. Otherwise, null. + * Gets an enty for the key. The entry expiration is not checked. * * @param {string} key - * @param {string} field - * @return {object} the value */ - var getEntryField = function (key, field) { + var getEntry = function (key) { try { // parse the entry var entry = JSON.parse(localStorage.getItem(key)); // ensure the entry and item are present if (nf.Common.isDefinedAndNotNull(entry)) { - - // if the entry is expired, drop it and return null - if (checkExpiration(entry)) { - nf.Storage.removeItem(key); - return null; - } - - // if the entry has the specified field return its value - if (nf.Common.isDefinedAndNotNull(entry[field])) { - return entry[field]; - } else { - return null; - } + return entry; } else { return null; } @@ -75,7 +61,7 @@ nf.Storage = (function () { return null; } }; - + return { /** * Initializes the storage. Items will be persisted for two days. Once the scripts runs @@ -86,11 +72,9 @@ nf.Storage = (function () { try { // get the next item var key = localStorage.key(i); - var entry = JSON.parse(localStorage.getItem(key)); - - if (checkExpiration(entry)) { - nf.Storage.removeItem(key); - } + + // attempt to get the item which will expire if necessary + nf.Storage.getItem(key); } catch (e) { } } @@ -118,6 +102,17 @@ nf.Storage = (function () { }, /** + * Returns whether there is an entry for this key. This will not check the expiration. If + * the entry is expired, it will return null on a subsequent getItem invocation. + * + * @param {string} key + * @returns {boolean} + */ + hasItem: function (key) { + return getEntry(key) !== null; + }, + + /** * Gets the item with the specified key. If an item with this key does * not exist, null is returned. If an item exists but cannot be parsed * or is malformed/unrecognized, null is returned. @@ -125,18 +120,44 @@ nf.Storage = (function () { * @param {type} key */ getItem: function (key) { - return getEntryField(key, 'item'); + var entry = getEntry(key); + if (entry === null) { + return null; + } + + // if the entry is expired, drop it and return null + if (checkExpiration(entry)) { + nf.Storage.removeItem(key); + return null; + } + + // if the entry has the specified field return its value + if (nf.Common.isDefinedAndNotNull(entry['item'])) { + return entry['item']; + } else { + return null; + } }, /** - * Gets the expiration for the specified item. If the item does not exists our could - * not be parsed, returns null. + * Gets the expiration for the specified item. This will not check the expiration. If + * the entry is expired, it will return null on a subsequent getItem invocation. * * @param {string} key * @returns {integer} */ getItemExpiration: function (key) { - return getEntryField(key, 'expires'); + var entry = getEntry(key); + if (entry === null) { + return null; + } + + // if the entry has the specified field return its value + if (nf.Common.isDefinedAndNotNull(entry['expires'])) { + return entry['expires']; + } else { + return null; + } }, /** http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java deleted file mode 100644 index 64c48bf..0000000 --- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java +++ /dev/null @@ -1,106 +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.ldap; - -import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authentication.AuthenticationResponse; -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.apache.nifi.util.FormatUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ldap.CommunicationException; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider; -import org.springframework.security.ldap.userdetails.LdapUserDetails; - -/** - * 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; - private long expiration; - - @Override - public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException { - } - - @Override - public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException { - final String rawExpiration = configurationContext.getProperty("Expiration Duration"); - if (StringUtils.isBlank(rawExpiration)) { - throw new ProviderCreationException("The Expiration Duration must be specified."); - } - - try { - expiration = FormatUtils.getTimeDuration(rawExpiration, TimeUnit.MILLISECONDS); - } catch (final IllegalArgumentException iae) { - throw new ProviderCreationException(String.format("The Expiration Duration '%s' is not a valid time duration", rawExpiration)); - } - - provider = getLdapAuthenticationProvider(configurationContext); - } - - protected abstract AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException; - - @Override - public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException { - if (provider == null) { - throw new IdentityAccessException("The LDAP authentication provider is not initialized."); - } - - try { - // perform the authentication - final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword()); - final Authentication authentication = provider.authenticate(token); - - // attempt to get the ldap user details to get the DN - if (authentication.getPrincipal() instanceof LdapUserDetails) { - final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal(); - return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration); - } else { - return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration); - } - } catch (final CommunicationException | AuthenticationServiceException e) { - logger.error(e.getMessage()); - if (logger.isDebugEnabled()) { - logger.debug(StringUtils.EMPTY, e); - } - throw new IdentityAccessException("Unable to query the configured directory server. See the logs for additional details.", e); - } catch (final BadCredentialsException bce) { - throw new InvalidLoginCredentialsException(bce.getMessage(), bce); - } - } - - @Override - public final void preDestruction() throws ProviderDestructionException { - } - -} http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java deleted file mode 100644 index e43c1ee..0000000 --- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java +++ /dev/null @@ -1,51 +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.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 url = configurationContext.getProperty("Url"); - if (StringUtils.isBlank(url)) { - throw new ProviderCreationException("The Active Directory 'Url' must be specified."); - } - - final String domain = configurationContext.getProperty("Domain"); - final String userSearchBase = configurationContext.getProperty("User Search Base"); - - final ActiveDirectoryLdapAuthenticationProvider activeDirectoryAuthenticationProvider - = new ActiveDirectoryLdapAuthenticationProvider(StringUtils.isBlank(domain) ? null : domain, url, StringUtils.isBlank(userSearchBase) ? null : userSearchBase); - - final String userSearchFilter = configurationContext.getProperty("User Search Filter"); - if (StringUtils.isNotBlank(userSearchFilter)) { - activeDirectoryAuthenticationProvider.setSearchFilter(userSearchFilter); - } - - return activeDirectoryAuthenticationProvider; - } -}
