From e61ccea7a040683d20e808531c79056a6724657d Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Tue, 10 Nov 2015 14:55:02 -0500 Subject: [PATCH] NIFI-655: - Updating the login API authenticate method to use a richer set of exceptions. - UI code clean. --- .../authentication/LoginIdentityProvider.java | 5 +- .../InvalidLoginCredentialsException.java | 33 ++++ .../login/LoginAuthenticationFilter.java | 23 +-- .../LoginIdentityProviderFactoryBean.java | 4 +- .../webapp/js/nf/canvas/nf-canvas-header.js | 45 +++--- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 13 +- .../src/main/webapp/js/nf/login/nf-login.js | 149 +++++++----------- .../src/main/webapp/js/nf/nf-common.js | 8 +- 8 files changed, 147 insertions(+), 133 deletions(-) create mode 100644 nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java index 95f2efae8a..9039a4de20 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java +++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java @@ -17,6 +17,7 @@ package org.apache.nifi.authentication; 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; @@ -29,10 +30,10 @@ public interface LoginIdentityProvider { * Authenticates the specified login credentials. * * @param credentials the credentials - * @return was able to check the user credentials and returns whether the user was authenticated + * @throws InvalidLoginCredentialsException The login credentials were invalid * @throws IdentityAccessException Unable to register the user due to an issue accessing the underlying storage */ - boolean authenticate(LoginCredentials credentials) throws IdentityAccessException; + void authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException; /** * Called immediately after instance creation for implementers to perform additional setup diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java new file mode 100644 index 0000000000..fbfa3248e7 --- /dev/null +++ b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java @@ -0,0 +1,33 @@ +/* + * 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.authentication.exception; + +/** + * Represents the case when the identity could not be confirmed because the + * login credentials were invalid. + */ +public class InvalidLoginCredentialsException extends RuntimeException { + + public InvalidLoginCredentialsException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidLoginCredentialsException(String message) { + super(message); + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java index 39f2782a02..1e68d168c6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.nifi.authentication.LoginCredentials; import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.authentication.exception.IdentityAccessException; +import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException; import org.apache.nifi.util.StringUtils; import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.jwt.JwtService; @@ -142,11 +143,13 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF } try { - if (loginIdentityProvider.authenticate(credentials)) { - return new LoginAuthenticationToken(credentials); - } else { - throw new BadCredentialsException("The supplied username and password are not valid."); - } + // attempt to authenticate + loginIdentityProvider.authenticate(credentials); + + // create the authentication token + return new LoginAuthenticationToken(credentials); + } catch (final InvalidLoginCredentialsException ilce) { + throw new BadCredentialsException("The supplied username and password are not valid.", ilce); } catch (final IdentityAccessException iae) { throw new AuthenticationServiceException(iae.getMessage(), iae); } @@ -195,11 +198,6 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF @Override protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException { - response.setContentType("text/plain"); - - final PrintWriter out = response.getWriter(); - out.println(failed.getMessage()); - if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); } else if (failed instanceof AuthenticationServiceException) { @@ -207,6 +205,11 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } + + response.setContentType("text/plain"); + + final PrintWriter out = response.getWriter(); + out.println(failed.getMessage()); } public void setJwtService(JwtService jwtService) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java index 27e94575a4..0d35b29bef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java @@ -259,9 +259,9 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable return new LoginIdentityProvider() { @Override - public boolean authenticate(LoginCredentials credentials) { + public void authenticate(LoginCredentials credentials) { try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseProvider.authenticate(credentials); + baseProvider.authenticate(credentials); } } 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 62b576488b..eab5297857 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 @@ -141,16 +141,22 @@ nf.CanvasHeader = (function () { nf.Shell.showPage(config.urls.helpDocument); }); - // hide the login link if the user is already logged in - if ($('#current-user').text() !== nf.Canvas.ANONYMOUS_USER_TEXT) { + // show the login link if supported and user is currently anonymous + var isAnonymous = $('#current-user').text() === nf.Canvas.ANONYMOUS_USER_TEXT; + if (supportsLogin === true && isAnonymous) { + // login link + $('#login-link').click(function () { + nf.Shell.showPage('login', false); + }); + } else { $('#login-link-container').css('display', 'none'); } - - // login link - $('#login-link').click(function () { - nf.Shell.showPage('login', false); - }); - + + // if login is not supported, don't show the current user + if (supportsLogin === false) { + $('#current-user-container').css('display', 'none'); + } + // logout link $('#logout-link').click(function () { nf.Storage.removeItem("jwt"); @@ -172,11 +178,11 @@ nf.CanvasHeader = (function () { handler: { click: function () { var selection = nf.CanvasUtils.getSelection(); - + // color the selected components selection.each(function (d) { var selected = d3.select(this); - + var revision = nf.Client.getRevision(); var selectedData = selected.datum(); @@ -215,7 +221,7 @@ nf.CanvasHeader = (function () { }); } }); - + // close the dialog $('#fill-color-dialog').modal('hide'); } @@ -256,20 +262,20 @@ nf.CanvasHeader = (function () { }); } }); - + // updates the color if its a valid hex color string var updateColor = function () { var hex = $('#fill-color-value').val(); - + // only update the fill color when its a valid hex color string // #[six hex characters|three hex characters] case insensitive if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex)) { $('#fill-color').minicolors('value', hex); } }; - + // apply fill color from field on blur and enter press - $('#fill-color-value').on('blur', updateColor).on('keyup', function(e) { + $('#fill-color-value').on('blur', updateColor).on('keyup', function (e) { var code = e.keyCode ? e.keyCode : e.which; if (code === $.ui.keyCode.ENTER) { updateColor(); @@ -328,23 +334,22 @@ nf.CanvasHeader = (function () { } } }); - + var toolbar = $('#toolbar'); var groupButton = $('#action-group'); - $(window).on('resize', function() { + $(window).on('resize', function () { if (toolbar.width() < MIN_TOOLBAR_WIDTH && groupButton.is(':visible')) { toolbar.find('.secondary').hide(); } else if (toolbar.width() > MIN_TOOLBAR_WIDTH && groupButton.is(':hidden')) { toolbar.find('.secondary').show(); } }); - + // set up the initial visibility if (toolbar.width() < MIN_TOOLBAR_WIDTH) { toolbar.find('.secondary').hide(); } }, - /** * Reloads and clears any warnings. */ @@ -359,7 +364,7 @@ nf.CanvasHeader = (function () { // hide the refresh link on the canvas $('#stats-last-refreshed').removeClass('alert'); $('#refresh-required-container').hide(); - + // hide the refresh link on the settings $('#settings-last-refreshed').removeClass('alert'); $('#settings-refresh-required-icon').hide(); 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 5d7efcbaad..c316ef28dd 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 @@ -1087,6 +1087,13 @@ nf.Canvas = (function () { dataType: 'json' }); + // get the login config + var loginXhr = $.ajax({ + type: 'GET', + url: config.urls.loginConfig, + dataType: 'json' + }); + // create the deferred cluster request var isClusteredRequest = $.Deferred(function (deferred) { $.ajax({ @@ -1106,8 +1113,9 @@ nf.Canvas = (function () { }).promise(); // ensure the config requests are loaded - $.when(configXhr, userXhr).done(function (configResult) { + $.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) { var configResponse = configResult[0]; + var loginResponse = loginResult[0]; // calculate the canvas offset var canvasContainer = $('#canvas-container'); @@ -1115,6 +1123,7 @@ nf.Canvas = (function () { // get the config details var configDetails = configResponse.config; + var loginDetails = loginResponse.config; // when both request complete, load the application isClusteredRequest.done(function () { @@ -1134,7 +1143,7 @@ nf.Canvas = (function () { nf.ContextMenu.init(); nf.CanvasToolbar.init(); nf.CanvasToolbox.init(); - nf.CanvasHeader.init(); + nf.CanvasHeader.init(loginDetails.supportsLogin); nf.GraphControl.init(); nf.Search.init(); nf.Settings.init(); 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 2da60e3619..12f7ed71f6 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 @@ -40,17 +40,18 @@ nf.Login = (function () { $('#login-message-container').show(); }; - var initializeLogin = function () { - }; - var showLogin = function () { - $('#login-container').show(); - $('#user-registration-container').hide(); - $('#nifi-registration-container').hide(); + // reset the forms + $('#username').val(''); + $('#password').val(''); $('#login-submission-button').text('Log in'); - }; - - var initializeUserRegistration = function () { + + // update the form visibility + $('#login-container').show(); + $('#nifi-registration-container').hide(); + + // set the focus + $('#username').focus(); }; var initializeNiFiRegistration = function () { @@ -64,26 +65,20 @@ nf.Login = (function () { }); }; - var showUserRegistration = function () { - showNiFiRegistration(); - - $('div.nifi-submit-justification').hide(); - $('#user-registration-container').show(); - $('#login-submission-button').text('Create'); - }; - var showNiFiRegistration = function () { + // reset the forms + $('#login-submission-button').text('Submit'); + $('#nifi-registration-justification').val(''); + + // update the form visibility $('#login-container').hide(); $('#nifi-registration-container').show(); - $('#login-submission-button').text('Submit'); }; var initializeSubmission = function () { $('#login-submission-button').on('click', function () { if ($('#login-container').is(':visible')) { login(); - } else if ($('#user-registration-container').is(':visible')) { - createUserAccount(); } else if ($('#nifi-registration-container').is(':visible')) { submitJustification(); } @@ -104,13 +99,51 @@ nf.Login = (function () { }).done(function (jwt) { // store the jwt and reload the page nf.Storage.setItem('jwt', jwt); + + // check to see if they actually have access now + $.ajax({ + type: 'GET', + url: config.urls.identity, + dataType: 'json' + }).done(function (response) { + if (response.identity === 'anonymous') { + showLogoutLink(); - // reload as appropriate - if (top !== window) { - parent.window.location = '/nifi'; - } else { - window.location = '/nifi'; - } + // show the user + var user = getJwtSubject(jwt); + $('#nifi-user-submit-justification').text(user); + + // show the registration form + initializeNiFiRegistration(); + showNiFiRegistration(); + } else { + // reload as appropriate + if (top !== window) { + parent.window.location = '/nifi'; + } else { + window.location = '/nifi'; + } + } + }).fail(function (xhr, status, error) { + showLogoutLink(); + + // show the user + var user = getJwtSubject(jwt); + $('#nifi-user-submit-justification').text(user); + + if (xhr.status === 401) { + initializeNiFiRegistration(); + showNiFiRegistration(); + } else { + $('#login-message-title').text('Unable to log in'); + $('#login-message').text(xhr.responseText); + + // update visibility + $('#login-container').hide(); + $('#login-submission-container').hide(); + $('#login-message-container').show(); + } + }); }).fail(function (xhr, status, error) { if (xhr.status === 400) { nf.Dialog.showOkDialog({ @@ -129,53 +162,6 @@ nf.Login = (function () { }); }; - var createUserAccount = function () { - var password = $('#registration-password').val(); - var passwordConfirmation = $('#registration-password-confirmation').val(); - - // ensure the password matches - if (password !== passwordConfirmation) { - nf.Dialog.showOkDialog({ - dialogContent: 'The specified passwords do not match.', - overlayBackground: false - }); - return; - } - - // attempt to create the user account registration - $.ajax({ - type: 'POST', - url: config.urls.registration, - data: { - 'username': $('#registration-username').val(), - 'password': password, - 'justification': $('#nifi-registration-justification').val() - } - }).done(function (jwt) { - // store the jwt - nf.Storage.setItem('jwt', jwt); - showLogoutLink(); - - // inform the user of their pending request - var markup = 'An administrator will process your request shortly.'; - if (supportsAnonymous === true) { - markup += '

In the meantime you can continue accessing anonymously.'; - } - - $('#login-message-title').text('Thanks!'); - $('#login-message').html(markup); - }).fail(function (xhr, status, error) { - $('#login-message-title').text('Unable to create user account'); - $('#login-message').text(xhr.responseText); - }).always(function () { - // update form visibility - $('#user-registration-container').hide(); - $('#nifi-registration-container').hide(); - $('#login-submission-container').hide(); - $('#login-message-container').show(); - }); - }; - var submitJustification = function () { // attempt to create the nifi account registration $.ajax({ @@ -236,12 +222,6 @@ nf.Login = (function () { var showLogoutLink = function () { $('#user-logout-container').show(); - - // handle logout - $('#user-logout').on('click', function () { - logout(); - window.location = '/nifi/login'; - }); }; return { @@ -297,11 +277,6 @@ nf.Login = (function () { // show the user $('#nifi-user-submit-justification').text(user); - // render the logout button if there is a token locally - if (nf.Storage.getItem('jwt') !== null) { - $('#nifi-user-submit-justification-logout').show(); - } - // anonymous user and 401 means they need nifi registration needsNiFiRegistration = true; } else { @@ -346,11 +321,6 @@ nf.Login = (function () { // show the user $('#nifi-user-submit-justification').text(user); - // render the logout button if there is a token locally - if (nf.Storage.getItem('jwt') !== null) { - $('#nifi-user-submit-justification-logout').show(); - } - // 401 from identity request and 200 from token means they have a certificate/token but have not yet requested an account needsNiFiRegistration = true; }).fail(function (tokenXhr) { @@ -402,7 +372,6 @@ nf.Login = (function () { if (showMessage === true) { initializeMessage(); } else if (needsLogin === true) { - initializeLogin(); showLogin(); } else if (needsNiFiRegistration === true) { initializeNiFiRegistration(); 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 9b321c32ba..1c828c8e90 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 @@ -72,13 +72,7 @@ $(document).ready(function () { // handle logout $('#user-logout').on('click', function () { nf.Storage.removeItem('jwt'); - - // reload as appropriate - if (top !== window) { - parent.window.location = '/nifi'; - } else { - window.location = '/nifi'; - } + window.location = '/nifi/login'; }); });