| /** |
| * 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.cxf.fediz.cxf.plugin; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.ws.rs.container.ContainerRequestFilter; |
| import javax.ws.rs.container.PreMatching; |
| import javax.ws.rs.core.Cookie; |
| import javax.ws.rs.core.HttpHeaders; |
| import javax.xml.bind.JAXBException; |
| |
| import org.w3c.dom.Element; |
| |
| import org.apache.cxf.BusFactory; |
| import org.apache.cxf.common.i18n.BundleUtils; |
| import org.apache.cxf.fediz.core.SecurityTokenThreadLocal; |
| import org.apache.cxf.fediz.core.config.FedizConfigurator; |
| import org.apache.cxf.fediz.core.config.FedizContext; |
| import org.apache.cxf.fediz.core.util.CookieUtils; |
| import org.apache.cxf.fediz.cxf.plugin.state.EHCacheSPStateManager; |
| import org.apache.cxf.fediz.cxf.plugin.state.ResponseState; |
| import org.apache.cxf.fediz.cxf.plugin.state.SPStateManager; |
| import org.apache.cxf.jaxrs.impl.HttpHeadersImpl; |
| import org.apache.cxf.jaxrs.impl.UriInfoImpl; |
| import org.apache.cxf.jaxrs.utils.ResourceUtils; |
| import org.apache.cxf.message.Message; |
| import org.apache.cxf.security.SecurityContext; |
| import org.apache.cxf.staxutils.StaxUtils; |
| import org.apache.wss4j.common.ext.WSSecurityException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| @PreMatching |
| public abstract class AbstractServiceProviderFilter implements ContainerRequestFilter { |
| |
| public static final String SECURITY_CONTEXT_TOKEN = |
| "org.apache.fediz.SECURITY_TOKEN"; |
| public static final String SECURITY_CONTEXT_STATE = |
| "org.apache.fediz.SECURITY_CONTEXT_STATE"; |
| |
| protected static final ResourceBundle BUNDLE = |
| BundleUtils.getBundle(AbstractServiceProviderFilter.class); |
| private static final Logger LOG = LoggerFactory.getLogger(AbstractServiceProviderFilter.class); |
| |
| private boolean addWebAppContext = true; |
| private boolean addEndpointAddressToContext; |
| |
| private FedizConfigurator configurator; |
| private String configFile; |
| private SPStateManager stateManager; |
| private long stateTimeToLive = 120000; |
| private String webAppDomain; |
| |
| public String getConfigFile() { |
| return configFile; |
| } |
| |
| public void setConfigFile(String configFile) { |
| this.configFile = configFile; |
| } |
| |
| @PostConstruct |
| public synchronized void configure() throws JAXBException, IOException { |
| if (configurator == null) { |
| String actualConfigFile = configFile; |
| if (actualConfigFile == null) { |
| actualConfigFile = "fediz_config.xml"; |
| } |
| try { |
| File f = new File(actualConfigFile); |
| if (!f.exists()) { |
| URL url = ResourceUtils.getResourceURL(actualConfigFile, |
| BusFactory.getThreadDefaultBus()); |
| if (url == null) { |
| url = new URL(actualConfigFile); |
| } |
| if (url != null) { |
| f = new File(url.getPath()); |
| } |
| } |
| configurator = new FedizConfigurator(); |
| configurator.loadConfig(f); |
| LOG.debug("Fediz configuration read from " + f.getAbsolutePath()); |
| } catch (JAXBException e) { |
| LOG.error("Error in parsing configuration", e); |
| throw e; |
| } catch (MalformedURLException e) { |
| LOG.error("Error in loading configuration file", e); |
| throw e; |
| } catch (Exception e) { |
| LOG.error("Error in loading configuration file", e); |
| throw new IOException(e); |
| } |
| } |
| |
| if (stateManager == null) { |
| stateManager = new EHCacheSPStateManager("fediz-ehcache.xml"); |
| } |
| } |
| |
| @PreDestroy |
| public synchronized void cleanup() throws IOException { |
| if (configurator != null) { |
| List<FedizContext> fedContextList = configurator.getFedizContextList(); |
| if (fedContextList != null) { |
| for (FedizContext fedContext : fedContextList) { |
| try { |
| fedContext.close(); |
| } catch (IOException ex) { |
| // |
| } |
| } |
| } |
| } |
| |
| stateManager.close(); |
| } |
| |
| protected boolean checkSecurityContext(FedizContext fedConfig, Message m) { |
| HttpHeaders headers = new HttpHeadersImpl(m); |
| Map<String, Cookie> cookies = headers.getCookies(); |
| |
| Cookie securityContextCookie = cookies.get(SECURITY_CONTEXT_TOKEN); |
| |
| ResponseState responseState = getValidResponseState(securityContextCookie, fedConfig, m); |
| if (responseState == null) { |
| return false; |
| } |
| |
| Cookie relayStateCookie = cookies.get(SECURITY_CONTEXT_STATE); |
| if (relayStateCookie == null) { |
| reportError("MISSING_RELAY_COOKIE"); |
| return false; |
| } |
| String originalRelayState = responseState.getState(); |
| if (!originalRelayState.equals(relayStateCookie.getValue())) { |
| // perhaps the response state should also be removed |
| reportError("INVALID_RELAY_STATE"); |
| return false; |
| } |
| |
| // Create SecurityContext |
| try { |
| Element token = |
| StaxUtils.read(new StringReader(responseState.getAssertion())).getDocumentElement(); |
| setSecurityContext(responseState, m, token); |
| } catch (Exception ex) { |
| reportError("INVALID_RESPONSE_STATE"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| protected void setSecurityContext( |
| ResponseState responseState, Message m, Element token |
| ) throws WSSecurityException { |
| CXFFedizPrincipal principal = |
| new CXFFedizPrincipal(responseState.getSubject(), responseState.getClaims(), |
| responseState.getRoles(), token); |
| |
| SecurityTokenThreadLocal.setToken(principal.getLoginToken()); |
| FedizSecurityContext context = |
| new FedizSecurityContext(principal, responseState.getRoles()); |
| m.put(SecurityContext.class, context); |
| } |
| |
| protected ResponseState getValidResponseState(Cookie securityContextCookie, |
| FedizContext fedConfig, |
| Message m) { |
| if (securityContextCookie == null) { |
| // most likely it means that the user has not been offered |
| // a chance to get logged on yet, though it might be that the browser |
| // has removed an expired cookie from its cache; warning is too noisy in the |
| // former case |
| reportTrace("MISSING_RESPONSE_STATE"); |
| return null; |
| } |
| String contextKey = securityContextCookie.getValue(); |
| ResponseState responseState = stateManager.getResponseState(contextKey); |
| |
| if (responseState == null) { |
| reportError("MISSING_RESPONSE_STATE"); |
| return null; |
| } |
| |
| if (CookieUtils.isStateExpired(responseState.getCreatedAt(), fedConfig.isDetectExpiredTokens(), |
| responseState.getExpiresAt(), getStateTimeToLive())) { |
| reportError("EXPIRED_RESPONSE_STATE"); |
| stateManager.removeResponseState(contextKey); |
| return null; |
| } |
| |
| String webAppContext = getWebAppContext(m); |
| if (webAppDomain != null |
| && (responseState.getWebAppDomain() == null |
| || !webAppDomain.equals(responseState.getWebAppDomain())) |
| || responseState.getWebAppContext() == null |
| || !webAppContext.equals(responseState.getWebAppContext())) { |
| stateManager.removeResponseState(contextKey); |
| reportError("INVALID_RESPONSE_STATE"); |
| return null; |
| } |
| if (responseState.getAssertion() == null) { |
| reportError("INVALID_RESPONSE_STATE"); |
| return null; |
| } |
| return responseState; |
| } |
| |
| protected FedizContext getFedizContext(Message message) { |
| String contextName = getWebAppContext(message); |
| String[] contextPath = contextName.split("/"); |
| if (contextPath.length > 0) { |
| contextName = "/" + contextPath[1]; |
| } |
| return getContextConfiguration(contextName); |
| } |
| |
| protected FedizContext getContextConfiguration(String contextName) { |
| if (configurator == null) { |
| throw new IllegalStateException("No Fediz configuration available"); |
| } |
| FedizContext config = configurator.getFedizContext(contextName); |
| if (config == null) { |
| throw new IllegalStateException("No Fediz configuration for context :" + contextName); |
| } |
| String catalinaBase = System.getProperty("catalina.base"); |
| if (catalinaBase != null && catalinaBase.length() > 0) { |
| config.setRelativePath(catalinaBase); |
| } |
| |
| return config; |
| } |
| |
| protected void reportError(String code) { |
| org.apache.cxf.common.i18n.Message errorMsg = |
| new org.apache.cxf.common.i18n.Message(code, BUNDLE); |
| LOG.warn(errorMsg.toString()); |
| } |
| |
| protected void reportTrace(String code) { |
| if (LOG.isDebugEnabled()) { |
| org.apache.cxf.common.i18n.Message errorMsg = |
| new org.apache.cxf.common.i18n.Message(code, BUNDLE); |
| LOG.debug(errorMsg.toString()); |
| } |
| } |
| |
| protected String getWebAppContext(Message m) { |
| if (addWebAppContext) { |
| if (addEndpointAddressToContext) { |
| return new UriInfoImpl(m).getBaseUri().getRawPath(); |
| } else { |
| String httpBasePath = (String)m.get("http.base.path"); |
| return URI.create(httpBasePath).getRawPath(); |
| } |
| } else { |
| return "/"; |
| } |
| } |
| |
| public void setAddWebAppContext(boolean addWebAppContext) { |
| this.addWebAppContext = addWebAppContext; |
| } |
| |
| public SPStateManager getStateManager() { |
| return stateManager; |
| } |
| |
| public void setStateManager(SPStateManager stateManager) { |
| this.stateManager = stateManager; |
| } |
| |
| public String getWebAppDomain() { |
| return webAppDomain; |
| } |
| |
| public void setWebAppDomain(String webAppDomain) { |
| this.webAppDomain = webAppDomain; |
| } |
| |
| public long getStateTimeToLive() { |
| return stateTimeToLive; |
| } |
| |
| public void setStateTimeToLive(long stateTimeToLive) { |
| this.stateTimeToLive = stateTimeToLive; |
| } |
| } |