From 7529694f23cf46f977f7f1210f91ec4636f35e3e Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Mon, 16 Nov 2015 21:18:04 -0500 Subject: [PATCH] NIFI-655: - Added an endpoint for access details including configuration, creating tokens, and checking status. - Updated DTOs and client side to utilize new endpoints. --- ...onDTO.java => AccessConfigurationDTO.java} | 23 +- .../nifi/web/api/dto/AccessStatusDTO.java | 101 +++++ ...ty.java => AccessConfigurationEntity.java} | 18 +- .../web/api/entity/AccessStatusEntity.java | 43 ++ .../apache/nifi/web/NiFiServiceFacade.java | 8 - .../web/NiFiWebApiSecurityConfiguration.java | 47 +- .../nifi/web/StandardNiFiServiceFacade.java | 17 - .../apache/nifi/web/api/AccessResource.java | 412 ++++++++++++++++++ .../nifi/web/api/ControllerResource.java | 48 -- .../main/resources/nifi-web-api-context.xml | 10 +- .../security/RegistrationStatusFilter.java | 251 ----------- .../nifi/web/security/jwt/JwtService.java | 29 +- .../login/LoginAuthenticationFilter.java | 243 ----------- .../resources/nifi-web-security-context.xml | 2 +- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 4 +- .../src/main/webapp/js/nf/login/nf-login.js | 199 +++------ .../src/main/webapp/js/nf/nf-common.js | 4 +- 17 files changed, 667 insertions(+), 792 deletions(-) rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/{LoginConfigurationDTO.java => AccessConfigurationDTO.java} (70%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/{LoginConfigurationEntity.java => AccessConfigurationEntity.java} (69%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java similarity index 70% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java index 60f644bbaf..d9719b3f80 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java @@ -20,12 +20,13 @@ import com.wordnik.swagger.annotations.ApiModelProperty; import javax.xml.bind.annotation.XmlType; /** - * Details for the login configuration. + * Details for the access configuration. */ -@XmlType(name = "loginConfig") -public class LoginConfigurationDTO { +@XmlType(name = "accessConfig") +public class AccessConfigurationDTO { private Boolean supportsLogin; + private Boolean supportsAnonymous; /** * @return Indicates whether or not this NiFi supports user login. @@ -41,4 +42,20 @@ public class LoginConfigurationDTO { public void setSupportsLogin(Boolean supportsLogin) { this.supportsLogin = supportsLogin; } + + /** + * @return Indicates whether or not this NiFi supports anonymous access. + */ + @ApiModelProperty( + value = "Indicates whether or not this NiFi supports anonymous.", + readOnly = true + ) + public Boolean getSupportsAnonymous() { + return supportsAnonymous; + } + + public void setSupportsAnonymous(Boolean supportsAnonymous) { + this.supportsAnonymous = supportsAnonymous; + } + } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java new file mode 100644 index 0000000000..712da0ecb1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.dto; + +import com.wordnik.swagger.annotations.ApiModelProperty; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds the users access status. + */ +@XmlRootElement(name = "accessStatus") +public class AccessStatusDTO { + + public static enum Status { + + UNKNOWN, + UNREGISTERED, + NOT_ACTIVE, + ACTIVE + } + + private String identity; + private String username; + private String status; + private String message; + + /** + * @return the user identity + */ + @ApiModelProperty( + value = "The user identity.", + readOnly = true + ) + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + /** + * @return the username + */ + @ApiModelProperty( + value = "The username.", + readOnly = true + ) + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the user access status + */ + @ApiModelProperty( + value = "The user access status.", + readOnly = true + ) + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + /** + * @return additional details about the user access status + */ + @ApiModelProperty( + value = "Additional details about the user access status.", + readOnly = true + ) + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java similarity index 69% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java index da62d6fad1..3af0e493b3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java @@ -17,26 +17,26 @@ package org.apache.nifi.web.api.entity; import javax.xml.bind.annotation.XmlRootElement; -import org.apache.nifi.web.api.dto.LoginConfigurationDTO; +import org.apache.nifi.web.api.dto.AccessConfigurationDTO; /** - * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a LoginConfigurationDTO. + * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessConfigurationDTO. */ -@XmlRootElement(name = "loginConfigurationEntity") -public class LoginConfigurationEntity extends Entity { +@XmlRootElement(name = "accessConfigurationEntity") +public class AccessConfigurationEntity extends Entity { - private LoginConfigurationDTO config; + private AccessConfigurationDTO config; /** - * The LoginConfigurationDTO that is being serialized. + * The AccessConfigurationDTO that is being serialized. * - * @return The LoginConfigurationDTO object + * @return The AccessConfigurationDTO object */ - public LoginConfigurationDTO getConfig() { + public AccessConfigurationDTO getConfig() { return config; } - public void setConfig(LoginConfigurationDTO config) { + public void setConfig(AccessConfigurationDTO config) { this.config = config; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java new file mode 100644 index 0000000000..f19a26881d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; +import org.apache.nifi.web.api.dto.AccessStatusDTO; + +/** + * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessStatusDTO. + */ +@XmlRootElement(name = "accessStatusEntity") +public class AccessStatusEntity extends Entity { + + private AccessStatusDTO accessStatus; + + /** + * The AccessStatusDTO that is being serialized. + * + * @return The AccessStatusDTO object + */ + public AccessStatusDTO getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(AccessStatusDTO accessStatus) { + this.accessStatus = accessStatus; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index 3c64a02434..f4d5821be1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -43,7 +43,6 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.ComponentHistoryDTO; import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO; -import org.apache.nifi.web.api.dto.LoginConfigurationDTO; import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO; @@ -178,13 +177,6 @@ public interface NiFiServiceFacade { */ ControllerConfigurationDTO getControllerConfiguration(); - /** - * Gets the login configuration for this controller. - * - * @return The login configuration - */ - LoginConfigurationDTO getLoginConfiguration(); - /** * Updates the configuration for this controller. * 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 20bbc55767..216e3119c7 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 @@ -16,15 +16,12 @@ */ package org.apache.nifi.web; -import javax.servlet.Filter; import org.apache.nifi.admin.service.UserService; import org.apache.nifi.authentication.LoginIdentityProvider; 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.login.LoginAuthenticationFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; import org.apache.nifi.web.security.jwt.JwtService; import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter; @@ -35,7 +32,6 @@ import org.apache.nifi.web.security.x509.X509CertificateValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -46,7 +42,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; /** @@ -72,7 +67,9 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte @Override public void configure(WebSecurity webSecurity) throws Exception { - webSecurity.ignoring().antMatchers(HttpMethod.GET, "/controller/login/config"); + webSecurity + .ignoring() + .antMatchers("/access/**"); } @Override @@ -80,19 +77,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte http .rememberMe().disable() .exceptionHandling() - .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties)) - .and() + .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties)) + .and() .authorizeRequests() - .anyRequest().fullyAuthenticated() - .and() + .anyRequest().fullyAuthenticated() + .and() .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS); - - // login authentication for /token - exchanges for JWT for subsequent API usage - http.addFilterBefore(buildLoginFilter("/token"), 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); + .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // cluster authorized user http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class); @@ -121,28 +112,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte auth.authenticationProvider(new NiFiAuthenticationProvider(userDetailsService)); } - private LoginAuthenticationFilter buildLoginFilter(final String url) { - final LoginAuthenticationFilter loginFilter = new LoginAuthenticationFilter(url); - loginFilter.setJwtService(jwtService); - loginFilter.setLoginIdentityProvider(loginIdentityProvider); - loginFilter.setUserDetailsService(userDetailsService); - loginFilter.setCertificateExtractor(certificateExtractor); - loginFilter.setPrincipalExtractor(principalExtractor); - loginFilter.setCertificateValidator(certificateValidator); - return loginFilter; - } - - private Filter buildRegistrationStatusFilter(final String url) { - final RegistrationStatusFilter registrationStatusFilter = new RegistrationStatusFilter(url); - registrationStatusFilter.setCertificateExtractor(certificateExtractor); - registrationStatusFilter.setPrincipalExtractor(principalExtractor); - registrationStatusFilter.setCertificateValidator(certificateValidator); - registrationStatusFilter.setProperties(properties); - registrationStatusFilter.setJwtService(jwtService); - registrationStatusFilter.setUserDetailsService(userDetailsService); - return registrationStatusFilter; - } - private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() { return new NodeAuthorizedUserFilter(properties); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 324be87efb..b13077dfc5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -153,7 +153,6 @@ import org.apache.nifi.web.util.SnippetUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.components.Validator; import org.apache.nifi.controller.ReportingTaskNode; @@ -164,7 +163,6 @@ import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.reporting.ComponentType; import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO; -import org.apache.nifi.web.api.dto.LoginConfigurationDTO; import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.ReportingTaskDTO; import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO; @@ -207,7 +205,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { // administrative services private AuditService auditService; private UserService userService; - private LoginIdentityProvider loginIdentityProvider; // cluster manager private WebClusterManager clusterManager; @@ -2350,16 +2347,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { return controllerFacade.getInstanceId(); } - @Override - public LoginConfigurationDTO getLoginConfiguration() { - final LoginConfigurationDTO loginConfiguration = new LoginConfigurationDTO(); - - // specify whether login should be supported - loginConfiguration.setSupportsLogin(loginIdentityProvider != null); - - return loginConfiguration; - } - @Override public ControllerConfigurationDTO getControllerConfiguration() { ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO(); @@ -3420,10 +3407,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { this.snippetUtils = snippetUtils; } - public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) { - this.loginIdentityProvider = loginIdentityProvider; - } - private boolean isPrimaryNode(String nodeId) { final Node primaryNode = clusterManager.getPrimaryNode(); return (primaryNode != null && primaryNode.getNodeId().getId().equals(nodeId)); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java new file mode 100644 index 0000000000..9cb4141154 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.nifi.util.NiFiProperties; +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import java.net.URI; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import org.apache.nifi.admin.service.AdministrationException; +import org.apache.nifi.authentication.AuthenticationResponse; +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.security.util.CertificateUtils; +import org.apache.nifi.util.StringUtils; +import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID; +import org.apache.nifi.web.api.dto.AccessStatusDTO; +import org.apache.nifi.web.api.dto.AccessConfigurationDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.AccessStatusEntity; +import org.apache.nifi.web.api.entity.AccessConfigurationEntity; +import org.apache.nifi.web.api.request.ClientIdParameter; +import org.apache.nifi.web.security.ProxiedEntitiesUtils; +import org.apache.nifi.web.security.UntrustedProxyException; +import org.apache.nifi.web.security.jwt.JwtService; +import org.apache.nifi.web.security.token.LoginAuthenticationToken; +import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; +import org.apache.nifi.web.security.x509.X509CertificateExtractor; +import org.apache.nifi.web.security.x509.X509CertificateValidator; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AccountStatusException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.authentication.AuthenticationServiceException; +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.preauth.x509.X509PrincipalExtractor; + +/** + * RESTful endpoint for managing a cluster. + */ +@Path("/access") +@Api( + value = "/access", + description = "Endpoints for obtaining an access token or checking access status" +) +public class AccessResource extends ApplicationResource { + + private NiFiProperties properties; + + private X509CertificateValidator certificateValidator; + private X509CertificateExtractor certificateExtractor; + private X509PrincipalExtractor principalExtractor; + + private LoginIdentityProvider loginIdentityProvider; + private JwtService jwtService; + + private AuthenticationUserDetailsService userDetailsService; + + /** + * Retrieves the access configuration for this NiFi. + * + * @param httpServletRequest the servlet request + * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response. + * @return A accessConfigurationEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Path("/config") + @ApiOperation( + value = "Retrieves the access configuration for this NiFi", + response = AccessConfigurationEntity.class + ) + public Response getLoginConfig( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", + required = false + ) + @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) { + + final AccessConfigurationDTO accessConfiguration = new AccessConfigurationDTO(); + + // specify whether login should be supported and only support for secure requests + accessConfiguration.setSupportsLogin(loginIdentityProvider != null && httpServletRequest.isSecure()); + accessConfiguration.setSupportsAnonymous(!properties.getAnonymousAuthorities().isEmpty() || !httpServletRequest.isSecure()); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final AccessConfigurationEntity entity = new AccessConfigurationEntity(); + entity.setRevision(revision); + entity.setConfig(accessConfiguration); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Gets the status the client's access. + * + * @param httpServletRequest the servlet request + * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response. + * @return A accessStatusEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + @Path("") + @ApiOperation( + value = "Gets the status the client's access", + response = AccessStatusEntity.class + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getAccessStatus( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", + required = false + ) + @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) { + + // only consider user specific access over https + if (!httpServletRequest.isSecure()) { + throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS."); + } + + final AccessStatusDTO accessStatus = new AccessStatusDTO(); + + try { + // look for a certificate + final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest); + + // if no certificate, just check the credentials + if (certificate == null) { + final String principal = jwtService.getAuthentication(httpServletRequest); + + // ensure we have something we can work with (certificate or crendentials) + if (principal == null) { + accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name()); + accessStatus.setMessage("No credentials supplied, unknown user."); + } else { + // set the user identity + accessStatus.setIdentity(principal); + accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + + // without a certificate, this is not a proxied request + final List chain = Arrays.asList(principal); + + // check authorization for this user + checkAuthorization(chain); + + // no issues with authorization + accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); + accessStatus.setStatus("Account is active and authorized"); + } + } else { + // we have a certificate so let's consider a proxy chain + final String principal = principalExtractor.extractPrincipal(certificate).toString(); + + try { + // validate the certificate + certificateValidator.validateClientCertificate(httpServletRequest, certificate); + } catch (CertificateExpiredException cee) { + throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee); + } catch (CertificateNotYetValidException cnyve) { + throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve); + } catch (final Exception e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + + // set the user identity + accessStatus.setIdentity(principal); + accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + + // ensure the proxy chain is authorized + checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal)); + + // no issues with authorization + accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); + accessStatus.setStatus("Account is active and authorized"); + } + } catch (final UsernameNotFoundException unfe) { + accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name()); + accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity())); + } catch (final AccountStatusException ase) { + accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name()); + accessStatus.setMessage(ase.getMessage()); + } catch (final UntrustedProxyException upe) { + throw new AccessDeniedException(upe.getMessage(), upe); + } catch (final AuthenticationServiceException ase) { + throw new AdministrationException(ase.getMessage(), ase); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the entity + final AccessStatusEntity entity = new AccessStatusEntity(); + entity.setRevision(revision); + entity.setAccessStatus(accessStatus); + + return generateOkResponse(entity).build(); + } + + /** + * 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 proxyChain) throws AuthenticationException { + userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain)); + } + + /** + * Creates a token for accessing the REST API via username/password. + * + * @param httpServletRequest the servlet request + * @param username the username + * @param password the password + * @return A JWT (string) + */ + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.TEXT_PLAIN) + @Path("/token") + @ApiOperation( + value = "Creates a token for accessing the REST API via username/password", + response = String.class + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response createAccessToken( + @Context HttpServletRequest httpServletRequest, + @FormParam("username") String username, + @FormParam("password") String password) { + + // only support access tokens when communicating over HTTPS + if (!httpServletRequest.isSecure()) { + throw new IllegalStateException("Access tokens are only issued over HTTPS."); + } + + // if not configuration for login, don't consider credentials + if (loginIdentityProvider == null) { + throw new IllegalStateException("Username/Password login not supported by this NiFi."); + } + + final LoginAuthenticationToken loginAuthenticationToken; + + // if we don't have username/password, consider JWT or x509 + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + // look for a certificate + final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest); + + // if there is no certificate, look for an existing token + if (certificate == null) { + // if not configured for login, don't consider existing tokens + if (loginIdentityProvider == null) { + throw new IllegalStateException("Login not supported."); + } + + // look for the principal + final String principal = jwtService.getAuthentication(httpServletRequest); + if (principal == null) { + throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request."); + } + + // create the authentication token + loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); + } else { + // extract the principal + final String principal = principalExtractor.extractPrincipal(certificate).toString(); + + try { + certificateValidator.validateClientCertificate(httpServletRequest, certificate); + } catch (CertificateExpiredException cee) { + throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee); + } catch (CertificateNotYetValidException cnyve) { + throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve); + } catch (final Exception e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + + // authorize the proxy if necessary + authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal)); + + // create the authentication token + loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); + } + } else { + try { + // attempt to authenticate + final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); + + // create the authentication token + loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration()); + } catch (final InvalidLoginCredentialsException ilce) { + throw new IllegalArgumentException("The supplied username and password are not valid.", ilce); + } catch (final IdentityAccessException iae) { + throw new AdministrationException(iae.getMessage(), iae); + } + } + + // generate JWT for response + final String token = jwtService.generateSignedToken(loginAuthenticationToken); + + // build the response + final URI uri = URI.create(generateResourceUri("access", "token")); + return generateCreatedResponse(uri, token).build(); + } + + /** + * Ensures the proxyChain is authorized before allowing the user to be authenticated. + * + * @param proxyChain the proxy chain + * @throws AuthenticationException if the proxy chain is not authorized + */ + private void authorizeProxyIfNecessary(final List proxyChain) throws AuthenticationException { + if (proxyChain.size() > 1) { + try { + userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain)); + } catch (final UsernameNotFoundException unfe) { + // if a username not found exception was thrown, the proxies were authorized and now + // we can issue a new ID token to the end user + } catch (final Exception e) { + // any other issue we're going to treat as an authentication exception which will return 401 + throw new AdministrationException(e.getMessage(), e) { + }; + } + } + } + + // setters + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } + + public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) { + this.loginIdentityProvider = loginIdentityProvider; + } + + public void setJwtService(JwtService jwtService) { + this.jwtService = jwtService; + } + + public void setCertificateValidator(X509CertificateValidator certificateValidator) { + this.certificateValidator = certificateValidator; + } + + public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { + this.certificateExtractor = certificateExtractor; + } + + public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { + this.principalExtractor = principalExtractor; + } + + public void setUserDetailsService(AuthenticationUserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + +} 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 93f21b2e6b..6e6739d652 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 @@ -78,10 +78,8 @@ import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.LongParameter; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.web.api.dto.LoginConfigurationDTO; import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity; import org.apache.nifi.web.api.entity.IdentityEntity; -import org.apache.nifi.web.api.entity.LoginConfigurationEntity; import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -656,52 +654,6 @@ public class ControllerResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the login configuration for this NiFi. - * - * @param httpServletRequest the servlet request - * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response. - * @return A loginConfigurationEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/login/config") - @ApiOperation( - value = "Retrieves the login configuration for this NiFi", - response = LoginConfigurationEntity.class - ) - public Response getLoginConfig( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - final LoginConfigurationDTO loginConfig = serviceFacade.getLoginConfiguration(); - - // only support login/registration when running securely - loginConfig.setSupportsLogin(loginConfig.getSupportsLogin() && httpServletRequest.isSecure()); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final LoginConfigurationEntity entity = new LoginConfigurationEntity(); - entity.setRevision(revision); - entity.setConfig(loginConfig); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Retrieves the configuration for this NiFi. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index 96c3f2b1ba..e992dc9dd0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -122,7 +122,6 @@ - @@ -242,6 +241,15 @@ + + + + + + + + + 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 deleted file mode 100644 index 606d2e3081..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.security; - -import java.io.IOException; -import java.io.PrintWriter; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -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.jwt.JwtService; -import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; -import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509CertificateValidator; -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 JwtService jwtService; - private AuthenticationUserDetailsService userDetailsService; - private X509CertificateValidator certificateValidator; - 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 String principal = jwtService.getAuthentication(request); - - // ensure we have something we can work with (certificate or crendentials) - if (principal == null) { - throw new BadCredentialsException("Unable to check registration status as no credentials were included with the request."); - } - - // without a certificate, this is not a proxied request - final List chain = Arrays.asList(principal); - - // check authorization for this user - checkAuthorization(chain); - - // no issues with authorization - final LoginCredentials tokenCredentials = new LoginCredentials(principal, null); - return new RegistrationStatusAuthenticationToken(tokenCredentials); - } else { - // we have a certificate so let's consider a proxy chain - final String principal = principalExtractor.extractPrincipal(certificate).toString(); - - try { - // validate the certificate - certificateValidator.validateClientCertificate(request, certificate); - } catch (CertificateExpiredException cee) { - final String message = String.format("Client certificate for (%s) is expired.", principal); - logger.info(message, cee); - if (logger.isDebugEnabled()) { - logger.debug("", cee); - } - return null; - } catch (CertificateNotYetValidException cnyve) { - final String message = String.format("Client certificate for (%s) is not yet valid.", principal); - logger.info(message, cnyve); - if (logger.isDebugEnabled()) { - logger.debug("", cnyve); - } - return null; - } catch (final Exception e) { - logger.info(e.getMessage()); - if (logger.isDebugEnabled()) { - logger.debug("", e); - } - return null; - } - - // ensure the proxy chain is authorized - checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(request, principal)); - - // no issues with authorization - 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 proxyChain) throws AuthenticationException { - userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain)); - } - - @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 setJwtService(JwtService jwtService) { - this.jwtService = jwtService; - } - - public void setCertificateValidator(X509CertificateValidator certificateValidator) { - this.certificateValidator = certificateValidator; - } - - public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { - this.certificateExtractor = certificateExtractor; - } - - public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { - this.principalExtractor = principalExtractor; - } - - public void setUserDetailsService(AuthenticationUserDetailsService userDetailsService) { - this.userDetailsService = userDetailsService; - } - - public void setProperties(NiFiProperties properties) { - this.properties = properties; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java index 5dfbdfec21..2f6c7267e2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java @@ -26,12 +26,9 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.SigningKeyResolverAdapter; import io.jsonwebtoken.UnsupportedJwtException; -import java.io.IOException; -import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Calendar; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.admin.service.AdministrationException; import org.apache.nifi.admin.service.KeyService; @@ -83,35 +80,25 @@ public class JwtService { } /** - * Adds a token for the specified authentication in the specified response. + * Generates a signed JWT token from the provided (Spring Security) login authentication token. * - * @param response The response to add the token to - * @param authentication The authentication to generate a token for - * @throws java.io.IOException if an io exception occurs + * @param authenticationToken + * @return a signed JWT containing the user identity and the identity provider, Base64-encoded */ - public void addToken(final HttpServletResponse response, final LoginAuthenticationToken authentication) throws IOException { + public String generateSignedToken(final LoginAuthenticationToken authenticationToken) { // set expiration to one day from now final Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(calendar.getTimeInMillis() + authentication.getExpiration()); + calendar.setTimeInMillis(calendar.getTimeInMillis() + authenticationToken.getExpiration()); // create a token the specified authentication - final String identity = authentication.getPrincipal().toString(); - final String username = authentication.getName(); + final String identity = authenticationToken.getPrincipal().toString(); + final String username = authenticationToken.getName(); // get/create the key for this user final String key = keyService.getOrCreateKey(identity); final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); // build the token - final String token = Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, keyBytes).compact(); - - // add the token as a response header - final PrintWriter out = response.getWriter(); - out.print(token); - - // mark the response as successful - response.setStatus(HttpServletResponse.SC_CREATED); - response.setContentType("text/plain"); + return Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, keyBytes).compact(); } - } 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 deleted file mode 100644 index c90364a6bc..0000000000 --- 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 +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.security.login; - -import org.apache.nifi.web.security.token.LoginAuthenticationToken; -import java.io.IOException; -import java.io.PrintWriter; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -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.admin.service.AdministrationException; -import org.apache.nifi.authentication.AuthenticationResponse; -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; -import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; -import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509CertificateValidator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -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 LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - - private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class); - - private AuthenticationUserDetailsService userDetailsService; - - private X509CertificateValidator certificateValidator; - private X509CertificateExtractor certificateExtractor; - private X509PrincipalExtractor principalExtractor; - - private LoginIdentityProvider loginIdentityProvider; - private JwtService jwtService; - - public LoginAuthenticationFilter(final String defaultFilterProcessesUrl) { - super(defaultFilterProcessesUrl); - - // do not continue filter chain... simply exchanging 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 the credentials in the request - final LoginCredentials credentials = getLoginCredentials(request); - - // if the credentials were not part of the request, attempt to log in with the certificate in the request - if (credentials == null) { - // look for a certificate - final X509Certificate certificate = certificateExtractor.extractClientCertificate(request); - - // if there is no certificate, look for an existing token - if (certificate == null) { - // if not configured for login, don't consider existing tokens - if (loginIdentityProvider == null) { - throw new BadCredentialsException("Login not supported."); - } - - final String principal = jwtService.getAuthentication(request); - - if (principal == null) { - throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request."); - } - - return new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); - } else { - // extract the principal - final String principal = principalExtractor.extractPrincipal(certificate).toString(); - - try { - certificateValidator.validateClientCertificate(request, certificate); - } catch (CertificateExpiredException cee) { - final String message = String.format("Client certificate for (%s) is expired.", principal); - logger.info(message, cee); - if (logger.isDebugEnabled()) { - logger.debug("", cee); - } - return null; - } catch (CertificateNotYetValidException cnyve) { - final String message = String.format("Client certificate for (%s) is not yet valid.", principal); - logger.info(message, cnyve); - if (logger.isDebugEnabled()) { - logger.debug("", cnyve); - } - return null; - } catch (final Exception e) { - logger.info(e.getMessage()); - if (logger.isDebugEnabled()) { - logger.debug("", e); - } - return null; - } - - // authorize the proxy if necessary - authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal)); - - return new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); - } - } else { - // if not configuration for login, don't consider credentials - if (loginIdentityProvider == null) { - throw new BadCredentialsException("Login not supported."); - } - - try { - // attempt to authenticate - final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(credentials); - - // create the authentication token - return new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration()); - } 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); - } - } - } - - /** - * Ensures the proxyChain is authorized before allowing the user to be authenticated. - * - * @param proxyChain the proxy chain - * @throws AuthenticationException if the proxy chain is not authorized - */ - private void authorizeProxyIfNecessary(final List proxyChain) throws AuthenticationException { - if (proxyChain.size() > 1) { - try { - userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain)); - } catch (final UsernameNotFoundException unfe) { - // if a username not found exception was thrown, the proxies were authorized and now - // we can issue a new ID token to the end user - } catch (final Exception e) { - // any other issue we're going to treat as an authentication exception which will return 401 - throw new AuthenticationException(e.getMessage(), e) { - }; - } - } - } - - 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 { - - try { - // generate JWT for response - jwtService.addToken(response, (LoginAuthenticationToken) authentication); - } catch (final AdministrationException ae) { - unsuccessfulAuthentication(request, response, new AuthenticationServiceException(ae.getMessage(), ae)); - } - } - - @Override - protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException { - if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } else if (failed instanceof AuthenticationServiceException) { - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } else { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } - - response.setContentType("text/plain"); - - final PrintWriter out = response.getWriter(); - out.println(failed.getMessage()); - } - - public void setJwtService(JwtService jwtService) { - this.jwtService = jwtService; - } - - public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) { - this.loginIdentityProvider = loginIdentityProvider; - } - - public void setCertificateValidator(X509CertificateValidator certificateValidator) { - this.certificateValidator = certificateValidator; - } - - public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { - this.certificateExtractor = certificateExtractor; - } - - public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { - this.principalExtractor = principalExtractor; - } - - public void setUserDetailsService(AuthenticationUserDetailsService userDetailsService) { - this.userDetailsService = userDetailsService; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml index 0ffd46c75b..c88d30395a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml @@ -29,7 +29,7 @@ - + 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 894ade84fa..09a821289c 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 @@ -65,7 +65,7 @@ nf.Canvas = (function () { banners: '../nifi-api/controller/banners', controller: '../nifi-api/controller', controllerConfig: '../nifi-api/controller/config', - loginConfig: '../nifi-api/controller/login/config', + accessConfig: '../nifi-api/access/config', cluster: '../nifi-api/cluster', d3Script: 'js/d3/d3.min.js' } @@ -1090,7 +1090,7 @@ nf.Canvas = (function () { // get the login config var loginXhr = $.ajax({ type: 'GET', - url: config.urls.loginConfig, + url: config.urls.accessConfig, dataType: 'json' }); 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 9e1dab60a3..b61988f12b 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 @@ -31,8 +31,9 @@ nf.Login = (function () { registration: '../nifi-api/registration', identity: '../nifi-api/controller/identity', users: '../nifi-api/controller/users', - token: '../nifi-api/token', - loginConfig: '../nifi-api/controller/login/config' + token: '../nifi-api/access/token', + accessStatus: '../nifi-api/access', + accessConfig: '../nifi-api/access/config' } }; @@ -210,167 +211,71 @@ nf.Login = (function () { init: function () { nf.Storage.init(); - var showMessage = false; - var needsLogin = false; - var needsNiFiRegistration = false; - if (nf.Storage.getItem('jwt') !== null) { showLogoutLink(); } - var token = $.ajax({ + // access status + var accessStatus = $.ajax({ type: 'GET', - url: config.urls.token + url: config.urls.accessStatus, + dataType: 'json' + }).fail(function (xhr, status, error) { + $('#login-message-title').text('Unable to check Access Status'); + $('#login-message').text(xhr.responseText); + initializeMessage(); }); - - var identity = $.ajax({ + + // access config + var accessConfigXhr = $.ajax({ type: 'GET', - url: config.urls.identity, + url: config.urls.accessConfig, dataType: 'json' }); - - var pageStateInit = $.Deferred(function (deferred) { - // get the current user's identity - 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') { - supportsAnonymous = true; - - // request a token without including credentials, if successful then the user is using a certificate - token.done(function (jwt) { - - // the user is using a certificate/token, 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-title').text('Success'); - $('#login-message').text('Your account is active and you are already logged in.'); - }).fail(function (xhr, status, error) { - if (xhr.status === 401) { - var user = nf.Common.getJwtSubject(jwt); - - // show the user - $('#nifi-user-submit-justification').text(user); - - // 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 - $('#login-message-title').text('Access Denied'); - if ($.trim(xhr.responseText) === '') { - $('#login-message').text('Unable to check registration status.'); - } else { - $('#login-message').text(xhr.responseText); - } - } - }).always(function () { - deferred.resolve(); - }); - }).fail(function (tokenXhr) { - if (tokenXhr.status === 400) { - // no credentials supplied so 400 must be due to an invalid/expired token - logout(); - } - - // 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-title').text('Success'); - $('#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 (jwt) { - var user = nf.Common.getJwtSubject(jwt); - - // show the user - $('#nifi-user-submit-justification').text(user); - - // 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) { - if (tokenXhr.status === 400) { - // no credentials supplied so 400 must be due to an invalid/expired token - logout(); - } - - // no token granted, user needs to login with their credentials - needsLogin = true; - }).always(function () { - deferred.resolve(); - }); - } else if (xhr.status === 403) { - // attempt to get a token for the current user without passing login credentials - token.done(function () { - showMessage = true; - - // the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate - $('#login-message-title').text('Access Denied'); - 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); - } - }).fail(function (tokenXhr) { - if (tokenXhr.status === 400) { - // no credentials supplied so 400 must be due to an invalid/expired token - logout(); - } - - // no token granted, user needs to login with their credentials - needsLogin = true; - }).always(function () { - deferred.resolve(); - }); - } else { - showMessage = true; - - // the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate - $('#login-message-title').text('Access Denied'); - 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); - } - - deferred.resolve(); - } - }); - }).promise(); - - var loginConfigXhr = $.ajax({ - type: 'GET', - url: config.urls.loginConfig, - dataType: 'json' - }); - - // render the page accordingly - $.when(loginConfigXhr, pageStateInit).done(function (loginResult) { - var loginResponse = loginResult[0]; - var loginConfig = loginResponse.config; - + + $.when(accessStatus, accessConfigXhr).done(function (accessStatusResult, accessConfigResult) { + var accessStatusResponse = accessStatusResult[0]; + var accessStatus = accessStatusResponse.accessStatus; + + var accessConfigResponse = accessConfigResult[0]; + var accessConfig = accessConfigResponse.config; + + // record whether this NiFi supports anonymous access + supportsAnonymous = accessConfig.supportsAnonymous; + + // possible login states + var needsLogin = false; + var needsNiFiRegistration = false; + var showMessage = false; + + // handle the status appropriately + if (accessStatus.status === 'UNKNOWN') { + needsLogin = true; + } else if (accessStatus.status === 'UNREGISTERED') { + needsNiFiRegistration = true; + + $('#nifi-user-submit-justification').text(accessStatus.username); + } else if (accessStatus.status === 'NOT_ACTIVE') { + showMessage = true; + + $('#login-message-title').text('Access Denied'); + $('#login-message').text(accessStatus.message); + } else if (accessStatus.status === 'ACTIVE') { + showMessage = true; + + $('#login-message-title').text('Success'); + $('#login-message').text('Your account is active and you are already logged in.'); + } + // if login is required, verify its supported - if (loginConfig.supportsLogin === false && needsLogin === true) { + if (accessConfig.supportsLogin === false && needsLogin === true) { $('#login-message-title').text('Access Denied'); $('#login-message').text('This NiFi is not configured to support login.'); showMessage = true; needsLogin = false; } + // initialize the page as appropriate if (showMessage === true) { initializeMessage(); } else if (needsLogin === true) { 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 53128b9c4b..3be6b919a1 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 @@ -102,7 +102,7 @@ nf.Common = (function () { } }, urls: { - token: '../nifi-api/token' + token: '../nifi-api/access/token' } }, @@ -163,7 +163,7 @@ nf.Common = (function () { if (timeRemaining < interval) { // if the token will expire before the next interval minus some bonus time, refresh now $.ajax({ - type: 'GET', + type: 'POST', url: nf.Common.config.urls.token }).done(function (jwt) { nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt));