Repository: nifi Updated Branches: refs/heads/NIFI-655 7f9807f46 -> 71d84117e
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. Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/71d84117 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/71d84117 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/71d84117 Branch: refs/heads/NIFI-655 Commit: 71d84117e4a3b67a90d9c83f2ff3f27bcdea6b95 Parents: 7f9807f Author: Matt Gilman <matt.c.gil...@gmail.com> Authored: Tue Nov 3 11:10:32 2015 -0500 Committer: Matt Gilman <matt.c.gil...@gmail.com> Committed: Tue Nov 3 11:10:32 2015 -0500 ---------------------------------------------------------------------- .../web/NiFiWebApiSecurityConfiguration.java | 15 +- .../apache/nifi/web/api/ControllerResource.java | 9 +- .../web/security/RegistrationStatusFilter.java | 217 +++++++++++++++++++ .../src/main/webapp/WEB-INF/pages/login.jsp | 1 + .../WEB-INF/partials/canvas/canvas-header.jsp | 3 + .../WEB-INF/partials/login/login-message.jsp | 20 ++ .../WEB-INF/partials/login/login-submission.jsp | 2 +- .../nifi-web-ui/src/main/webapp/css/header.css | 4 + .../webapp/js/nf/canvas/nf-canvas-header.js | 5 + .../src/main/webapp/js/nf/canvas/nf-canvas.js | 3 +- .../src/main/webapp/js/nf/login/nf-login.js | 128 ++++++++--- .../src/main/webapp/js/nf/nf-common.js | 10 + 12 files changed, 382 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java index e30f812..d6feef0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java @@ -23,6 +23,7 @@ import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.security.NiFiAuthenticationProvider; import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter; import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint; +import org.apache.nifi.web.security.RegistrationStatusFilter; import org.apache.nifi.web.security.form.LoginAuthenticationFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; 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 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()) { 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 http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class); @@ -134,6 +138,15 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte private Filter buildRegistrationFilter(final String url) { 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() { return new NodeAuthorizedUserFilter(properties); http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.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/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 1a23626..78e7d94 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -929,7 +929,7 @@ public class ControllerResource extends ApplicationResource { IdentityEntity entity = new IdentityEntity(); entity.setRevision(revision); entity.setUserId(user.getId()); - entity.setIdentity(user.getDn()); + entity.setIdentity(user.getUserName()); // generate the response return clusterContext(generateOkResponse(entity)).build(); @@ -945,14 +945,17 @@ public class ControllerResource extends ApplicationResource { @Consumes(MediaType.WILDCARD) @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) @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( value = "Retrieves the user details, including the authorities, about the user making the request", response = AuthorityEntity.class, authorizations = { @Authorization(value = "Read Only", type = "ROLE_MONITOR"), @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( http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.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/RegistrationStatusFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java new file mode 100644 index 0000000..073de67 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java @@ -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; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp index 5cdce5b..58bdaad 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp @@ -35,6 +35,7 @@ ${nf.login.script.tags} </head> <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/user-registration-form.jsp"/> <jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/> http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp index 54f6811..43e306e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp @@ -48,6 +48,9 @@ <span id="current-user" class="hidden"></span> <span id="login-link" class="link">login</span> </li> + <li id="logout-link-container" style="display: none;"> + <span id="logout-link" class="link">logout</span> + </li> <li> <span id="help-link" class="link">help</span> </li> http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp new file mode 100644 index 0000000..5284c23 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp @@ -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> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp index 787bb56..5749ab9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp @@ -15,6 +15,6 @@ limitations under the License. --%> <%@ 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> </div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css index 0f08b47..7161a8c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css @@ -506,6 +506,10 @@ div.search-glass-pane { /* styles for the status link */ +#current-user { + margin-right: 8px; +} + #utilities-container { float: right; } http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/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 653d895..551f33e 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 @@ -147,6 +147,11 @@ nf.CanvasHeader = (function () { } else { $('#login-link-container').css('display', 'none'); } + + // logout link + $('#logout-link').click(function () { + nf.Storage.removeItem("jwt"); + }); // initialize the new template dialog $('#new-template-dialog').modal({ http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/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 d2d9e82..28359d7 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 @@ -1054,6 +1054,7 @@ nf.Canvas = (function () { nf.Common.setAuthorities(authoritiesResponse.authorities); // 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 (identityResponse.identity !== 'anonymous') { @@ -1064,7 +1065,7 @@ nf.Canvas = (function () { }).fail(function () { // 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. - // TODO - render logout button + $('#logout-link-container').show(); }).always(function () { deferred.resolve(); }); http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/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 695a11c..45ef691 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 @@ -25,6 +25,8 @@ nf.Login = (function () { var config = { urls: { + registrationStatus: '../nifi-api/registration/status', + registration: '../nifi-api/registration', identity: '../nifi-api/controller/identity', users: '../nifi-api/controller/users', token: '../nifi-api/token', @@ -32,6 +34,10 @@ nf.Login = (function () { } }; + var initializeMessage = function () { + $('#login-message-container').show(); + }; + var initializeLogin = function () { return $.ajax({ type: 'GET', @@ -45,8 +51,29 @@ nf.Login = (function () { // handle login click $('#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 } }).done(function (response) { - // TODO - // // hide the registration pane - // $('#registration-pane').hide(); - // - // // show the message pane - // $('#message-pane').show(); - // $('#message-title').text('Thanks'); - // $('#message-content').text('Your request will be processed shortly.'); - }).fail(nf.Common.handleAjaxError); + $('#login-message').text('Thanks! Your request will be processed shortly.'); + }).fail(function (xhr, status, error) { + $('#login-message').text(xhr.responseText); + }).always(function () { + // update form visibility + $('#nifi-registration-container').hide(); + $('#login-submission-container').hide(); + $('#login-message-container').show(); + }); } }); + + $('#login-submission-container').show(); }; return { @@ -132,6 +161,7 @@ nf.Login = (function () { init: function () { nf.Storage.init(); + var showMessage = false; var needsLogin = false; var needsNiFiRegistration = false; @@ -140,47 +170,87 @@ nf.Login = (function () { url: config.urls.token }); + var identity = $.ajax({ + type: 'GET', + url: config.urls.identity, + dataType: 'json' + }); + var pageStateInit = $.Deferred(function(deferred) { // get the current user's identity - $.ajax({ - type: 'GET', - url: config.urls.identity, - dataType: 'json' - }).done(function (response) { - var identity = response.identity; - - // if the user is anonymous they need to login - if (identity === 'anonymous') { + identity.done(function (response) { + // if the user is anonymous see if they need to login or if they are working with a certificate + if (response.identity === 'anonymous') { + // request a token without including credentials, if successful then the user is using a certificate token.done(function () { - // anonymous user and 200 from token means they have a certificate but have not yet requested an account - needsNiFiRegistration = true; - }).fail(function (xhr, status, error) { - // no token granted, user needs to login with their credentials + // the user is using a certificate, see if their account is active/pending/revoked/etc + $.ajax({ + type: 'GET', + 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; + 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) { + // unable to get identity (and no anonymous user) see if we can offer login if (xhr.status === 401) { // attempt to get a token for the current user without passing login credentials token.done(function () { // 401 from identity request and 200 from token means they have a certificate but have not yet requested an account needsNiFiRegistration = true; - }).fail(function (xhr, status, error) { + }).fail(function () { // no token granted, user needs to login with their credentials needsLogin = true; }); - } else if (xhr.status === 403) { - // the user is logged in with certificate or credentials but their account is still pending. error message should indicate - // TODO - show error + } else { + showMessage = true; + + // 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(); }); }).promise(); // render the page accordingly $.when(pageStateInit).done(function () { - if (needsLogin === true) { + if (showMessage === true) { + initializeMessage(); + } else if (needsLogin === true) { initializeLogin(); } else if (needsNiFiRegistration === true) { initializeNiFiRegistration(); http://git-wip-us.apache.org/repos/asf/nifi/blob/71d84117/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 642bc31..c798a61 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 @@ -50,6 +50,16 @@ $(document).ready(function () { // hide the loading indicator $('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 $('img.setting-icon').qtip(nf.Common.config.tooltipConfig);