blob: 8367eecc1c9bc6e7bd4e094350ac8e6575686157 [file] [log] [blame]
/*
* 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.registry.web.security.authentication;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.security.authentication.AuthenticationRequest;
import org.apache.nifi.registry.security.authentication.IdentityProvider;
import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
import org.apache.nifi.registry.security.util.ProxiedEntitiesUtils;
import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Note: This class is deprecated and is being considered for complete removal in favor of using {@link IdentityFilter}.
* It is remaining in place for the time being until the pattern of authentication implemented by {@link IdentityFilter}
* has been more thoroughly vetted in real use.
*/
@Deprecated
public class IdentityAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final RequestMatcher requiresAuthenticationRequestMatcher = new RequestMatcher() {
@Override
public boolean matches(HttpServletRequest httpServletRequest) {
return NiFiUserUtils.getNiFiUser() == null;
}
};
private final IdentityProvider identityProvider;
public IdentityAuthenticationFilter(IdentityProvider identityProvider, AuthenticationManager authenticationManager, String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl)); // Authentication will only be initiated for the request url matching this pattern
setAuthenticationManager(authenticationManager);
this.identityProvider = identityProvider;
}
public IdentityAuthenticationFilter(IdentityProvider identityProvider, AuthenticationManager authenticationManager) {
super(requiresAuthenticationRequestMatcher);
setAuthenticationManager(authenticationManager);
this.identityProvider = identityProvider;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// Only require authentication from an identity provider if the NiFi registry is running securely.
if (!httpServletRequest.isSecure()) {
throw new InvalidAuthenticationException("Authentication of user identity claim is only avaialble when running a securely.");
}
AuthenticationRequest authenticationRequest = identityProvider.extractCredentials(httpServletRequest);
if (authenticationRequest == null) {
throw new InvalidAuthenticationException("User credentials not found in httpServletRequest by " + identityProvider.getClass().getSimpleName());
}
Authentication authentication = new AuthenticationRequestToken(authenticationRequest, identityProvider.getClass(), httpServletRequest.getRemoteAddr());
Authentication authenticationResult = getAuthenticationManager().authenticate(authentication); // See IdentityAuthenticationProvider for authentication impl.
if (authenticationResult == null) {
throw new InvalidAuthenticationException("User credentials not authenticated by " + identityProvider.getClass().getSimpleName());
}
return authenticationResult;
// Super class will invoke successfulAuthentication() or unsuccessfulAuthentication() depending on the outcome of the authentication attempt
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Authentication success for " + authResult);
SecurityContextHolder.getContext().setAuthentication(authResult);
if (StringUtils.isNotBlank(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN))) {
response.setHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
}
// continue the filter chain, which now holds a NiFiUser in the SecurityContext's authentication
chain.doFilter(request, response);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
this.logger.debug("Authentication request failed: " + failed.toString(), failed);
SecurityContextHolder.clearContext();
this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
// populate the response
if (StringUtils.isNotBlank(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN))) {
response.setHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_DETAILS, failed.getMessage());
}
// 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 (failed instanceof InvalidAuthenticationException) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
out.println(failed.getMessage());
} else if (failed instanceof UntrustedProxyException) { // thrown in X509IdentityProviderAuthenticationProvider
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.println(failed.getMessage());
} else if (failed instanceof AuthenticationServiceException) {
logger.error(String.format("Unable to authorize: %s", failed.getMessage()), failed);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
out.println(String.format("Unable to authorize: %s", failed.getMessage()));
} else {
logger.error(String.format("Unable to authorize: %s", failed.getMessage()), failed);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.println("Access is denied.");
}
// log the failure
logger.warn(String.format("Rejecting access to web api: %s", failed.getMessage()));
logger.debug(StringUtils.EMPTY, failed);
}
}