NIFI-655:

- Updating the login API authenticate method to use a richer set of exceptions.
- UI code clean.
This commit is contained in:
Matt Gilman 2015-11-10 14:55:02 -05:00
parent 2e158d15be
commit e61ccea7a0
8 changed files with 147 additions and 133 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)) {
// attempt to authenticate
loginIdentityProvider.authenticate(credentials);
// create the authentication token
return new LoginAuthenticationToken(credentials);
} else {
throw new BadCredentialsException("The supplied username and password are not valid.");
}
} 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) {

View File

@ -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);
}
}

View File

@ -141,15 +141,21 @@ 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) {
$('#login-link-container').css('display', 'none');
}
// 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');
}
// 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 () {
@ -344,7 +350,6 @@ nf.CanvasHeader = (function () {
toolbar.find('.secondary').hide();
}
},
/**
* Reloads and clears any warnings.
*/

View File

@ -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();

View File

@ -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();
}
@ -105,12 +100,50 @@ nf.Login = (function () {
// 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();
// 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 += '<br/><br/>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();

View File

@ -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';
});
});