You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/10/13 04:19:48 UTC
[3/4] nifi git commit: NIFI-655: - Refactoring web security to use
Spring Security Java Configuration. - Introducing security in Web UI in order
to get JWT.
NIFI-655:
- Refactoring web security to use Spring Security Java Configuration.
- Introducing security in Web UI in order to get JWT.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/61046707
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/61046707
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/61046707
Branch: refs/heads/NIFI-655
Commit: 6104670735325492fd58ca12d123a061818d62cd
Parents: 31fba6b
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Oct 7 13:33:34 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Oct 7 13:46:11 2015 -0400
----------------------------------------------------------------------
.../resources/nifi-administration-context.xml | 2 +-
.../org/apache/nifi/web/server/JettyServer.java | 5 +-
.../org/apache/nifi/web/NiFiServiceFacade.java | 6 +
.../nifi/web/NiFiWebApiConfiguration.java | 24 +
.../web/NiFiWebApiSecurityConfiguration.java | 127 +++++
.../nifi/web/StandardNiFiServiceFacade.java | 22 +
.../nifi/web/api/ApplicationResource.java | 10 +-
.../org/apache/nifi/web/api/UserResource.java | 33 ++
.../nifi/web/controller/ControllerFacade.java | 13 +-
.../src/main/webapp/WEB-INF/web.xml | 13 +-
.../nifi/integration/util/NiFiTestUser.java | 21 +-
.../org/apache/nifi/web/security/DnUtils.java | 85 ----
.../security/NiFiAuthenticationEntryPoint.java | 69 +++
.../web/security/NiFiAuthenticationFilter.java | 144 ++++++
.../security/NiFiAuthenticationProvider.java | 62 +++
.../nifi/web/security/ProxiedEntitiesUtils.java | 168 +++++++
.../anonymous/NiFiAnonymousUserFilter.java | 55 +-
.../NiFiAuthenticationEntryPoint.java | 69 ---
.../authorization/NiFiAuthorizationService.java | 33 +-
.../authorization/NodeAuthorizedUserFilter.java | 128 -----
.../security/form/FormAuthenticationFilter.java | 72 +++
.../security/jwt/JwtAuthenticationFilter.java | 58 +++
.../security/jwt/JwtAuthenticationProvider.java | 47 ++
.../nifi/web/security/jwt/JwtService.java | 51 ++
.../security/node/NodeAuthorizedUserFilter.java | 127 +++++
.../NewAccountAuthenticationRequestToken.java | 41 ++
.../token/NewAccountAuthenticationToken.java | 48 ++
.../token/NiFiAuthenticationRequestToken.java | 55 ++
.../security/token/NiFiAuthorizationToken.java | 51 ++
.../web/security/user/NewAccountRequest.java | 31 ++
.../nifi/web/security/user/NiFiUserDetails.java | 3 +-
.../nifi/web/security/user/NiFiUserUtils.java | 21 +
.../security/x509/X509AuthenticationFilter.java | 257 +---------
.../x509/X509AuthenticationFilterOld.java | 317 ++++++++++++
.../x509/X509AuthenticationProvider.java | 31 ++
.../resources/nifi-web-security-context.xml | 62 +--
.../NiFiAuthorizationServiceTest.java | 502 +++++++++----------
.../nifi-framework/nifi-web/nifi-web-ui/pom.xml | 22 +-
.../apache/nifi/web/NiFiWebUiConfiguration.java | 35 ++
.../web/NiFiWebUiSecurityConfiguration.java | 69 +++
.../src/main/webapp/WEB-INF/pages/login.jsp | 66 +++
.../WEB-INF/partials/canvas/canvas-header.jsp | 3 +
.../nifi-web-ui/src/main/webapp/WEB-INF/web.xml | 44 +-
pom.xml | 2 +-
44 files changed, 2204 insertions(+), 900 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
index a36619f..8cb4b97 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
@@ -23,7 +23,7 @@
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- user authority provider -->
- <bean id="authorityProvider" class="org.apache.nifi.authorization.AuthorityProviderFactoryBean" depends-on="clusterManager flowController">
+ <bean id="authorityProvider" class="org.apache.nifi.authorization.AuthorityProviderFactoryBean" depends-on="clusterManager">
<property name="properties" ref="nifiProperties"/>
</bean>
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 99c11a8..b2b3013 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -614,8 +614,9 @@ public class JettyServer implements NiFiServer {
private SslContextFactory createSslContextFactory() {
final SslContextFactory contextFactory = new SslContextFactory();
- // need client auth
- contextFactory.setNeedClientAuth(props.getNeedClientAuth());
+ // client auth
+ contextFactory.setWantClientAuth(true);
+ contextFactory.setNeedClientAuth(false);
/* below code sets JSSE system properties when values are provided */
// keystore properties
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
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 c98b1e4..7395cfc 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
@@ -1236,6 +1236,12 @@ public interface NiFiServiceFacade {
Collection<UserDTO> getUsers(Boolean grouped);
/**
+ * Creates a new account request.
+ * @return user
+ */
+ UserDTO createUser();
+
+ /**
* Updates the specified user accordingly.
*
* @param user The user to update
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
new file mode 100644
index 0000000..04264e7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
@@ -0,0 +1,24 @@
+package org.apache.nifi.web;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportResource;
+
+/**
+ *
+ */
+@Configuration
+@Import({ NiFiWebApiSecurityConfiguration.class})
+@ImportResource( {"classpath:nifi-context.xml",
+ "classpath:nifi-administration-context.xml",
+ "classpath:nifi-cluster-manager-context.xml",
+ "classpath:nifi-cluster-protocol-context.xml",
+ "classpath:nifi-web-security-context.xml",
+ "classpath:nifi-web-api-context.xml"} )
+public class NiFiWebApiConfiguration {
+
+ public NiFiWebApiConfiguration() {
+ super();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/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
new file mode 100644
index 0000000..305aaf6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import org.apache.nifi.admin.service.UserService;
+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.node.NodeAuthorizedUserFilter;
+import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
+import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+/**
+ * NiFi Web Api Spring security
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private NiFiProperties properties;
+ private UserService userService;
+ private AuthenticationUserDetailsService userDetailsService;
+
+ public NiFiWebApiSecurityConfiguration() {
+ super(true); // disable defaults
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .rememberMe().disable()
+ .exceptionHandling()
+ .authenticationEntryPoint(new NiFiAuthenticationEntryPoint())
+ .and()
+ .authorizeRequests()
+ .anyRequest().fullyAuthenticated()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+ // cluster - authorized user
+ final NodeAuthorizedUserFilter authorizedUserFilter = new NodeAuthorizedUserFilter(properties);
+ http.addFilterBefore(authorizedUserFilter, AnonymousAuthenticationFilter.class);
+
+ // x509
+ http.addFilterBefore(buildX509Filter(), AnonymousAuthenticationFilter.class);
+
+ // anonymous
+ final NiFiAnonymousUserFilter anonymousFilter = new NiFiAnonymousUserFilter();
+ anonymousFilter.setProperties(properties);
+ anonymousFilter.setUserService(userService);
+ http.anonymous().authenticationFilter(anonymousFilter);
+ }
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ // override xxxBean method so the authentication manager is available in app context (necessary for the method level security)
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ // x509
+ final AuthenticationProvider x509AuthenticationProvider = new NiFiAuthenticationProvider(new X509AuthenticationProvider(), userDetailsService);
+
+ auth
+ .authenticationProvider(x509AuthenticationProvider);
+ }
+
+ private X509AuthenticationFilter buildX509Filter() throws Exception {
+ final X509AuthenticationFilter x509Filter = new X509AuthenticationFilter();
+ x509Filter.setPrincipalExtractor(new SubjectDnX509PrincipalExtractor());
+ x509Filter.setCertificateExtractor(new X509CertificateExtractor());
+ x509Filter.setCertificateValidator(new OcspCertificateValidator(properties));
+ x509Filter.setAuthenticationManager(authenticationManager());
+ return x509Filter;
+ }
+
+ @Autowired
+ public void setUserDetailsService(AuthenticationUserDetailsService userDetailsService) {
+ this.userDetailsService = userDetailsService;
+ }
+
+ @Autowired
+ public void setUserService(UserService userService) {
+ this.userService = userService;
+ }
+
+ @Autowired
+ public void setProperties(NiFiProperties properties) {
+ this.properties = properties;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
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 2286213..e47b339 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
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.web;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -32,6 +33,8 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
@@ -152,6 +155,7 @@ import org.apache.nifi.web.util.SnippetUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.Validator;
import org.apache.nifi.controller.ReportingTaskNode;
@@ -168,6 +172,7 @@ import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.NodeProcessGroupStatusDTO;
import org.apache.nifi.web.dao.ControllerServiceDAO;
import org.apache.nifi.web.dao.ReportingTaskDAO;
+import org.apache.nifi.web.security.user.NewAccountRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
@@ -1808,6 +1813,23 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
@Override
+ public UserDTO createUser() {
+ NewAccountRequest newAccountRequest = NiFiUserUtils.getNewAccountRequest();
+
+ // log the new user account request
+ logger.info("Requesting new user account for " + newAccountRequest.getUsername());
+
+ // get the justification
+ String justification = newAccountRequest.getJustification();
+ if (justification == null) {
+ justification = StringUtils.EMPTY;
+ }
+
+ // create the pending user account
+ return dtoFactory.createUserDTO(userService.createPendingUserAccount(newAccountRequest.getUsername(), justification));
+ }
+
+ @Override
public UserDTO updateUser(UserDTO userDto) {
NiFiUser user;
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.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/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index aa51925..7ad0504 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -23,7 +23,6 @@ import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
-import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
@@ -44,9 +43,8 @@ import org.apache.nifi.action.Operation;
import org.apache.nifi.cluster.context.ClusterContext;
import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
import org.apache.nifi.cluster.manager.impl.WebClusterManager;
-import org.apache.nifi.web.security.DnUtils;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.entity.Entity;
import org.apache.nifi.web.api.request.ClientIdParameter;
@@ -361,12 +359,10 @@ public abstract class ApplicationResource {
result.put(PROXY_SCHEME_HTTP_HEADER, httpServletRequest.getScheme());
}
- // if this is a secure request, add the custom headers for proxying user requests
- final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(httpServletRequest);
- if (cert != null) {
+ if (httpServletRequest.isSecure()) {
// add the certificate DN to the proxy chain
- final String xProxiedEntitiesChain = DnUtils.getXProxiedEntitiesChain(httpServletRequest);
+ final String xProxiedEntitiesChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(httpServletRequest);
if (StringUtils.isNotBlank(xProxiedEntitiesChain)) {
result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, xProxiedEntitiesChain);
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.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/UserResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
index 4a61ef4..c7a84c3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
@@ -16,12 +16,15 @@
*/
package org.apache.nifi.web.api;
+import com.sun.jersey.api.Responses;
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 com.wordnik.swagger.annotations.Authorization;
+import java.io.PrintWriter;
+import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -33,12 +36,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
+import static javax.ws.rs.HttpMethod.POST;
+import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -59,9 +65,11 @@ import org.apache.nifi.web.api.entity.UserSearchResultsEntity;
import org.apache.nifi.web.api.entity.UsersEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.web.NiFiServiceFacade;
import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.springframework.security.access.prepost.PreAuthorize;
/**
@@ -83,6 +91,31 @@ public class UserResource extends ApplicationResource {
private NiFiProperties properties;
private NiFiServiceFacade serviceFacade;
+ @POST
+ @Consumes(MediaType.WILDCARD)
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("") // necessary due to a bug in swagger
+ @ApiOperation(
+ value = "Creates a user",
+ response = String.class
+ )
+ public Response createUser() {
+ if (!properties.getSupportNewAccountRequests()) {
+ return Responses.notFound().entity("This NiFi does not support new account requests.").build();
+ }
+
+ final NiFiUser nifiUser = NiFiUserUtils.getNiFiUser();
+ if (nifiUser != null) {
+ throw new IllegalArgumentException("User account already created " + nifiUser.getDn());
+ }
+
+ // create an account request for the current user
+ final UserDTO user = serviceFacade.createUser();
+
+ final String uri = generateResourceUri("controller", "templates", user.getId());
+ return generateCreatedResponse(URI.create(uri), "Not authorized. User account created. Authorization pending.").build();
+ }
+
/**
* Gets all users that are registered within this Controller.
*
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 8bf5553..3bc8120 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -113,6 +113,7 @@ import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.reporting.BulletinQuery;
import org.apache.nifi.reporting.ComponentType;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -822,17 +823,7 @@ public class ControllerFacade {
final Map<String, String> attributes = event.getAttributes();
// calculate the dn chain
- final List<String> dnChain = new ArrayList<>();
-
- // build the dn chain
- NiFiUser chainedUser = user;
- do {
- // add the entry for this user
- dnChain.add(chainedUser.getDn());
-
- // go to the next user in the chain
- chainedUser = chainedUser.getChain();
- } while (chainedUser != null);
+ final List<String> dnChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(user);
// ensure the users in this chain are allowed to download this content
final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
index 4ce319e..b57998d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
@@ -16,15 +16,12 @@
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>nifi-api</display-name>
<context-param>
+ <param-name>contextClass</param-name>
+ <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
+ </context-param>
+ <context-param>
<param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:nifi-context.xml
- classpath:nifi-web-api-context.xml
- classpath:nifi-web-security-context.xml
- classpath:nifi-administration-context.xml
- classpath:nifi-cluster-manager-context.xml
- classpath:nifi-cluster-protocol-context.xml
- </param-value>
+ <param-value>org.apache.nifi.web</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
index 52f4522..c0e9246 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
@@ -22,8 +22,7 @@ import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import java.util.Map;
import javax.ws.rs.core.MediaType;
-import org.apache.nifi.web.security.DnUtils;
-import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
/**
*
@@ -37,7 +36,7 @@ public class NiFiTestUser {
public NiFiTestUser(Client client, String dn) {
this.client = client;
- this.proxyDn = DnUtils.formatProxyDn(dn);
+ this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(dn);
}
/**
@@ -70,7 +69,7 @@ public class NiFiTestUser {
}
// perform the query
- return resource.accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn).get(ClientResponse.class);
+ return resource.accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).get(ClientResponse.class);
}
/**
@@ -94,7 +93,7 @@ public class NiFiTestUser {
*/
public ClientResponse testPost(String url, Object entity) throws Exception {
// get the resource
- WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// include the request entity
if (entity != null) {
@@ -115,7 +114,7 @@ public class NiFiTestUser {
*/
public ClientResponse testPostMultiPart(String url, Object entity) throws Exception {
// get the resource
- WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// include the request entity
if (entity != null) {
@@ -143,7 +142,7 @@ public class NiFiTestUser {
// get the resource
WebResource.Builder resourceBuilder
- = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// add the form data if necessary
if (!entity.isEmpty()) {
@@ -164,7 +163,7 @@ public class NiFiTestUser {
*/
public ClientResponse testPut(String url, Object entity) throws Exception {
// get the resource
- WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// include the request entity
if (entity != null) {
@@ -192,7 +191,7 @@ public class NiFiTestUser {
// get the resource
WebResource.Builder resourceBuilder
- = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// add the form data if necessary
if (!entity.isEmpty()) {
@@ -224,7 +223,7 @@ public class NiFiTestUser {
*/
public ClientResponse testDelete(String url, Object entity) throws Exception {
// get the resource
- WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+ WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
// append any query parameters
if (entity != null) {
@@ -255,7 +254,7 @@ public class NiFiTestUser {
}
// perform the request
- return resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn).delete(ClientResponse.class);
+ return resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).delete(ClientResponse.class);
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.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/DnUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
deleted file mode 100644
index f3bd11e..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
+++ /dev/null
@@ -1,85 +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.security.cert.X509Certificate;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- *
- */
-public class DnUtils {
-
- private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
-
- /**
- * @param request http request
- * @return the X-ProxiedEntitiesChain from the specified request
- */
- public static String getXProxiedEntitiesChain(final HttpServletRequest request) {
- String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
- final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(request);
- if (cert != null) {
- final SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
- final String extractedPrincipal = principalExtractor.extractPrincipal(cert).toString();
- final String formattedPrincipal = formatProxyDn(extractedPrincipal);
- if (StringUtils.isBlank(xProxiedEntitiesChain)) {
- xProxiedEntitiesChain = formattedPrincipal;
- } else {
- xProxiedEntitiesChain += formattedPrincipal;
- }
- }
-
- return xProxiedEntitiesChain;
- }
-
- /**
- * Formats the specified DN to be set as a HTTP header using well known
- * conventions.
- *
- * @param dn raw dn
- * @return the dn formatted as an HTTP header
- */
- public static String formatProxyDn(String dn) {
- return "<" + dn + ">";
- }
-
- /**
- * Tokenizes the specified proxy chain.
- *
- * @param rawProxyChain raw chain
- * @return tokenized proxy chain
- */
- public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
- final Deque<String> dnList = new ArrayDeque<>();
-
- // parse the proxy chain
- final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
- while (rawProxyChainMatcher.find()) {
- dnList.push(rawProxyChainMatcher.group(1));
- }
-
- return dnList;
- }
-}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.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/NiFiAuthenticationEntryPoint.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
new file mode 100644
index 0000000..6cae1f0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
@@ -0,0 +1,69 @@
+/*
+ * 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 javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.WebAttributes;
+
+/**
+ * This is our own implementation of
+ * org.springframework.security.web.AuthenticationEntryPoint that allows us to
+ * send the response to the client exactly how we want to and log the results.
+ */
+public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+ private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
+
+ /**
+ * Always returns a 403 error code to the client.
+ *
+ * @param request request
+ * @param response response
+ * @param ae ae
+ * @throws java.io.IOException ex
+ * @throws javax.servlet.ServletException ex
+ */
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
+ // get the last exception - the exception that is being passed in is a generic no credentials found
+ // exception because the authentication could not be found in the security context. the actual cause
+ // of the problem is stored in the session as the authentication_exception
+ Object authenticationException = request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
+
+ // log request result
+ if (authenticationException instanceof AuthenticationException) {
+ ae = (AuthenticationException) authenticationException;
+ logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
+ }
+
+ // set the response status
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ response.setContentType("text/plain");
+
+ // write the response message
+ PrintWriter out = response.getWriter();
+ out.println("Access is denied.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
new file mode 100644
index 0000000..52ac2f1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -0,0 +1,144 @@
+/*
+ * 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 javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ *
+ */
+public abstract class NiFiAuthenticationFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
+
+ private AuthenticationManager authenticationManager;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ /**
+ *
+ * @param request
+ * @param response
+ * @param chain
+ * @throws java.io.IOException
+ * @throws javax.servlet.ServletException
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
+ }
+
+ if (requiresAuthentication((HttpServletRequest) request)) {
+ authenticate((HttpServletRequest) request, (HttpServletResponse) response);
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ private boolean requiresAuthentication(final HttpServletRequest request) {
+ return NiFiUserUtils.getNiFiUser() == null && NiFiUserUtils.getNewAccountRequest() == null;
+ }
+
+ private void authenticate(final HttpServletRequest request, final HttpServletResponse response) {
+ try {
+ final Authentication authenticated = attemptAuthentication(request, response);
+ if (authenticated != null) {
+ final Authentication authorized = authenticationManager.authenticate(authenticated);
+ successfulAuthorization(request, response, authorized);
+ }
+ } catch (final AuthenticationException ae) {
+ unsuccessfulAuthorization(request, response, ae);
+ }
+ }
+
+ public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response);
+
+ protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Authentication success: " + authResult);
+ }
+
+ SecurityContextHolder.getContext().setAuthentication(authResult);
+ ProxiedEntitiesUtils.successfulAuthorization(request, response, authResult);
+ }
+
+ protected void unsuccessfulAuthorization(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
+ ProxiedEntitiesUtils.unsuccessfulAuthorization(request, response, failed);
+ }
+
+ /**
+ * Determines if the specified request is attempting to register a new user account.
+ *
+ * @param request http request
+ * @return true if new user
+ */
+ protected final boolean isNewAccountRequest(HttpServletRequest request) {
+ if ("POST".equalsIgnoreCase(request.getMethod())) {
+ String path = request.getPathInfo();
+ if (StringUtils.isNotBlank(path)) {
+ if ("/controller/users".equals(path)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extracts the justification from the specified request.
+ *
+ * @param request The request
+ * @return The justification
+ */
+ protected final String getJustification(HttpServletRequest request) {
+ // get the justification
+ String justification = request.getParameter("justification");
+ if (justification == null) {
+ justification = StringUtils.EMPTY;
+ }
+ return justification;
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+ this.authenticationManager = authenticationManager;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.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/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
new file mode 100644
index 0000000..5f15b76
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -0,0 +1,62 @@
+package org.apache.nifi.web.security;
+
+import org.apache.nifi.web.security.token.NewAccountAuthenticationToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.springframework.security.authentication.AuthenticationProvider;
+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.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ *
+ */
+public class NiFiAuthenticationProvider implements AuthenticationProvider {
+
+ private final AuthenticationProvider provider;
+ private final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+
+ public NiFiAuthenticationProvider(final AuthenticationProvider provider, final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+ this.provider = provider;
+ this.userDetailsService = userDetailsService;
+ }
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ final NiFiAuthenticationRequestToken request = (NiFiAuthenticationRequestToken) authentication;
+
+ // ensure the base provider could authenticate
+ final Authentication result = provider.authenticate(request);
+ if (result == null) {
+ return null;
+ }
+
+ try {
+ // defer to the nifi user details service to authorize the user
+ final UserDetails userDetails = userDetailsService.loadUserDetails(request);
+
+ // build an authentication for accesing nifi
+ return new NiFiAuthorizationToken(userDetails);
+ } catch (final UsernameNotFoundException unfe) {
+ // if the result was an authenticated new account request and it could not be authorized because the user was not found,
+ // return the token so the new account could be created
+ if (isNewAccountAuthenticationToken(result)) {
+ return result;
+ } else {
+ throw unfe;
+ }
+ }
+ }
+
+ private boolean isNewAccountAuthenticationToken(final Authentication authentication) {
+ return NewAccountAuthenticationToken.class.isAssignableFrom(authentication.getClass());
+ }
+
+ @Override
+ public boolean supports(Class<?> authentication) {
+ return provider.supports(authentication) && NiFiAuthenticationRequestToken.class.isAssignableFrom(authentication);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.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/ProxiedEntitiesUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
new file mode 100644
index 0000000..e0d810c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
@@ -0,0 +1,168 @@
+/*
+ * 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.security.cert.X509Certificate;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ *
+ */
+public class ProxiedEntitiesUtils {
+
+ public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
+ public static final String PROXY_ENTITIES_ACCEPTED = "X-ProxiedEntitiesAccepted";
+ public static final String PROXY_ENTITIES_DETAILS = "X-ProxiedEntitiesDetails";
+
+ private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
+
+ /**
+ * @param request http request
+ * @return the X-ProxiedEntitiesChain from the specified request
+ */
+ public static String getXProxiedEntitiesChain(final HttpServletRequest request) {
+ String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
+ final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(request);
+ if (cert != null) {
+ final SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
+ final String extractedPrincipal = principalExtractor.extractPrincipal(cert).toString();
+ final String formattedPrincipal = formatProxyDn(extractedPrincipal);
+ if (StringUtils.isBlank(xProxiedEntitiesChain)) {
+ xProxiedEntitiesChain = formattedPrincipal;
+ } else {
+ xProxiedEntitiesChain += formattedPrincipal;
+ }
+ }
+
+ return xProxiedEntitiesChain;
+ }
+
+ /**
+ * Builds the dn chain for the specified user.
+ *
+ * @param user The current user
+ * @return The dn chain for that user
+ */
+ public static List<String> getXProxiedEntitiesChain(final NiFiUser user) {
+ // calculate the dn chain
+ final List<String> dnChain = new ArrayList<>();
+
+ // build the dn chain
+ NiFiUser chainedUser = user;
+ do {
+ // add the entry for this user
+ dnChain.add(chainedUser.getDn());
+
+ // go to the next user in the chain
+ chainedUser = chainedUser.getChain();
+ } while (chainedUser != null);
+
+ return dnChain;
+ }
+
+ /**
+ * Formats the specified DN to be set as a HTTP header using well known
+ * conventions.
+ *
+ * @param dn raw dn
+ * @return the dn formatted as an HTTP header
+ */
+ public static String formatProxyDn(String dn) {
+ return "<" + dn + ">";
+ }
+
+// /**
+// * Tokenizes the specified proxy chain.
+// *
+// * @param rawProxyChain raw chain
+// * @return tokenized proxy chain
+// */
+// public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
+// final Deque<String> dnList = new ArrayDeque<>();
+//
+// // parse the proxy chain
+// final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
+// while (rawProxyChainMatcher.find()) {
+// dnList.push(rawProxyChainMatcher.group(1));
+// }
+//
+// return dnList;
+// }
+
+ public static List<String> buildProxyChain(final HttpServletRequest request, final String username) {
+ String principal;
+ if (username.startsWith("<") && username.endsWith(">")) {
+ principal = username;
+ } else {
+ principal = formatProxyDn(username);
+ }
+
+ // look for a proxied user
+ if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+ principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
+ }
+
+ // parse the proxy chain
+ final List<String> proxyChain = new ArrayList<>();
+ final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(principal);
+ while (rawProxyChainMatcher.find()) {
+ proxyChain.add(rawProxyChainMatcher.group(1));
+ }
+
+ return proxyChain;
+ }
+
+ public static String extractProxiedEntitiesChain(final HttpServletRequest request, final String username) {
+ String principal;
+ if (username.startsWith("<") && username.endsWith(">")) {
+ principal = username;
+ } else {
+ principal = formatProxyDn(username);
+ }
+
+ // look for a proxied user
+ if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+ principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
+ }
+ return principal;
+ }
+
+ public static void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
+ if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+ response.setHeader(PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
+ }
+ }
+
+ public static void unsuccessfulAuthorization(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
+ if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+ response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.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/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
index 295f09c..7026124 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
@@ -16,8 +16,6 @@
*/
package org.apache.nifi.web.security.anonymous;
-import java.util.ArrayList;
-import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.service.AdministrationException;
@@ -25,11 +23,10 @@ import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.web.security.user.NiFiUserDetails;
import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
/**
@@ -51,45 +48,31 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter {
@Override
protected Authentication createAuthentication(HttpServletRequest request) {
- Authentication authentication;
- try {
- // load the anonymous user from the database
- NiFiUser user = userService.getUserByDn(NiFiUser.ANONYMOUS_USER_DN);
- NiFiUserDetails userDetails = new NiFiUserDetails(user);
+ Authentication authentication = null;
- // get the granted authorities
- List<GrantedAuthority> authorities = new ArrayList<>(userDetails.getAuthorities());
- authentication = new AnonymousAuthenticationToken(ANONYMOUS_KEY, userDetails, authorities);
- } catch (AdministrationException ase) {
- // record the issue
- anonymousUserFilterLogger.warn("Unable to load anonymous user from accounts database: " + ase.getMessage());
- if (anonymousUserFilterLogger.isDebugEnabled()) {
- anonymousUserFilterLogger.warn(StringUtils.EMPTY, ase);
- }
+ // only support anonymous when the request is non-secure or one way ssl
+// if (!request.isSecure() || !properties.getNeedClientAuth()) {
+ if (true) {
+ try {
+ // load the anonymous user from the database
+ NiFiUser user = userService.getUserByDn(NiFiUser.ANONYMOUS_USER_DN);
+ NiFiUserDetails userDetails = new NiFiUserDetails(user);
- // defer to the base implementation
- authentication = super.createAuthentication(request);
+ // get the granted authorities
+ authentication = new NiFiAuthorizationToken(userDetails);
+ } catch (AdministrationException ase) {
+ // record the issue
+ anonymousUserFilterLogger.warn("Unable to load anonymous user from accounts database: " + ase.getMessage());
+ if (anonymousUserFilterLogger.isDebugEnabled()) {
+ anonymousUserFilterLogger.warn(StringUtils.EMPTY, ase);
+ }
+ }
}
return authentication;
}
- /**
- * Only supports anonymous users for non-secure requests or one way ssl.
- *
- * @param request request
- * @return true if allowed
- */
- @Override
- protected boolean applyAnonymousForThisRequest(HttpServletRequest request) {
- // anonymous for non secure requests
- if ("http".equalsIgnoreCase(request.getScheme())) {
- return true;
- }
-
- return !properties.getNeedClientAuth();
- }
-
/* setters */
+
public void setUserService(UserService userService) {
this.userService = userService;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.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/authentication/NiFiAuthenticationEntryPoint.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
deleted file mode 100644
index cd5f1ac..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
+++ /dev/null
@@ -1,69 +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.authentication;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.WebAttributes;
-
-/**
- * This is our own implementation of
- * org.springframework.security.web.AuthenticationEntryPoint that allows us to
- * send the response to the client exactly how we want to and log the results.
- */
-public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
- private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
-
- /**
- * Always returns a 403 error code to the client.
- *
- * @param request request
- * @param response response
- * @param ae ae
- * @throws java.io.IOException ex
- * @throws javax.servlet.ServletException ex
- */
- @Override
- public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
- // get the last exception - the exception that is being passed in is a generic no credentials found
- // exception because the authentication could not be found in the security context. the actual cause
- // of the problem is stored in the session as the authentication_exception
- Object authenticationException = request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
-
- // log request result
- if (authenticationException instanceof AuthenticationException) {
- ae = (AuthenticationException) authenticationException;
- logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
- }
-
- // set the response status
- response.setStatus(HttpServletResponse.SC_FORBIDDEN);
- response.setContentType("text/plain");
-
- // write the response message
- PrintWriter out = response.getWriter();
- out.println("Access is denied.");
- }
-}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.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/authorization/NiFiAuthorizationService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 95b4669..c3a5e43 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -16,34 +16,35 @@
*/
package org.apache.nifi.web.security.authorization;
-import java.util.Deque;
-import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.service.AccountDisabledException;
import org.apache.nifi.admin.service.AccountNotFoundException;
import org.apache.nifi.admin.service.AccountPendingException;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.admin.service.UserService;
import org.apache.nifi.authorization.Authority;
-import org.apache.nifi.web.security.DnUtils;
import org.apache.nifi.user.NiFiUser;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.UntrustedProxyException;
import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AccountStatusException;
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.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* UserDetailsService that will verify user identity and grant user authorities.
*/
-public class NiFiAuthorizationService implements UserDetailsService {
+public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> {
private static final Logger logger = LoggerFactory.getLogger(NiFiAuthorizationService.class);
@@ -58,30 +59,30 @@ public class NiFiAuthorizationService implements UserDetailsService {
* made for each individual request as a whole (without other request
* potentially impacting it).
*
- * @param rawProxyChain proxy chain
+ * @param request request
* @return user details
* @throws UsernameNotFoundException ex
* @throws org.springframework.dao.DataAccessException ex
*/
@Override
- public synchronized UserDetails loadUserByUsername(String rawProxyChain) throws UsernameNotFoundException, DataAccessException {
+ public synchronized UserDetails loadUserDetails(NiFiAuthenticationRequestToken request) throws UsernameNotFoundException, DataAccessException {
NiFiUserDetails userDetails = null;
- final Deque<String> dnList = DnUtils.tokenizeProxyChain(rawProxyChain);
+ final List<String> chain = new ArrayList<>(request.getChain());
// ensure valid input
- if (dnList.size() == 0) {
- logger.warn("Malformed proxy chain: " + rawProxyChain);
+ if (chain.isEmpty()) {
+ logger.warn("Malformed proxy chain: " + StringUtils.join(request.getChain()));
throw new UntrustedProxyException("Malformed proxy chain.");
}
NiFiUser proxy = null;
// process each part of the proxy chain
- for (final Iterator<String> dnIter = dnList.iterator(); dnIter.hasNext();) {
- final String dn = dnIter.next();
+ for (final ListIterator<String> chainIter = request.getChain().listIterator(chain.size()); chainIter.hasPrevious();) {
+ final String dn = chainIter.previous();
// if there is another dn after this one, this dn is a proxy for the request
- if (dnIter.hasNext()) {
+ if (chainIter.hasPrevious()) {
try {
// get the user details for the proxy
final NiFiUserDetails proxyDetails = getNiFiUserDetails(dn);
@@ -110,7 +111,7 @@ public class NiFiAuthorizationService implements UserDetailsService {
userService.createPendingUserAccount(dn, "Automatic account request generated for unknown proxy.");
// propagate the exception to return the appropriate response
- throw new UsernameNotFoundException(String.format("An account request was generated for the proxy '%s'.", dn));
+ throw new UntrustedProxyException(String.format("An account request was generated for the proxy '%s'.", dn));
} catch (AdministrationException ae) {
throw new AuthenticationServiceException(String.format("Unable to create an account request for '%s': %s", dn, ae.getMessage()), ae);
} catch (IllegalArgumentException iae) {
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.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/authorization/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
deleted file mode 100644
index 80feed7..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
+++ /dev/null
@@ -1,128 +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.authorization;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.controller.FlowController;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.user.NiFiUser;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.util.WebUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.security.authentication.AuthenticationDetailsSource;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-import org.springframework.web.filter.GenericFilterBean;
-
-/**
- * Custom filter to extract a user's authorities from the request where the user
- * was authenticated by the cluster manager and populate the threadlocal with
- * the authorized user. If the request contains the appropriate header with
- * authorities and the application instance is a node connected to the cluster,
- * then the authentication/authorization steps remaining in the filter chain are
- * skipped.
- *
- * Checking if the application instance is a connected node is important because
- * it prevents external clients from faking the request headers and bypassing
- * the authentication processing chain.
- */
-public class NodeAuthorizedUserFilter extends GenericFilterBean {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(NodeAuthorizedUserFilter.class);
-
- public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails";
-
- private NiFiProperties properties;
- private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
- private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
- private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
-
- // get the proxied user's authorities
- final String hexEncodedUserDetails = httpServletRequest.getHeader(PROXY_USER_DETAILS);
-
- // check if the request has the necessary header information and this instance is configured as a node
- if (StringUtils.isNotBlank(hexEncodedUserDetails) && properties.isNode()) {
-
- // get the flow controller from the Spring context
- final ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- final FlowController flowController = ctx.getBean("flowController", FlowController.class);
-
- // check that we are connected to the cluster
- if (flowController.getNodeId() != null) {
- try {
- // get the DN from the cert in the request
- final X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
- if (certificate != null) {
- // extract the principal from the certificate
- final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
- final String dn = certificatePrincipal.toString();
-
- // only consider the pre-authorized user when the request came from the NCM according to the DN in the certificate
- final String clusterManagerDN = flowController.getClusterManagerDN();
- if (clusterManagerDN != null && clusterManagerDN.equals(dn)) {
- // deserialize hex encoded object
- final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails);
-
- // if we have a valid object, set the authentication token and bypass the remaining authentication processing chain
- if (userDetailsObj instanceof NiFiUserDetails) {
- final NiFiUserDetails userDetails = (NiFiUserDetails) userDetailsObj;
- final NiFiUser user = userDetails.getNiFiUser();
-
- // log the request attempt - response details will be logged later
- logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getDn(), httpServletRequest.getMethod(),
- httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
-
- // we do not create the authentication token with the X509 certificate because the certificate is from the sending system, not the proxied user
- final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- token.setDetails(authenticationDetailsSource.buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(token);
- }
- }
- }
- } catch (final ClassNotFoundException cnfe) {
- LOGGER.warn("Classpath issue detected because failed to deserialize authorized user in request header due to: " + cnfe, cnfe);
- }
- }
- }
-
- chain.doFilter(request, response);
- }
-
- /* setters */
- public void setProperties(NiFiProperties properties) {
- this.properties = properties;
- }
-}
http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.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/form/FormAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
new file mode 100644
index 0000000..5367ba2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
@@ -0,0 +1,72 @@
+/*
+ * 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.form;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+
+/**
+ */
+public class FormAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(FormAuthenticationFilter.class);
+
+ private JwtService jwtService;
+
+ public FormAuthenticationFilter(final String defaultFilterProcessesUrl) {
+ super(defaultFilterProcessesUrl);
+ }
+
+ @Override
+ public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
+ final String username = request.getParameter("username");
+ final String password = request.getParameter("password");
+ return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password));
+ }
+
+ @Override
+ protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication) throws IOException, ServletException {
+ // generate JWT for response
+ jwtService.addToken(response, authentication);
+ }
+
+ @Override
+ protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
+ // set the response status
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ response.setContentType("text/plain");
+
+ final PrintWriter out = response.getWriter();
+ out.println("Invalid username/password");
+ }
+
+ public void setJwtService(JwtService jwtService) {
+ this.jwtService = jwtService;
+ }
+
+}