/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.server.security; import com.thoughtworks.go.config.CruiseConfig; import com.thoughtworks.go.config.SecurityConfig; import com.thoughtworks.go.listener.ConfigChangedListener; import com.thoughtworks.go.listener.PluginRoleChangeListener; import com.thoughtworks.go.listener.SecurityConfigChangeListener; import com.thoughtworks.go.server.service.GoConfigService; import com.thoughtworks.go.server.service.PluginRoleService; import com.thoughtworks.go.util.TimeProvider; import org.apache.log4j.Logger; import org.springframework.security.Authentication; import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.ui.FilterChainOrder; import org.springframework.security.ui.SpringSecurityFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @understands when a logged in user's authorization needs to be redone to get the new roles. */ public class RemoveAdminPermissionFilter extends SpringSecurityFilter implements ConfigChangedListener, PluginRoleChangeListener { private static final Logger LOGGER = Logger.getLogger(RemoveAdminPermissionFilter.class); protected static final String SECURITY_CONFIG_LAST_CHANGE = "security_config_last_changed_time"; private SecurityConfig securityConfig; private GoConfigService goConfigService; private TimeProvider timeProvider; private PluginRoleService pluginRoleService; private volatile long lastChangedTime; public RemoveAdminPermissionFilter(GoConfigService goConfigService, TimeProvider timeProvider, PluginRoleService pluginRoleService) { this.goConfigService = goConfigService; this.timeProvider = timeProvider; this.pluginRoleService = pluginRoleService; } public void initialize() { pluginRoleService.register(this); goConfigService.register(this); goConfigService.register(new SecurityConfigChangeListener() { @Override public void onEntityConfigChange(Object entity) { lastChangedTime = timeProvider.currentTimeMillis(); } }); } public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { chain.doFilter(request, response); return; } synchronized (request.getRequestedSessionId().intern()) { long localCopyOfLastChangedTime = lastChangedTime;//This is so that the volatile variable is accessed only once. Long previousLastChangedTime = (Long) request.getSession().getAttribute(SECURITY_CONFIG_LAST_CHANGE); if (previousLastChangedTime == null) { request.getSession().setAttribute(SECURITY_CONFIG_LAST_CHANGE, localCopyOfLastChangedTime); } else if (previousLastChangedTime < localCopyOfLastChangedTime) { request.getSession().setAttribute(SECURITY_CONFIG_LAST_CHANGE, localCopyOfLastChangedTime); authentication.setAuthenticated(false); } } chain.doFilter(request, response); } public int getOrder() { return FilterChainOrder.BASIC_PROCESSING_FILTER - 1; } public void onConfigChange(CruiseConfig newCruiseConfig) { SecurityConfig newSecurityConfig = securityConfig(newCruiseConfig); if (this.securityConfig != null && !this.securityConfig.equals(newSecurityConfig)) { LOGGER.info(String.format("[Configuration Changed] Security Configuration is changed. Updating the last changed time.")); this.lastChangedTime = timeProvider.currentTimeMillis(); } this.securityConfig = newSecurityConfig; } private SecurityConfig securityConfig(CruiseConfig newCruiseConfig) { return newCruiseConfig.server().security(); } @Override public void onPluginRoleChange() { this.lastChangedTime = timeProvider.currentTimeMillis(); } }