Repository: cxf-fediz Updated Branches: refs/heads/master 3c1016cae -> 71b3cee9d
Adding a new Jetty9 plugin Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/71b3cee9 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/71b3cee9 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/71b3cee9 Branch: refs/heads/master Commit: 71b3cee9d2a636cfac577f463f77981baaa701ff Parents: 3c1016c Author: Colm O hEigeartaigh <[email protected]> Authored: Thu Sep 17 17:36:46 2015 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Thu Sep 17 17:36:46 2015 +0100 ---------------------------------------------------------------------- plugins/jetty8/pom.xml | 36 -- plugins/jetty9/README.txt | 10 + plugins/jetty9/pom.xml | 153 +++++ plugins/jetty9/src/main/assembly/assembly.xml | 37 ++ .../fediz/jetty9/FederationAuthenticator.java | 587 +++++++++++++++++++ .../fediz/jetty9/FederationIdentityService.java | 92 +++ .../fediz/jetty9/FederationLoginService.java | 169 ++++++ .../fediz/jetty9/FederationUserIdentity.java | 91 +++ .../fediz/jetty9/FederationUserPrincipal.java | 61 ++ plugins/pom.xml | 1 + pom.xml | 1 + 11 files changed, 1202 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty8/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/jetty8/pom.xml b/plugins/jetty8/pom.xml index ec3ce73..725bac1 100644 --- a/plugins/jetty8/pom.xml +++ b/plugins/jetty8/pom.xml @@ -51,48 +51,12 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-xml</artifactId> - <version>${jetty8.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - <version>${jetty8.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-jsp</artifactId> - <version>${jetty8.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>${junit.version}</version> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.apache.cxf.fediz</groupId> <artifactId>fediz-core</artifactId> <version>${project.version}</version> <type>jar</type> <scope>compile</scope> </dependency> - <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpclient</artifactId> - <version>${httpclient.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>net.htmlparser.jericho</groupId> - <artifactId>jericho-html</artifactId> - <version>${jericho.version}</version> - <scope>test</scope> - </dependency> </dependencies> <build> <plugins> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/README.txt ---------------------------------------------------------------------- diff --git a/plugins/jetty9/README.txt b/plugins/jetty9/README.txt new file mode 100644 index 0000000..266a6f8 --- /dev/null +++ b/plugins/jetty9/README.txt @@ -0,0 +1,10 @@ +Fediz configuration in Jetty +---------------------------- + +The Jetty installation must be updated before a Web Application can be deployed. + +The following wiki page gives instructions how to do that: +http://cxf.apache.org/fediz-jetty.html + +The following wiki page explains the fediz configuration which is Container independent: +http://cxf.apache.org/fediz-configuration.html http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/jetty9/pom.xml b/plugins/jetty9/pom.xml new file mode 100644 index 0000000..31e2433 --- /dev/null +++ b/plugins/jetty9/pom.xml @@ -0,0 +1,153 @@ +<?xml version="1.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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.cxf.fediz</groupId> + <artifactId>plugin</artifactId> + <version>1.3.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>fediz-jetty9</artifactId> + <name>Apache Fediz Plugin for Jetty 9</name> + <packaging>bundle</packaging> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${jetty9.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>${jetty9.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util</artifactId> + <version>${jetty9.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.cxf.fediz</groupId> + <artifactId>fediz-core</artifactId> + <version>${project.version}</version> + <type>jar</type> + <scope>compile</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>zip-file</id> + <phase>package</phase> + <goals> + <goal>attached</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/main/assembly/assembly.xml</descriptor> + </descriptors> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Implementation-Title>Apache CXF Fediz</Implementation-Title> + <Implementation-Vendor>The Apache Software + Foundation</Implementation-Vendor> + <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id> + <Implementation-Version>${project.version}</Implementation-Version> + <Specification-Title>Apache CXF Fediz</Specification-Title> + <Specification-Vendor>The Apache Software + Foundation</Specification-Vendor> + <Specification-Version>${project.version}</Specification-Version> + <Export-Package> + org.apache.cxf.fediz.jetty.*;version="${project.version}" + </Export-Package> + <Import-Package> + !org.apache.cxf.fediz.jetty*, + org.eclipse.jetty.*;version="[7.6,9)", + org.apache.cxf.fediz.core.*, + *;resolution:=optional + </Import-Package> + </instructions> + </configuration> + </plugin> +<!-- + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-idp-sts</id> + <phase>generate-resources</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.apache.cxf.fediz</groupId> + <artifactId>fediz-idp</artifactId> + <version>${project.version}</version> + <type>war</type> + <overWrite>true</overWrite> + </artifactItem> + <artifactItem> + <groupId>org.apache.cxf.fediz</groupId> + <artifactId>fediz-idp-sts</artifactId> + <version>${project.version}</version> + <type>war</type> + <overWrite>true</overWrite> + </artifactItem> + </artifactItems> + <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename> + <outputDirectory>target</outputDirectory> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + </configuration> + </execution> + </executions> + </plugin> +--> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/assembly/assembly.xml ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/assembly/assembly.xml b/plugins/jetty9/src/main/assembly/assembly.xml new file mode 100644 index 0000000..99a74db --- /dev/null +++ b/plugins/jetty9/src/main/assembly/assembly.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 +http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>zip-with-dependencies</id> + <formats> + <format>zip</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + <dependencySets> + <dependencySet> + <outputDirectory>/</outputDirectory> + <useProjectArtifact>true</useProjectArtifact> + <unpack>false</unpack> + <scope>runtime</scope> + </dependencySet> + </dependencySets> +</assembly> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java new file mode 100644 index 0000000..1b8e8ca --- /dev/null +++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java @@ -0,0 +1,587 @@ +/** + * 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.jetty9; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Map; + +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.xml.bind.JAXBException; + +import org.w3c.dom.Document; + +import org.apache.cxf.fediz.core.FederationConstants; +import org.apache.cxf.fediz.core.SAMLSSOConstants; +import org.apache.cxf.fediz.core.config.FederationProtocol; +import org.apache.cxf.fediz.core.config.FedizConfigurator; +import org.apache.cxf.fediz.core.config.FedizContext; +import org.apache.cxf.fediz.core.config.SAMLProtocol; +import org.apache.cxf.fediz.core.exception.ProcessingException; +import org.apache.cxf.fediz.core.processor.FedizProcessor; +import org.apache.cxf.fediz.core.processor.FedizProcessorFactory; +import org.apache.cxf.fediz.core.processor.FedizRequest; +import org.apache.cxf.fediz.core.processor.FedizResponse; +import org.apache.cxf.fediz.core.processor.RedirectionResponse; +import org.apache.wss4j.common.util.DOM2Writer; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.security.UserAuthentication; +import org.eclipse.jetty.security.authentication.DeferredAuthentication; +import org.eclipse.jetty.security.authentication.LoginAuthenticator; +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.Authentication; +import org.eclipse.jetty.server.Authentication.User; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/** + * Federation Authenticator. + * <p> + * This authenticator implements form authentication will redirect to the Identity Provider + * by sending a WS-Federation SignIn request. + * </p> + * <p> + * The federation authenticator redirects unauthenticated requests to an Identity Provider which use any kind of + * mechanism to authenticate the user. + * FederationAuthentication uses {@link SessionAuthentication} to wrap Authentication results so that they are + * associated with the session. + * </p> + */ +// CHECKSTYLE:OFF +public class FederationAuthenticator extends LoginAuthenticator { + + public static final String J_URI = "org.eclipse.jetty.security.form_URI"; + public static final String J_POST = "org.eclipse.jetty.security.form_POST"; + + private static final Logger LOG = Log.getLogger(FederationAuthenticator.class); + + private static final String SECURITY_TOKEN_ATTR = "org.apache.fediz.SECURITY_TOKEN"; + + private String configFile; + private FedizConfigurator configurator; + private String encoding = "UTF-8"; + + public FederationAuthenticator() { + } + + + /** + * + */ + @Override + public void setConfiguration(AuthConfiguration configuration) { + super.setConfiguration(configuration); + // is called after the bean setting -> do initialization here + LOG.debug(configuration.getInitParameterNames().toString()); + try { + File f = new File(getConfigFile()); + if (!f.exists()) { + String jettyHome = System.getProperty("jetty.home"); + if (jettyHome != null && jettyHome.length() > 0) { + f = new File(jettyHome.concat(File.separator + getConfigFile())); + } + } + configurator = new FedizConfigurator(); + configurator.loadConfig(f); + LOG.debug("Fediz configuration read from " + f.getAbsolutePath()); + } catch (JAXBException e) { + //[TODO] use other exception + throw new RuntimeException("Failed to load Fediz configuration", + e); + //throw new ServerAuthException("Failed to load Fediz configuration", + // e); + } + + } + + /* ------------------------------------------------------------ */ + public String getAuthMethod() { + return "WSFED"; + } + + public String getConfigFile() { + return configFile; + } + + public void setConfigFile(String configFile) { + this.configFile = configFile; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /* ------------------------------------------------------------ */ + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) + throws ServerAuthException { + + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + + HttpSession session = request.getSession(true); + + String contextName = request.getSession().getServletContext().getContextPath(); + if (contextName == null || contextName.isEmpty()) { + contextName = "/"; + } + FedizContext fedConfig = getContextConfiguration(contextName); + + // Check to see if it is a metadata request + try { + if (request.getRequestURL().indexOf(FederationConstants.METADATA_PATH_URI) != -1 + || request.getRequestURL().indexOf(getMetadataURI(fedConfig)) != -1) { + if (LOG.isDebugEnabled()) { + LOG.debug("Metadata document requested"); + } + response.setContentType("text/xml"); + PrintWriter out = response.getWriter(); + + FedizProcessor wfProc = + FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol()); + try { + Document metadata = wfProc.getMetaData(request, fedConfig); + out.write(DOM2Writer.nodeToString(metadata)); + return Authentication.SEND_CONTINUE; + } catch (Exception ex) { + LOG.warn("Failed to get metadata document: " + ex.getMessage()); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return Authentication.SEND_FAILURE; + } + } + } catch (IOException e) { + throw new ServerAuthException(e); + } + + if (!mandatory) { + return new DeferredAuthentication(this); + } + + try { + req.setCharacterEncoding(this.encoding); + } catch (UnsupportedEncodingException ex) { + LOG.warn("Unsupported encoding '" + this.encoding + "'", ex); + } + + String uri = request.getRequestURI(); + if (uri == null) { + uri = URIUtil.SLASH; + } + + try { + String action = request.getParameter(FederationConstants.PARAM_ACTION); + String responseToken = getResponseToken(request, fedConfig); + + // Handle a request for authentication. + if (isSignInRequest(request, fedConfig)) { + + FedizResponse wfRes = null; + if (LOG.isDebugEnabled()) { + LOG.debug("SignIn request found"); + } + + if (responseToken == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("SignIn request must contain a response token from the IdP"); + } + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return Authentication.SEND_FAILURE; + } else { + + FedizRequest wfReq = new FedizRequest(); + wfReq.setAction(action); + wfReq.setResponseToken(responseToken); + wfReq.setState(request.getParameter("RelayState")); + wfReq.setRequest(request); + + X509Certificate certs[] = + (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate"); + wfReq.setCerts(certs); + + FederationLoginService fedLoginService = (FederationLoginService)this._loginService; + UserIdentity user = fedLoginService.login(null, wfReq, fedConfig); + if (user != null) + { + session=renewSession(request,response); + + FederationUserIdentity fui = (FederationUserIdentity)user; + session.setAttribute(SECURITY_TOKEN_ATTR, fui.getToken()); + + // Redirect to original request + String nuri; + synchronized(session) + { + nuri = (String) session.getAttribute(J_URI); + + if (nuri == null || nuri.length() == 0) + { + nuri = request.getContextPath(); + if (nuri.length() == 0) { + nuri = URIUtil.SLASH; + } + } + Authentication cached=new SessionAuthentication(getAuthMethod(), user, wfRes); + session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); + } + response.setContentLength(0); + response.sendRedirect(response.encodeRedirectURL(nuri)); + + return new FederationAuthentication(getAuthMethod(), user); + } + + // not authenticated + if (LOG.isDebugEnabled()) { + LOG.debug("WSFED authentication FAILED"); + } + if (response != null) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + + } + } else if (FederationConstants.ACTION_SIGNOUT_CLEANUP.equals(action)) { + if (LOG.isDebugEnabled()) { + LOG.debug("SignOutCleanup request found"); + LOG.debug("SignOutCleanup action..."); + } + session.invalidate(); + + final ServletOutputStream responseOutputStream = response.getOutputStream(); + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("logout.jpg"); + if (inputStream == null) { + LOG.warn("Could not write logout.jpg"); + return Authentication.SEND_FAILURE; + } + int read = 0; + byte[] buf = new byte[1024]; + while ((read = inputStream.read(buf)) != -1) { + responseOutputStream.write(buf, 0, read); + } + inputStream.close(); + responseOutputStream.flush(); + return Authentication.SEND_SUCCESS; + } else if (action != null) { + LOG.warn("Not supported action found in parameter wa: " + action); + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return Authentication.UNAUTHENTICATED; + } + + // Look for cached authentication + Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED); + if (authentication != null) + { + // Has authentication been revoked? + if (authentication instanceof Authentication.User + && isTokenExpired(fedConfig, ((Authentication.User)authentication).getUserIdentity())) { + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + } + else + { + //logout + String logoutUrl = fedConfig.getLogoutURL(); + if (logoutUrl != null && !logoutUrl.isEmpty() && uri.equals(contextName + logoutUrl)) { + session.invalidate(); + + FedizProcessor wfProc = + FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol()); + signOutRedirectToIssuer(request, response, wfProc); + + return Authentication.SEND_CONTINUE; + } + + String j_uri = (String)session.getAttribute(J_URI); + if (j_uri != null) + { + @SuppressWarnings("unchecked") + MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(J_POST); + if (j_post != null) + { + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) { + buf.append("?").append(request.getQueryString()); + } + + if (j_uri.equals(buf.toString())) + { + // This is a retry of an original POST request + // so restore method and parameters + + session.removeAttribute(J_POST); + Request base_request = (Request)req; + // (req instanceof Request)?(Request)req:HttpConnection.getCurrentConnection().getRequest(); + base_request.setMethod(HttpMethod.POST.asString()); + base_request.setQueryParameters(j_post); + } + } + else + session.removeAttribute(J_URI); + + } + return authentication; + } + } + + + // if we can't send challenge + if (DeferredAuthentication.isDeferred(response)) + { + LOG.debug("auth deferred {}",session.getId()); + return Authentication.UNAUTHENTICATED; + } + + // remember the current URI + synchronized (session) + { + // But only if it is not set already, or we save every uri that leads to a login form redirect + if (session.getAttribute(J_URI)==null) // || alwaysSaveUri) + { + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) { + buf.append("?").append(request.getQueryString()); + } + session.setAttribute(J_URI, buf.toString()); + + if (MimeTypes.Type.FORM_ENCODED.equals(req.getContentType()) && HttpMethod.POST.equals(request.getMethod())) + { + Request base_request = (Request)req; + //(req instanceof Request)?(Request)req:HttpConnection.getCurrentConnection().getRequest(); + base_request.extractParameters(); + session.setAttribute(J_POST, new MultiMap<String>(base_request.getQueryParameters())); + } + } + } + + FedizProcessor wfProc = + FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol()); + signInRedirectToIssuer(request, response, wfProc); + + return Authentication.SEND_CONTINUE; + + } catch (IOException e) { + throw new ServerAuthException(e); + } + /* + * catch (ServletException e) { throw new ServerAuthException(e); } + */ + } + + private boolean isTokenExpired(FedizContext fedConfig, UserIdentity userIdentity) { + if (fedConfig.isDetectExpiredTokens()) { + try { + FederationUserIdentity fui = (FederationUserIdentity)userIdentity; + Date tokenExpires = fui.getExpiryDate(); + if (tokenExpires == null) { + LOG.debug("Token doesn't expire"); + return false; + } + + Date currentTime = new Date(); + if (!currentTime.after(tokenExpires)) { + return false; + } else { + LOG.warn("Token already expired. Clean up and redirect"); + + return true; + } + } catch (ClassCastException ex) { + LOG.warn("UserIdentity must be instance of FederationUserIdentity"); + throw new IllegalStateException("UserIdentity must be instance of FederationUserIdentity"); + } + } + + return false; + } + + private boolean isSignInRequest(ServletRequest request, FedizContext fedConfig) { + if (fedConfig.getProtocol() instanceof FederationProtocol + && FederationConstants.ACTION_SIGNIN.equals( + request.getParameter(FederationConstants.PARAM_ACTION))) { + return true; + } else if (fedConfig.getProtocol() instanceof SAMLProtocol + && request.getParameter(SAMLSSOConstants.RELAY_STATE) != null) { + return true; + } + + return false; + } + + private String getResponseToken(ServletRequest request, FedizContext fedConfig) { + if (fedConfig.getProtocol() instanceof FederationProtocol) { + return request.getParameter(FederationConstants.PARAM_RESULT); + } else if (fedConfig.getProtocol() instanceof SAMLProtocol) { + return request.getParameter(SAMLSSOConstants.SAML_RESPONSE); + } + return null; + } + + private String getMetadataURI(FedizContext fedConfig) { + if (fedConfig.getProtocol().getMetadataURI() != null) { + return fedConfig.getProtocol().getMetadataURI(); + } else if (fedConfig.getProtocol() instanceof FederationProtocol) { + return FederationConstants.METADATA_PATH_URI; + } else if (fedConfig.getProtocol() instanceof SAMLProtocol) { + return SAMLSSOConstants.FEDIZ_SAML_METADATA_PATH_URI; + } + + return FederationConstants.METADATA_PATH_URI; + } + + /* ------------------------------------------------------------ */ + public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, + User validatedUser) throws ServerAuthException { + return true; + } + + /** + * Called to redirect sign-in to the IDP/Issuer + * + * @param request + * Request we are processing + * @param response + * Response we are populating + * @param processor + * FederationProcessor + * @throws IOException + * If the forward to the login page fails and the call to + * {@link HttpServletResponse#sendError(int, String)} throws an + * {@link IOException} + */ + protected void signInRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor) + throws IOException { + + //Not supported in jetty 7.6 + //String contextName = request.getServletContext().getContextPath(); + String contextName = request.getSession().getServletContext().getContextPath(); + if (contextName == null || contextName.isEmpty()) { + contextName = "/"; + } + FedizContext fedCtx = this.configurator.getFedizContext(contextName); + try { + RedirectionResponse redirectionResponse = processor.createSignInRequest(request, fedCtx); + String redirectURL = redirectionResponse.getRedirectionURL(); + if (redirectURL != null) { + Map<String, String> headers = redirectionResponse.getHeaders(); + if (!headers.isEmpty()) { + for (String headerName : headers.keySet()) { + response.addHeader(headerName, headers.get(headerName)); + } + } + + response.sendRedirect(redirectURL); + } else { + LOG.warn("Failed to create SignInRequest."); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create SignInRequest."); + } + } catch (ProcessingException ex) { + LOG.warn("Failed to create SignInRequest: " + ex.getMessage()); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create SignInRequest."); + } + + } + + protected void signOutRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor) + throws IOException { + + //Not supported in jetty 7.6 + //String contextName = request.getServletContext().getContextPath(); + String contextName = request.getSession().getServletContext().getContextPath(); + if (contextName == null || contextName.isEmpty()) { + contextName = "/"; + } + FedizContext fedCtx = this.configurator.getFedizContext(contextName); + try { + RedirectionResponse redirectionResponse = + processor.createSignOutRequest(request, null, fedCtx); //TODO + String redirectURL = redirectionResponse.getRedirectionURL(); + if (redirectURL != null) { + Map<String, String> headers = redirectionResponse.getHeaders(); + if (!headers.isEmpty()) { + for (String headerName : headers.keySet()) { + response.addHeader(headerName, headers.get(headerName)); + } + } + + response.sendRedirect(redirectURL); + } else { + LOG.warn("Failed to create SignOutRequest."); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create SignOutRequest."); + } + } catch (ProcessingException ex) { + LOG.warn("Failed to create SignOutRequest: " + ex.getMessage()); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create SignOutRequest."); + } + } + + private 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 jettyHome = System.getProperty("jetty.home"); + if (jettyHome != null && jettyHome.length() > 0) { + config.setRelativePath(jettyHome); + } + return config; + } + + /* ------------------------------------------------------------ */ + /** + * This Authentication represents a just completed Federation authentication. Subsequent requests from the same + * user are authenticated by the presents of a {@link SessionAuthentication} instance in their session. + */ + public static class FederationAuthentication extends UserAuthentication implements + Authentication.ResponseSent { + + public FederationAuthentication(String method, UserIdentity userIdentity) { + super(method, userIdentity); + } + + @Override + public String toString() { + return "WSFED" + super.toString(); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationIdentityService.java ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationIdentityService.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationIdentityService.java new file mode 100644 index 0000000..b742e05 --- /dev/null +++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationIdentityService.java @@ -0,0 +1,92 @@ +/** + * 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.jetty9; + +import java.security.Principal; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.RoleRunAsToken; +import org.eclipse.jetty.security.RunAsToken; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/** + * Federation Identity Service implementation. + * This service handles only role reference maps passed in an + * associated {@link org.eclipse.jetty.server.UserIdentity.Scope}. If there are roles + * refs present, then associate will wrap the UserIdentity with one + * that uses the role references in the + * {@link org.eclipse.jetty.server.UserIdentity#isUserInRole(String, org.eclipse.jetty.server.UserIdentity.Scope)} + * implementation. All other operations are effectively noops. + * + */ +public class FederationIdentityService implements IdentityService { + private static final Logger LOG = Log.getLogger(FederationIdentityService.class); + + public FederationIdentityService() { + } + + + /** + * If there are roles refs present in the scope, then wrap the UserIdentity + * with one that uses the role references in the + * {@link UserIdentity#isUserInRole(String, org.eclipse.jetty.server.UserIdentity.Scope)} + */ + public Object associate(UserIdentity user) { + return null; + } + + public void disassociate(Object previous) { + } + + public Object setRunAs(UserIdentity user, RunAsToken token) { + return token; + } + + public void unsetRunAs(Object lastToken) { + } + + public RunAsToken newRunAsToken(String runAsName) { + return new RoleRunAsToken(runAsName); + } + + public UserIdentity getSystemUserIdentity() { + return null; + } + + public UserIdentity newUserIdentity( + final Subject subject, final Principal userPrincipal, final String[] roles) { + + try { + FederationUserPrincipal fup = (FederationUserPrincipal)userPrincipal; + return new FederationUserIdentity(subject, userPrincipal, roles, fup.getFedizResponse()); + } catch (ClassCastException ex) { + LOG.warn("Principal must be instance of FederationUserPrincipal"); + throw new IllegalStateException("Principal must be instance of FederationUserPrincipal"); + } + + + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationLoginService.java ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationLoginService.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationLoginService.java new file mode 100644 index 0000000..f058002 --- /dev/null +++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationLoginService.java @@ -0,0 +1,169 @@ +/** + * 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.jetty9; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.security.auth.Subject; +import javax.servlet.ServletRequest; + +import org.apache.cxf.fediz.core.config.FedizContext; +import org.apache.cxf.fediz.core.exception.ProcessingException; +import org.apache.cxf.fediz.core.processor.FedizProcessor; +import org.apache.cxf.fediz.core.processor.FedizProcessorFactory; +import org.apache.cxf.fediz.core.processor.FedizRequest; +import org.apache.cxf.fediz.core.processor.FedizResponse; +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class FederationLoginService extends AbstractLifeCycle implements LoginService { + private static final Logger LOG = Log.getLogger(FederationLoginService.class); + + protected IdentityService identityService = new FederationIdentityService(); + protected String name; + + + public FederationLoginService() { + } + + public FederationLoginService(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + if (isRunning()) { + throw new IllegalStateException("Running"); + } + + this.name = name; + } + + @Override + protected void doStart() throws Exception { + LOG.debug("doStart"); + super.doStart(); + } + + /** + * username will be null since the credentials will contain all the relevant info + */ + public UserIdentity login(String username, Object credentials, FedizContext config) { + + try { + FedizResponse wfRes = null; + FedizRequest wfReq = (FedizRequest)credentials; + + if (LOG.isDebugEnabled()) { + LOG.debug("Process SignIn request"); + LOG.debug("token=\n" + wfReq.getResponseToken()); + } + + FedizProcessor wfProc = + FedizProcessorFactory.newFedizProcessor(config.getProtocol()); + try { + wfRes = wfProc.processRequest(wfReq, config); + } catch (ProcessingException ex) { + LOG.warn("Federation processing failed: " + ex.getMessage()); + return null; + } + + + // Validate the AudienceRestriction in Security Token (e.g. SAML) + // against the configured list of audienceURIs + if (wfRes.getAudience() != null) { + List<String> audienceURIs = config.getAudienceUris(); + boolean validAudience = false; + for (String a : audienceURIs) { + if (wfRes.getAudience().startsWith(a)) { + validAudience = true; + break; + } + } + + if (!validAudience) { + LOG.warn("Token AudienceRestriction [" + wfRes.getAudience() + + "] doesn't match with specified list of URIs."); + return null; + } + } + + List<String> roles = wfRes.getRoles(); + if (roles == null || roles.size() == 0) { + roles = Collections.singletonList("Authenticated"); + } + + FederationUserPrincipal user = new FederationUserPrincipal(wfRes.getUsername(), wfRes); + + Subject subject = new Subject(); + subject.getPrincipals().add(user); + + String[] aRoles = new String[roles.size()]; + roles.toArray(aRoles); + + return identityService.newUserIdentity(subject, user, aRoles); + + } catch (Exception ex) { + LOG.warn(ex); + } + + return null; + } + + public boolean validate(UserIdentity user) { + try { + FederationUserIdentity fui = (FederationUserIdentity)user; + return fui.getExpiryDate().after(new Date()); + } catch (ClassCastException ex) { + LOG.warn("UserIdentity must be instance of FederationUserIdentity"); + throw new IllegalStateException("UserIdentity must be instance of FederationUserIdentity"); + } + } + + @Override + public IdentityService getIdentityService() { + return identityService; + } + + @Override + public void setIdentityService(IdentityService service) { + identityService = service; + } + + public void logout(UserIdentity user) { + + } + + @Override + public UserIdentity login(String username, Object credentials, ServletRequest arg2) { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserIdentity.java ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserIdentity.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserIdentity.java new file mode 100644 index 0000000..5c57a89 --- /dev/null +++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserIdentity.java @@ -0,0 +1,91 @@ +/** + * 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.jetty9; + + +import java.security.Principal; +import java.util.Date; + +import javax.security.auth.Subject; + +import org.w3c.dom.Element; +import org.apache.cxf.fediz.core.processor.FedizResponse; +import org.eclipse.jetty.server.UserIdentity; + +public class FederationUserIdentity implements UserIdentity { + + private Subject subject; + private Principal principal; + private String[] roles; + private FedizResponse fedResponse; + + public FederationUserIdentity(Subject subject, Principal principal, + String[] roles, FedizResponse fedResponse) { + this.subject = subject; + this.principal = principal; + this.roles = roles; + this.fedResponse = fedResponse; + } + + + public Subject getSubject() { + return subject; + } + + public Principal getUserPrincipal() { + return principal; + } + + public boolean isUserInRole(String role, Scope scope) { + if (scope != null && scope.getRoleRefMap() != null) { + role = scope.getRoleRefMap().get(role); + } + + if (this.roles != null) { + for (String r : this.roles) { + if (r.equals(role)) { + return true; + } + } + } + return false; + } + + public Date getExpiryDate() { + return fedResponse.getTokenExpires(); + } + + public String getIssuer() { + return fedResponse.getIssuer(); + } + + public String getAudience() { + return fedResponse.getAudience(); + } + + public String getId() { + return fedResponse.getUniqueTokenId(); + } + + public Element getToken() { + return fedResponse.getToken(); + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserPrincipal.java ---------------------------------------------------------------------- diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserPrincipal.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserPrincipal.java new file mode 100644 index 0000000..02176ec --- /dev/null +++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationUserPrincipal.java @@ -0,0 +1,61 @@ +/** + * 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.jetty9; + +import org.w3c.dom.Element; +import org.apache.cxf.fediz.core.ClaimCollection; +import org.apache.cxf.fediz.core.FedizPrincipal; +import org.apache.cxf.fediz.core.processor.FedizResponse; + +public class FederationUserPrincipal implements FedizPrincipal { + private String name; + private ClaimCollection claims; + private FedizResponse response; + + public FederationUserPrincipal(String name, FedizResponse response) { + this.name = name; + this.response = response; + this.claims = new ClaimCollection(response.getClaims()); + } + + @Override + public String getName() { + return name; + } + + + @Override + public ClaimCollection getClaims() { + return claims; + } + + // not public available + //[TODO] maybe find better approach, custom UserIdentity + FedizResponse getFedizResponse() { + return response; + } + + @Override + public Element getLoginToken() { + return response.getToken(); + } + + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/plugins/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/pom.xml b/plugins/pom.xml index dcc4152..fb9c343 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -35,6 +35,7 @@ <module>tomcat7</module> <module>tomcat8</module> <module>jetty8</module> + <module>jetty9</module> <module>spring</module> <module>spring2</module> <module>cxf</module> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/71b3cee9/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 1833f00..ca2fe02 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ <javax.validation.version>1.1.0.Final</javax.validation.version> <jericho.version>3.3</jericho.version> <jetty8.version>8.1.12.v20130726</jetty8.version> + <jetty9.version>9.3.3.v20150827</jetty9.version> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <ognl.version>3.0.8</ognl.version>
