/** * ***************************************************************************** * * Copyright (c) 2004-2012 Oracle Corporation. * * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Winston Prakash * ****************************************************************************** */ package hudson.security; import hudson.util.ThreadLocalUtils; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.eclipse.hudson.security.HudsonSecurityEntitiesHolder; import org.eclipse.hudson.security.HudsonSecurityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.RememberMeServices; /** * {@link Filter} that Hudson uses to implement security support. * * <p> This is the instance the servlet container creates, but internally this * just acts as a proxy to the real {@link Filter}, created by * {@link SecurityRealm#createFilter(FilterConfig)}. * * @author Kohsuke Kawaguchi * @since 1.160 */ public class HudsonFilter implements Filter { private transient Logger logger = LoggerFactory.getLogger(HudsonFilter.class); /** * The SecurityRealm specific filter. */ private volatile Filter filter; /** * The {@link #init(FilterConfig)} may be called before the Hudson instance * is up (which is required for initialization of the filter). So we store * the filterConfig for later lazy-initialization of the filter. */ private FilterConfig filterConfig; /** * {@link AuthenticationManager} proxy so that the Spring Security filter * chain can stay the same even when security setting is reconfigured. * * @deprecated in 1.271. This proxy always delegate to * {@code Hudson.getInstance().getSecurityRealm().getSecurityComponents().manager}, * so use that instead. */ public static final AuthenticationManagerProxy AUTHENTICATION_MANAGER = new AuthenticationManagerProxy(); /** * {@link UserDetailsService} proxy so that the Spring Security filter chain * can stay the same even when security setting is reconfigured. * * @deprecated in 1.271. This proxy always delegate to * {@code Hudson.getInstance().getSecurityRealm().getSecurityComponents().userDetails}, * so use that instead. */ public static final UserDetailsServiceProxy USER_DETAILS_SERVICE_PROXY = new UserDetailsServiceProxy(); /** * {@link RememberMeServices} proxy so that the Spring Security filter chain * can stay the same even when security setting is reconfigured. * * @deprecated in 1.271. This proxy always delegate to * {@code Hudson.getInstance().getSecurityRealm().getSecurityComponents().rememberMe}, * so use that instead. */ public static final RememberMeServicesProxy REMEMBER_ME_SERVICES_PROXY = new RememberMeServicesProxy(); public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; HudsonSecurityManager hudsonSecurityManager = HudsonSecurityEntitiesHolder.getHudsonSecurityManager(); try { SecurityRealm securityRealm = hudsonSecurityManager.getSecurityRealm(); reset(securityRealm); logger.debug("securityRealm is {}", securityRealm); logger.debug("Security initialized"); } catch (Exception exc) { // see HUDSON-4592. In some containers this happens before // WebAppMain.contextInitialized kicks in, which makes // the whole thing fail hard before a nicer error check // in WebAppMain.contextInitialized. So for now, // just report it here, and let the WebAppMain handle the failure gracefully. logger.error("Failed to initialize Hudson", exc); } // This is how we make this security filter available to the rest of Hudson. HudsonSecurityEntitiesHolder.setHudsonSecurityFilter(this); } /** * Gets the {@link HudsonFilter} created for the given * {@link ServletContext}. */ public static HudsonFilter get(ServletContext context) { // As of 3.0.0 we no longer set it to the context return HudsonSecurityEntitiesHolder.getHudsonSecurityFilter(); } /** * Reset the proxies and filter for a change in {@link SecurityRealm}. */ public void reset(SecurityRealm securityRealm) throws ServletException { if (securityRealm != null) { SecurityRealm.SecurityComponents sc = securityRealm.getSecurityComponents(); AUTHENTICATION_MANAGER.setDelegate(sc.manager); USER_DETAILS_SERVICE_PROXY.setDelegate(sc.userDetails); REMEMBER_ME_SERVICES_PROXY.setDelegate(sc.rememberMe); // make sure this.filter is always a valid filter. Filter oldf = this.filter; Filter newf = securityRealm.createFilter(this.filterConfig); newf.init(this.filterConfig); this.filter = newf; if (oldf != null) { oldf.destroy(); } } else { // no security related filter needed. AUTHENTICATION_MANAGER.setDelegate(null); USER_DETAILS_SERVICE_PROXY.setDelegate(null); REMEMBER_ME_SERVICES_PROXY.setDelegate(null); filter = null; } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.debug("doFilter"); // to deal with concurrency, we need to capture the object. Filter f = filter; if (f == null) { // Hudson is starting up. chain.doFilter(request, response); } else { f.doFilter(request, response, chain); } ThreadLocalUtils.removeThreadLocals(); } public void destroy() { // the filter can be null if the filter is not initialized yet. if (filter != null) { filter.destroy(); } } }