mirror of https://github.com/apache/nifi.git
NIFI-655:
- Adding a new endpoint to obtain the status of a user registration. - Updated the login page loading to ensure all possible states work.
This commit is contained in:
parent
7f9807f461
commit
71d84117e4
|
@ -23,6 +23,7 @@ import org.apache.nifi.util.NiFiProperties;
|
||||||
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
|
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
|
||||||
import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
|
import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
|
||||||
import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
|
import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
|
||||||
|
import org.apache.nifi.web.security.RegistrationStatusFilter;
|
||||||
import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
|
import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
|
||||||
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
|
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
|
||||||
import org.apache.nifi.web.security.jwt.JwtService;
|
import org.apache.nifi.web.security.jwt.JwtService;
|
||||||
|
@ -90,11 +91,14 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||||
// login authentication for /token - exchanges for JWT for subsequent API usage
|
// login authentication for /token - exchanges for JWT for subsequent API usage
|
||||||
http.addFilterBefore(buildLoginFilter("/token"), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(buildLoginFilter("/token"), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
// verify the configured login authenticator supports registration
|
// verify the configured login authenticator supports user login registration
|
||||||
if (loginIdentityProvider.supportsRegistration()) {
|
if (loginIdentityProvider.supportsRegistration()) {
|
||||||
http.addFilterBefore(buildRegistrationFilter("/registration"), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(buildRegistrationFilter("/registration"), UsernamePasswordAuthenticationFilter.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// registration status - will check the status of a user's account registration (regardless if its based on login or not)
|
||||||
|
http.addFilterBefore(buildRegistrationStatusFilter("/registration/status"), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
// cluster authorized user
|
// cluster authorized user
|
||||||
http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
|
http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
|
||||||
|
@ -134,6 +138,15 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||||
private Filter buildRegistrationFilter(final String url) {
|
private Filter buildRegistrationFilter(final String url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Filter buildRegistrationStatusFilter(final String url) {
|
||||||
|
final RegistrationStatusFilter registrationFilter = new RegistrationStatusFilter(url);
|
||||||
|
registrationFilter.setCertificateExtractor(certificateExtractor);
|
||||||
|
registrationFilter.setPrincipalExtractor(principalExtractor);
|
||||||
|
registrationFilter.setProperties(properties);
|
||||||
|
registrationFilter.setUserDetailsService(userDetailsService);
|
||||||
|
return registrationFilter;
|
||||||
|
}
|
||||||
|
|
||||||
private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
|
private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
|
||||||
return new NodeAuthorizedUserFilter(properties);
|
return new NodeAuthorizedUserFilter(properties);
|
||||||
|
|
|
@ -929,7 +929,7 @@ public class ControllerResource extends ApplicationResource {
|
||||||
IdentityEntity entity = new IdentityEntity();
|
IdentityEntity entity = new IdentityEntity();
|
||||||
entity.setRevision(revision);
|
entity.setRevision(revision);
|
||||||
entity.setUserId(user.getId());
|
entity.setUserId(user.getId());
|
||||||
entity.setIdentity(user.getDn());
|
entity.setIdentity(user.getUserName());
|
||||||
|
|
||||||
// generate the response
|
// generate the response
|
||||||
return clusterContext(generateOkResponse(entity)).build();
|
return clusterContext(generateOkResponse(entity)).build();
|
||||||
|
@ -945,14 +945,17 @@ public class ControllerResource extends ApplicationResource {
|
||||||
@Consumes(MediaType.WILDCARD)
|
@Consumes(MediaType.WILDCARD)
|
||||||
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||||
@Path("/authorities")
|
@Path("/authorities")
|
||||||
@PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
|
@PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')")
|
||||||
@ApiOperation(
|
@ApiOperation(
|
||||||
value = "Retrieves the user details, including the authorities, about the user making the request",
|
value = "Retrieves the user details, including the authorities, about the user making the request",
|
||||||
response = AuthorityEntity.class,
|
response = AuthorityEntity.class,
|
||||||
authorizations = {
|
authorizations = {
|
||||||
@Authorization(value = "Read Only", type = "ROLE_MONITOR"),
|
@Authorization(value = "Read Only", type = "ROLE_MONITOR"),
|
||||||
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
|
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
|
||||||
@Authorization(value = "Administrator", type = "ROLE_ADMIN")
|
@Authorization(value = "Administrator", type = "ROLE_ADMIN"),
|
||||||
|
@Authorization(value = "Proxy", type = "ROLE_PROXY"),
|
||||||
|
@Authorization(value = "NiFi", type = "ROLE_NIFI"),
|
||||||
|
@Authorization(value = "Provenance", type = "ROLE_PROVENANCE")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.web.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.nifi.authentication.LoginCredentials;
|
||||||
|
import org.apache.nifi.util.NiFiProperties;
|
||||||
|
import org.apache.nifi.util.StringUtils;
|
||||||
|
import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
|
||||||
|
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.authentication.AccountStatusException;
|
||||||
|
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.userdetails.AuthenticationUserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchanges a successful login with the configured provider for a ID token for accessing the API.
|
||||||
|
*/
|
||||||
|
public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFilter {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(RegistrationStatusFilter.class);
|
||||||
|
|
||||||
|
private NiFiProperties properties;
|
||||||
|
private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
|
||||||
|
private X509CertificateExtractor certificateExtractor;
|
||||||
|
private X509PrincipalExtractor principalExtractor;
|
||||||
|
|
||||||
|
public RegistrationStatusFilter(final String defaultFilterProcessesUrl) {
|
||||||
|
super(defaultFilterProcessesUrl);
|
||||||
|
|
||||||
|
// do not continue filter chain... simply exchaning authentication for token
|
||||||
|
setContinueChainBeforeSuccessfulAuthentication(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
|
||||||
|
// only suppport login when running securely
|
||||||
|
if (!request.isSecure()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for a certificate
|
||||||
|
final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
|
||||||
|
|
||||||
|
// if no certificate, just check the credentials
|
||||||
|
if (certificate == null) {
|
||||||
|
final LoginCredentials credentials = getLoginCredentials(request);
|
||||||
|
|
||||||
|
if (credentials == null) {
|
||||||
|
throw new BadCredentialsException("Unable to check registration status as no credentials were included with the request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(request, credentials.getUsername()));
|
||||||
|
return new RegistrationStatusAuthenticationToken(credentials);
|
||||||
|
} else {
|
||||||
|
// we have a certificate so let's use that
|
||||||
|
final String principal = extractPrincipal(certificate);
|
||||||
|
checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(request, principal));
|
||||||
|
|
||||||
|
final LoginCredentials preAuthenticatedCredentials = new LoginCredentials(principal, null);
|
||||||
|
return new RegistrationStatusAuthenticationToken(preAuthenticatedCredentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the status of the proxy.
|
||||||
|
*
|
||||||
|
* @param proxyChain the proxy chain
|
||||||
|
* @throws AuthenticationException if the proxy chain is not authorized
|
||||||
|
*/
|
||||||
|
private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
|
||||||
|
userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractPrincipal(final X509Certificate certificate) {
|
||||||
|
// extract the principal
|
||||||
|
final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
|
||||||
|
return ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoginCredentials getLoginCredentials(HttpServletRequest request) {
|
||||||
|
final String username = request.getParameter("username");
|
||||||
|
final String password = request.getParameter("password");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new LoginCredentials(username, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
// mark as successful
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
response.setContentType("text/plain");
|
||||||
|
response.setContentLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException ae) throws IOException, ServletException {
|
||||||
|
// set the response status
|
||||||
|
response.setContentType("text/plain");
|
||||||
|
|
||||||
|
// write the response message
|
||||||
|
PrintWriter out = response.getWriter();
|
||||||
|
|
||||||
|
// use the type of authentication exception to determine the response code
|
||||||
|
if (ae instanceof UsernameNotFoundException) {
|
||||||
|
if (properties.getSupportNewAccountRequests()) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
out.println("Not authorized.");
|
||||||
|
} else {
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
out.println("Access is denied.");
|
||||||
|
}
|
||||||
|
} else if (ae instanceof AccountStatusException) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
out.println(ae.getMessage());
|
||||||
|
} else if (ae instanceof UntrustedProxyException) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
out.println(ae.getMessage());
|
||||||
|
} else if (ae instanceof AuthenticationServiceException) {
|
||||||
|
logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
out.println(String.format("Unable to authorize: %s", ae.getMessage()));
|
||||||
|
} else {
|
||||||
|
logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
out.println("Access is denied.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// log the failure
|
||||||
|
logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
|
||||||
|
|
||||||
|
// optionally log the stack trace
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(StringUtils.EMPTY, ae);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an Authentication Token for logging in. Once a user is authenticated, they can be issues an ID token.
|
||||||
|
*/
|
||||||
|
public static class RegistrationStatusAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
|
|
||||||
|
final LoginCredentials credentials;
|
||||||
|
|
||||||
|
public RegistrationStatusAuthenticationToken(final LoginCredentials credentials) {
|
||||||
|
super(null);
|
||||||
|
setAuthenticated(true);
|
||||||
|
this.credentials = credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginCredentials getLoginCredentials() {
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return credentials.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return credentials.getUsername();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
|
||||||
|
this.certificateExtractor = certificateExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
|
||||||
|
this.principalExtractor = principalExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties(NiFiProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,7 @@
|
||||||
${nf.login.script.tags}
|
${nf.login.script.tags}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<jsp:include page="/WEB-INF/partials/login/login-message.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
|
<jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/login/user-registration-form.jsp"/>
|
<jsp:include page="/WEB-INF/partials/login/user-registration-form.jsp"/>
|
||||||
<jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
|
<jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
<span id="current-user" class="hidden"></span>
|
<span id="current-user" class="hidden"></span>
|
||||||
<span id="login-link" class="link">login</span>
|
<span id="login-link" class="link">login</span>
|
||||||
</li>
|
</li>
|
||||||
|
<li id="logout-link-container" style="display: none;">
|
||||||
|
<span id="logout-link" class="link">logout</span>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span id="help-link" class="link">help</span>
|
<span id="help-link" class="link">help</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<%--
|
||||||
|
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.
|
||||||
|
--%>
|
||||||
|
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||||
|
<div id="login-message-container" class="hidden">
|
||||||
|
<div id="login-message"></div>
|
||||||
|
</div>
|
|
@ -15,6 +15,6 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
--%>
|
--%>
|
||||||
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||||
<div id="login-submission-container">
|
<div id="login-submission-container" class="hidden">
|
||||||
<button id="login-submission-button" type="submit" class="btn">Log in</button>
|
<button id="login-submission-button" type="submit" class="btn">Log in</button>
|
||||||
</div>
|
</div>
|
|
@ -506,6 +506,10 @@ div.search-glass-pane {
|
||||||
|
|
||||||
/* styles for the status link */
|
/* styles for the status link */
|
||||||
|
|
||||||
|
#current-user {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#utilities-container {
|
#utilities-container {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,11 @@ nf.CanvasHeader = (function () {
|
||||||
} else {
|
} else {
|
||||||
$('#login-link-container').css('display', 'none');
|
$('#login-link-container').css('display', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logout link
|
||||||
|
$('#logout-link').click(function () {
|
||||||
|
nf.Storage.removeItem("jwt");
|
||||||
|
});
|
||||||
|
|
||||||
// initialize the new template dialog
|
// initialize the new template dialog
|
||||||
$('#new-template-dialog').modal({
|
$('#new-template-dialog').modal({
|
||||||
|
|
|
@ -1054,6 +1054,7 @@ nf.Canvas = (function () {
|
||||||
nf.Common.setAuthorities(authoritiesResponse.authorities);
|
nf.Common.setAuthorities(authoritiesResponse.authorities);
|
||||||
|
|
||||||
// at this point the user may be themselves or anonymous
|
// at this point the user may be themselves or anonymous
|
||||||
|
$('#current-user').text(identityResponse.identity).show();
|
||||||
|
|
||||||
// if the user is logged, we want to determine if they were logged in using a certificate
|
// if the user is logged, we want to determine if they were logged in using a certificate
|
||||||
if (identityResponse.identity !== 'anonymous') {
|
if (identityResponse.identity !== 'anonymous') {
|
||||||
|
@ -1064,7 +1065,7 @@ nf.Canvas = (function () {
|
||||||
}).fail(function () {
|
}).fail(function () {
|
||||||
// if this request succeeds, it means the user is logged in using their certificate.
|
// if this request succeeds, it means the user is logged in using their certificate.
|
||||||
// if this request fails, it means the user is logged in with login credentials so we want to render a logout button.
|
// if this request fails, it means the user is logged in with login credentials so we want to render a logout button.
|
||||||
// TODO - render logout button
|
$('#logout-link-container').show();
|
||||||
}).always(function () {
|
}).always(function () {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,8 @@ nf.Login = (function () {
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
urls: {
|
urls: {
|
||||||
|
registrationStatus: '../nifi-api/registration/status',
|
||||||
|
registration: '../nifi-api/registration',
|
||||||
identity: '../nifi-api/controller/identity',
|
identity: '../nifi-api/controller/identity',
|
||||||
users: '../nifi-api/controller/users',
|
users: '../nifi-api/controller/users',
|
||||||
token: '../nifi-api/token',
|
token: '../nifi-api/token',
|
||||||
|
@ -32,6 +34,10 @@ nf.Login = (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var initializeMessage = function () {
|
||||||
|
$('#login-message-container').show();
|
||||||
|
};
|
||||||
|
|
||||||
var initializeLogin = function () {
|
var initializeLogin = function () {
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
|
@ -45,8 +51,29 @@ nf.Login = (function () {
|
||||||
|
|
||||||
// handle login click
|
// handle login click
|
||||||
$('#login-button').on('click', function () {
|
$('#login-button').on('click', function () {
|
||||||
login().done(function (response) {
|
login().done(function (response, status, xhr) {
|
||||||
|
var authorization = xhr.getResponseHeader('Authorization');
|
||||||
|
var badToken = false;
|
||||||
|
|
||||||
|
// ensure there was a token in the response
|
||||||
|
if (authorization) {
|
||||||
|
var tokens = authorization.split(/ /);
|
||||||
|
|
||||||
|
// ensure the token is the appropriate length
|
||||||
|
if (tokens.length === 2) {
|
||||||
|
// store the jwt and reload the page
|
||||||
|
nf.Storage.setItem('jwt', tokens[1]);
|
||||||
|
window.location = '/nifi';
|
||||||
|
} else {
|
||||||
|
badToken = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
badToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badToken === true) {
|
||||||
|
// TODO - show unable to parse response token
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,17 +139,19 @@ nf.Login = (function () {
|
||||||
'justification': justification
|
'justification': justification
|
||||||
}
|
}
|
||||||
}).done(function (response) {
|
}).done(function (response) {
|
||||||
// TODO
|
$('#login-message').text('Thanks! Your request will be processed shortly.');
|
||||||
// // hide the registration pane
|
}).fail(function (xhr, status, error) {
|
||||||
// $('#registration-pane').hide();
|
$('#login-message').text(xhr.responseText);
|
||||||
//
|
}).always(function () {
|
||||||
// // show the message pane
|
// update form visibility
|
||||||
// $('#message-pane').show();
|
$('#nifi-registration-container').hide();
|
||||||
// $('#message-title').text('Thanks');
|
$('#login-submission-container').hide();
|
||||||
// $('#message-content').text('Your request will be processed shortly.');
|
$('#login-message-container').show();
|
||||||
}).fail(nf.Common.handleAjaxError);
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#login-submission-container').show();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -132,6 +161,7 @@ nf.Login = (function () {
|
||||||
init: function () {
|
init: function () {
|
||||||
nf.Storage.init();
|
nf.Storage.init();
|
||||||
|
|
||||||
|
var showMessage = false;
|
||||||
var needsLogin = false;
|
var needsLogin = false;
|
||||||
var needsNiFiRegistration = false;
|
var needsNiFiRegistration = false;
|
||||||
|
|
||||||
|
@ -140,47 +170,87 @@ nf.Login = (function () {
|
||||||
url: config.urls.token
|
url: config.urls.token
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var identity = $.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: config.urls.identity,
|
||||||
|
dataType: 'json'
|
||||||
|
});
|
||||||
|
|
||||||
var pageStateInit = $.Deferred(function(deferred) {
|
var pageStateInit = $.Deferred(function(deferred) {
|
||||||
// get the current user's identity
|
// get the current user's identity
|
||||||
$.ajax({
|
identity.done(function (response) {
|
||||||
type: 'GET',
|
// if the user is anonymous see if they need to login or if they are working with a certificate
|
||||||
url: config.urls.identity,
|
if (response.identity === 'anonymous') {
|
||||||
dataType: 'json'
|
// request a token without including credentials, if successful then the user is using a certificate
|
||||||
}).done(function (response) {
|
|
||||||
var identity = response.identity;
|
|
||||||
|
|
||||||
// if the user is anonymous they need to login
|
|
||||||
if (identity === 'anonymous') {
|
|
||||||
token.done(function () {
|
token.done(function () {
|
||||||
// anonymous user and 200 from token means they have a certificate but have not yet requested an account
|
// the user is using a certificate, see if their account is active/pending/revoked/etc
|
||||||
needsNiFiRegistration = true;
|
$.ajax({
|
||||||
}).fail(function (xhr, status, error) {
|
type: 'GET',
|
||||||
// no token granted, user needs to login with their credentials
|
url: config.urls.registrationStatus
|
||||||
|
}).done(function () {
|
||||||
|
showMessage = true;
|
||||||
|
|
||||||
|
// account is active and good
|
||||||
|
$('#login-message').text('Your account is active and you are already logged in.');
|
||||||
|
deferred.resolve();
|
||||||
|
}).fail(function (xhr, status, error) {
|
||||||
|
if (xhr.status === 401) {
|
||||||
|
// anonymous user and 401 means they need nifi registration
|
||||||
|
needsNiFiRegistration = true;
|
||||||
|
} else {
|
||||||
|
showMessage = true;
|
||||||
|
|
||||||
|
// anonymous user and non-401 means they already have an account and it's pending/revoked
|
||||||
|
if ($.trim(xhr.responseText) === '') {
|
||||||
|
$('#login-message').text('Unable to check registration status.');
|
||||||
|
} else {
|
||||||
|
$('#login-message').text(xhr.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
}).fail(function () {
|
||||||
|
// no token granted, user has no certificate and needs to login with their credentials
|
||||||
needsLogin = true;
|
needsLogin = true;
|
||||||
|
deferred.resolve();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
showMessage = true;
|
||||||
|
|
||||||
|
// the user is not anonymous and has an active account (though maybe role-less)
|
||||||
|
$('#login-message').text('Your account is active and you are already logged in.');
|
||||||
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
}).fail(function (xhr, status, error) {
|
}).fail(function (xhr, status, error) {
|
||||||
|
// unable to get identity (and no anonymous user) see if we can offer login
|
||||||
if (xhr.status === 401) {
|
if (xhr.status === 401) {
|
||||||
// attempt to get a token for the current user without passing login credentials
|
// attempt to get a token for the current user without passing login credentials
|
||||||
token.done(function () {
|
token.done(function () {
|
||||||
// 401 from identity request and 200 from token means they have a certificate but have not yet requested an account
|
// 401 from identity request and 200 from token means they have a certificate but have not yet requested an account
|
||||||
needsNiFiRegistration = true;
|
needsNiFiRegistration = true;
|
||||||
}).fail(function (xhr, status, error) {
|
}).fail(function () {
|
||||||
// no token granted, user needs to login with their credentials
|
// no token granted, user needs to login with their credentials
|
||||||
needsLogin = true;
|
needsLogin = true;
|
||||||
});
|
});
|
||||||
} else if (xhr.status === 403) {
|
} else {
|
||||||
// the user is logged in with certificate or credentials but their account is still pending. error message should indicate
|
showMessage = true;
|
||||||
// TODO - show error
|
|
||||||
|
// the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate
|
||||||
|
if ($.trim(xhr.responseText) === '') {
|
||||||
|
$('#login-message').text('Unable to authorize you to use this NiFi and anonymous access is disabled.');
|
||||||
|
} else {
|
||||||
|
$('#login-message').text(xhr.responseText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).always(function () {
|
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
});
|
});
|
||||||
}).promise();
|
}).promise();
|
||||||
|
|
||||||
// render the page accordingly
|
// render the page accordingly
|
||||||
$.when(pageStateInit).done(function () {
|
$.when(pageStateInit).done(function () {
|
||||||
if (needsLogin === true) {
|
if (showMessage === true) {
|
||||||
|
initializeMessage();
|
||||||
|
} else if (needsLogin === true) {
|
||||||
initializeLogin();
|
initializeLogin();
|
||||||
} else if (needsNiFiRegistration === true) {
|
} else if (needsNiFiRegistration === true) {
|
||||||
initializeNiFiRegistration();
|
initializeNiFiRegistration();
|
||||||
|
|
|
@ -50,6 +50,16 @@ $(document).ready(function () {
|
||||||
// hide the loading indicator
|
// hide the loading indicator
|
||||||
$('div.loading-container').removeClass('ajax-loading');
|
$('div.loading-container').removeClass('ajax-loading');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// include jwt when possible
|
||||||
|
$.ajaxSetup({
|
||||||
|
'beforeSend': function(xhr) {
|
||||||
|
var token = nf.Storage.getItem('jwt');
|
||||||
|
if (token) {
|
||||||
|
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// initialize the tooltips
|
// initialize the tooltips
|
||||||
$('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
|
$('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
|
||||||
|
|
Loading…
Reference in New Issue