diff --git a/agent/pom.xml b/agent/pom.xml
deleted file mode 100644
index 3ba348b63..000000000
--- a/agent/pom.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
- org.exoplatform.gatein.sso
- sso-parent
- ../pom.xml
- 6.6.x-SNAPSHOT
-
-
- 4.0.0
- sso-agent
- jar
- GateIn SSO - Agent
-
-
- 0.18
-
-
-
-
- org.exoplatform.gatein.sso
- sso-auth-callback
-
-
- org.gatein.common
- common-common
-
-
- org.apache.tomcat
- tomcat-catalina
- provided
-
-
- org.exoplatform.core
- exo.core.component.organization.api
- provided
-
-
- org.exoplatform.gatein.wci
- wci-wci
- provided
-
-
- org.apache.httpcomponents
- httpclient
- provided
-
-
- org.jboss.security
- jboss-negotiation-spnego
- provided
-
-
- org.picketlink
- picketlink-federation
- provided
-
-
- org.picketlink.distribution
- picketlink-wildfly8
- provided
-
-
- org.mockito
- mockito-core
- test
-
-
- junit
- junit
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- true
-
-
-
-
-
-
-
-
- run-its
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- false
-
-
-
-
-
-
-
-
diff --git a/agent/src/main/java/org/gatein/sso/agent/GenericAgent.java b/agent/src/main/java/org/gatein/sso/agent/GenericAgent.java
deleted file mode 100644
index 24b52a690..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/GenericAgent.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2012, Red Hat Middleware, LLC, and individual
- * contributors as indicated by the @authors tag. See the
- * copyright.txt in the distribution for a full listing of
- * individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.gatein.sso.agent;
-
-import org.apache.commons.lang.StringUtils;
-import org.exoplatform.commons.utils.ListAccess;
-import org.exoplatform.commons.utils.PropertyManager;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.exoplatform.services.organization.Query;
-import org.exoplatform.services.organization.User;
-import org.gatein.wci.security.Credentials;
-
-import org.exoplatform.container.ExoContainer;
-import org.exoplatform.container.ExoContainerContext;
-import org.exoplatform.container.PortalContainer;
-import org.exoplatform.container.RootContainer;
-import org.exoplatform.services.organization.OrganizationService;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Base agent superclass used by other SSO agents (CAS, OpenAM)
- *
- * @author Marek Posolda
- */
-public abstract class GenericAgent
-{
- private static Log log = ExoLogger.getLogger(GenericAgent.class);
-
- public static final String AUTHENTICATED_CREDENTIALS = "authenticatedCredentials";
-
- private static final String IS_CASE_INSENSITIVE = "exo.auth.case.insensitive";
-
-
- public void saveSSOCredentials(String username, HttpServletRequest httpRequest)
- {
- boolean case_insensitive_bool = true;
- String case_insensitive_str = PropertyManager.getProperty(IS_CASE_INSENSITIVE);
- if(case_insensitive_str != null) {
- case_insensitive_bool = Boolean.valueOf(case_insensitive_str);
- }
- if(case_insensitive_bool) {
- username = getUserPrincipal(username);
- }
- //Use empty password....it shouldn't be needed...this is a SSO login. The password has
- //already been presented with the SSO server. It should not be passed around for
- //better security
- Credentials credentials = new Credentials(username, "");
-
- httpRequest.getSession().setAttribute(Credentials.CREDENTIALS, credentials);
-
- // This is needed when using default login module stack instead of SSOLoginModule. In this case, GateIn authentication is done thanks to PortalLoginModule.
- httpRequest.getSession().setAttribute(GenericAgent.AUTHENTICATED_CREDENTIALS, credentials);
-
- log.debug("Credentials of user " + username + " saved into HTTP session.");
- }
-
- /**
- * @param username
- * @return gets the right username if the login input contains capital letters: EXOGTN-2267
- */
- public String getUserPrincipal(String username) {
- try {
- OrganizationService organizationService =
- (OrganizationService) getContainer()
- .getComponentInstance(OrganizationService.class);
- Query query = new Query();
- query.setUserName(username);
- ListAccess users = organizationService.getUserHandler().findUsersByQuery(query);
- if (users.getSize() >= 1) {
- String loadedUsername = "";
- User[] listusers = users.load(0, users.getSize());
- int found = 0;
- for(User user : listusers){
- if (username.equalsIgnoreCase(user.getUserName())) {
- loadedUsername = user.getUserName();
- found ++;
- }
- }
- if(found == 1 && StringUtils.isNotBlank(loadedUsername))
- username = loadedUsername;
- else
- log.warn("duplicate entry for user " + username);
-
- }
- } catch (Exception exception) {
- log.warn("Error while retrieving user " + username + " from IDM stores " , exception);
- }
- return username;
- }
-
- /**
- * @return Gives the {@link ExoContainer} that fits best with the current context
- */
- protected final ExoContainer getContainer()
- {
- ExoContainer container = ExoContainerContext.getCurrentContainer();
- if (container instanceof RootContainer) {
- container = PortalContainer.getInstance();
- }
- // The container is a PortalContainer or a StandaloneContainer
- return container;
- }
-
-}
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/AbstractLogoutFilter.java b/agent/src/main/java/org/gatein/sso/agent/filter/AbstractLogoutFilter.java
deleted file mode 100644
index 71dbc2b46..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/filter/AbstractLogoutFilter.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2006, Red Hat Middleware, LLC, and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.gatein.sso.agent.filter;
-
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.gatein.common.http.QueryStringParser;
-import org.gatein.sso.agent.filter.api.AbstractSSOInterceptor;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-/**
- * @author Sohil Shah
- */
-public abstract class AbstractLogoutFilter extends AbstractSSOInterceptor
-{
- protected String logoutUrl;
- private static final String fileEncoding = System.getProperty("file.encoding");
-
- private static final String SSO_LOGOUT_FLAG = "SSO_LOGOUT_FLAG";
- private static final String SSO_LOGOUT_REQ_URI = "SSO_LOGOUT_REQ_URI";
- private static final String SSO_LOGOUT_REQ_QUERY_STRING = "SSO_LOGOUT_REQ_QUERY_STRING";
-
- protected final Log log = ExoLogger.getLogger(this.getClass());
-
- protected void initImpl()
- {
- this.logoutUrl = getInitParameter("LOGOUT_URL");
-
- log.info("Reading filter configuration: logoutUrl=" + this.logoutUrl);
- }
-
- public void destroy()
- {
- }
-
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException
- {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
-
- boolean isLogoutInProgress = this.isLogoutInProgress(httpRequest);
-
- if (isLogoutInProgress)
- {
- boolean redirectionSent = handleLogout(httpRequest, httpResponse);
- if (redirectionSent)
- {
- return;
- }
- }
- // This means that we returned from SSO logout, but we need to redirect request to portal logout URI (something like
- // /portal/classic/home?portal:componentId=UIPortal&portal:action=Logout) because current request is not logout request
- // This can happen with some SSO servers, which doesn't redirect to logout URL (CAS)
- else if (httpRequest.getSession().getAttribute(SSO_LOGOUT_FLAG) != null)
- {
- // Restore previously saved logout URI
- HttpSession httpSession = httpRequest.getSession();
- String restoredURI = (String)httpSession.getAttribute(SSO_LOGOUT_REQ_URI);
- String restoredQueryString = (String)httpSession.getAttribute(SSO_LOGOUT_REQ_QUERY_STRING);
-
- // Cleanup all helper session attributes but keep SSO_LOGOUT_FLAG
- httpSession.removeAttribute(SSO_LOGOUT_REQ_URI);
- httpSession.removeAttribute(SSO_LOGOUT_REQ_QUERY_STRING);
-
- if (restoredURI != null && restoredQueryString != null)
- {
- String portalLogoutURI = restoredURI + "?" + restoredQueryString;
- portalLogoutURI = httpResponse.encodeRedirectURL(portalLogoutURI);
- httpResponse.sendRedirect(portalLogoutURI);
-
- if (log.isTraceEnabled())
- {
- log.trace("SSO logout performed. Redirecting to portal logout URI: " + portalLogoutURI);
- }
-
- return;
- }
- }
-
- chain.doFilter(request, response);
- }
-
- protected boolean isLogoutInProgress(HttpServletRequest request) throws UnsupportedEncodingException
- {
- // set character encoding before retrieving request parameters
- if(fileEncoding!=null)
- {
- request.setCharacterEncoding(fileEncoding);
- }
-
- String action = null;
- String queryString = request.getQueryString();
- if (queryString != null) {
- // The QueryStringParser currently only likes & and not &
- queryString = queryString.replace("&", "&");
- Map queryParams = QueryStringParser.getInstance().parseQueryString(queryString);
- String[] portalActions = queryParams.get("portal:action");
- if (portalActions != null && portalActions.length > 0) {
- action = portalActions[0];
- }
- }
-
- if (action != null && action.equals("Logout"))
- {
- return true;
- }
-
- return false;
- }
-
- /**
- * Handle logout on SSO server side
- *
- * @param httpRequest
- * @param httpResponse
- * @return true if redirection to SSO server was send. We need to return immediately from filter invocation then
- * @throws IOException
- */
- protected boolean handleLogout(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
- {
- HttpSession httpSession = httpRequest.getSession();
-
- // We need to perform redirection to SSO server to handle logout on SSO side
- if (httpRequest.getSession().getAttribute(SSO_LOGOUT_FLAG) == null)
- {
- httpSession.setAttribute(SSO_LOGOUT_FLAG, Boolean.TRUE);
- httpSession.setAttribute(SSO_LOGOUT_REQ_URI, httpRequest.getRequestURI());
- httpSession.setAttribute(SSO_LOGOUT_REQ_QUERY_STRING, httpRequest.getQueryString());
-
- String redirectUrl = this.getRedirectUrl(httpRequest);
- redirectUrl = httpResponse.encodeRedirectURL(redirectUrl);
- httpResponse.sendRedirect(redirectUrl);
-
- if (log.isTraceEnabled())
- {
- log.trace("Redirecting to SSO logout URL: " + redirectUrl);
- }
-
- return true;
- }
- else
- {
- // We returned from SSO server. Clear the LOGOUT flag and continue with this httpRequest
- httpSession.removeAttribute(SSO_LOGOUT_FLAG);
- if (log.isTraceEnabled())
- {
- log.trace("SSO logout performed and SSO_LOGOUT_FLAG removed from session. Continue with portal logout");
- }
-
- return false;
- }
- }
-
- protected abstract String getRedirectUrl(HttpServletRequest httpRequest);
-}
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/InitiateLoginFilter.java b/agent/src/main/java/org/gatein/sso/agent/filter/InitiateLoginFilter.java
deleted file mode 100644
index 59103b8a1..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/filter/InitiateLoginFilter.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- *
- */
-package org.gatein.sso.agent.filter;
-
-import java.io.IOException;
-
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.gatein.sso.agent.GenericAgent;
-import org.gatein.sso.agent.filter.api.AbstractSSOInterceptor;
-import org.gatein.sso.agent.opensso.OpenSSOAgent;
-import org.gatein.wci.security.Credentials;
-
-import org.exoplatform.commons.utils.PropertyManager;
-
-/**
- * @author soshah
- */
-public class InitiateLoginFilter extends AbstractSSOInterceptor {
- private static Log log = ExoLogger.getLogger(InitiateLoginFilter.class);
-
- private static final int DEFAULT_MAX_NUMBER_OF_LOGIN_ERRORS = 3;
-
- private String ssoServerUrl;
-
- private String ssoCookieName;
-
- private String loginUrl;
-
- private int maxNumberOfLoginErrors;
-
- private boolean attachUsernamePasswordToLoginURL;
-
- private OpenSSOAgent openSSOAgent;
-
- @Override
- protected void initImpl() {
- this.ssoServerUrl = getInitParameter("ssoServerUrl");
- this.ssoCookieName = getInitParameter("ssoCookieName");
- this.loginUrl = getInitParameter("loginUrl");
-
- String maxNumberOfLoginErrorsConfig = getInitParameter("maxNumberOfLoginErrors");
- this.maxNumberOfLoginErrors = maxNumberOfLoginErrorsConfig == null ? DEFAULT_MAX_NUMBER_OF_LOGIN_ERRORS
- : Integer.parseInt(maxNumberOfLoginErrorsConfig);
-
- String attachUsernamePasswordToLoginURLConfig = getInitParameter("attachUsernamePasswordToLoginURL");
- this.attachUsernamePasswordToLoginURL =
- attachUsernamePasswordToLoginURLConfig == null
- || Boolean.parseBoolean(attachUsernamePasswordToLoginURLConfig);
- }
-
- public void doFilter(ServletRequest request,
- ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- try {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse resp = (HttpServletResponse) response;
-
- this.processSSOToken(req, resp);
-
- // Redirection can be already performed from processSSOToken call
- if (resp.isCommitted()) {
- return;
- }
-
- String portalContext = req.getContextPath();
- if (req.getAttribute("abort") != null) {
- String ssoSuffix = PropertyManager.getProperty("gatein.sso.uri.suffix");
- if (StringUtils.isBlank(ssoSuffix)) {
- ssoSuffix = "/sso";
- }
- String ssoRedirect = portalContext + (ssoSuffix.startsWith("/") ? ssoSuffix : ("/" + ssoSuffix));
- resp.sendRedirect(ssoRedirect);
- return;
- }
-
- String loginRedirectURL = getLoginRedirectUrl(req);
- if (StringUtils.isBlank(loginRedirectURL)) {
- log.warn("Can't redirect to null SSO URL");
- chain.doFilter(request, response);
- } else {
- loginRedirectURL = resp.encodeRedirectURL(loginRedirectURL);
- resp.sendRedirect(loginRedirectURL);
- }
- } catch (Exception e) {
- throw new ServletException(e);
- }
- }
-
- @Override
- public void destroy() {
- // Nothing to proceed
- }
-
- protected OpenSSOAgent getOpenSSOAgent() {
- if (this.openSSOAgent == null) {
- OpenSSOAgent openssoAgent = getExoContainer().getComponentInstanceOfType(OpenSSOAgent.class);
- if (openssoAgent == null) {
- throw new IllegalStateException("OpenSSOAgent component not provided in PortalContainer");
- }
-
- openssoAgent.setServerUrl(ssoServerUrl);
- openssoAgent.setCookieName(ssoCookieName);
- this.openSSOAgent = openssoAgent;
- }
-
- return this.openSSOAgent;
- }
-
- protected void processSSOToken(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws Exception {
- try {
- // See if an OpenSSO Token was used
- getOpenSSOAgent().validateTicket(httpRequest, httpResponse);
- } catch (IllegalStateException ilse) {
- // Somehow cookie failed validation, retry by starting the opensso login
- // process again.
- // To avoid infinite loop of redirects, we are tracking maximum number
- // of SSO errors for this client
- int currentNumberOfErrors = getCountOfUnsuccessfulAttempts(httpRequest);
- log.warn("Count of login errors: " + currentNumberOfErrors);
-
- if (currentNumberOfErrors >= maxNumberOfLoginErrors) {
- log.warn("Max. number of login errors reached. Rethrowing exception");
- throw ilse;
- } else {
- httpRequest.setAttribute("abort", Boolean.TRUE);
- }
- }
- }
-
- // Tracking maximum number of SSO errors for this client in session attribute
- private int getCountOfUnsuccessfulAttempts(HttpServletRequest httpRequest) {
- Integer currentNumberOfErrors = (Integer) httpRequest.getSession().getAttribute("InitiateLoginFilter.currentNumberOfErrors");
- if (currentNumberOfErrors == null) {
- currentNumberOfErrors = 0;
- }
-
- currentNumberOfErrors = currentNumberOfErrors + 1;
- httpRequest.getSession().setAttribute("InitiateLoginFilter.currentNumberOfErrors", currentNumberOfErrors);
-
- return currentNumberOfErrors;
- }
-
- protected String getLoginRedirectUrl(HttpServletRequest req) {
- if (this.loginUrl == null) {
- return null;
- }
- StringBuilder url = new StringBuilder(this.loginUrl);
-
- if (attachUsernamePasswordToLoginURL) {
- String fakePassword = req.getSession().getId() + "_" + System.currentTimeMillis();
-
- // Try to use username from authenticated credentials
- String username;
- Credentials creds = (Credentials) req.getSession().getAttribute(GenericAgent.AUTHENTICATED_CREDENTIALS);
- if (creds != null) {
- username = creds.getUsername();
- } else {
- // Fallback to fakePassword, but this should't happen (credentials
- // should always be available when this method is called)
- username = fakePassword;
- }
-
- // Use sessionId and system millis as password (similar like spnego is
- // doing)
- url.append("?username=").append(username).append("&password=").append(fakePassword);
- }
-
- return url.toString();
- }
-}
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOCDLoginRedirectFilter.java b/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOCDLoginRedirectFilter.java
deleted file mode 100644
index bd2d9e380..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOCDLoginRedirectFilter.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2012, Red Hat Middleware, LLC, and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- *
- */
-
-package org.gatein.sso.agent.filter;
-
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.gatein.sso.agent.opensso.OpenSSOAgentImpl;
-
-import javax.servlet.http.HttpServletRequest;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Random;
-
-/**
- * Filter is used for redirection to OpenSSO CDCServlet. It is intended to be used in Cross-Domain authentication scenario
- * when GateIn and OpenSSO servers are in different DNS domains.
- *
- * @author Marek Posolda
- */
-public class OpenSSOCDLoginRedirectFilter extends LoginRedirectFilter
-{
- private static final Log log = ExoLogger.getLogger(OpenSSOCDLoginRedirectFilter.class);
-
- private String openSSORealm;
- private String agentUrl;
-
- private Random random = new Random();
- private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", new Locale("en"));
- private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss", new Locale("en"));
-
- @Override
- public void initImpl()
- {
- super.initImpl();
-
- this.openSSORealm = getInitParameter("OpenSSORealm");
- this.agentUrl = getInitParameter("AgentUrl");
- log.info("Filter configuration: loginUrl=" + loginUrl +
- ", openSSORealm=" + openSSORealm +
- ", agentUrl=" + agentUrl);
- }
-
- /**
- * Constructs URL for redirection to OpenSSO CDCServlet.
- * It will be something like:
- * {@code
- * http://localhost:8888/opensso/cdcservlet?realm=gatein&goto=http://opensso.local.network:8080/portal/initiatessologin&
- * ProviderID=http://opensso.local.network:8080/portal/initiatessologin/?Realm=ggatein
- * &RequestID=124&IssueInstant=2012-04-10T23:28:50Z&MajorVersion=1&MinorVersion=0
- * }
- *
- * @return url for redirection
- */
- @Override
- protected String getLoginRedirectURL(HttpServletRequest httpRequest)
- {
- try
- {
- StringBuilder urlBuilder = new StringBuilder(loginUrl);
- urlBuilder.append("?realm=").append(openSSORealm);
- urlBuilder.append("&goto=").append(URLEncoder.encode(agentUrl, "UTF-8"));
-
- // We need to use Realm=g because of bug (or strange behaviour) of OpenAM, which cuts first character of realmName during parsing
- // Update GTNSSO-28 - This extra char is supposed to be a "slash" on pre-10.1 versions. So, adding the "g"
- // is not really appropriate. Replacing the "g" with "/" makes it work for both 9.x up to 12.0 (latest tested).
- String providerId = agentUrl + "/?Realm=" + URLEncoder.encode("/" + openSSORealm, "UTF-8");
- urlBuilder.append("&ProviderID=").append(URLEncoder.encode(providerId, "UTF-8"));
-
- // Generate random number for parameter "inResponseTo" and save it to session. This ID must be in response message in parameter "inResponseTo"
- int requestId = random.nextInt(100000) + 1;
- urlBuilder.append("&RequestID=").append(requestId);
- httpRequest.getSession().setAttribute(OpenSSOAgentImpl.IN_RESPONSE_TO_ATTR, requestId);
-
- String issueInstant = getFormattedDate();
- urlBuilder.append("&IssueInstant=" + URLEncoder.encode(issueInstant, "UTF-8"));
-
- urlBuilder.append("&MajorVersion=1&MinorVersion=0");
-
- String urlToRedirect = urlBuilder.toString();
-
- if (log.isTraceEnabled())
- {
- log.trace("URL for redirection to CDCServlet: " + urlToRedirect);
- }
-
- return urlToRedirect;
- }
- catch (UnsupportedEncodingException uee)
- {
- throw new RuntimeException(uee);
- }
-
- }
-
- private String getFormattedDate()
- {
- Date d = new Date();
- return dateFormat.format(d) + "T" + timeFormat.format(d) + "Z";
- }
-
-}
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOLogoutFilter.java b/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOLogoutFilter.java
deleted file mode 100644
index fcbb36e89..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/filter/OpenSSOLogoutFilter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-* JBoss, a division of Red Hat
-* Copyright 2006, Red Hat Middleware, LLC, and individual contributors as indicated
-* by the @authors tag. See the copyright.txt in the distribution for a
-* full listing of individual contributors.
-*
-* This is free software; you can redistribute it and/or modify it
-* under the terms of the GNU Lesser General Public License as
-* published by the Free Software Foundation; either version 2.1 of
-* the License, or (at your option) any later version.
-*
-* This software is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this software; if not, write to the Free
-* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-*/
-package org.gatein.sso.agent.filter;
-
-import java.net.URLEncoder;
-import javax.servlet.http.HttpServletRequest;
-
-//Works for GateIn Portal Logout URL = {AnyURL}?portal:componentId=UIPortal&portal:action=Logout
-
-/**
- * Usage:
- *
- * Add the following to portal.war/WEB-INF/web.xml
- *
- *
- * OpenSSOLogoutFilter
- * org.gatein.sso.agent.filter.OpenSSOLogoutFilter
- *
- *
- * LOGOUT_URL
- * http://localhost:8888/opensso/UI/Logout
- *
- *
- *
- *
- * OpenSSOLogoutFilter
- * /*
- *
- *
- *
- *
- */
-
-
-/**
- * @author Sohil Shah
- */
-public class OpenSSOLogoutFilter extends AbstractLogoutFilter
-{
- protected String getRedirectUrl(HttpServletRequest httpRequest)
- {
- try
- {
- String parameters = URLEncoder.encode(
- "portal:componentId=UIPortal&portal:action=Logout", "UTF-8");
-
- String redirectUrl = this.logoutUrl+"?realm=gatein&goto="+httpRequest.getRequestURL()+"?"+parameters;
-
- return redirectUrl;
- }
- catch(Exception e)
- {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/PicketlinkSTSIntegrationFilter.java b/agent/src/main/java/org/gatein/sso/agent/filter/PicketlinkSTSIntegrationFilter.java
deleted file mode 100644
index 73ec84271..000000000
--- a/agent/src/main/java/org/gatein/sso/agent/filter/PicketlinkSTSIntegrationFilter.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2012, Red Hat Middleware, LLC, and individual
- * contributors as indicated by the @authors tag. See the
- * copyright.txt in the distribution for a full listing of
- * individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.gatein.sso.agent.filter;
-
-import org.exoplatform.container.web.AbstractFilter;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.exoplatform.services.security.jaas.UserPrincipal;
-import org.jboss.security.SecurityContext;
-import org.jboss.security.SecurityContextAssociation;
-import org.jboss.security.client.SecurityClient;
-import org.jboss.security.client.SecurityClientFactory;
-
-import javax.security.auth.Subject;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Set;
-import org.picketlink.identity.federation.core.wstrust.SamlCredential;
-
-/**
- * Filter for set {@link SamlCredential} into {@link SecurityClient}, which enables to propagate authentication from SAML2 ticket into
- * underlying EJB or WS calls.
- *
- * @author Marek Posolda
- */
-public class PicketlinkSTSIntegrationFilter extends AbstractFilter
-{
- private static Log log = ExoLogger.getLogger(PicketlinkSTSIntegrationFilter.class);
-
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
- {
- HttpServletRequest httpRequest = (HttpServletRequest)request;
- if (httpRequest.getRemoteUser() != null)
- {
- try
- {
- SamlCredential samlCredential = getSamlCredential();
-
- if (log.isTraceEnabled())
- {
- log.trace("Found SamlCredential inside Subject: " + samlCredential);
- }
-
- // Now set the security context, which can be used in EJB or other calls
- if (samlCredential != null)
- {
- SecurityClient client = SecurityClientFactory.getSecurityClient();
- // Simple login just updates the security context
- client.setSimple(new UserPrincipal(httpRequest.getRemoteUser()), samlCredential);
- client.login();
-
- if (log.isTraceEnabled())
- {
- log.trace("SecurityClient successfully updated with SAMLCredential");
- }
- }
-
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
-
- chain.doFilter(request, response);
- }
-
- public void destroy()
- {
- }
-
- private SamlCredential getSamlCredential()
- {
- Subject subj = getCurrentSubject();
-
- if (log.isTraceEnabled())
- {
- log.trace("Found subject " + subj);
- }
-
- if (subj == null)
- {
- return null;
- }
-
- Set
SAML 2 Unsolicited Response: Sales SAML 2.0 (make sure you have deployed the sales-post-sig application)
-
-
-
-
-
\ No newline at end of file
diff --git a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/error.jsp b/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/error.jsp
deleted file mode 100644
index 43bb87450..000000000
--- a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/error.jsp
+++ /dev/null
@@ -1,12 +0,0 @@
- Error!
-
-
-
- The username and password you supplied are not valid.
-
-Click here
-to retry login
-
-
-
-
diff --git a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login-error.jsp b/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login-error.jsp
deleted file mode 100644
index c933f8064..000000000
--- a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login-error.jsp
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-Welcome to JBoss Application Server 7
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login.jsp b/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login.jsp
deleted file mode 100644
index e41a550d6..000000000
--- a/saml/gatein-saml-portal/src/main/resources/idp-sig.war/jsp/login.jsp
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-Welcome to JBoss Application Server 7
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/saml/pom.xml b/saml/pom.xml
deleted file mode 100644
index e58ac99bb..000000000
--- a/saml/pom.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
- org.exoplatform.gatein.sso
- sso-parent
- 6.6.x-SNAPSHOT
- ../pom.xml
-
-
- 4.0.0
- org.exoplatform.gatein.sso
- sso-saml-parent
- pom
-
- GateIn SSO - SAML
-
-
- gatein-saml-plugin
- gatein-saml-portal
-
-
-
diff --git a/spnego/pom.xml b/spnego/pom.xml
deleted file mode 100644
index ab86d8dec..000000000
--- a/spnego/pom.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
- org.exoplatform.gatein.sso
- sso-parent
- ../pom.xml
- 6.6.x-SNAPSHOT
-
-
- 4.0.0
- spnego
- jar
- GateIn SPNEGO LoginModule based on JBoss Negotiation
-
-
-
- org.exoplatform.core
- exo.core.component.organization.api
-
-
- org.jboss.security
- jboss-negotiation-spnego
-
-
- org.exoplatform.gatein.sso
- sso-agent
-
-
-
-
-
-
-
-
-
-
- run-its
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- true
-
-
-
-
-
-
-
-
diff --git a/spnego/src/main/java/org/gatein/sso/spnego/SPNEGOLoginModule.java b/spnego/src/main/java/org/gatein/sso/spnego/SPNEGOLoginModule.java
deleted file mode 100644
index 7a17903ca..000000000
--- a/spnego/src/main/java/org/gatein/sso/spnego/SPNEGOLoginModule.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2011, Red Hat Middleware, LLC, and individual
- * contributors as indicated by the @authors tag. See the
- * copyright.txt in the distribution for a full listing of
- * individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.gatein.sso.spnego;
-
-import org.exoplatform.services.security.jaas.UserPrincipal;
-import org.jboss.security.negotiation.common.NegotiationContext;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import java.security.Principal;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Modified version of {@link org.jboss.security.negotiation.spnego.SPNEGOLoginModule} customized for portal purposes
- *
- * @author Marek Posolda
- */
-public class SPNEGOLoginModule extends org.jboss.security.negotiation.spnego.SPNEGOLoginModule
-{
- private String usernamePasswordDomain;
-
- // TODO: Workaround. Remove once getIdentityFromSubject method will be added to superclass.
- @Override
- public void initialize(final Subject subject, final CallbackHandler callbackHandler, final Map sharedState,
- final Map options)
- {
- super.initialize(subject, callbackHandler, sharedState, options);
- usernamePasswordDomain = (String) options.get("usernamePasswordDomain");
- }
-
- // TODO: Workaround. Remove once getIdentityFromSubject method will be added to superclass.
- @Override
- protected Object innerLogin() throws LoginException
- {
- NegotiationContext negotiationContext = NegotiationContext.getCurrentNegotiationContext();
-
- if (negotiationContext == null)
- {
- if (usernamePasswordDomain == null)
- {
- throw new LoginException("No NegotiationContext and no usernamePasswordDomain defined.");
- }
-
- return usernamePasswordLogin();
- }
- else
- {
- return super.innerLogin();
- }
- }
-
- // TODO: Workaround. Remove once getIdentityFromSubject method will be added to superclass.
- private Object usernamePasswordLogin() throws LoginException
- {
- log.debug("Falling back to username/password authentication");
-
- LoginContext lc = new LoginContext(usernamePasswordDomain, callbackHandler);
- lc.login();
-
- Subject userSubject = lc.getSubject();
-
- Principal identity = getIdentityFromSubject(userSubject);
- setIdentity(identity);
-
- return Boolean.TRUE;
- }
-
-
- /**
- * Obtaining identity from subject. We need to find instance of {@link UserPrincipal}
- * , which is added here during FORM authentication.
- *
- * @param userSubject subject from FORM authentication
- * @return identity
- * @throws javax.security.auth.login.LoginException
- */
- protected Principal getIdentityFromSubject(Subject userSubject) throws LoginException
- {
- Set principals = userSubject.getPrincipals(UserPrincipal.class);
- if (principals.isEmpty())
- {
- throw new LoginException("No UserPrincipals returned after login.");
- }
- else if (principals.size() > 1)
- {
- log.warn("Multiple UserPrincipals returned, using first principal in set.");
- }
-
- Principal identity = (Principal) principals.iterator().next();
- return identity;
- }
-}
diff --git a/spnegosso/README.md b/spnegosso/README.md
deleted file mode 100644
index 50b4ad5c9..000000000
--- a/spnegosso/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-This is other spnego implementation beside https://github.com/gatein/gatein-sso/tree/master/spnego
-We introduce new spnego implementation because existing implementation dose not work on gatein tomcat packaging.
-This implementation work well on both jboss and tomcat packaging.
-
-###Build and configure
-
-1. Configure SPNEGO Server
- You configure SPNEGO server follow the guideline at gatein document: https://docs.jboss.org/author/display/GTNPORTAL37/SPNEGO
-
-2. Build and deploy spnegosso
- - Use maven to build gatein-spnego project
- - Copy spnegosso-${VERSION}.jar to $GATEIN_TOMCAT/lib folder
-
-3. Configure gatein
- - Append this login module configuration into $GATEIN_HOME/conf/jaas.conf
-```
-spnego-server {
- com.sun.security.auth.module.Krb5LoginModule required
- storeKey=true
- doNotPrompt=true
- useKeyTab=true
- keyTab="/etc/krb5.keytab"
- principal="HTTP/server.local.network@LOCAL.NETWORK"
- useFirstPass=true
- debug=true
- isInitiator=false;
-};
-```
-
- - Change SSO section in the file $GATEIN_HOME/gatein/conf/configuration.properties to be like this:
-```
-gatein.sso.enabled=true
-gatein.sso.filter.spnego.enabled=true
-gatein.sso.callback.enabled=false
-gatein.sso.skip.jsp.redirection=false
-gatein.sso.login.module.enabled=true
-gatein.sso.login.module.class=org.gatein.security.sso.spnego.SPNEGOSSOLoginModule
-gatein.sso.filter.login.sso.url=/@@portal.container.name@@/spnegosso
-gatein.sso.filter.initiatelogin.enabled=false
-gatein.sso.valve.enabled=false
-gatein.sso.filter.logout.enabled=false
-```
-
diff --git a/spnegosso/pom.xml b/spnegosso/pom.xml
deleted file mode 100644
index 61a1fef89..000000000
--- a/spnegosso/pom.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- 4.0.0
-
-
- org.exoplatform.gatein.sso
- sso-parent
- ../pom.xml
- 6.6.x-SNAPSHOT
-
- spnegosso
-
-
-
- javax.servlet
- javax.servlet-api
-
-
- org.exoplatform.gatein.sso
- sso-agent
-
-
- org.gatein.common
- common-common
-
-
-
-
diff --git a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOFilter.java b/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOFilter.java
deleted file mode 100644
index 2985c0c51..000000000
--- a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOFilter.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2012 eXo Platform SAS.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.gatein.security.sso.spnego;
-
-import java.io.IOException;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.UUID;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.FilterChain;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.gatein.common.util.Base64;
-import org.gatein.sso.agent.filter.api.AbstractSSOInterceptor;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.Oid;
-
-public class SPNEGOSSOFilter extends AbstractSSOInterceptor {
- private static final Log log = ExoLogger.getLogger(AbstractSSOInterceptor.class);
-
- private static final GSSManager MANAGER = GSSManager.getInstance();
-
- private LoginContext loginContext;
- private String[] patterns = {"/login", "/spnegosso"};
- private String loginServletPath = "/login";
- private String securityDomain = "spnego-server";
-
- public SPNEGOSSOFilter() {}
-
- @Override
- protected void initImpl() {
- String patternParam = this.getInitParameter("patterns");
- if(patternParam != null && !patternParam.isEmpty()) {
- this.patterns = patternParam.split(",");
- }
-
- String loginServlet = this.getInitParameter("loginServletPath");
- if(loginServlet != null && !loginServlet.isEmpty()) {
- this.loginServletPath = loginServlet;
- }
-
- String domain = this.getInitParameter("securityDomain");
- if(domain != null && !domain.isEmpty()) {
- this.securityDomain = domain;
- }
-
- try {
- this.loginContext = new LoginContext(this.securityDomain);
- } catch (LoginException ex) {
- log.warn("Exception while init LoginContext, so SPNEGO SSO will not work", ex);
- }
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- final HttpServletRequest req = (HttpServletRequest)request;
- final HttpServletResponse resp = (HttpServletResponse)response;
-
- //. Check if this is not spnego login request
- if(!isSpnegoLoginRequest(req)) {
- chain.doFilter(request, response);
- return;
- }
-
- SPNEGOSSOContext.setCurrentRequest(req);
- final String contextPath = req.getContextPath();
- final String loginURI = contextPath + this.loginServletPath;
- final String requestURI = req.getRequestURI();
- String username = req.getParameter("username");
- final String remoteUser = req.getRemoteUser();
-
- if(username != null || remoteUser != null) {
- if(!loginURI.equalsIgnoreCase(requestURI)) {
- // Redirect to /login if current request is /spnegosso to avoid error 404
- // when user access to /spnegosso?username=username or when loggedIn user access to /spengosso
- StringBuilder login = new StringBuilder(loginURI);
- if(req.getQueryString() != null) {
- login.append("?").append(req.getQueryString());
- }
- resp.sendRedirect(login.toString());
- } else {
- chain.doFilter(req, resp);
- }
- return;
- }
-
- String principal = null;
- final String auth = req.getHeader("Authorization");
- if(auth != null) {
- try {
- principal = this.login(req, resp, auth);
- } catch (Exception ex) {
- log.error("Exception occur when trying to login with SPNEGO", ex);
- }
- }
-
- if(principal != null && !principal.isEmpty()) {
- username = principal.substring(0, principal.indexOf('@'));
- // We don't need user password when he login using SSO (SPNEGO)
- // But LoginServlet require password is not empty to call login action instead of display input form
- // So, we need to generate a random password
- String password = UUID.randomUUID().toString();
-
- HttpSession session = req.getSession();
- session.setAttribute("SPNEGO_PRINCIPAL", username);
-
- StringBuilder login = new StringBuilder(loginURI)
- .append("?username=")
- .append(username)
- .append("&password=")
- .append(password);
- String initURL = req.getParameter("initialURI");
- if(initURL != null) {
- login.append("&initialURI=").append(initURL);
- }
-
- resp.sendRedirect(login.toString());
- } else {
- if(!loginURI.equals(requestURI)) {
- RequestDispatcher dispatcher = req.getRequestDispatcher("/login");
- dispatcher.include(req, resp);
- } else {
- chain.doFilter(req, resp);
- }
- resp.setHeader("WWW-Authenticate", "Negotiate");
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- }
- }
-
- private boolean isSpnegoLoginRequest(HttpServletRequest request) {
- final String uri = request.getRequestURI();
- final String context = request.getContextPath();
- for(String pattern : this.patterns) {
- if(uri.equals(context.concat(pattern))) {
- return true;
- }
- }
- return false;
- }
-
- private String login(HttpServletRequest req, HttpServletResponse resp, String auth) throws Exception {
- if(this.loginContext == null) {
- return null;
- }
- this.loginContext.login();
-
- final String principal;
- final String tok = auth.substring("Negotiate".length() + 1);
- final byte[] gss = Base64.decode(tok);
-
- GSSContext context = null;
- byte[] token = null;
- context = MANAGER.createContext(getServerCredential(loginContext.getSubject()));
- token = context.acceptSecContext(gss, 0, gss.length);
-
- if (null == token) {
- return null;
- }
-
- resp.setHeader("WWW-Authenticate", "Negotiate" + ' ' + Base64.encodeBytes(token));
-
- if (!context.isEstablished()) {
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- return null;
- }
-
- principal = context.getSrcName().toString();
- context.dispose();
-
- this.loginContext.logout();
-
- return principal;
- }
-
-
- /**
- * Returns the {@link org.ietf.jgss.GSSCredential} the server uses for pre-authentication.
- *
- * @param subject account server uses for pre-authentication
- * @return credential that allows server to authenticate clients
- * @throws java.security.PrivilegedActionException
- */
- static GSSCredential getServerCredential(final Subject subject)
- throws PrivilegedActionException {
-
- final PrivilegedExceptionAction action =
- new PrivilegedExceptionAction() {
- public GSSCredential run() throws GSSException {
- return MANAGER.createCredential(
- null
- , GSSCredential.INDEFINITE_LIFETIME
- , getOid()
- , GSSCredential.ACCEPT_ONLY);
- }
- };
- return Subject.doAs(subject, action);
- }
-
- /**
- * Returns the Universal Object Identifier representation of
- * the SPNEGO mechanism.
- *
- * @return Object Identifier of the GSS-API mechanism
- */
- private static Oid getOid() {
- Oid oid = null;
- try {
- oid = new Oid("1.3.6.1.5.5.2");
- } catch (GSSException gsse) {
- gsse.printStackTrace();
- }
- return oid;
- }
-
- @Override
- public void destroy() {}
-}
diff --git a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOLoginModule.java b/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOLoginModule.java
deleted file mode 100644
index 0227c18ab..000000000
--- a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOLoginModule.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2012 eXo Platform SAS.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.gatein.security.sso.spnego;
-
-import javax.security.auth.login.LoginException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-import org.exoplatform.container.ExoContainer;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.exoplatform.services.security.Authenticator;
-import org.exoplatform.services.security.Identity;
-import org.exoplatform.services.security.UsernameCredential;
-import org.exoplatform.services.security.jaas.AbstractLoginModule;
-
-public class SPNEGOSSOLoginModule extends AbstractLoginModule {
- private static final Log log = ExoLogger.getLogger(SPNEGOSSOLoginModule.class);
-
- public static final String OPTION_ENABLE_FALLBACK_FORM_AUTHENTICATION = "enableFormAuthentication";
-
- @Override
- protected Log getLogger() {
- return log;
- }
-
- @Override
- public boolean login() throws LoginException {
- try {
- ExoContainer container = getContainer();
-
- HttpServletRequest servletRequest = SPNEGOSSOContext.getCurrentRequest();
- if (servletRequest == null) {
- log.debug("HttpServletRequest is null. SPNEGOLoginModule will be ignored.");
- return false;
- }
-
- HttpSession session = servletRequest.getSession();
- String username = (String)session.getAttribute("SPNEGO_PRINCIPAL");
- if(username != null) {
- establishSecurityContext(container, username);
- if (log.isTraceEnabled()) {
- log.trace("Successfully established security context for user " + username);
- }
- return true;
- }
-
- } catch (Exception ex) {
- log.error("Exception when trying to login with SPNEGO", ex);
- }
-
- // Disable fallback to FORM authentication
- if("false".equalsIgnoreCase((String)this.options.get(OPTION_ENABLE_FALLBACK_FORM_AUTHENTICATION))) {
- throw new LoginException("FORM authentication was disabled by SPNEGO login module.");
- }
-
- return false;
- }
-
- @Override
- public boolean commit() throws LoginException {
- return true;
- }
-
- @Override
- public boolean abort() throws LoginException {
- return true;
- }
-
- @Override
- public boolean logout() throws LoginException {
- return true;
- }
-
- protected void establishSecurityContext(ExoContainer container, String username) throws Exception {
- Authenticator authenticator = container.getComponentInstanceOfType(Authenticator.class);
-
- if (authenticator == null) {
- throw new LoginException("No Authenticator component found, check your configuration");
- }
-
- Identity identity = authenticator.createIdentity(username);
-
- sharedState.put("exo.security.identity", identity);
- sharedState.put("javax.security.auth.login.name", username);
- subject.getPublicCredentials().add(new UsernameCredential(username));
- }
-}
diff --git a/spnegosso/src/main/resources/conf/portal/configuration.xml b/spnegosso/src/main/resources/conf/portal/configuration.xml
deleted file mode 100644
index 6ac3b3280..000000000
--- a/spnegosso/src/main/resources/conf/portal/configuration.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
- org.gatein.sso.integration.SSOFilterIntegrator
-
- SSOFilterIntegratorPlugin
- addPlugin
- org.gatein.sso.integration.SSOFilterIntegratorPlugin
-
-
- filterClass
- org.gatein.security.sso.spnego.SPNEGOSSOFilter
-
-
- enabled
- ${gatein.sso.filter.spnego.enabled:false}
-
-
- filterMapping
- /*
-
-
-
- patterns
- ${gatein.sso.filter.spnego.pattern:/login,/spnegosso}
-
-
- loginServletPath
- ${gatein.sso.filter.spnego.loginServletPath:/login}
-
-
- securityDomain
- ${gatein.sso.filter.spnego.securityDomain:spnego-server}
-
-
-
-
-
diff --git a/sso-agent/pom.xml b/sso-agent/pom.xml
new file mode 100644
index 000000000..1c88dbf75
--- /dev/null
+++ b/sso-agent/pom.xml
@@ -0,0 +1,61 @@
+
+
+ org.exoplatform.gatein.sso
+ sso-parent
+ 6.6.x-SNAPSHOT
+
+
+ 4.0.0
+ sso-agent
+ jar
+ GateIn SSO - Agent
+
+
+ 0.01
+
+
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ provided
+
+
+ org.exoplatform.core
+ exo.core.component.organization.api
+ provided
+
+
+ org.exoplatform.gatein.wci
+ wci-wci
+ provided
+
+
+ org.exoplatform.ws
+ exo.ws.rest.core
+ provided
+
+
+ org.gatein.common
+ common-common
+ provided
+
+
+ org.apache.httpcomponents
+ httpclient
+ provided
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java b/sso-agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java
similarity index 90%
rename from agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java
index ef0563dc8..d81dd2677 100644
--- a/agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/filter/LoginRedirectFilter.java
@@ -27,12 +27,12 @@
import java.io.IOException;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
/**
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java
similarity index 98%
rename from agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java
index 735c93c43..304167ab8 100644
--- a/agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/AbstractSSOInterceptor.java
@@ -30,8 +30,8 @@
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
/**
* Base {@link SSOInterceptor} which adds possibility to be initialized either through Servlet API or through eXo kernel
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java
similarity index 97%
rename from agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java
index 72117cee0..0ea02e424 100644
--- a/agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptor.java
@@ -26,7 +26,7 @@
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.xml.InitParams;
-import javax.servlet.Filter;
+import jakarta.servlet.Filter;
/**
* SSOInterceptor is actually filter, which can be configured through exo kernel
diff --git a/agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java
similarity index 98%
rename from agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java
index fce90064d..1004ecba9 100644
--- a/agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/filter/api/SSOInterceptorInitializationContext.java
@@ -29,7 +29,7 @@
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
-import javax.servlet.FilterConfig;
+import jakarta.servlet.FilterConfig;
/**
* Context, which encapsulates all initialization configuration about {@link SSOInterceptor} and is able to recognize
diff --git a/sso-agent/src/main/java/org/gatein/sso/agent/login/SSOLoginModule.java b/sso-agent/src/main/java/org/gatein/sso/agent/login/SSOLoginModule.java
new file mode 100644
index 000000000..f51a5e516
--- /dev/null
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/login/SSOLoginModule.java
@@ -0,0 +1,151 @@
+/*
+ * JBoss, a division of Red Hat
+ * Copyright 2006, Red Hat Middleware, LLC, and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.gatein.sso.agent.login;
+
+import java.lang.reflect.Method;
+
+import javax.security.auth.login.LoginException;
+
+import org.gatein.sso.agent.tomcat.ServletAccess;
+import org.gatein.wci.security.Credentials;
+
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.security.Authenticator;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.services.security.UsernameCredential;
+import org.exoplatform.services.security.jaas.AbstractLoginModule;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * @author Sohil Shah
+ */
+public final class SSOLoginModule extends AbstractLoginModule {
+
+ private static final Log log = ExoLogger.getLogger(SSOLoginModule.class);
+
+ public static final String AUTHENTICATED_CREDENTIALS = "authenticatedCredentials";
+
+ /** JACC get context method. */
+ private static Method getContextMethod;
+
+ static {
+ try {
+ Class> policyContextClass = Thread.currentThread().getContextClassLoader().loadClass("javax.security.jacc.PolicyContext");
+ getContextMethod = policyContextClass.getDeclaredMethod("getContext", String.class);
+ } catch (ClassNotFoundException ignore) {
+ log.debug("JACC not found ignoring it", ignore);
+ } catch (Exception e) {
+ log.error("Could not obtain JACC get context method", e);
+ }
+ }
+
+ public boolean login() throws LoginException {
+ try {
+ // Check credentials stored and propagated in session.
+ String username = null;
+ HttpServletRequest request = getCurrentHttpServletRequest();
+
+ if (request == null) {
+ log.debug("HttpServletRequest is null. SSOLoginModule will be ignored.");
+ return false;
+ }
+
+ Credentials credsFromSession = (Credentials) request.getSession().getAttribute(AUTHENTICATED_CREDENTIALS);
+ if (credsFromSession != null) {
+ username = credsFromSession.getUsername();
+ if (log.isTraceEnabled()) {
+ log.trace("Found credentials in session. Username: " + username);
+ }
+ }
+
+ if (username == null) {
+ // SSO token could not be validated...hence a user id cannot be found
+ log.debug("SSOLogin Failed. Credential Not Found!!");
+ return false;
+ }
+
+ // Perform authentication by setting up the proper Application State
+ Authenticator authenticator = (Authenticator) getContainer()
+ .getComponentInstanceOfType(Authenticator.class);
+
+ if (authenticator == null) {
+ throw new LoginException(
+ "No Authenticator component found, check your configuration");
+ }
+
+ Identity identity = authenticator.createIdentity(username);
+
+ sharedState.put("exo.security.identity", identity);
+ sharedState.put("javax.security.auth.login.name", username);
+ subject.getPublicCredentials().add(new UsernameCredential(username));
+
+ return true;
+ } catch (final Throwable e) {
+ throw new LoginException(e.getMessage());
+ }
+ }
+
+ public boolean logout() throws LoginException {
+ return true;
+ }
+
+ public boolean abort() throws LoginException {
+ return true;
+ }
+
+ public boolean commit() throws LoginException {
+ return true;
+ }
+
+ @Override
+ protected Log getLogger() {
+ return log;
+ }
+
+ protected HttpServletRequest getCurrentHttpServletRequest() {
+ HttpServletRequest request = null;
+
+ // JBoss way
+ if (getContextMethod != null) {
+ try {
+ request = (HttpServletRequest) getContextMethod.invoke(null, "jakarta.servlet.http.HttpServletRequest");
+ } catch (Throwable e) {
+ log.error("LoginModule error. Turn off session credentials checking with proper configuration option of " +
+ "LoginModule set to false");
+ log.error(this, e);
+ }
+ }
+ // Tomcat way (Assumed that ServletAccessValve has been configured in
+ // context.xml)
+ else {
+ request = ServletAccess.getRequest();
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Returning HttpServletRequest " + request);
+ }
+
+ return request;
+ }
+}
diff --git a/agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java b/sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java
similarity index 95%
rename from agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java
index 15c909732..c21c51ffc 100644
--- a/agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccess.java
@@ -22,8 +22,8 @@
package org.gatein.sso.agent.tomcat;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
/**
* @author Marek Posolda
diff --git a/agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java b/sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java
similarity index 92%
rename from agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java
rename to sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java
index 60c3a6157..675698c6e 100644
--- a/agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java
+++ b/sso-agent/src/main/java/org/gatein/sso/agent/tomcat/ServletAccessValve.java
@@ -28,11 +28,11 @@
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
-import javax.servlet.ServletException;
+import jakarta.servlet.ServletException;
import java.io.IOException;
/**
- * Valve for adding {@link javax.servlet.http.HttpServletRequest} and {@link javax.servlet.http.HttpServletResponse} into threadLocal
+ * Valve for adding {@link jakarta.servlet.http.HttpServletRequest} and {@link jakarta.servlet.http.HttpServletResponse} into threadLocal
* so that it can be accessed from Login Modules during authentication.
*
* @author Marek Posolda
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java
similarity index 95%
rename from integration/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java
index 4107bab19..158980534 100644
--- a/integration/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java
+++ b/sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateFilter.java
@@ -28,11 +28,11 @@
import org.exoplatform.services.log.Log;
import org.gatein.sso.agent.filter.api.SSOInterceptor;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSODelegateLoginModule.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateLoginModule.java
similarity index 100%
rename from integration/src/main/java/org/gatein/sso/integration/SSODelegateLoginModule.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateLoginModule.java
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSODelegateValve.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateValve.java
similarity index 98%
rename from integration/src/main/java/org/gatein/sso/integration/SSODelegateValve.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateValve.java
index 138999a33..c72eaa110 100644
--- a/integration/src/main/java/org/gatein/sso/integration/SSODelegateValve.java
+++ b/sso-agent/src/main/java/org/gatein/sso/integration/SSODelegateValve.java
@@ -29,7 +29,7 @@
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-import javax.servlet.ServletException;
+import jakarta.servlet.ServletException;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
@@ -47,7 +47,7 @@
* Delegates work to another valve configured through option 'delegateValveClassName'. It's possible to disable
* delegation by boolean parameter 'ssoDelegationEnabled'.
*
- * Actually delegation will be enabled only for SSO scenario, which require integration with Tomcat valves (SAML, SPNEGO)
+ * Actually delegation will be enabled only for SSO scenario, which require integration with Tomcat valves (SAML)
*
* @author Marek Posolda
*/
@@ -64,7 +64,7 @@ public class SSODelegateValve implements Valve, Contained, MBeanRegistration, Li
private String delegateValveClassName;
// Delegate valve is not null only if we are in SSO mode and delegation is enabled
- // Delegate will be either SAML or SPNEGO valve
+ // Delegate will be either SAML valve
private Valve delegate;
// This is not null only if delegation is disabled
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java
similarity index 97%
rename from integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java
index 764d296b3..b77483b50 100644
--- a/integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java
+++ b/sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegrator.java
@@ -1,41 +1,41 @@
-/*
- * JBoss, a division of Red Hat
- * Copyright 2012, Red Hat Middleware, LLC, and individual
- * contributors as indicated by the @authors tag. See the
- * copyright.txt in the distribution for a full listing of
- * individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.gatein.sso.integration;
-
-import org.gatein.sso.agent.filter.api.SSOInterceptor;
-
-import java.util.Map;
-
-/**
- * Kernel component, which holds references to all configured {@link org.gatein.sso.agent.filter.api.SSOInterceptor}
- * instances
- *
- * @author Marek Posolda
- */
-public interface SSOFilterIntegrator
-{
-
- public Map getSSOInterceptors();
-
-}
+/*
+ * JBoss, a division of Red Hat
+ * Copyright 2012, Red Hat Middleware, LLC, and individual
+ * contributors as indicated by the @authors tag. See the
+ * copyright.txt in the distribution for a full listing of
+ * individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.gatein.sso.integration;
+
+import org.gatein.sso.agent.filter.api.SSOInterceptor;
+
+import java.util.Map;
+
+/**
+ * Kernel component, which holds references to all configured {@link org.gatein.sso.agent.filter.api.SSOInterceptor}
+ * instances
+ *
+ * @author Marek Posolda
+ */
+public interface SSOFilterIntegrator
+{
+
+ public Map getSSOInterceptors();
+
+}
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorImpl.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorImpl.java
similarity index 100%
rename from integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorImpl.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorImpl.java
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java
similarity index 99%
rename from integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java
index ab02e356b..d926c0be5 100644
--- a/integration/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java
+++ b/sso-agent/src/main/java/org/gatein/sso/integration/SSOFilterIntegratorPlugin.java
@@ -37,8 +37,7 @@
*
* @author Marek Posolda
*/
-public class SSOFilterIntegratorPlugin extends BaseComponentPlugin
-{
+public class SSOFilterIntegratorPlugin extends BaseComponentPlugin {
private final SSOInterceptor filter;
private final boolean enabled;
private final String filterMapping;
diff --git a/integration/src/main/java/org/gatein/sso/integration/SSOUtils.java b/sso-agent/src/main/java/org/gatein/sso/integration/SSOUtils.java
similarity index 100%
rename from integration/src/main/java/org/gatein/sso/integration/SSOUtils.java
rename to sso-agent/src/main/java/org/gatein/sso/integration/SSOUtils.java
diff --git a/integration/src/test/java/org/gatein/sso/integration/SSODelegateFilterTest.java b/sso-agent/src/test/java/org/gatein/sso/integration/SSODelegateFilterTest.java
similarity index 100%
rename from integration/src/test/java/org/gatein/sso/integration/SSODelegateFilterTest.java
rename to sso-agent/src/test/java/org/gatein/sso/integration/SSODelegateFilterTest.java
diff --git a/sso-saml-plugin/pom.xml b/sso-saml-plugin/pom.xml
new file mode 100644
index 000000000..85956e6ff
--- /dev/null
+++ b/sso-saml-plugin/pom.xml
@@ -0,0 +1,434 @@
+
+
+ org.exoplatform.gatein.sso
+ sso-parent
+ 6.6.x-SNAPSHOT
+
+
+ 4.0.0
+ sso-saml-plugin
+ jar
+
+ GateIn SSO - SAML Identity provider plugin
+
+
+ 2.5.5.Final
+ 4.0.20.Final
+ 1.5.1
+ 2.0.8.Final
+ 2.0.4
+ 2.0.3.GA
+
+ 1.0.0.Final
+ 1.0.1.Final
+ 1.0.1.Final
+ 2.0.3.Final
+ 1.0.2.Final
+
+ 0
+ true
+
+
+
+
+
+
+ org.picketlink
+ picketlink-parent
+ ${org.picketlink.federation.version}
+ import
+ pom
+
+
+ org.picketlink
+ picketlink-jbas-common
+ ${org.picketlink.federation.version}
+ sources
+
+
+ org.picketlink
+ picketlink-common
+ sources
+ ${org.picketlink.federation.version}
+
+
+ *
+ *
+
+
+
+
+ org.picketlink
+ picketlink-config
+ sources
+ ${org.picketlink.federation.version}
+
+
+ *
+ *
+
+
+
+
+ org.picketlink
+ picketlink-federation
+ sources
+ ${org.picketlink.federation.version}
+
+
+ *
+ *
+
+
+
+
+ org.picketbox
+ picketbox
+ sources
+ ${org.picketbox.version}
+
+
+ *
+ *
+
+
+
+
+
+
+ org.jboss
+ jbossxb
+ ${org.jboss.jbossxb.version}
+
+
+ org.jboss.security
+ jbosssx
+ ${org.jboss.security.jbosssx.version}
+
+
+ org.jboss.security
+ jbossxacml
+ ${org.jboss.security.jbossxacml.version}
+
+
+ org.jboss.spec.javax.resource
+ jboss-connector-api_1.7_spec
+ ${org.jboss.spec.javax.resource.version}
+
+
+ org.jboss.spec.javax.security.jacc
+ jboss-jacc-api_1.5_spec
+ ${org.jboss.spec.javax.security.jacc.version}
+
+
+ org.jboss.spec.javax.servlet
+ jboss-servlet-api_3.1_spec
+
+
+
+
+ org.jboss.spec.javax.security.auth.message
+ jboss-jaspi-api_1.0_spec
+ ${org.jboss.spec.javax.security.auth.message.version}
+
+
+ org.jboss.spec.javax.xml.ws
+ jboss-jaxws-api_2.2_spec
+ ${org.jboss.spec.javax.xml.ws.version}
+
+
+ org.jboss.spec.javax.xml.soap
+ jboss-saaj-api_1.4_spec
+ ${org.jboss.spec.javax.xml.soap.version}
+
+
+ org.apache.santuario
+ xmlsec
+ ${org.apache.santuario.version}
+
+
+ javax.servlet
+ servlet-api
+
+
+
+
+
+
+
+
+
+ ${project.groupId}
+ sso-agent
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+
+
+
+ org.picketlink
+ picketlink-common
+ sources
+
+
+ org.picketlink
+ picketlink-config
+ sources
+
+
+ org.picketlink
+ picketlink-federation
+ sources
+
+
+ org.picketlink
+ picketlink-jbas-common
+ sources
+
+
+ org.picketbox
+ picketbox
+ sources
+
+
+
+ org.exoplatform.ws
+ exo.ws.rest.core
+ provided
+
+
+ org.hibernate
+ hibernate-core
+ provided
+
+
+ javax.annotation
+ jsr250-api
+
+
+ javax.enterprise
+ cdi-api
+
+
+
+ org.jboss.spec.javax.resource
+ jboss-connector-api_1.7_spec
+
+
+ org.jboss.spec.javax.security.jacc
+ jboss-jacc-api_1.5_spec
+
+
+ org.jboss.spec.javax.security.auth.message
+ jboss-jaspi-api_1.0_spec
+
+
+ org.jboss.spec.javax.xml.ws
+ jboss-jaxws-api_2.2_spec
+
+
+ org.jboss.spec.javax.xml.soap
+ jboss-saaj-api_1.4_spec
+
+
+
+ org.jboss.logging
+ jboss-logging
+
+
+ org.jboss
+ jbossxb
+
+
+ org.jboss.security
+ jbossxacml
+
+
+ org.jboss.security
+ jbosssx
+
+
+ org.apache.santuario
+ xmlsec
+
+
+
+ org.picketlink
+ picketlink-idm-impl
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ provided
+
+
+
+ org.exoplatform.gatein.wci
+ wci-wci
+
+
+ org.exoplatform.ws
+ exo.ws.rest.core
+
+
+ org.exoplatform.core
+ exo.core.component.security.core
+
+
+ org.exoplatform.core
+ exo.core.component.organization.api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+ ${project.artifactId}
+
+
+ src/main/java
+
+
+ src/main/resources
+
+
+ target/picketlink
+
+
+
+
+ src/test/java
+
+
+ src/test/resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack
+ generate-sources
+
+ unpack
+
+
+
+
+ org.picketlink
+ picketlink-common
+ sources
+ false
+
+
+ org.picketlink
+ picketlink-config
+ sources
+ ${org.picketlink.federation.version}
+ false
+
+
+ org.picketlink
+ picketlink-federation
+ sources
+ ${org.picketlink.federation.version}
+ false
+
+
+ org.picketlink
+ picketlink-jbas-common
+ sources
+ ${org.picketlink.federation.version}
+ false
+
+
+ org.picketbox
+ picketbox
+ sources
+ ${org.picketbox.version}
+ false
+
+
+ ${project.build.directory}/picketlink
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-patch-plugin
+ 1.2
+
+ ${project.build.directory}
+
+
+
+ java.security.acl.Group-patch
+
+
+ ${basedir}/src/main/patches/java.security.acl.Group.patch
+
+ ${project.build.directory}/java.security.acl.Group-patches-applied.txt
+ skip
+
+ process-resources
+
+ apply
+
+
+
+ javax.servlet-patch
+
+ ${basedir}/src/main/patches/javax.servlet.patch
+
+ ${project.build.directory}/javax.servlet-patches-applied.txt
+
+ process-resources
+
+ apply
+
+
+
+
+
+
+
+
diff --git a/agent/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java b/sso-saml-plugin/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java
similarity index 93%
rename from agent/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java
rename to sso-saml-plugin/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java
index 7d1c2cfb7..b0622cd5f 100644
--- a/agent/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java
+++ b/sso-saml-plugin/src/main/java/org/gatein/sso/agent/login/SAML2IntegrationLoginModule.java
@@ -20,23 +20,26 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-
package org.gatein.sso.agent.login;
+import java.security.Principal;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+
+import org.picketlink.identity.federation.bindings.jboss.auth.SAML2LoginModule;
+
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.RootContainer;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.Authenticator;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.security.UsernameCredential;
-import org.picketlink.identity.federation.bindings.jboss.auth.SAML2LoginModule;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginException;
-import java.security.Principal;
-import java.util.Map;
/**
* Login module for integration with GateIn. It's running on GateIn (SAML SP) side.
@@ -57,6 +60,8 @@ public class SAML2IntegrationLoginModule extends SAML2LoginModule
// Default value is false, so we are preferring delegation to JbossLoginModule and using roles from portal DB.
private static final String OPTION_USE_SAML_ROLES = "useSAMLRoles";
+ private static final Log log = ExoLogger.getLogger(SAML2IntegrationLoginModule.class);
+
private static final String[] ALL_VALID_OPTIONS =
{
OPTION_PORTAL_CONTAINER_NAME,
@@ -88,13 +93,11 @@ public void initialize(Subject subject, CallbackHandler callbackHandler, Map arg0) {
}
@Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, String arg1) {
+ public jakarta.servlet.ServletRegistration.Dynamic addServlet(String arg0, String arg1) {
return delegate.addServlet(arg0, arg1);
}
@Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, Servlet arg1) {
+ public jakarta.servlet.ServletRegistration.Dynamic addServlet(String arg0, Servlet arg1) {
return delegate.addServlet(arg0, arg1);
}
@Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, Class extends Servlet> arg1) {
+ public jakarta.servlet.ServletRegistration.Dynamic addServlet(String arg0, Class extends Servlet> arg1) {
return delegate.addServlet(arg0, arg1);
}
@@ -222,7 +222,7 @@ public InputStream getResourceAsStream(String arg0) {
return fsResource.openStream();
}
} catch (IOException e) {
- log.warn("Error occurred when retieving file" + arg0, e);
+ LOG.warn("Error occurred when retieving file" + arg0, e);
}
}
return delegate.getResourceAsStream(arg0);
@@ -238,23 +238,11 @@ public String getServerInfo() {
return delegate.getServerInfo();
}
- @SuppressWarnings("deprecation")
- @Override
- public Servlet getServlet(String arg0) throws ServletException {
- return delegate.getServlet(arg0);
- }
-
@Override
public String getServletContextName() {
return delegate.getServletContextName();
}
- @SuppressWarnings("deprecation")
- @Override
- public Enumeration getServletNames() {
- return delegate.getServletNames();
- }
-
@Override
public ServletRegistration getServletRegistration(String arg0) {
return delegate.getServletRegistration(arg0);
@@ -265,12 +253,6 @@ public ServletRegistration getServletRegistration(String arg0) {
return delegate.getServletRegistrations();
}
- @SuppressWarnings("deprecation")
- @Override
- public Enumeration getServlets() {
- return delegate.getServlets();
- }
-
@Override
public SessionCookieConfig getSessionCookieConfig() {
return delegate.getSessionCookieConfig();
@@ -281,12 +263,6 @@ public void log(String arg0) {
delegate.log(arg0);
}
- @SuppressWarnings("deprecation")
- @Override
- public void log(Exception arg0, String arg1) {
- delegate.log(arg0, arg1);
- }
-
@Override
public void log(String arg0, Throwable arg1) {
delegate.log(arg0, arg1);
@@ -339,7 +315,7 @@ public String getVirtualServerName() {
}
@Override
- public javax.servlet.ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) {
+ public jakarta.servlet.ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) {
return delegate.addJspFile(servletName, jspFile);
}
diff --git a/saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java
similarity index 98%
rename from saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java
rename to sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java
index b2505b45e..ca62df499 100644
--- a/saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java
+++ b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSAML11SPRedirectFormAuthenticator.java
@@ -8,8 +8,8 @@
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
diff --git a/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSPFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSPFormAuthenticator.java
new file mode 100644
index 000000000..c177f8ceb
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/AbstractSPFormAuthenticator.java
@@ -0,0 +1,853 @@
+package org.gatein.sso.saml.plugin.valve;
+
+import static org.gatein.sso.saml.plugin.valve.AbstractSAML11SPRedirectFormAuthenticator.handleSAML11UnsolicitedResponse;
+import static org.picketlink.common.util.StringUtil.isNotNull;
+import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.picketlink.common.ErrorCodes;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.exceptions.fed.AssertionExpiredException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.common.util.StringUtil;
+import org.picketlink.config.federation.AuthPropertyType;
+import org.picketlink.config.federation.KeyProviderType;
+import org.picketlink.identity.federation.bindings.tomcat.sp.holder.ServiceProviderSAMLContext;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditEvent;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditEventType;
+import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
+import org.picketlink.identity.federation.core.saml.v2.holders.DestinationInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.web.core.HTTPContext;
+import org.picketlink.identity.federation.web.process.ServiceProviderBaseProcessor;
+import org.picketlink.identity.federation.web.process.ServiceProviderSAMLRequestProcessor;
+import org.picketlink.identity.federation.web.process.ServiceProviderSAMLResponseProcessor;
+import org.picketlink.identity.federation.web.util.HTTPRedirectUtil;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil.RedirectBindingUtilDestHolder;
+import org.picketlink.identity.federation.web.util.ServerDetector;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.services.organization.Membership;
+import org.exoplatform.services.organization.OrganizationService;
+import org.exoplatform.services.security.MembershipEntry;
+import org.exoplatform.services.security.MembershipHashSet;
+import org.exoplatform.services.security.RolesExtractor;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * Abstract class to be extended by Service Provider valves to handle SAML
+ * requests and responses. forked from
+ * org.picketlink.identity.federation.bindings.tomcat.sp.AbstractSPFormAuthenticator
+ * and made compatible with Tomcat 8.5 since picketlink doesn't provide such a
+ * support
+ */
+public abstract class AbstractSPFormAuthenticator extends BaseFormAuthenticator {
+ private static final String FORM_PRINCIPAL_NOTE = "org.apache.catalina.authenticator.PRINCIPAL";
+
+ protected boolean jbossEnv = false;
+
+ AbstractSPFormAuthenticator() {
+ super();
+ ServerDetector detector = new ServerDetector();
+ jbossEnv = detector.isJboss();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.picketlink.identity.federation.bindings.tomcat.sp.BaseFormAuthenticator
+ * #processStart()
+ */
+ @Override
+ protected void startPicketLink() throws LifecycleException {
+ super.startPicketLink();
+ initKeyProvider(context);
+ }
+
+ /**
+ *
+ * Send the request to the IDP. Subclasses should override this method to
+ * implement how requests must be sent to the IDP.
+ *
+ *
+ * @param destination idp url
+ * @param samlDocument request or response document
+ * @param relayState used in SAML Workflow
+ * @param response Apache Catalina HTTP Response
+ * @param request Apache Catalina HTTP Request
+ * @param willSendRequest are we sending Request or Response to IDP
+ * @param destinationQueryStringWithSignature used only with Redirect binding
+ * and with signature enabled.
+ * @throws ProcessingException Exception to indicate a server processing error
+ * @throws ConfigurationException Exception indicating an issue with the
+ * configuration
+ * @throws IOException I/O exception
+ */
+ protected void sendRequestToIDP(String destination,
+ Document samlDocument,
+ String relayState,
+ Request request,
+ Response response,
+ boolean willSendRequest,
+ String destinationQueryStringWithSignature) throws ProcessingException,
+ ConfigurationException,
+ IOException {
+
+ if (isAjaxRequest(request) && request.getUserPrincipal() == null) {
+ response.sendError(Response.SC_FORBIDDEN);
+ } else {
+ if (isHttpPostBinding()) {
+ sendHttpPostBindingRequest(destination, samlDocument, relayState, response, willSendRequest);
+ } else {
+ sendHttpRedirectRequest(destination,
+ samlDocument,
+ relayState,
+ response,
+ willSendRequest,
+ destinationQueryStringWithSignature);
+ }
+ }
+ }
+
+ /**
+ *
+ * Sends a HTTP Redirect request to the IDP.
+ *
+ *
+ * @param destination idp url
+ * @param samlDocument SAML request document
+ * @param relayState used in SAML Workflow
+ * @param response Apache Catalina HTTP Response
+ * @param willSendRequest are we sending Request or Response to IDP
+ * @param destinationQueryStringWithSignature used only with Redirect binding
+ * and with signature enabled.
+ * @throws IOException I/O exception
+ * @throws UnsupportedEncodingException when decoding SAML Message
+ * @throws ConfigurationException Exception indicating an issue with the
+ * configuration
+ * @throws ProcessingException Exception to indicate a server processing error
+ */
+ protected void sendHttpRedirectRequest(String destination,
+ Document samlDocument,
+ String relayState,
+ Response response,
+ boolean willSendRequest,
+ String destinationQueryStringWithSignature) throws IOException,
+ ProcessingException,
+ ConfigurationException {
+ String destinationQueryString = null;
+
+ // We already have queryString with signature from
+ // SAML2SignatureGenerationHandler
+ if (destinationQueryStringWithSignature != null) {
+ destinationQueryString = destinationQueryStringWithSignature;
+ } else {
+ String samlMessage = DocumentUtil.getDocumentAsString(samlDocument);
+ String base64Request = RedirectBindingUtil.deflateBase64URLEncode(samlMessage.getBytes("UTF-8"));
+ destinationQueryString = RedirectBindingUtil.getDestinationQueryString(base64Request, relayState, willSendRequest);
+ }
+
+ RedirectBindingUtilDestHolder holder = new RedirectBindingUtilDestHolder();
+
+ holder.setDestination(destination).setDestinationQueryString(destinationQueryString);
+
+ HTTPRedirectUtil.sendRedirectForRequestor(RedirectBindingUtil.getDestinationURL(holder), response);
+ }
+
+ /**
+ *
+ * Sends a HTTP POST request to the IDP.
+ *
+ *
+ * @param destination idp url
+ * @param samlDocument request or response document
+ * @param relayState used in SAML Workflow
+ * @param response Apache Catalina HTTP Response
+ * @param willSendRequest are we sending Request or Response to IDP
+ * @throws ProcessingException Exception to indicate a server processing error
+ * @throws ConfigurationException Exception indicating an issue with the
+ * configuration
+ * @throws IOException I/O exception
+ */
+ protected void sendHttpPostBindingRequest(String destination,
+ Document samlDocument,
+ String relayState,
+ Response response,
+ boolean willSendRequest) throws ProcessingException,
+ IOException,
+ ConfigurationException {
+ String samlMessage = PostBindingUtil.base64Encode(DocumentUtil.getDocumentAsString(samlDocument));
+
+ DestinationInfoHolder destinationHolder = new DestinationInfoHolder(destination, samlMessage, relayState);
+
+ PostBindingUtil.sendPost(destinationHolder, response, willSendRequest);
+ }
+
+ /**
+ *
+ * Initialize the KeyProvider configurations. This configurations are to be
+ * used during signing and validation of SAML assertions.
+ *
+ *
+ * @param context Apache Catalina Context
+ * @throws LifecycleException any exception occurred while processing key
+ * provider
+ */
+ protected void initKeyProvider(Context context) throws LifecycleException {
+ if (!doSupportSignature()) {
+ return;
+ }
+
+ KeyProviderType keyProvider = this.spConfiguration.getKeyProvider();
+
+ if (keyProvider == null && doSupportSignature())
+ throw new LifecycleException(ErrorCodes.NULL_VALUE + "KeyProvider is null for context=" + context.getName());
+
+ try {
+ String keyManagerClassName = keyProvider.getClassName();
+ if (keyManagerClassName == null)
+ throw new RuntimeException(ErrorCodes.NULL_VALUE + "KeyManager class name");
+
+ Class> clazz = SecurityActions.loadClass(getClass(), keyManagerClassName);
+
+ if (clazz == null)
+ throw new ClassNotFoundException(ErrorCodes.CLASS_NOT_LOADED + keyManagerClassName);
+ this.keyManager = (TrustKeyManager) clazz.newInstance();
+
+ List authProperties = CoreConfigUtil.getKeyProviderProperties(keyProvider);
+
+ keyManager.setAuthProperties(authProperties);
+ keyManager.setValidatingAlias(keyProvider.getValidatingAlias());
+
+ String identityURL = this.spConfiguration.getIdentityURL();
+
+ // Special case when you need X509Data in SignedInfo
+ if (authProperties != null) {
+ for (AuthPropertyType authPropertyType : authProperties) {
+ String key = authPropertyType.getKey();
+ if (GeneralConstants.X509CERTIFICATE.equals(key)) {
+ // we need X509Certificate in SignedInfo. The value is the alias
+ // name
+ keyManager.addAdditionalOption(GeneralConstants.X509CERTIFICATE, authPropertyType.getValue());
+ break;
+ }
+ }
+ }
+ keyManager.addAdditionalOption(ServiceProviderBaseProcessor.IDP_KEY, new URL(identityURL).getHost());
+ } catch (Exception e) {
+ logger.trustKeyManagerCreationError(e);
+ throw new LifecycleException(e.getLocalizedMessage());
+ }
+
+ logger.trace("Key Provider=" + keyProvider.getClassName());
+ }
+
+ @Override
+ protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException {
+ if (response instanceof Response) {
+ LoginConfig loginConfig = request.getContext().getLoginConfig();
+ Response catalinaResponse = (Response) response;
+ return authenticate(request, catalinaResponse, loginConfig);
+ }
+ throw logger.samlSPResponseNotCatalinaResponseError(response);
+ }
+
+ /**
+ * Authenticate the request
+ *
+ * @param request Apache Catalina Request
+ * @param response Apache Catalina Response
+ * @return true if authenticated, else false
+ * @throws IOException any I/O exception
+ */
+ public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
+ if (response instanceof Response) {
+ LoginConfig loginConfig = request.getContext().getLoginConfig();
+ Response catalinaResponse = (Response) response;
+ return authenticate(request, catalinaResponse, loginConfig);
+ }
+ throw logger.samlSPResponseNotCatalinaResponseError(response);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.apache.catalina.authenticator.FormAuthenticator#authenticate(org.apache
+ * .catalina.connector.Request, org.apache.catalina.connector.Response,
+ * org.apache.catalina.deploy.LoginConfig)
+ */
+ private boolean authenticate(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ try {
+ // needs to be done first, *before* accessing any parameters.
+ // super.authenticate(..) gets called to late
+ String characterEncoding = getCharacterEncoding();
+ if (characterEncoding != null) {
+ request.setCharacterEncoding(characterEncoding);
+ }
+
+ Session session = request.getSessionInternal(true);
+
+ // check if this call is resulting from the redirect after successful
+ // authentication.
+ // if so, make the authentication successful and continue the original
+ // request
+ if (saveRestoreRequest && matchRequest(request)) {
+ logger.trace("Restoring request from session '" + session.getIdInternal() + "'");
+ Principal savedPrincipal = (Principal) session.getNote(FORM_PRINCIPAL_NOTE);
+ register(request,
+ response,
+ savedPrincipal,
+ HttpServletRequest.FORM_AUTH,
+ (String) session.getNote(Constants.SESS_USERNAME_NOTE),
+ (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
+
+ // try to restore the original request (including post data, etc...)
+ if (restoreRequest(request, session)) {
+ // success! user is authenticated; continue processing original
+ // request
+ logger.trace("Continuing with restored request.");
+ return true;
+ } else {
+ // no saved request found...
+ logger.trace("Restore of original request failed!");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return false;
+ }
+ }
+
+ // Eagerly look for Local LogOut
+ boolean localLogout = isLocalLogout(request);
+
+ if (localLogout) {
+ try {
+ sendToLogoutPage(request, response, session);
+ } catch (ServletException e) {
+ logger.samlLogoutError(e);
+ throw new IOException(e);
+ }
+ return false;
+ }
+
+ String samlRequest = request.getParameter(GeneralConstants.SAML_REQUEST_KEY);
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+
+ Principal principal = request.getUserPrincipal();
+
+ // If we have already authenticated the user and there is no request from
+ // IDP or logout from user
+ if (principal != null && !(isGlobalLogout(request) || isNotNull(samlRequest) || isNotNull(samlResponse)))
+ return true;
+
+ // General User Request
+ if (!isNotNull(samlRequest) && !isNotNull(samlResponse)) {
+ return generalUserRequest(request, response, loginConfig);
+ }
+
+ // Handle a SAML Response from IDP
+ if (isNotNull(samlResponse)) {
+ return handleSAMLResponse(request, response, loginConfig);
+ }
+
+ // Handle SAML Requests from IDP
+ if (isNotNull(samlRequest)) {
+ return handleSAMLRequest(request, response, loginConfig);
+ } // end if
+
+ return localAuthentication(request, response, loginConfig);
+ } catch (IOException e) {
+ if (StringUtil.isNotNull(spConfiguration.getErrorPage())) {
+ try {
+ request.getRequestDispatcher(spConfiguration.getErrorPage()).forward(request.getRequest(), response);
+ } catch (ServletException e1) {
+ logger.samlErrorPageForwardError(spConfiguration.getErrorPage(), e1);
+ }
+ return false;
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ /**
+ *
+ * Indicates if the current request is a GlobalLogout request.
+ *
+ *
+ * @param request Apache Catalina Request
+ * @return true if this is a global SAML logout
+ */
+ private boolean isGlobalLogout(Request request) {
+ String gloStr = request.getParameter(GeneralConstants.GLOBAL_LOGOUT);
+ return isNotNull(gloStr) && "true".equalsIgnoreCase(gloStr);
+ }
+
+ /**
+ *
+ * Indicates if the current request is a LocalLogout request.
+ *
+ *
+ * @param request Apache Catalina Request
+ * @return true if this is a local SAML logout
+ */
+ private boolean isLocalLogout(Request request) {
+ try {
+ if (request.getCharacterEncoding() == null) {
+ request.setCharacterEncoding("UTF-8");
+ }
+ } catch (UnsupportedEncodingException e) {
+ logger.error("Request have no encoding, and we are unable to set it to UTF-8");
+ logger.error(e);
+ }
+ String lloStr = request.getParameter(GeneralConstants.LOCAL_LOGOUT);
+ return isNotNull(lloStr) && "true".equalsIgnoreCase(lloStr);
+ }
+
+ /**
+ * Handle the IDP Request
+ *
+ * @param request Apache Catalina Request
+ * @param response Apache Catalina Response
+ * @param loginConfig Apache Catalina Login Config
+ * @return true if processed by SAML Workflow
+ * @throws IOException any I/O error while authenticating
+ */
+ private boolean handleSAMLRequest(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ String samlRequest = request.getParameter(GeneralConstants.SAML_REQUEST_KEY);
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ try {
+ ServiceProviderSAMLRequestProcessor requestProcessor = new ServiceProviderSAMLRequestProcessor(
+ request.getMethod()
+ .equals("POST"),
+ this.serviceURL,
+ this.picketLinkConfiguration);
+ requestProcessor.setTrustKeyManager(keyManager);
+ boolean result = requestProcessor.process(samlRequest, httpContext, handlers, chainLock);
+
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent("Info");
+ auditEvent.setType(PicketLinkAuditEventType.REQUEST_FROM_IDP);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+
+ // If response is already commited, we need to stop with processing of
+ // HTTP request
+ if (response.isCommitted() || response.isAppCommitted())
+ return false;
+
+ if (result)
+ return result;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+
+ return localAuthentication(request, response, loginConfig);
+ }
+
+ private Document toSAMLResponseDocument(String samlResponse, boolean isPostBinding) throws ParsingException {
+ InputStream dataStream = null;
+
+ if (isPostBinding) {
+ // deal with SAML response from IDP
+ dataStream = PostBindingUtil.base64DecodeAsStream(samlResponse);
+ } else {
+ // deal with SAML response from IDP
+ dataStream = RedirectBindingUtil.base64DeflateDecode(samlResponse);
+ }
+
+ try {
+ return DocumentUtil.getDocument(dataStream);
+ } catch (Exception e) {
+ logger.samlResponseFromIDPParsingFailed();
+ throw new ParsingException("", e);
+ }
+ }
+
+ /**
+ * Handle IDP Response
+ *
+ * @param request Apache Catalina Request
+ * @param response Apache Catalina Response
+ * @param loginConfig Apache Catalina Login Config
+ * @return true if logged in in SAML SP side
+ * @throws IOException any I/O error in authentication process
+ */
+ private boolean handleSAMLResponse(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ if (!super.validate(request)) {
+ throw new IOException(ErrorCodes.VALIDATION_CHECK_FAILED);
+ }
+
+ String samlVersion = getSAMLVersion(request);
+
+ if (!JBossSAMLConstants.VERSION_2_0.get().equals(samlVersion)) {
+ return handleSAML11UnsolicitedResponse(request, response, loginConfig, this);
+ }
+
+ return handleSAML2Response(request, response, loginConfig);
+ }
+
+ private boolean handleSAML2Response(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ Session session = request.getSessionInternal(true);
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ Principal principal = request.getUserPrincipal();
+
+ boolean willSendRequest;// deal with SAML response from IDP
+
+ try {
+ ServiceProviderSAMLResponseProcessor responseProcessor =
+ new ServiceProviderSAMLResponseProcessor(request.getMethod()
+ .equals("POST"),
+ serviceURL,
+ this.picketLinkConfiguration);
+ if (auditHelper != null) {
+ responseProcessor.setAuditHelper(auditHelper);
+ }
+
+ responseProcessor.setTrustKeyManager(keyManager);
+
+ SAML2HandlerResponse saml2HandlerResponse = responseProcessor.process(samlResponse,
+ httpContext,
+ handlers,
+ chainLock);
+
+ Document samlResponseDocument = saml2HandlerResponse.getResultingDocument();
+ String relayState = saml2HandlerResponse.getRelayState();
+
+ String destination = saml2HandlerResponse.getDestination();
+
+ willSendRequest = saml2HandlerResponse.getSendRequest();
+
+ String destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
+
+ if (destination != null && samlResponseDocument != null) {
+ sendRequestToIDP(destination,
+ samlResponseDocument,
+ relayState,
+ request,
+ response,
+ willSendRequest,
+ destinationQueryStringWithSignature);
+ } else {
+ // See if the session has been invalidated
+
+ boolean sessionValidity = session.isValid();
+
+ if (!sessionValidity) {
+ sendToLogoutPage(request, response, session);
+ return false;
+ }
+
+ // We got a response with the principal
+ List roles = saml2HandlerResponse.getRoles();
+
+ if (principal == null) {
+ principal = (Principal) session.getSession().getAttribute(GeneralConstants.PRINCIPAL_ID);
+ }
+
+ String username = principal.getName();
+ String password = ServiceProviderSAMLContext.EMPTY_PASSWORD;
+
+ roles.addAll(extractGateinRoles(username));
+ if (logger.isTraceEnabled()) {
+ logger.trace("Roles determined for username=" + username + "=" + Arrays.toString(roles.toArray()));
+ }
+
+ // Map to JBoss specific principal
+ if ((new ServerDetector()).isJboss() || jbossEnv) {
+ // Push a context
+ ServiceProviderSAMLContext.push(username, roles);
+ principal = context.getRealm().authenticate(username, password);
+ ServiceProviderSAMLContext.clear();
+ } else {
+ // tomcat env
+ principal = getGenericPrincipal(request, username, roles);
+ }
+
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent("Info");
+ auditEvent.setType(PicketLinkAuditEventType.RESPONSE_FROM_IDP);
+ auditEvent.setSubjectName(username);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+
+ // Redirect the user to the originally requested URL
+ if (saveRestoreRequest) {
+ // Store the authenticated principal in the session.
+ session.setNote(FORM_PRINCIPAL_NOTE, principal);
+
+ // Redirect to the original URL. Note that this will trigger the
+ // authenticator again, but on resubmission we will look in the
+ // session notes to retrieve the authenticated principal and
+ // prevent reauthentication
+ String requestURI = savedRequestURL(session);
+
+ if (requestURI != null) {
+ logger.trace("Redirecting back to original Request URI: " + requestURI);
+ response.sendRedirect(response.encodeRedirectURL(requestURI));
+ }
+ }
+
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
+ return true;
+ }
+ } catch (ProcessingException pe) {
+ Throwable t = pe.getCause();
+ if (t != null && t instanceof AssertionExpiredException) {
+ logger.error("Assertion has expired. Asking IDP for reissue");
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent("Info");
+ auditEvent.setType(PicketLinkAuditEventType.EXPIRED_ASSERTION);
+ auditEvent.setAssertionID(((AssertionExpiredException) t).getId());
+ auditHelper.audit(auditEvent);
+ }
+ // Just issue a fresh request back to IDP
+ return generalUserRequest(request, response, loginConfig);
+ }
+ logger.samlSPHandleRequestError(pe);
+ throw logger.samlSPProcessingExceptionError(pe);
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+
+ return localAuthentication(request, response, loginConfig);
+ }
+
+ private String getSAMLVersion(Request request) {
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+ String version;
+
+ try {
+ Document samlDocument = toSAMLResponseDocument(samlResponse, "POST".equalsIgnoreCase(request.getMethod()));
+ Element element = samlDocument.getDocumentElement();
+
+ // let's try SAML 2.0 Version attribute first
+ version = element.getAttribute("Version");
+
+ if (isNullOrEmpty(version)) {
+ // fallback to SAML 1.1 Minor and Major attributes
+ String minorVersion = element.getAttribute("MinorVersion");
+ String majorVersion = element.getAttribute("MajorVersion");
+
+ version = minorVersion + "." + majorVersion;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Could not extract version from SAML Response.", e);
+ }
+
+ return version;
+ }
+
+ protected boolean isPOSTBindingResponse() {
+ return spConfiguration.isIdpUsesPostBinding();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.picketlink.identity.federation.bindings.tomcat.sp.BaseFormAuthenticator
+ * #getBinding()
+ */
+ @Override
+ protected String getBinding() {
+ return spConfiguration.getBindingType();
+ }
+
+ /**
+ * Handle the user invocation for the first time
+ *
+ * @param request Apache Catalina Request
+ * @param response Apache Catalina Response
+ * @param loginConfig Apache Catalina Login Config
+ * @return true if logged in in SAML SP side
+ * @throws IOException any I/O error in authentication process
+ */
+ private boolean generalUserRequest(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ Session session = request.getSessionInternal(true);
+ boolean willSendRequest = false;
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ boolean postBinding = spConfiguration.getBindingType().equals("POST");
+
+ // Neither saml request nor response from IDP
+ // So this is a user request
+ SAML2HandlerResponse saml2HandlerResponse = null;
+ try {
+ ServiceProviderBaseProcessor baseProcessor = new ServiceProviderBaseProcessor(postBinding,
+ serviceURL,
+ this.picketLinkConfiguration);
+ if (issuerID != null)
+ baseProcessor.setIssuer(issuerID);
+
+ // If the user has a different desired idp
+ String idp = (String) request.getAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP);
+ if (StringUtil.isNotNull(idp)) {
+ baseProcessor.setIdentityURL(idp);
+ } else {
+ baseProcessor.setIdentityURL(identityURL);
+ }
+ baseProcessor.setAuditHelper(auditHelper);
+
+ saml2HandlerResponse = baseProcessor.process(httpContext, handlers, chainLock);
+ } catch (ProcessingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ParsingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ConfigurationException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ }
+
+ willSendRequest = saml2HandlerResponse.getSendRequest();
+
+ Document samlResponseDocument = saml2HandlerResponse.getResultingDocument();
+ String relayState = saml2HandlerResponse.getRelayState();
+
+ String destination = saml2HandlerResponse.getDestination();
+ String destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
+
+ if (destination != null && samlResponseDocument != null) {
+ try {
+ if (saveRestoreRequest && !isGlobalLogout(request)) {
+ this.saveRequest(request, session);
+ }
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent("Info");
+ auditEvent.setType(PicketLinkAuditEventType.REQUEST_TO_IDP);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+ sendRequestToIDP(destination,
+ samlResponseDocument,
+ relayState,
+ request,
+ response,
+ willSendRequest,
+ destinationQueryStringWithSignature);
+ return false;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+ }
+
+ return localAuthentication(request, response, loginConfig);
+ }
+
+ /**
+ * Extract Gatein roles to put in Principal
+ *
+ * @param userId
+ * @return
+ */
+ private List extractGateinRoles(String userId) {
+ OrganizationService organizationService =
+ PortalContainer.getInstance().getComponentInstanceOfType(OrganizationService.class);
+ RolesExtractor rolesExtractor = PortalContainer.getInstance().getComponentInstanceOfType(RolesExtractor.class);
+ List result = new ArrayList<>();
+ Set entries = new MembershipHashSet();
+ Collection memberships;
+ try {
+ memberships = organizationService.getMembershipHandler().findMembershipsByUser(userId);
+ } catch (Exception e) {
+ memberships = null;
+ }
+ if (memberships != null) {
+ for (Membership membership : memberships)
+ entries.add(new MembershipEntry(membership.getGroupId(), membership.getMembershipType()));
+ }
+ result.addAll(rolesExtractor.extractRoles(userId, entries));
+ return result;
+ }
+
+ /**
+ *
+ * Indicates if the SP is configure with HTTP POST Binding.
+ *
+ *
+ * @return true if post binding
+ */
+ protected boolean isHttpPostBinding() {
+ return getBinding().equalsIgnoreCase("POST");
+ }
+
+ public Context getContext() {
+ return (Context) getContainer();
+ }
+
+ @Override
+ public boolean restoreRequest(Request request, Session session) throws IOException {
+ return super.restoreRequest(request, session);
+ }
+
+ /**
+ * Subclasses need to return the context path based on the capability of their
+ * servlet api
+ *
+ * @return Servlet Context Path
+ */
+ protected abstract String getContextPath();
+
+ protected Principal getGenericPrincipal(Request request, String username, List roles) {
+ // sometimes, IDP send username in assertion with capitals letters, or with
+ // inconsistent format.
+ // this option allows to force the username in lower case, just before
+ // creating the principal,
+ // so that, all operations in exo side will use a consistant format.
+ String forceLowerCase = System.getProperty("gatein.sso.saml.username.forcelowercase", "false");
+ if (forceLowerCase.equalsIgnoreCase("true")) {
+ username = username.toLowerCase();
+ }
+ return new GenericPrincipal(username, null, roles);
+ }
+
+ private boolean isAjaxRequest(Request request) {
+ String requestedWithHeader = request.getHeader(GeneralConstants.HTTP_HEADER_X_REQUESTED_WITH);
+ return requestedWithHeader != null && "XMLHttpRequest".equalsIgnoreCase(requestedWithHeader);
+ }
+}
diff --git a/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/BaseFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/BaseFormAuthenticator.java
new file mode 100644
index 000000000..eb8571056
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/BaseFormAuthenticator.java
@@ -0,0 +1,675 @@
+package org.gatein.sso.saml.plugin.valve;
+
+import static org.picketlink.common.constants.GeneralConstants.CONFIG_FILE_LOCATION;
+import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.gatein.sso.saml.plugin.filter.SAMLSPServletContextWrapper;
+import org.picketlink.common.ErrorCodes;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.common.util.StringUtil;
+import org.picketlink.common.util.SystemPropertiesUtil;
+import org.picketlink.config.federation.PicketLinkType;
+import org.picketlink.config.federation.SPType;
+import org.picketlink.config.federation.handler.Handlers;
+import org.picketlink.identity.federation.api.saml.v2.metadata.MetaDataExtractor;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditHelper;
+import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
+import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.factories.SAML2HandlerChainFactory;
+import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerChainConfig;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChain;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChainConfig;
+import org.picketlink.identity.federation.core.saml.v2.util.HandlerUtil;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
+import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
+import org.picketlink.identity.federation.web.config.AbstractSAMLConfigurationProvider;
+import org.picketlink.identity.federation.web.util.ConfigurationUtil;
+import org.picketlink.identity.federation.web.util.SAMLConfigurationProvider;
+import org.w3c.dom.Document;
+
+import org.exoplatform.commons.utils.PropertyManager;
+import org.exoplatform.container.xml.Deserializer;
+
+/**
+ * Base Class for Service Provider Form Authenticators forked from
+ * org.picketlink.identity.federation.bindings.tomcat.sp.BaseFormAuthenticator
+ * and made compatible with Tomcat 8.5 since picketlink doesn't provide such a
+ * support
+ */
+public abstract class BaseFormAuthenticator extends FormAuthenticator {
+ protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ protected boolean enableAudit = false;
+
+ protected PicketLinkAuditHelper auditHelper = null;
+
+ protected TrustKeyManager keyManager;
+
+ protected SPType spConfiguration = null;
+
+ protected PicketLinkType picketLinkConfiguration = null;
+
+ protected String serviceURL = null;
+
+ protected String identityURL = null;
+
+ protected String issuerID = null;
+
+ protected String configFile;
+
+ /**
+ * If the service provider is configured with an IDP metadata file, then this
+ * certificate can be picked up from the metadata
+ */
+ protected transient X509Certificate idpCertificate = null;
+
+ protected transient SAML2HandlerChain chain = null;
+
+ protected transient String samlHandlerChainClass = null;
+
+ protected Map chainConfigOptions = new HashMap();
+
+ // Whether the authenticator has to to save and restore request
+ protected boolean saveRestoreRequest = true;
+
+ /**
+ * A Lock for Handler operations in the chain
+ */
+ protected Lock chainLock = new ReentrantLock();
+
+ protected String canonicalizationMethod = CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS;
+
+ /**
+ * The user can inject a fully qualified name of a
+ * {@link SAMLConfigurationProvider}
+ */
+ protected SAMLConfigurationProvider configProvider = null;
+
+ protected int timerInterval = -1;
+
+ protected Timer timer = null;
+
+ public BaseFormAuthenticator() {
+ super();
+ }
+
+ protected String idpAddress = null;
+
+ /**
+ * If the request.getRemoteAddr is not exactly the IDP address that you have
+ * keyed in your deployment descriptor for keystore alias, you can set it here
+ * explicitly
+ *
+ * @param idpAddress IP address of IDP
+ */
+ public void setIdpAddress(String idpAddress) {
+ this.idpAddress = idpAddress;
+ }
+
+ /**
+ * Get the name of the configuration file
+ *
+ * @return SAML config file path
+ */
+ public String getConfigFile() {
+ return configFile;
+ }
+
+ /**
+ * Set the name of the configuration file
+ *
+ * @param configFile set config file path
+ */
+ public void setConfigFile(String configFile) {
+ this.configFile = configFile;
+ }
+
+ /**
+ * Set the SAML Handler Chain Class fqn
+ *
+ * @param samlHandlerChainClass FQN of SAML Handler Chain
+ */
+ public void setSamlHandlerChainClass(String samlHandlerChainClass) {
+ this.samlHandlerChainClass = samlHandlerChainClass;
+ }
+
+ /**
+ * Set the service URL
+ *
+ * @param serviceURL Service URL
+ */
+ public void setServiceURL(String serviceURL) {
+ this.serviceURL = serviceURL;
+ }
+
+ /**
+ * Set whether the authenticator saves/restores the request during form
+ * authentication
+ *
+ * @param saveRestoreRequest saves/restores the request during authentication
+ * if true
+ */
+ public void setSaveRestoreRequest(boolean saveRestoreRequest) {
+ this.saveRestoreRequest = saveRestoreRequest;
+ }
+
+ /**
+ * Set the {@link SAMLConfigurationProvider} fqn
+ *
+ * @param cp fqn of a {@link SAMLConfigurationProvider}
+ */
+ public void setConfigProvider(String cp) {
+ if (cp == null)
+ throw new IllegalStateException(ErrorCodes.NULL_ARGUMENT + cp);
+ Class> clazz = SecurityActions.loadClass(getClass(), cp);
+ if (clazz == null)
+ throw new RuntimeException(ErrorCodes.CLASS_NOT_LOADED + cp);
+ try {
+ configProvider = (SAMLConfigurationProvider) clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(ErrorCodes.CANNOT_CREATE_INSTANCE + cp + ":" + e.getMessage());
+ }
+ }
+
+ /**
+ * Set an instance of the {@link SAMLConfigurationProvider}
+ *
+ * @param configProvider SAML IDP/SP config provider
+ */
+ public void setConfigProvider(SAMLConfigurationProvider configProvider) {
+ this.configProvider = configProvider;
+ }
+
+ /**
+ * Get the {@link SPType}
+ *
+ * @return SAML SP configuration
+ */
+ public SPType getConfiguration() {
+ return spConfiguration;
+ }
+
+ /**
+ * Set a separate issuer id
+ *
+ * @param issuerID id of the issuer
+ */
+ public void setIssuerID(String issuerID) {
+ this.issuerID = issuerID;
+ }
+
+ /**
+ * Set the logout page
+ *
+ * @param logOutPage logout page URL
+ */
+ public void setLogOutPage(String logOutPage) {
+ logger.warn("Option logOutPage is now configured with the PicketLinkSP element.");
+
+ }
+
+ /**
+ * Set the Timer Value to reload the configuration
+ *
+ * @param value an integer value that represents timer value (in miliseconds)
+ */
+ public void setTimerInterval(String value) {
+ if (StringUtil.isNotNull(value)) {
+ timerInterval = Integer.parseInt(value);
+ }
+ }
+
+ /**
+ * Perform validation os the request object
+ *
+ * @param request Apache Catalina Request
+ * @return true if request contains a SAML Response parameter
+ */
+ protected boolean validate(Request request) {
+ return request.getParameter("SAMLResponse") != null;
+ }
+
+ /**
+ * Get the Identity URL
+ *
+ * @return Identity URL
+ */
+ public String getIdentityURL() {
+ return identityURL;
+ }
+
+ /**
+ * Get the {@link X509Certificate} of the IDP if provided via the IDP metadata
+ * file
+ *
+ * @return {@link X509Certificate} or null
+ */
+ public X509Certificate getIdpCertificate() {
+ return idpCertificate;
+ }
+
+ /**
+ * Fall back on local authentication at the service provider side
+ *
+ * @param request Apache Catalina Request
+ * @param response Apache Catalina Response
+ * @param loginConfig Apache Catalina Login Config
+ * @return true if authenticated
+ * @throws IOException any I/O error during authentication
+ */
+ protected boolean localAuthentication(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ if (request.getUserPrincipal() == null) {
+ logger.samlSPFallingBackToLocalFormAuthentication();// fallback
+ try {
+ return super.authenticate(request, response.getResponse());
+ } catch (NoSuchMethodError e) {
+ // Use Reflection
+ try {
+ Method method = super.getClass().getMethod("authenticate",
+ new Class[] { HttpServletRequest.class, HttpServletResponse.class,
+ LoginConfig.class });
+ return (Boolean) method.invoke(this,
+ new Object[] { request.getRequest(), response.getResponse(),
+ loginConfig });
+ } catch (Exception ex) {
+ throw logger.unableLocalAuthentication(ex);
+ }
+ }
+ } else
+ return true;
+ }
+
+ /**
+ * Return the SAML Binding that this authenticator supports
+ *
+ * @return supported SAML Binding
+ */
+ protected abstract String getBinding();
+
+ /**
+ * Attempt to process a metadata file available locally
+ *
+ * @param idpMetadataFile path of configuration file of IDP Metadata
+ */
+ protected void processIDPMetadataFile(String idpMetadataFile) {
+ ServletContext servletContext = context.getServletContext();
+ InputStream is = servletContext.getResourceAsStream(idpMetadataFile);
+ if (is == null)
+ return;
+
+ Object metadata = null;
+ try {
+ Document samlDocument = DocumentUtil.getDocument(is);
+ SAMLParser parser = new SAMLParser();
+ metadata = parser.parse(DocumentUtil.getNodeAsStream(samlDocument));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ IDPSSODescriptorType idpSSO = null;
+ if (metadata instanceof EntitiesDescriptorType) {
+ EntitiesDescriptorType entities = (EntitiesDescriptorType) metadata;
+ idpSSO = handleMetadata(entities);
+ } else {
+ idpSSO = handleMetadata((EntityDescriptorType) metadata);
+ }
+ if (idpSSO == null) {
+ logger.samlSPUnableToGetIDPDescriptorFromMetadata();
+ return;
+ }
+ List endpoints = idpSSO.getSingleSignOnService();
+ for (EndpointType endpoint : endpoints) {
+ String endpointBinding = endpoint.getBinding().toString();
+ if (endpointBinding.contains("HTTP-POST"))
+ endpointBinding = "POST";
+ else if (endpointBinding.contains("HTTP-Redirect"))
+ endpointBinding = "REDIRECT";
+ if (getBinding().equals(endpointBinding)) {
+ identityURL = endpoint.getLocation().toString();
+ break;
+ }
+ }
+ List keyDescriptors = idpSSO.getKeyDescriptor();
+ if (keyDescriptors.size() > 0) {
+ this.idpCertificate = MetaDataExtractor.getCertificate(keyDescriptors.get(0));
+ }
+ }
+
+ /**
+ * Process the configuration from the configuration file
+ */
+ @SuppressWarnings("deprecation")
+ protected void processConfiguration() {
+ ServletContext servletContext = context.getServletContext();
+ InputStream is = null;
+ try {
+ if (isNullOrEmpty(this.configFile)) {
+ this.configFile = PropertyManager.getProperty("gatein.sso.saml.config.file");
+ if (StringUtils.contains(this.configFile, "$")) {
+ this.configFile = Deserializer.resolveString(this.configFile);
+ }
+ if (StringUtils.isBlank(this.configFile)) {
+ this.configFile = CONFIG_FILE_LOCATION;
+ }
+ if (Files.exists(Paths.get(this.configFile))) {
+ is = new FileInputStream(this.configFile);
+ } else {
+ this.configFile = SAMLSPServletContextWrapper.FILE_PREFIX + this.configFile;
+ is = servletContext.getResourceAsStream(this.configFile);
+ }
+ } else {
+ try {
+ is = new FileInputStream(this.configFile);
+ } catch (FileNotFoundException e) {
+ throw logger.samlIDPConfigurationError(e);
+ }
+ }
+
+ // Work on the IDP Configuration
+ if (configProvider != null) {
+ try {
+ if (is == null) {
+ // Try the older version
+ is = servletContext.getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
+
+ // Additionally parse the deprecated config file
+ if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
+ ((AbstractSAMLConfigurationProvider) configProvider).setConfigFile(is);
+ }
+ } else {
+ // Additionally parse the consolidated config file
+ if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
+ ((AbstractSAMLConfigurationProvider) configProvider).setConsolidatedConfigFile(is);
+ }
+ }
+
+ picketLinkConfiguration = configProvider.getPicketLinkConfiguration();
+ spConfiguration = configProvider.getSPConfiguration();
+ } catch (ProcessingException e) {
+ throw logger.samlSPConfigurationError(e);
+ } catch (ParsingException e) {
+ throw logger.samlSPConfigurationError(e);
+ }
+ } else {
+ if (is != null) {
+ try {
+ picketLinkConfiguration = ConfigurationUtil.getConfiguration(is);
+ spConfiguration = (SPType) picketLinkConfiguration.getIdpOrSP();
+ } catch (ParsingException e) {
+ logger.trace(e);
+ throw logger.samlSPConfigurationError(e);
+ }
+ } else {
+ is = servletContext.getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
+ if (is == null)
+ throw logger.configurationFileMissing(configFile);
+ spConfiguration = ConfigurationUtil.getSPConfiguration(is);
+ }
+ }
+
+ if (this.picketLinkConfiguration != null) {
+ enableAudit = picketLinkConfiguration.isEnableAudit();
+
+ // See if we have the system property enabled
+ if (!enableAudit) {
+ String sysProp = SecurityActions.getSystemProperty(GeneralConstants.AUDIT_ENABLE, "NULL");
+ if (!"NULL".equals(sysProp)) {
+ enableAudit = Boolean.parseBoolean(sysProp);
+ }
+ }
+
+ if (enableAudit) {
+ if (auditHelper == null) {
+ String securityDomainName = PicketLinkAuditHelper.getSecurityDomainName(servletContext);
+
+ auditHelper = new PicketLinkAuditHelper(securityDomainName);
+ }
+ }
+ }
+
+ if (StringUtil.isNotNull(spConfiguration.getIdpMetadataFile())) {
+ processIDPMetadataFile(spConfiguration.getIdpMetadataFile());
+ } else {
+ this.identityURL = spConfiguration.getIdentityURL();
+ }
+ this.serviceURL = spConfiguration.getServiceURL();
+ this.canonicalizationMethod = spConfiguration.getCanonicalizationMethod();
+
+ logger.samlSPSettingCanonicalizationMethod(canonicalizationMethod);
+ XMLSignatureUtil.setCanonicalizationMethodType(canonicalizationMethod);
+
+ logger.trace("Identity Provider URL=" + this.identityURL);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ logger.trace("Can't close Input Stream", e);
+ }
+ }
+ }
+ }
+
+ protected IDPSSODescriptorType handleMetadata(EntitiesDescriptorType entities) {
+ IDPSSODescriptorType idpSSO = null;
+
+ List entityDescs = entities.getEntityDescriptor();
+ for (Object entityDescriptor : entityDescs) {
+ if (entityDescriptor instanceof EntitiesDescriptorType) {
+ idpSSO = getIDPSSODescriptor(entities);
+ } else
+ idpSSO = handleMetadata((EntityDescriptorType) entityDescriptor);
+ if (idpSSO != null)
+ break;
+ }
+ return idpSSO;
+ }
+
+ protected IDPSSODescriptorType handleMetadata(EntityDescriptorType entityDescriptor) {
+ return CoreConfigUtil.getIDPDescriptor(entityDescriptor);
+ }
+
+ protected IDPSSODescriptorType getIDPSSODescriptor(EntitiesDescriptorType entities) {
+ List entityDescs = entities.getEntityDescriptor();
+ for (Object entityDescriptor : entityDescs) {
+
+ if (entityDescriptor instanceof EntitiesDescriptorType) {
+ return getIDPSSODescriptor((EntitiesDescriptorType) entityDescriptor);
+ }
+ return CoreConfigUtil.getIDPDescriptor((EntityDescriptorType) entityDescriptor);
+ }
+ return null;
+ }
+
+ protected void initializeHandlerChain() throws ConfigurationException, ProcessingException {
+ populateChainConfig();
+ SAML2HandlerChainConfig handlerChainConfig = new DefaultSAML2HandlerChainConfig(chainConfigOptions);
+
+ Set samlHandlers = chain.handlers();
+
+ for (SAML2Handler handler : samlHandlers) {
+ handler.initChainConfig(handlerChainConfig);
+ }
+ }
+
+ protected void populateChainConfig() throws ConfigurationException, ProcessingException {
+ chainConfigOptions.put(GeneralConstants.CONFIGURATION, spConfiguration);
+ chainConfigOptions.put(GeneralConstants.ROLE_VALIDATOR_IGNORE, "false"); // No
+ // validator
+ // as
+ // tomcat
+ // realm
+ // does
+ // validn
+
+ if (doSupportSignature()) {
+ chainConfigOptions.put(GeneralConstants.KEYPAIR, keyManager.getSigningKeyPair());
+ // If there is a need for X509Data in signedinfo
+ String certificateAlias = (String) keyManager.getAdditionalOption(GeneralConstants.X509CERTIFICATE);
+ if (certificateAlias != null) {
+ chainConfigOptions.put(GeneralConstants.X509CERTIFICATE, keyManager.getCertificate(certificateAlias));
+ }
+ }
+ }
+
+ protected void sendToLogoutPage(Request request, Response response, Session session) throws IOException, ServletException {
+ // we are invalidated.
+ RequestDispatcher dispatch = context.getServletContext().getRequestDispatcher(this.getConfiguration().getLogOutPage());
+ if (dispatch == null)
+ logger.samlSPCouldNotDispatchToLogoutPage(this.getConfiguration().getLogOutPage());
+ else {
+ logger.trace("Forwarding request to logOutPage: " + this.getConfiguration().getLogOutPage());
+ session.expire();
+ try {
+ dispatch.forward(request, response);
+ } catch (Exception e) {
+ // JBAS5.1 and 6 quirkiness
+ dispatch.forward(request.getRequest(), response);
+ }
+ }
+ }
+
+ // Mock test purpose
+ public void testStart() throws LifecycleException {
+ this.saveRestoreRequest = false;
+ if (context == null)
+ throw new RuntimeException("Catalina Context not set up");
+ startPicketLink();
+ }
+
+ protected void startPicketLink() throws LifecycleException {
+ SystemPropertiesUtil.ensure();
+ Handlers handlers = null;
+
+ // Introduce a timer to reload configuration if desired
+ if (timerInterval > 0) {
+ if (timer == null) {
+ timer = new Timer();
+ }
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ // Clear the configuration
+ picketLinkConfiguration = null;
+ spConfiguration = null;
+
+ processConfiguration();
+ try {
+ initKeyProvider(context);
+ } catch (LifecycleException e) {
+ logger.trace(e.getMessage());
+ }
+ }
+ }, timerInterval, timerInterval);
+ }
+
+ // Get the chain from config
+ if (StringUtil.isNullOrEmpty(samlHandlerChainClass)) {
+ chain = SAML2HandlerChainFactory.createChain();
+ } else {
+ try {
+ chain = SAML2HandlerChainFactory.createChain(this.samlHandlerChainClass);
+ } catch (ProcessingException e1) {
+ throw new LifecycleException(e1);
+ }
+ }
+
+ ServletContext servletContext = context.getServletContext();
+
+ this.processConfiguration();
+
+ try {
+ if (picketLinkConfiguration != null) {
+ handlers = picketLinkConfiguration.getHandlers();
+ } else {
+ // Get the handlers
+ String handlerConfigFileName = GeneralConstants.HANDLER_CONFIG_FILE_LOCATION;
+ handlers = ConfigurationUtil.getHandlers(servletContext.getResourceAsStream(handlerConfigFileName));
+ }
+
+ chain.addAll(HandlerUtil.getHandlers(handlers));
+
+ this.initKeyProvider(context);
+ this.populateChainConfig();
+ this.initializeHandlerChain();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ if (this.picketLinkConfiguration == null) {
+ this.picketLinkConfiguration = new PicketLinkType();
+
+ this.picketLinkConfiguration.setIdpOrSP(getConfiguration());
+ this.picketLinkConfiguration.setHandlers(handlers);
+ }
+ }
+
+ /**
+ *
+ * Indicates if digital signatures/validation of SAML assertions are enabled.
+ * Subclasses that supports signature should override this method.
+ *
+ *
+ * @return true if SP Configuration supports signature
+ */
+ protected boolean doSupportSignature() {
+ if (spConfiguration != null) {
+ return spConfiguration.isSupportsSignature();
+ }
+ return false;
+ }
+
+ protected abstract void initKeyProvider(Context context) throws LifecycleException;
+
+ public void setAuditHelper(PicketLinkAuditHelper auditHelper) {
+ this.auditHelper = auditHelper;
+ }
+}
diff --git a/saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/SecurityActions.java b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/SecurityActions.java
similarity index 100%
rename from saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/SecurityActions.java
rename to sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/SecurityActions.java
diff --git a/saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java
similarity index 65%
rename from saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java
rename to sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java
index ae8afcacd..e5314939f 100644
--- a/saml/gatein-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java
+++ b/sso-saml-plugin/src/main/java/org/gatein/sso/saml/plugin/valve/ServiceProviderAuthenticator.java
@@ -2,12 +2,6 @@
import org.apache.catalina.LifecycleException;
-
-/**
- * Unified Service Provider Authenticator
- * Forked from org.picketlink.identity.federation.bindings.tomcat.sp.ServiceProviderAuthenticator
- * and made compatible with Tomcat 8.5 since picketlink doesn't provide such a support
- */
public class ServiceProviderAuthenticator extends AbstractSPFormAuthenticator {
@Override
diff --git a/sso-saml-plugin/src/main/java/org/jboss/security/Group.java b/sso-saml-plugin/src/main/java/org/jboss/security/Group.java
new file mode 100644
index 000000000..d0de68131
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/jboss/security/Group.java
@@ -0,0 +1,16 @@
+package org.jboss.security;
+
+import java.security.Principal;
+import java.util.Enumeration;
+
+public interface Group extends Principal {
+
+ public boolean addMember(Principal user);
+
+ public boolean removeMember(Principal user);
+
+ public boolean isMember(Principal member);
+
+ public Enumeration extends Principal> members();
+
+}
diff --git a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOContext.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SOAPSAMLXACMLServlet.java
similarity index 55%
rename from spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOContext.java
rename to sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SOAPSAMLXACMLServlet.java
index ef93badc6..4c4058324 100644
--- a/spnegosso/src/main/java/org/gatein/security/sso/spnego/SPNEGOSSOContext.java
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SOAPSAMLXACMLServlet.java
@@ -1,5 +1,8 @@
/*
- * Copyright (C) 2012 eXo Platform SAS.
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
@@ -16,17 +19,14 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.gatein.security.sso.spnego;
+package org.picketlink.identity.federation.bindings.servlets;
-import javax.servlet.http.HttpServletRequest;
-
-public class SPNEGOSSOContext {
- private static ThreadLocal currentRequest = new ThreadLocal();
-
- public static void setCurrentRequest(HttpServletRequest req) {
- currentRequest.set(req);
- }
- public static HttpServletRequest getCurrentRequest() {
- return currentRequest.get();
- }
-}
+/**
+ * Servlet that can read SOAP 1.1 messages that contain an XACML query in saml payload
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Jan 27, 2009
+ */
+public class SOAPSAMLXACMLServlet extends org.picketlink.identity.federation.web.servlets.saml.SOAPSAMLXACMLServlet {
+ private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SecurityActions.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SecurityActions.java
new file mode 100644
index 000000000..615b2e1f6
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/servlets/SecurityActions.java
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.servlets;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Privileged Blocks
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Mar 17, 2009
+ */
+class SecurityActions {
+ static void setSystemProperty(final String key, final String value) {
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ System.setProperty(key, value);
+ return null;
+ }
+ });
+ } else {
+ System.setProperty(key, value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolFactory.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolFactory.java
new file mode 100644
index 000000000..b255fea34
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolFactory.java
@@ -0,0 +1,146 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.stspool;
+
+import org.picketlink.identity.federation.core.wstrust.STSClient;
+import org.picketlink.identity.federation.core.wstrust.STSClientConfig;
+import org.picketlink.identity.federation.core.wstrust.STSClientCreationCallBack;
+import org.picketlink.identity.federation.core.wstrust.STSClientPool;
+import org.picketlink.identity.federation.core.wstrust.STSClientFactory;
+
+
+/**
+ * Simple factory for creating {@link STSClient}s.
+ *
+ * @author Peter Skopek
+ */
+public final class STSClientPoolFactory implements STSClientPool {
+
+ private STSClientPoolInternal stsClientPoolInternal;
+
+ private STSClientPoolFactory() {
+ stsClientPoolInternal = new STSClientPoolInternal();
+ }
+
+ private static class LazySTSClientFactory {
+ private static final STSClientPoolFactory INSTANCE = new STSClientPoolFactory();
+ }
+
+ /**
+ * Get instance of {@link STSClientPool}.
+ *
+ * @return {@link STSClientPool} instance
+ */
+ public static STSClientPool getPoolInstance() {
+ STSClientPoolFactory cf = LazySTSClientFactory.INSTANCE;
+ STSClientFactory.setInstance(cf);
+ return cf;
+ }
+
+ /**
+ * This method initializes sub pool of clients by given configuration data and returns client from that pool.
+ *
+ * When pooling is disabled it does nothing.
+ *
+ * @param config to construct the pool of clients
+ */
+ public void createPool(final STSClientConfig config) {
+ createPool(STSClientPoolInternal.DEFAULT_NUM_STS_CLIENTS, config);
+ }
+
+ /**
+ * This method initializes sub pool of clients by given configuration data and returns client from that pool.
+ * initialNumberOfClients is used to initialize the pool for the given number of clients.
+ *
+ * When pooling is disabled it does nothing.
+ *
+ * @param initialNumberOfClients initial number of clients in the pool
+ * @param config to construct the pool of clients
+ */
+ public void createPool(int initialNumberOfClients, final STSClientConfig config) {
+ stsClientPoolInternal.initialize(initialNumberOfClients, config);
+ }
+
+ /**
+ * This method initializes sub pool of clients by given configuration data.
+ * initialNumberOfClients is used to initialize the pool for the given number of clients.
+ *
+ * When pooling is disabled it does nothing.
+ *
+ * @param initialNumberOfClients initial number of clients in the pool
+ * @param callBack which provide configuration
+ */
+
+ public void createPool(int initialNumberOfClients, final STSClientCreationCallBack callBack) {
+ stsClientPoolInternal.initialize(initialNumberOfClients, callBack);
+ }
+
+ /**
+ * Destroys client sub pool denoted by given config.
+ *
+ * @param config {@link STSClientConfiguration} to find client sub pool to destroy
+ */
+ public void destroyPool(final STSClientConfig config) {
+ stsClientPoolInternal.destroy(config);
+ }
+
+
+ /**
+ * Destroy all the pools attached to given module name.
+ *
+ * @param moduleName module name to destroy pools or "" or null to destroy default module's pools.
+ */
+ public void destroyPool(final String moduleName) {
+ stsClientPoolInternal.destroy(moduleName);
+ }
+
+ /**
+ * Returns given {@link STSClient} back to the sub pool of clients.
+ * Sub pool is determined automatically from client configuration.
+ *
+ * @param {@link STSClient} to return back to the sub pool of clients
+ */
+ public void returnClient(final STSClient stsClient) {
+ stsClientPoolInternal.putIn(stsClient);
+ }
+
+ /**
+ * Get STSClient from sub pool denoted by config.
+ * @param config {@link STSClientConfiguration} to find client sub pool
+ * @return {@link STSClient} from the sub pool of clients
+ */
+ public STSClient getClient(final STSClientConfig config) {
+ STSClient client = stsClientPoolInternal.takeOut(config);
+ if (client == null) {
+ // non pooled client
+ return new STSClient(config);
+ }
+ return client;
+ }
+
+ /**
+ * Checks whether given config has already sub pool of clients created.
+ *
+ * @param config {@link STSClientConfiguration} to find client sub pool
+ * @return true if config was already used as sub pool key
+ */
+ public boolean configExists(final STSClientConfig config) {
+ return stsClientPoolInternal.isConfigInitialized(config);
+ }
+
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolInternal.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolInternal.java
new file mode 100644
index 000000000..c671d23d6
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/stspool/STSClientPoolInternal.java
@@ -0,0 +1,292 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.stspool;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.identity.federation.bindings.util.ModuleUtils;
+import org.picketlink.identity.federation.core.wstrust.STSClient;
+import org.picketlink.identity.federation.core.wstrust.STSClientConfig;
+import org.picketlink.identity.federation.core.wstrust.STSClientConfigKeyProvider;
+import org.picketlink.identity.federation.core.wstrust.STSClientCreationCallBack;
+
+/**
+ * Simple pool of {@link STSClient} classes.
+ * This class is not intended to be used directly by user code. Use {@link STSClientPoolFactory} class instead.
+ *
+ * @author Peter Skopek : pskopek at (redhat.com)
+ *
+ */
+class STSClientPoolInternal {
+
+ private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+ static int DEFAULT_NUM_STS_CLIENTS = 10;
+
+ private Hashtable> free = new Hashtable>();
+ private Hashtable> inUse = new Hashtable>();
+ private Hashtable configs = new Hashtable();
+
+ STSClientPoolInternal() {
+ }
+
+ void initialize(int numberOfSTSClients, STSClientConfig stsClientConfig) {
+ internalInitialize(numberOfSTSClients, stsClientConfig, null);
+ }
+
+ void initialize(STSClientConfig stsClientConfig) {
+ internalInitialize(DEFAULT_NUM_STS_CLIENTS, stsClientConfig, null);
+ }
+
+
+ void initialize(int numberOfSTSClients, STSClientCreationCallBack clientCreationCallBack) {
+ internalInitialize(numberOfSTSClients, null, clientCreationCallBack);
+ }
+
+ private synchronized void internalInitialize(final int numberOfSTSClients, STSClientConfig stsClientConfig, STSClientCreationCallBack clientCreationCallBack) {
+
+ if (numberOfSTSClients <= 0) {
+ return;
+ }
+
+
+ String key = null;
+ if (clientCreationCallBack != null) {
+ key = substituteKey(clientCreationCallBack.getKey());
+ } else {
+ key = key(stsClientConfig);
+ }
+
+ if (!configs.containsKey(key)) {
+ ArrayList clients = new ArrayList(numberOfSTSClients);
+ if (clientCreationCallBack != null) {
+ for (int i = 0; i < numberOfSTSClients; i++) {
+ clients.add(clientCreationCallBack.createClient());
+ }
+ } else {
+ for (int i = 0; i < numberOfSTSClients; i++) {
+ clients.add(new STSClient(stsClientConfig));
+ }
+ }
+ STSConfigData configData = new STSConfigData();
+ configData.initialNumberOfClients = numberOfSTSClients;
+ if (clientCreationCallBack != null) {
+ configData.config = null;
+ configData.callBack = clientCreationCallBack;
+ } else {
+ configData.config = stsClientConfig;
+ configData.callBack = null;
+ }
+ configs.put(key, configData);
+ free.put(key, clients);
+ inUse.put(key, new ArrayList(numberOfSTSClients));
+ } else {
+ // free pool already contains given key:
+ throw logger.freePoolAlreadyContainsGivenKey(key);
+ }
+
+ }
+
+ synchronized void destroy(STSClientConfig stsClientConfig) {
+ String key = key(stsClientConfig);
+ free.remove(key);
+ inUse.remove(key);
+ configs.remove(key);
+ }
+
+ synchronized void destroy(String moduleName) {
+ String module = moduleName;
+ if (moduleName == null || moduleName.isEmpty()) {
+ module = ModuleUtils.getCurrentModuleId();
+ }
+ int removed = 0;
+ removeByPrefix(module, free);
+ removeByPrefix(module, inUse);
+ removed += removeByPrefix(module, configs);
+ if (removed == 0) {
+ // fallback to modified prefix
+ module = "deployment." + module;
+ removeByPrefix(module, free);
+ removeByPrefix(module, inUse);
+ removeByPrefix(module, configs);
+ }
+ }
+
+
+ STSClient takeOut(STSClientConfig stsClientConfig) {
+ String key = key(stsClientConfig);
+ return takeOutInternal(key);
+ }
+
+
+ STSClient takeOut(String key) {
+ String substKey = substituteKey(key);
+ STSClient client = takeOutInternal(substKey);
+ if (client == null) {
+ STSConfigData configData = configs.get(substKey);
+ if (configData == null) {
+ throw logger.cannotGetSTSConfigByKey(substKey);
+ }
+ if (configData.callBack != null) {
+ internalInitialize(DEFAULT_NUM_STS_CLIENTS, null, configData.callBack);
+ }
+ else if (configData.config != null) {
+ internalInitialize(DEFAULT_NUM_STS_CLIENTS, configData.config, configData.callBack);
+ }
+ client = takeOutInternal(substKey);
+ }
+ return client;
+ }
+
+ boolean isConfigInitialized(STSClientConfig stsClientConfig) {
+ if (stsClientConfig == null) {
+ return false;
+ }
+ STSConfigData configData = configs.get(key(stsClientConfig));
+ return (configData != null);
+ }
+
+ boolean isConfigInitialized(String key) {
+ if (key == null) {
+ return false;
+ }
+ STSConfigData configData = configs.get(substituteKey(key));
+ return (configData != null);
+ }
+
+ void putIn(STSClientConfigKeyProvider keyProvider, STSClient client) {
+ putInInternal(substituteKey(keyProvider.getSTSClientConfigKey()), client);
+ }
+
+ void putIn(String key, STSClient client) {
+ putInInternal(substituteKey(key), client);
+ }
+
+ void putIn(STSClient client) {
+ putInInternal(substituteKey(client.getSTSClientConfigKey()), client);
+ }
+
+ private synchronized STSClient takeOutInternal(String key) {
+ // no key substitution
+ ArrayList clients = free.get(key);
+ if (clients != null) {
+ int size = clients.size();
+ STSClient client;
+ if (size > 0) {
+ client = clients.remove(size - 1);
+ } else {
+ addClients(key);
+ client = clients.remove(clients.size() -1);
+ }
+ markInUse(key, client);
+ return client;
+ }
+ return null;
+ }
+
+ private void addClients(String key) {
+ // no key substitution
+ STSConfigData configData = configs.get(key);
+ if (configData != null) {
+ ArrayList freeClientPool = free.get(key);
+ if (freeClientPool != null) {
+ ArrayList clients = new ArrayList(configData.initialNumberOfClients);
+ if (configData.config != null) {
+ for (int i = 0; i < configData.initialNumberOfClients; i++) {
+ clients.add(new STSClient(configData.config));
+ }
+ } else {
+ for (int i = 0; i < configData.initialNumberOfClients; i++) {
+ clients.add(configData.callBack.createClient());
+ }
+ }
+ freeClientPool.addAll(clients);
+ } else {
+ // cannot get free client pool key:
+ throw logger.cannotGetFreeClientPoolKey(key);
+ }
+ } else {
+ // cannot get STS config by key:
+ throw logger.cannotGetSTSConfigByKey(key);
+ }
+ }
+
+ private void markInUse(String key, STSClient client) {
+ // no key substitution
+ ArrayList usedClients = inUse.get(key);
+ if (usedClients != null) {
+ usedClients.add(client);
+ } else {
+ // cannot get used clients by key:
+ logger.cannotGetUsedClientsByKey(key);
+ }
+ }
+
+ private synchronized void putInInternal(String key, STSClient client) {
+ // no key substitution
+ STSConfigData configData = configs.get(key);
+ if (configData == null) {
+ // attempt to return client not from pool, we can silently ignore it
+ return;
+ }
+
+ ArrayList freeClients = free.get(key);
+ ArrayList usedClients = inUse.get(key);
+
+ if (usedClients != null && !usedClients.remove(client)) {
+ // removing non existing client from used clients by key:
+ throw logger.removingNonExistingClientFromUsedClientsByKey(key);
+ }
+
+ freeClients.add(client);
+
+ }
+
+ private String key(STSClientConfig stsClientConfig) {
+ return substituteKey(stsClientConfig.getSTSClientConfigKey());
+ }
+
+ private String substituteKey(String originalKey) {
+ if (originalKey != null && originalKey.indexOf(STSClientConfig.SUBSTITUTE_MODULE) != -1) {
+ return originalKey.replaceAll("\\Q" + STSClientConfig.SUBSTITUTE_MODULE + "\\E", ModuleUtils.getCurrentModuleId());
+ }
+ return originalKey;
+ }
+
+ private int removeByPrefix(String prefix, Hashtable hashTbl) {
+ int num = 0;
+ Enumeration keys = hashTbl.keys();
+ while(keys.hasMoreElements()) {
+ String k = keys.nextElement();
+ if (k.startsWith(prefix)) {
+ num++;
+ hashTbl.remove(k);
+ }
+ }
+ return num;
+ }
+
+}
+
+class STSConfigData {
+ STSClientConfig config;
+ STSClientCreationCallBack callBack;
+ int initialNumberOfClients = STSClientPoolInternal.DEFAULT_NUM_STS_CLIENTS;
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractGenericHeaderAuthenticator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractGenericHeaderAuthenticator.java
new file mode 100644
index 000000000..5f2008726
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractGenericHeaderAuthenticator.java
@@ -0,0 +1,235 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.StringTokenizer;
+
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * JBAS-2283: Provide custom header based authentication support
+ *
+ * Header Authenticator that deals with userid from the request header Requires two attributes configured on the Tomcat Service
+ * - one for the http header denoting the authenticated identity and the other is the SESSION cookie
+ *
+ * @author Anil Saldhana
+ * @author Stefan Guilhen
+ * @version $Revision$
+ * @since Sep 11, 2006
+ */
+public abstract class AbstractGenericHeaderAuthenticator extends FormAuthenticator {
+
+ protected static final PicketLinkLogger log = PicketLinkLoggerFactory.getLogger();
+
+ // JBAS-4804: AbstractGenericHeaderAuthenticator injection of ssoid and sessioncookie name.
+ private String httpHeaderForSSOAuth = null;
+
+ private String sessionCookieForSSOAuth = null;
+
+ /**
+ *
+ * Obtain the value of the httpHeaderForSSOAuth attribute. This attribute is used to indicate the request
+ * header ids that have to be checked in order to retrieve the SSO identity set by a third party security system.
+ *
+ *
+ * @return a String containing the value of the httpHeaderForSSOAuth attribute.
+ */
+ public String getHttpHeaderForSSOAuth() {
+ return httpHeaderForSSOAuth;
+ }
+
+ /**
+ *
+ * Set the value of the httpHeaderForSSOAuth attribute. This attribute is used to indicate the request header
+ * ids that have to be checked in order to retrieve the SSO identity set by a third party security system.
+ *
+ *
+ * @param httpHeaderForSSOAuth a String containing the value of the httpHeaderForSSOAuth
+ * attribute.
+ */
+ public void setHttpHeaderForSSOAuth(String httpHeaderForSSOAuth) {
+ this.httpHeaderForSSOAuth = httpHeaderForSSOAuth;
+ }
+
+ /**
+ *
+ * Obtain the value of the sessionCookieForSSOAuth attribute. This attribute is used to indicate the names of
+ * the SSO cookies that may be present in the request object.
+ *
+ *
+ * @return a String containing the names (separated by a ',') of the SSO cookies that may have
+ * been set by a third party security system in the request.
+ */
+ public String getSessionCookieForSSOAuth() {
+ return sessionCookieForSSOAuth;
+ }
+
+ /**
+ *
+ * Set the value of the sessionCookieForSSOAuth attribute. This attribute is used to indicate the names of the
+ * SSO cookies that may be present in the request object.
+ *
+ *
+ * @param sessionCookieForSSOAuth a String containing the names (separated by a ',') of the SSO
+ * cookies that may have been set by a third party security system in the request.
+ */
+ public void setSessionCookieForSSOAuth(String sessionCookieForSSOAuth) {
+ this.sessionCookieForSSOAuth = sessionCookieForSSOAuth;
+ }
+
+ /**
+ *
+ * Creates an instance of AbstractGenericHeaderAuthenticator.
+ *
+ */
+ public AbstractGenericHeaderAuthenticator() {
+ super();
+ }
+
+ public boolean performAuthentication(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
+ boolean trace = log.isTraceEnabled();
+ if (log.isTraceEnabled()) {
+ log.trace("Authenticating user");
+ }
+
+ Principal principal = request.getUserPrincipal();
+ if (principal != null) {
+ if (trace)
+ log.trace("Already authenticated '" + principal.getName() + "'");
+ return true;
+ }
+
+ Realm realm = context.getRealm();
+ Session session = request.getSessionInternal(true);
+
+ String username = getUserId(request);
+ String password = getSessionCookie(request);
+
+ // Check if there is sso id as well as sessionkey
+ if (username == null || password == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Username is null or password(sessionkey) is null:fallback to form auth");
+ }
+ return super.authenticate(request, response);
+ }
+ principal = realm.authenticate(username, password);
+
+ if (principal == null) {
+ forwardToErrorPage(request, response, config);
+ return false;
+ }
+
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
+ return true;
+ }
+
+ /**
+ * Get the username from the request header
+ *
+ * @param request
+ * @return
+ */
+ protected String getUserId(Request request) {
+ String ssoid = null;
+ // We can have a comma-separated ids
+ String ids = this.httpHeaderForSSOAuth;
+
+ if (ids == null || ids.length() == 0)
+ throw new IllegalStateException("Http headers configuration in tomcat service missing");
+
+ StringTokenizer st = new StringTokenizer(ids, ",");
+ while (st.hasMoreTokens()) {
+ ssoid = request.getHeader(st.nextToken());
+ if (ssoid != null)
+ break;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("SSOID-" + ssoid);
+ }
+ return ssoid;
+ }
+
+ /**
+ * Obtain the session cookie from the request
+ *
+ * @param request
+ * @return
+ */
+ protected String getSessionCookie(Request request) {
+ Cookie[] cookies = request.getCookies();
+ log.trace("Cookies:" + cookies);
+ int numCookies = cookies != null ? cookies.length : 0;
+
+ // We can have comma-separated ids
+ String ids = sessionCookieForSSOAuth;
+
+ if (ids == null || ids.length() == 0)
+ throw new IllegalStateException("Session cookies configuration in tomcat service missing");
+
+ StringTokenizer st = new StringTokenizer(ids, ",");
+ while (st.hasMoreTokens()) {
+ String cookieToken = st.nextToken();
+ String val = getCookieValue(cookies, numCookies, cookieToken);
+ if (val != null)
+ return val;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Session Cookie not found");
+ }
+ return null;
+ }
+
+ /**
+ * Get the value of a cookie if the name matches the token
+ *
+ * @param cookies array of cookies
+ * @param numCookies number of cookies in the array
+ * @param token Key
+ * @return value of cookie
+ */
+ protected String getCookieValue(Cookie[] cookies, int numCookies, String token) {
+ for (int i = 0; i < numCookies; i++) {
+ Cookie cookie = cookies[i];
+ log.trace("Matching cookieToken:" + token + " with cookie name=" + cookie.getName());
+ if (token.equals(cookie.getName())) {
+ if (log.isTraceEnabled()) {
+ log.trace("Cookie-" + token + " value=" + cookie.getValue());
+ }
+ return cookie.getValue();
+ }
+ }
+ return null;
+ }
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractPicketLinkAuthenticator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractPicketLinkAuthenticator.java
new file mode 100644
index 000000000..48d4074a1
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractPicketLinkAuthenticator.java
@@ -0,0 +1,202 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+
+/**
+ *
An authenticator that delegates actual authentication to a realm, and in turn to a security manager, by presenting a
+ * "conventional" identity. The security manager must accept the conventional identity and generate the real identity for the
+ * authenticated principal.
+ *
Subclasses should override some methods to provide especific implementation according with the binding/environment.
+ *
+ * @author Ovidiu Feodorov
+ * @author Anil.Saldhana@redhat.com
+ * @author Pedro Silva
+ *
+ */
+public abstract class AbstractPicketLinkAuthenticator extends AuthenticatorBase {
+
+ protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ /**
+ * This is the auth method used in the register method
+ */
+ protected String authMethod = "SECURITY_DOMAIN";
+
+ /**
+ * The authenticator may not be aware of the user name until after the underlying security exercise is complete. The Subject
+ * will have the proper user name. Hence we may need to perform an additional authentication now with the user name we have
+ * obtained.
+ */
+ protected boolean needSubjectPrincipalSubstitution = true;
+
+ protected SubjectSecurityInteraction subjectInteraction = null;
+
+ protected String subjectInteractionClassName = "org.picketlink.identity.federation.bindings.jboss.subject.PicketLinkJBossSubjectInteraction";
+
+ /**
+ * Set the auth method via WEB-INF/context.xml (JBoss AS)
+ *
+ * @param authMethod
+ */
+ public void setAuthMethod(String authMethod) {
+ this.authMethod = authMethod;
+ }
+
+ public void setNeedSubjectPrincipalSubstitution(String needSubjectPrincipalSubstitutionVal) {
+ this.needSubjectPrincipalSubstitution = Boolean.valueOf(needSubjectPrincipalSubstitutionVal);
+ }
+
+ /**
+ * Set this if you want to override the default {@link SubjectSecurityInteraction}
+ *
+ * @param subjectRetrieverClassName
+ */
+ public void setSubjectInteractionClassName(String subjectRetrieverClassName) {
+ this.subjectInteractionClassName = subjectRetrieverClassName;
+ }
+
+ /**
+ *
Actually performs the authentication. Subclasses should call this method when implementing the AuthenticatorBase.authenticate method.
+ *
This method was created to allow different signatures for the AuthenticatorBase.authenticate method according with the catalina version.
Subclasses should override this method to register an authenticated Principal.
+ *
+ * @param request
+ * @param response
+ * @param principal
+ * @param password
+ */
+ protected abstract void doRegister(Request request, Response response, Principal principal, String password);
+
+ protected Principal getSubjectPrincipal() {
+ if (subjectInteraction == null) {
+ Class> clazz = loadClass(getClass(), subjectInteractionClassName);
+ try {
+ subjectInteraction = (SubjectSecurityInteraction) clazz.newInstance();
+ subjectInteraction.setSecurityDomain(context.getRealm().getContainer().getName());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Subject subject = subjectInteraction.get();
+ if (subject != null) {
+ Set principals = subject.getPrincipals();
+ if (!principals.isEmpty()) {
+ return subject.getPrincipals().iterator().next();
+ }
+ }
+ return null;
+ }
+
+ Class> loadClass(final Class> theClass, final String fqn) {
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ public Class> run() {
+ ClassLoader classLoader = theClass.getClassLoader();
+
+ Class> clazz = loadClass(classLoader, fqn);
+ if (clazz == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ clazz = loadClass(classLoader, fqn);
+ }
+ return clazz;
+ }
+ });
+ }
+
+ Class> loadClass(final ClassLoader cl, final String fqn) {
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ public Class> run() {
+ try {
+ return cl.loadClass(fqn);
+ } catch (ClassNotFoundException e) {
+ }
+ return null;
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SSLValve.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SSLValve.java
new file mode 100644
index 000000000..863bb30e0
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SSLValve.java
@@ -0,0 +1,103 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import jakarta.servlet.ServletException;
+
+import org.apache.catalina.valves.ValveBase;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+
+/**
+ * Valve to fill the SSL information in the request
+ * mod_header is used to fill the headers and the valve
+ * will fill the parameters of the request.
+ * In httpd.conf add the following:
+ *
+ *
+ * RequestHeader set SSL_CLIENT_CERT "%{SSL_CLIENT_CERT}s"
+ * RequestHeader set SSL_CIPHER "%{SSL_CIPHER}s"
+ * RequestHeader set SSL_SESSION_ID "%{SSL_SESSION_ID}s"
+ * RequestHeader set SSL_CIPHER_USEKEYSIZE "%{SSL_CIPHER_USEKEYSIZE}s"
+ *
+ *
+ * Visit: https://community.jboss.org/wiki/SSLModproxyForwarding
+ *
+ * @author Jean-Frederic Clere
+ * @author Anil Saldhana
+ * @since November 07, 2013
+ */
+public class SSLValve extends ValveBase{
+ protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException {
+
+ // mod_header converts the '\n' into ' ' so we have to rebuild the client certificate
+ String strcert0 = request.getHeader("ssl_client_cert");
+
+ if (isNotNull(strcert0)) {
+
+ String strcert1 = strcert0.replace(' ', '\n');
+ String strcert2 = strcert1.substring(28, strcert1.length()-26);
+ String strcert3 = new String("-----BEGIN CERTIFICATE-----\n");
+ String strcert4 = strcert3.concat(strcert2);
+ String strcerts = strcert4.concat("\n-----END CERTIFICATE-----\n");
+
+ // ByteArrayInputStream bais = new ByteArrayInputStream(strcerts.getBytes("UTF-8"));
+ ByteArrayInputStream bais = new ByteArrayInputStream(strcerts.getBytes());
+ X509Certificate jsseCerts[] = null;
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(bais);
+ jsseCerts = new X509Certificate[1];
+ jsseCerts[0] = cert;
+ } catch (CertificateException certificateException) {
+ logger.error("SSLValve failed :" + strcerts);
+ logger.error(certificateException);
+ }
+ request.setAttribute("jakarta.servlet.request.X509Certificate", jsseCerts);
+ }
+ strcert0 = request.getHeader("ssl_cipher");
+ if (isNotNull(strcert0)) {
+ request.setAttribute("jakarta.servlet.request.cipher_suite", strcert0);
+ }
+ strcert0 = request.getHeader("ssl_session_id");
+ if (isNotNull(strcert0)) {
+ request.setAttribute("jakarta.servlet.request.ssl_session", strcert0);
+ }
+ strcert0 = request.getHeader("ssl_cipher_usekeysize");
+ if (isNotNull(strcert0)) {
+ request.setAttribute("jakarta.servlet.request.key_size", strcert0);
+ }
+ getNext().invoke(request, response);
+ }
+
+ private boolean isNotNull(String str) {
+ return str != null && !"".equals(str.trim());
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SecurityActions.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SecurityActions.java
new file mode 100644
index 000000000..9e5cb808d
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SecurityActions.java
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Privileged Blocks
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Dec 9, 2008
+ */
+class SecurityActions {
+
+ /**
+ *
+ * Loads a {@link Class} using the fullQualifiedName supplied. This method tries first to load from the
+ * specified {@link Class}, if not found it will try to load from using TCL.
+ *
+ * Loads a class from the specified {@link ClassLoader} using the fullQualifiedName supplied.
+ *
+ *
+ * @param classLoader
+ * @param fullQualifiedName
+ * @return
+ */
+ static Class> loadClass(final ClassLoader classLoader, final String fullQualifiedName) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ public Class> run() {
+ try {
+ return classLoader.loadClass(fullQualifiedName);
+ } catch (ClassNotFoundException e) {
+ }
+ return null;
+ }
+ });
+ } else {
+ try {
+ return classLoader.loadClass(fullQualifiedName);
+ } catch (ClassNotFoundException e) {
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Get a system property
+ *
+ * @param key the key for the property
+ * @param defaultValue A default value to return if the property is not set (Can be null)
+ * @return
+ */
+ static String getProperty(final String key, final String defaultValue) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public String run() {
+ return System.getProperty(key, defaultValue);
+ }
+ });
+ } else {
+ return System.getProperty(key, defaultValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SubjectSecurityInteraction.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SubjectSecurityInteraction.java
new file mode 100644
index 000000000..5cb5a26ef
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SubjectSecurityInteraction.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.security.Principal;
+
+import javax.security.auth.Subject;
+
+/**
+ * Interface to retrieve a subject
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Sep 13, 2011
+ */
+public interface SubjectSecurityInteraction {
+ /**
+ * Obtain a subject based on implementation
+ *
+ * @return
+ */
+ Subject get();
+
+ /**
+ * Clean up the {@link Principal} from the security cache
+ *
+ * @param principal
+ * @return
+ */
+ boolean cleanup(Principal principal);
+
+ /**
+ *
Sets the security domain name
+ *
+ * @param securityDomain
+ */
+ void setSecurityDomain(String securityDomain);
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatAttributeManager.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatAttributeManager.java
new file mode 100644
index 000000000..00470c117
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatAttributeManager.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.picketlink.identity.federation.core.interfaces.AttributeManager;
+
+/**
+ * An implementation of attribute manager to get attributes of an identity
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Aug 31, 2009
+ */
+public class TomcatAttributeManager implements AttributeManager {
+ /**
+ * @see AttributeManager#getAttributes(Principal, List)
+ */
+ public Map getAttributes(Principal userPrincipal, List attributeKeys) {
+ return new HashMap();
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatRoleGenerator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatRoleGenerator.java
new file mode 100644
index 000000000..112d9338f
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatRoleGenerator.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.identity.federation.core.interfaces.RoleGenerator;
+
+/**
+ * Generate roles from Tomcat Principal
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Jan 21, 2009
+ */
+public class TomcatRoleGenerator implements RoleGenerator {
+
+ private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ /**
+ * @see RoleGenerator#generateRoles(Principal)
+ * @throws IllegalArgumentException if principal is not of type GenericPrincipal or User
+ */
+ public List generateRoles(Principal principal) {
+ String className = principal.getClass().getCanonicalName();
+
+ if (principal instanceof GenericPrincipal == false && principal instanceof User == false)
+ throw logger.wrongTypeError("principal is not tomcat principal:" + className);
+ List userRoles = new ArrayList();
+
+ if (principal instanceof GenericPrincipal) {
+ GenericPrincipal gp = (GenericPrincipal) principal;
+ String[] roles = gp.getRoles();
+ if (roles.length > 0)
+ userRoles.addAll(Arrays.asList(roles));
+ } else if (principal instanceof User) {
+ User tomcatUser = (User) principal;
+ Iterator> iter = tomcatUser.getRoles();
+ while (iter.hasNext()) {
+ Role tomcatRole = (Role) iter.next();
+ userRoles.add(tomcatRole.getRolename());
+ }
+ }
+ return userRoles;
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractAccountChooserValve.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractAccountChooserValve.java
new file mode 100644
index 000000000..78ce6625b
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractAccountChooserValve.java
@@ -0,0 +1,333 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.util.StringUtil;
+import org.picketlink.identity.federation.bindings.tomcat.sp.plugins.PropertiesAccountMapProvider;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+
+/**
+ * PLINK-344: Account Chooser At the Service Provider to enable redirection to
+ * the appropriate IDP
+ *
+ * @author Anil Saldhana
+ * @since January 21, 2014
+ */
+public abstract class AbstractAccountChooserValve extends ValveBase implements Lifecycle {
+ protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ public static final String ACCOUNT_CHOOSER_COOKIE_NAME = "picketlink.account.name";
+
+ public static final String ACCOUNT_PARAMETER = "idp";
+
+ public static final String AUTHENTICATING = "AUTHENTICATING";
+
+ public static final String STATE = "STATE";
+
+ /**
+ * Domain Name to be used in the cookie that is sent out
+ */
+ protected String domainName;
+
+ protected String accountChooserPage = "/accountChooser.html";
+
+ protected ConcurrentHashMap idpMap = new ConcurrentHashMap<>();
+
+ private String accountIDPMapProviderName = PropertiesAccountMapProvider.class.getName();
+
+ protected AccountIDPMapProvider accountIDPMapProvider;
+
+ /**
+ * Sets the account chooser cookie expiry. By default, we choose -1 which
+ * means cookie exists for the remainder of the browser session.
+ */
+ protected int cookieExpiry = -1;
+
+ @Override
+ public void initInternal() throws LifecycleException {
+ super.initInternal();
+ try {
+ Class> clazz = SecurityActions.loadClass(getClass(), this.accountIDPMapProviderName);
+
+ if (clazz == null) {
+ throw logger.classNotLoadedError(this.accountIDPMapProviderName);
+ }
+
+ accountIDPMapProvider = (AccountIDPMapProvider) clazz.newInstance();
+
+ Context context = (Context) getContainer();
+ accountIDPMapProvider.setServletContext(context.getServletContext());
+ idpMap.putAll(accountIDPMapProvider.getIDPMap());
+ } catch (Exception e) {
+ throw new LifecycleException("Could not start " + getClass().getName() + ".", e);
+ }
+ }
+
+ /**
+ * Set the domain name for the cookie to be sent to the browser There is no
+ * default. Setting the domain name for the cookie is optional.
+ *
+ * @param domainName
+ */
+ public void setDomainName(String domainName) {
+ this.domainName = domainName;
+ }
+
+ /**
+ * Set the cookie expiry in seconds. Default value is -1
+ *
+ * @param value
+ */
+ public void setCookieExpiry(String value) {
+ try {
+ int expiry = Integer.parseInt(value);
+ cookieExpiry = expiry;
+ } catch (NumberFormatException nfe) {
+ logger.processingError(nfe);
+ }
+ }
+
+ /**
+ * Set the fully qualified name of the implementation of
+ * {@link org.picketlink.identity.federation.bindings.tomcat.sp.AbstractAccountChooserValve.AccountIDPMapProvider}
+ * Default:
+ * {@link org.picketlink.identity.federation.bindings.tomcat.sp.plugins.PropertiesAccountMapProvider}
+ *
+ * @param idpMapProviderName
+ */
+ public void setAccountIDPMapProvider(String idpMapProviderName) {
+ this.accountIDPMapProviderName = idpMapProviderName;
+ }
+
+ /**
+ * Set the name of the html or jsp page that has the accounts for the user to
+ * choose. Default: "/accountChooser.html" is used
+ *
+ * @param pageName
+ */
+ public void setAccountChooserPage(String pageName) {
+ this.accountChooserPage = pageName;
+ }
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException {
+ Session session = request.getSessionInternal();
+
+ if (idpMap.isEmpty()) {
+ idpMap.putAll(accountIDPMapProvider.getIDPMap());
+ }
+
+ String sessionState = (String) session.getNote(STATE);
+
+ String idpChosenKey = request.getParameter(ACCOUNT_PARAMETER);
+ String cookieValue = cookieValue(request);
+ if (cookieValue != null || AUTHENTICATING.equals(sessionState)) {
+ if (idpChosenKey != null) {
+ String chosenIDP = idpMap.get(idpChosenKey);
+ request.setAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP, chosenIDP);
+ }
+
+ // Case when user is directed to IDP and wants to change the IDP. So he
+ // enters the URL again
+ if (AUTHENTICATING.equals(sessionState) && request.getParameter(GeneralConstants.SAML_RESPONSE_KEY) == null) {
+ session.removeNote(STATE);
+ redirectToChosenPage(accountChooserPage, request, response);
+ return;
+ }
+ proceedToAuthentication(request, response, cookieValue);
+ } else {
+ if (idpChosenKey != null) {
+ String chosenIDP = idpMap.get(idpChosenKey);
+ if (chosenIDP != null) {
+ request.setAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP, chosenIDP);
+ session.setNote(STATE, AUTHENTICATING);
+ proceedToAuthentication(request, response, idpChosenKey);
+ } else {
+ logger.configurationFileMissing(":IDP Mapping");
+ throw new ServletException();
+ }
+ } else {
+ // redirect to provided html
+ // saveRequest(request, request.getSessionInternal());
+ redirectToChosenPage(accountChooserPage, request, response);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Proceed to the Service Provider Authentication Mechanism
+ *
+ * @param request
+ * @param response
+ * @param cookieValue
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void proceedToAuthentication(Request request, Response response, String cookieValue) throws IOException,
+ ServletException {
+ Session session = request.getSessionInternal(false);
+ try {
+ /*
+ * String sessionState = (String) session.getNote(STATE); // Case when
+ * user is directed to IDP and wants to change the IDP. So he enters the
+ * URL again if (AUTHENTICATING.equals(sessionState) &&
+ * request.getParameter(GeneralConstants.SAML_RESPONSE_KEY) == null) {
+ * session.removeNote(STATE);
+ * redirectToChosenPage(accountConfirmationPage, request, response);
+ * return; }
+ */
+ getNext().invoke(request, response);
+ } finally {
+ String state = session != null ? (String) session.getNote(STATE) : null;
+
+ // If we are authenticated and registered at the service provider
+ if (request.getUserPrincipal() != null && StringUtil.isNotNull(state)) {
+ session.removeNote(STATE);
+ // Send back a cookie
+ Context context = (Context) getContainer();
+ String contextpath = context.getPath();
+
+ if (cookieValue == null) {
+ cookieValue = request.getParameter(AbstractAccountChooserValve.ACCOUNT_PARAMETER);
+ }
+
+ Cookie cookie = new Cookie(ACCOUNT_CHOOSER_COOKIE_NAME, cookieValue);
+ cookie.setPath(contextpath);
+ cookie.setMaxAge(cookieExpiry);
+ if (domainName != null) {
+ cookie.setDomain(domainName);
+ }
+ response.addCookie(cookie);
+ }
+ }
+ }
+
+ /**
+ * Redirect user to a page
+ *
+ * @param page
+ * @param request
+ * @param response
+ * @throws ServletException
+ * @throws IOException
+ */
+ protected void redirectToChosenPage(String page, Request request, Response response) throws ServletException, IOException {
+ Context context = (Context) getContainer();
+ RequestDispatcher requestDispatcher = context.getServletContext().getRequestDispatcher(page);
+ if (requestDispatcher != null) {
+ requestDispatcher.forward(request.getRequest(), response);
+ }
+ }
+
+ protected String cookieValue(Request request) {
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ String cookieName = cookie.getName();
+ String cookieDomain = cookie.getDomain();
+ if (cookieDomain != null && cookieDomain.equalsIgnoreCase(domainName)) {
+ // Found a cookie with the same domain name
+ if (ACCOUNT_CHOOSER_COOKIE_NAME.equals(cookieName)) {
+ // Found cookie
+ String cookieValue = cookie.getValue();
+ String chosenIDP = idpMap.get(cookieValue);
+ if (chosenIDP != null) {
+ request.setAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP, chosenIDP);
+ return cookieValue;
+ }
+ }
+ } else {
+ if (ACCOUNT_CHOOSER_COOKIE_NAME.equals(cookieName)) {
+ // Found cookie
+ String cookieValue = cookie.getValue();
+ String chosenIDP = idpMap.get(cookieValue);
+ if (chosenIDP != null) {
+ request.setAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP, chosenIDP);
+ return cookieValue;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Save the original request information into our session.
+ *
+ * @param request The request to be saved
+ * @param session The session to contain the saved information
+ * @throws IOException
+ */
+ protected abstract void saveRequest(Request request, Session session) throws IOException;
+
+ /**
+ * Restore the original request from information stored in our session. If the
+ * original request is no longer present (because the session timed out),
+ * return false; otherwise, return true.
+ *
+ * @param request The request to be restored
+ * @param session The session containing the saved information
+ */
+ protected abstract boolean restoreRequest(Request request, Session session) throws IOException;
+
+ /**
+ * Interface for obtaining the Identity Provider Mapping
+ */
+ public interface AccountIDPMapProvider {
+ /**
+ * Set the servlet context for resources on web classpath
+ *
+ * @param servletContext
+ */
+ void setServletContext(ServletContext servletContext);
+
+ /**
+ * Set a {@link java.lang.ClassLoader} for the Provider
+ *
+ * @param classLoader
+ */
+ void setClassLoader(ClassLoader classLoader);
+
+ /**
+ * Get a map of AccountName versus IDP URLs
+ *
+ * @return
+ */
+ Map getIDPMap() throws IOException;
+ }
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSAML11SPRedirectFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSAML11SPRedirectFormAuthenticator.java
new file mode 100644
index 000000000..a5f7497ac
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSAML11SPRedirectFormAuthenticator.java
@@ -0,0 +1,134 @@
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.picketlink.common.ErrorCodes;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.identity.federation.bindings.tomcat.sp.holder.ServiceProviderSAMLContext;
+import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.util.AssertionUtil;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11AssertionType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11AuthenticationStatementType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11StatementAbstractType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectType;
+import org.picketlink.identity.federation.saml.v1.protocol.SAML11ResponseType;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
+import org.picketlink.identity.federation.web.util.ServerDetector;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.picketlink.common.util.StringUtil.isNotNull;
+
+/**
+ * Authenticator for SAML 1.1 processing at the Service Provider
+ * @author anil saldhana
+ * @since Jul 7, 2011
+ */
+public abstract class AbstractSAML11SPRedirectFormAuthenticator extends AbstractSPFormAuthenticator {
+
+ @Override
+ public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
+ if (handleSAML11UnsolicitedResponse(request, response, this)) {
+ return true;
+ }
+
+ logger.trace("Falling back on local Form Authentication if available");
+ // fallback
+ return super.authenticate(request, response);
+ }
+
+ public static boolean handleSAML11UnsolicitedResponse(Request request, HttpServletResponse response, AbstractSPFormAuthenticator formAuthenticator) throws IOException {
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+
+ Principal principal = request.getUserPrincipal();
+
+ // If we have already authenticated the user and there is no request from IDP or logout from user
+ if (principal != null)
+ return true;
+
+ Session session = request.getSessionInternal(true);
+
+ // See if we got a response from IDP
+ if (isNotNull(samlResponse)) {
+ boolean isValid = false;
+ try {
+ isValid = formAuthenticator.validate(request);
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw new IOException();
+ }
+ if (!isValid)
+ throw new IOException(ErrorCodes.VALIDATION_CHECK_FAILED);
+
+ try {
+ InputStream base64DecodedResponse = RedirectBindingUtil.base64DeflateDecode(samlResponse);
+ SAMLParser parser = new SAMLParser();
+ SAML11ResponseType saml11Response = (SAML11ResponseType) parser.parse(base64DecodedResponse);
+
+ List assertions = saml11Response.get();
+ if (assertions.size() > 1) {
+ logger.trace("More than one assertion from IDP. Considering the first one.");
+ }
+ String username = null;
+ List roles = new ArrayList();
+ SAML11AssertionType assertion = assertions.get(0);
+ if (assertion != null) {
+ // Get the subject
+ List statements = assertion.getStatements();
+ for (SAML11StatementAbstractType statement : statements) {
+ if (statement instanceof SAML11AuthenticationStatementType) {
+ SAML11AuthenticationStatementType subStat = (SAML11AuthenticationStatementType) statement;
+ SAML11SubjectType subject = subStat.getSubject();
+ username = subject.getChoice().getNameID().getValue();
+ }
+ }
+ roles = AssertionUtil.getRoles(assertion, null);
+ }
+
+ String password = ServiceProviderSAMLContext.EMPTY_PASSWORD;
+
+ // Map to JBoss specific principal
+ if ((new ServerDetector()).isJboss() || formAuthenticator.jbossEnv) {
+ // Push a context
+ ServiceProviderSAMLContext.push(username, roles);
+ principal = formAuthenticator.getContext().getRealm().authenticate(username, password);
+ ServiceProviderSAMLContext.clear();
+ } else {
+ // tomcat env
+ SPUtil spUtil = new SPUtil();
+ principal = spUtil.createGenericPrincipal(request, username, roles);
+ }
+
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+
+ if (formAuthenticator.saveRestoreRequest) {
+ formAuthenticator.restoreRequest(request, session);
+ }
+ formAuthenticator.register(request, response, principal, FORM_METHOD, username, password);
+
+ return true;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ }
+ }
+
+ return false;
+ }
+
+ protected void startPicketLink() throws LifecycleException{
+ super.startPicketLink();
+ this.spConfiguration.setBindingType("REDIRECT");
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSPFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSPFormAuthenticator.java
new file mode 100644
index 000000000..6154aa660
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSPFormAuthenticator.java
@@ -0,0 +1,812 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import static org.picketlink.common.util.StringUtil.isNotNull;
+import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
+import static org.picketlink.identity.federation.bindings.tomcat.sp.AbstractSAML11SPRedirectFormAuthenticator.handleSAML11UnsolicitedResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.jboss.security.audit.AuditLevel;
+import org.picketlink.common.ErrorCodes;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.exceptions.TrustKeyProcessingException;
+import org.picketlink.common.exceptions.fed.AssertionExpiredException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.common.util.StringUtil;
+import org.picketlink.config.federation.AuthPropertyType;
+import org.picketlink.config.federation.KeyProviderType;
+import org.picketlink.identity.federation.bindings.tomcat.sp.holder.ServiceProviderSAMLContext;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditEvent;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditEventType;
+import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
+import org.picketlink.identity.federation.core.saml.v2.holders.DestinationInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.web.core.HTTPContext;
+import org.picketlink.identity.federation.web.process.ServiceProviderBaseProcessor;
+import org.picketlink.identity.federation.web.process.ServiceProviderSAMLRequestProcessor;
+import org.picketlink.identity.federation.web.process.ServiceProviderSAMLResponseProcessor;
+import org.picketlink.identity.federation.web.util.HTTPRedirectUtil;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil.RedirectBindingUtilDestHolder;
+import org.picketlink.identity.federation.web.util.ServerDetector;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * Abstract class to be extended by Service Provider valves to handle SAML
+ * requests and responses.
+ *
+ * Indicates if the current request is a LocalLogout request.
+ *
+ *
+ * @param request
+ * @return
+ */
+ private boolean isLocalLogout(Request request) {
+ String lloStr = request.getParameter(GeneralConstants.LOCAL_LOGOUT);
+ return isNotNull(lloStr) && "true".equalsIgnoreCase(lloStr);
+ }
+
+ /**
+ * Handle the IDP Request
+ *
+ * @param request
+ * @param response
+ * @param loginConfig
+ * @return
+ * @throws IOException
+ */
+ private boolean handleSAMLRequest(Request request, HttpServletResponse response) throws IOException {
+ String samlRequest = request.getParameter(GeneralConstants.SAML_REQUEST_KEY);
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ try {
+ ServiceProviderSAMLRequestProcessor requestProcessor = new ServiceProviderSAMLRequestProcessor(
+ request.getMethod()
+ .equals("POST"),
+ this.serviceURL,
+ this.picketLinkConfiguration);
+ requestProcessor.setTrustKeyManager(keyManager);
+ boolean result = requestProcessor.process(samlRequest, httpContext, handlers, chainLock);
+
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
+ auditEvent.setType(PicketLinkAuditEventType.REQUEST_FROM_IDP);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+
+ // If response is already commited, we need to stop with processing of
+ // HTTP request
+ if (response.isCommitted() || response.isCommitted())
+ return false;
+
+ if (result)
+ return result;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+
+ return localAuthentication(request, response);
+ }
+
+ private Document toSAMLResponseDocument(String samlResponse, boolean isPostBinding) throws ParsingException {
+ InputStream dataStream = null;
+
+ if (isPostBinding) {
+ // deal with SAML response from IDP
+ dataStream = PostBindingUtil.base64DecodeAsStream(samlResponse);
+ } else {
+ // deal with SAML response from IDP
+ dataStream = RedirectBindingUtil.base64DeflateDecode(samlResponse);
+ }
+
+ try {
+ return DocumentUtil.getDocument(dataStream);
+ } catch (Exception e) {
+ logger.samlResponseFromIDPParsingFailed();
+ throw new ParsingException("", e);
+ }
+ }
+
+ /**
+ * Handle IDP Response
+ *
+ * @param request
+ * @param response
+ * @param loginConfig
+ * @return
+ * @throws IOException
+ */
+ private boolean handleSAMLResponse(Request request, HttpServletResponse response) throws IOException {
+ if (!super.validate(request)) {
+ throw new IOException(ErrorCodes.VALIDATION_CHECK_FAILED);
+ }
+
+ String samlVersion = getSAMLVersion(request);
+
+ if (!JBossSAMLConstants.VERSION_2_0.get().equals(samlVersion)) {
+ return handleSAML11UnsolicitedResponse(request, response, this);
+ }
+
+ return handleSAML2Response(request, response);
+ }
+
+ private boolean handleSAML2Response(Request request, HttpServletResponse response) throws IOException {
+ Session session = request.getSessionInternal(true);
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ Principal principal = request.getUserPrincipal();
+
+ boolean willSendRequest;// deal with SAML response from IDP
+
+ try {
+ ServiceProviderSAMLResponseProcessor responseProcessor =
+ new ServiceProviderSAMLResponseProcessor(request.getMethod()
+ .equals("POST"),
+ serviceURL,
+ this.picketLinkConfiguration);
+ if (auditHelper != null) {
+ responseProcessor.setAuditHelper(auditHelper);
+ }
+
+ responseProcessor.setTrustKeyManager(keyManager);
+
+ SAML2HandlerResponse saml2HandlerResponse = responseProcessor.process(samlResponse,
+ httpContext,
+ handlers,
+ chainLock);
+
+ Document samlResponseDocument = saml2HandlerResponse.getResultingDocument();
+ String relayState = saml2HandlerResponse.getRelayState();
+
+ String destination = saml2HandlerResponse.getDestination();
+
+ willSendRequest = saml2HandlerResponse.getSendRequest();
+
+ String destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
+
+ if (destination != null && samlResponseDocument != null) {
+ sendRequestToIDP(destination,
+ samlResponseDocument,
+ relayState,
+ request,
+ response,
+ willSendRequest,
+ destinationQueryStringWithSignature);
+ } else {
+ // See if the session has been invalidated
+
+ boolean sessionValidity = session.isValid();
+
+ if (!sessionValidity) {
+ sendToLogoutPage(request, response, session);
+ return false;
+ }
+
+ // We got a response with the principal
+ List roles = saml2HandlerResponse.getRoles();
+ if (principal == null)
+ principal = (Principal) session.getSession().getAttribute(GeneralConstants.PRINCIPAL_ID);
+
+ String username = principal.getName();
+ String password = ServiceProviderSAMLContext.EMPTY_PASSWORD;
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Roles determined for username=" + username + "=" + Arrays.toString(roles.toArray()));
+ }
+
+ // Map to JBoss specific principal
+ if ((new ServerDetector()).isJboss() || jbossEnv) {
+ // Push a context
+ ServiceProviderSAMLContext.push(username, roles);
+ principal = context.getRealm().authenticate(username, password);
+ ServiceProviderSAMLContext.clear();
+ } else {
+ // tomcat env
+ principal = getGenericPrincipal(request, username, roles);
+ }
+
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
+ auditEvent.setType(PicketLinkAuditEventType.RESPONSE_FROM_IDP);
+ auditEvent.setSubjectName(username);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+
+ // Redirect the user to the originally requested URL
+ if (saveRestoreRequest) {
+ // Store the authenticated principal in the session.
+ session.setNote(FORM_PRINCIPAL_NOTE, principal);
+
+ // Redirect to the original URL. Note that this will trigger the
+ // authenticator again, but on resubmission we will look in the
+ // session notes to retrieve the authenticated principal and
+ // prevent reauthentication
+ String requestURI = savedRequestURL(session);
+
+ if (requestURI != null) {
+ logger.trace("Redirecting back to original Request URI: " + requestURI);
+ response.sendRedirect(response.encodeRedirectURL(requestURI));
+ }
+ }
+
+ register(request, response, principal, FORM_METHOD, username, password);
+ return true;
+ }
+ } catch (ProcessingException pe) {
+ Throwable t = pe.getCause();
+ if (t != null && t instanceof AssertionExpiredException) {
+ logger.error("Assertion has expired. Asking IDP for reissue");
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
+ auditEvent.setType(PicketLinkAuditEventType.EXPIRED_ASSERTION);
+ auditEvent.setAssertionID(((AssertionExpiredException) t).getId());
+ auditHelper.audit(auditEvent);
+ }
+ // Just issue a fresh request back to IDP
+ return generalUserRequest(request, response);
+ }
+ logger.samlSPHandleRequestError(pe);
+ throw logger.samlSPProcessingExceptionError(pe);
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+
+ return localAuthentication(request, response);
+ }
+
+ private String getSAMLVersion(Request request) {
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+ String version;
+
+ try {
+ Document samlDocument = toSAMLResponseDocument(samlResponse, "POST".equalsIgnoreCase(request.getMethod()));
+ Element element = samlDocument.getDocumentElement();
+
+ // let's try SAML 2.0 Version attribute first
+ version = element.getAttribute("Version");
+
+ if (isNullOrEmpty(version)) {
+ // fallback to SAML 1.1 Minor and Major attributes
+ String minorVersion = element.getAttribute("MinorVersion");
+ String majorVersion = element.getAttribute("MajorVersion");
+
+ version = minorVersion + "." + majorVersion;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Could not extract version from SAML Response.", e);
+ }
+
+ return version;
+ }
+
+ protected boolean isPOSTBindingResponse() {
+ return spConfiguration.isIdpUsesPostBinding();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.picketlink.identity.federation.bindings.tomcat.sp.BaseFormAuthenticator
+ * #getBinding()
+ */
+ @Override
+ protected String getBinding() {
+ return spConfiguration.getBindingType();
+ }
+
+ /**
+ * Handle the user invocation for the first time
+ *
+ * @param request
+ * @param response
+ * @param loginConfig
+ * @return
+ * @throws IOException
+ */
+ private boolean generalUserRequest(Request request, HttpServletResponse response) throws IOException {
+ Session session = request.getSessionInternal(true);
+ boolean willSendRequest = false;
+ HTTPContext httpContext = new HTTPContext(request, response, context.getServletContext());
+ Set handlers = chain.handlers();
+
+ boolean postBinding = spConfiguration.getBindingType().equals("POST");
+
+ // Neither saml request nor response from IDP
+ // So this is a user request
+ SAML2HandlerResponse saml2HandlerResponse = null;
+ try {
+ ServiceProviderBaseProcessor baseProcessor = new ServiceProviderBaseProcessor(postBinding,
+ serviceURL,
+ this.picketLinkConfiguration);
+ if (issuerID != null)
+ baseProcessor.setIssuer(issuerID);
+
+ // If the user has a different desired idp
+ String idp = (String) request.getAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP);
+ if (StringUtil.isNotNull(idp)) {
+ baseProcessor.setIdentityURL(idp);
+ } else {
+ baseProcessor.setIdentityURL(identityURL);
+ }
+ baseProcessor.setAuditHelper(auditHelper);
+
+ saml2HandlerResponse = baseProcessor.process(httpContext, handlers, chainLock);
+ } catch (ProcessingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ParsingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ConfigurationException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ }
+
+ willSendRequest = saml2HandlerResponse.getSendRequest();
+
+ Document samlResponseDocument = saml2HandlerResponse.getResultingDocument();
+ String relayState = saml2HandlerResponse.getRelayState();
+
+ String destination = saml2HandlerResponse.getDestination();
+ String destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
+
+ if (destination != null && samlResponseDocument != null) {
+ try {
+ if (saveRestoreRequest && !isGlobalLogout(request)) {
+ this.saveRequest(request, session);
+ }
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
+ auditEvent.setType(PicketLinkAuditEventType.REQUEST_TO_IDP);
+ auditEvent.setWhoIsAuditing(getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+ sendRequestToIDP(destination,
+ samlResponseDocument,
+ relayState,
+ request,
+ response,
+ willSendRequest,
+ destinationQueryStringWithSignature);
+ return false;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+ }
+
+ return localAuthentication(request, response);
+ }
+
+ /**
+ *
+ * Indicates if the SP is configure with HTTP POST Binding.
+ *
+ *
+ * @return
+ */
+ protected boolean isHttpPostBinding() {
+ return getBinding().equalsIgnoreCase("POST");
+ }
+
+ public Context getContext() {
+ return (Context) getContainer();
+ }
+
+ @Override
+ public boolean restoreRequest(Request request, Session session) throws IOException {
+ return super.restoreRequest(request, session);
+ }
+
+ /**
+ * Subclasses need to return the context path based on the capability of their
+ * servlet api
+ *
+ * @return
+ */
+ protected abstract String getContextPath();
+
+ protected Principal getGenericPrincipal(Request request, String username, List roles) {
+ return (new SPUtil()).createGenericPrincipal(request, username, roles);
+ }
+
+ private boolean isAjaxRequest(Request request) {
+ String requestedWithHeader = request.getHeader(GeneralConstants.HTTP_HEADER_X_REQUESTED_WITH);
+ return requestedWithHeader != null && "XMLHttpRequest".equalsIgnoreCase(requestedWithHeader);
+ }
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/BaseFormAuthenticator.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/BaseFormAuthenticator.java
new file mode 100644
index 000000000..1d38fbf7e
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/BaseFormAuthenticator.java
@@ -0,0 +1,717 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.picketlink.common.ErrorCodes;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.common.util.StringUtil;
+import org.picketlink.common.util.SystemPropertiesUtil;
+import org.picketlink.config.federation.PicketLinkType;
+import org.picketlink.config.federation.SPType;
+import org.picketlink.config.federation.handler.Handlers;
+import org.picketlink.identity.federation.api.saml.v2.metadata.MetaDataExtractor;
+import org.picketlink.identity.federation.core.audit.PicketLinkAuditHelper;
+import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
+import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.factories.SAML2HandlerChainFactory;
+import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerChainConfig;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChain;
+import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChainConfig;
+import org.picketlink.identity.federation.core.saml.v2.util.HandlerUtil;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
+import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
+import org.picketlink.identity.federation.web.config.AbstractSAMLConfigurationProvider;
+import org.picketlink.identity.federation.web.util.ConfigurationUtil;
+import org.picketlink.identity.federation.web.util.SAMLConfigurationProvider;
+import org.w3c.dom.Document;
+
+import static org.picketlink.common.constants.GeneralConstants.CONFIG_FILE_LOCATION;
+import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
+
+/**
+ * Base Class for Service Provider Form Authenticators
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Jun 9, 2009
+ */
+public abstract class BaseFormAuthenticator extends FormAuthenticator {
+ protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ protected boolean enableAudit = false;
+
+ protected PicketLinkAuditHelper auditHelper = null;
+
+ protected TrustKeyManager keyManager;
+
+ protected SPType spConfiguration = null;
+
+ protected PicketLinkType picketLinkConfiguration = null;
+
+ protected String serviceURL = null;
+
+ protected String identityURL = null;
+
+ protected String issuerID = null;
+
+ protected String configFile;
+
+ /**
+ * If the service provider is configured with an IDP metadata file, then this
+ * certificate can be picked up from the metadata
+ */
+ protected transient X509Certificate idpCertificate = null;
+
+ protected transient SAML2HandlerChain chain = null;
+
+ protected transient String samlHandlerChainClass = null;
+
+ protected Map chainConfigOptions = new HashMap<>();
+
+ // Whether the authenticator has to to save and restore request
+ protected boolean saveRestoreRequest = true;
+
+ /**
+ * A Lock for Handler operations in the chain
+ */
+ protected Lock chainLock = new ReentrantLock();
+
+ protected String canonicalizationMethod = CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS;
+
+ /**
+ * The user can inject a fully qualified name of a
+ * {@link SAMLConfigurationProvider}
+ */
+ protected SAMLConfigurationProvider configProvider = null;
+
+ /**
+ * Servlet3 related changes forced Tomcat to change the authenticate method
+ * signature in the FormAuthenticator. For now, we use reflection for forward
+ * compatibility. This has to be changed in future.
+ */
+ private Method theSuperRegisterMethod = null;
+
+ /**
+ * If it is determined that we are running in a Tomcat6/JBAS5 environment,
+ * there is no need to seek the super.register method that conforms to the
+ * servlet3 spec changes
+ */
+ private boolean seekSuperRegisterMethod = true;
+
+ protected int timerInterval = -1;
+
+ protected Timer timer = null;
+
+ public BaseFormAuthenticator() {
+ super();
+ }
+
+ protected String idpAddress = null;
+
+ /**
+ * If the request.getRemoteAddr is not exactly the IDP address that you have
+ * keyed in your deployment descriptor for keystore alias, you can set it here
+ * explicitly
+ */
+ public void setIdpAddress(String idpAddress) {
+ this.idpAddress = idpAddress;
+ }
+
+ /**
+ * Get the name of the configuration file
+ *
+ * @return
+ */
+ public String getConfigFile() {
+ return configFile;
+ }
+
+ /**
+ * Set the name of the configuration file
+ *
+ * @param configFile
+ */
+ public void setConfigFile(String configFile) {
+ this.configFile = configFile;
+ }
+
+ /**
+ * Set the SAML Handler Chain Class fqn
+ *
+ * @param samlHandlerChainClass
+ */
+ public void setSamlHandlerChainClass(String samlHandlerChainClass) {
+ this.samlHandlerChainClass = samlHandlerChainClass;
+ }
+
+ /**
+ * Set the service URL
+ *
+ * @param serviceURL
+ */
+ public void setServiceURL(String serviceURL) {
+ this.serviceURL = serviceURL;
+ }
+
+ /**
+ * Set whether the authenticator saves/restores the request during form
+ * authentication
+ *
+ * @param saveRestoreRequest
+ */
+ public void setSaveRestoreRequest(boolean saveRestoreRequest) {
+ this.saveRestoreRequest = saveRestoreRequest;
+ }
+
+ /**
+ * Set the {@link SAMLConfigurationProvider} fqn
+ *
+ * @param cp fqn of a {@link SAMLConfigurationProvider}
+ */
+ public void setConfigProvider(String cp) {
+ if (cp == null)
+ throw new IllegalStateException(ErrorCodes.NULL_ARGUMENT + cp);
+ Class> clazz = SecurityActions.loadClass(getClass(), cp);
+ if (clazz == null)
+ throw new RuntimeException(ErrorCodes.CLASS_NOT_LOADED + cp);
+ try {
+ configProvider = (SAMLConfigurationProvider) clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(ErrorCodes.CANNOT_CREATE_INSTANCE + cp + ":" + e.getMessage());
+ }
+ }
+
+ /**
+ * Set an instance of the {@link SAMLConfigurationProvider}
+ *
+ * @param configProvider
+ */
+ public void setConfigProvider(SAMLConfigurationProvider configProvider) {
+ this.configProvider = configProvider;
+ }
+
+ /**
+ * Get the {@link SPType}
+ *
+ * @return
+ */
+ public SPType getConfiguration() {
+ return spConfiguration;
+ }
+
+ /**
+ * Set a separate issuer id
+ *
+ * @param issuerID
+ */
+ public void setIssuerID(String issuerID) {
+ this.issuerID = issuerID;
+ }
+
+ /**
+ * Set the logout page
+ *
+ * @param logOutPage
+ */
+ public void setLogOutPage(String logOutPage) {
+ logger.warn("Option logOutPage is now configured with the PicketLinkSP element.");
+
+ }
+
+ /**
+ * Set the Timer Value to reload the configuration
+ *
+ * @param value an integer value that represents timer value (in miliseconds)
+ */
+ public void setTimerInterval(String value) {
+ if (StringUtil.isNotNull(value)) {
+ timerInterval = Integer.parseInt(value);
+ }
+ }
+
+ /**
+ * Perform validation os the request object
+ *
+ * @param request
+ * @return
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ protected boolean validate(Request request) {
+ return request.getParameter("SAMLResponse") != null;
+ }
+
+ /**
+ * Get the Identity URL
+ *
+ * @return
+ */
+ public String getIdentityURL() {
+ return identityURL;
+ }
+
+ /**
+ * Get the {@link X509Certificate} of the IDP if provided via the IDP metadata
+ * file
+ *
+ * @return {@link X509Certificate} or null
+ */
+ public X509Certificate getIdpCertificate() {
+ return idpCertificate;
+ }
+
+ /**
+ * This method is a hack!!! Tomcat on account of Servlet3 changed their
+ * authenticator method signatures We utilize Java Reflection to identify the
+ * super register method on the first call and save it. Subsquent invocations
+ * utilize the saved {@link Method}
+ *
+ * @see org.apache.catalina.authenticator.AuthenticatorBase#register(org.apache.catalina.connector.Request,
+ * org.apache.catalina.connector.Response, java.security.Principal,
+ * java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void register(Request request,
+ HttpServletResponse response,
+ Principal principal,
+ String arg3,
+ String arg4,
+ String arg5) {
+ // Try the JBossAS6 version
+ if (theSuperRegisterMethod == null && seekSuperRegisterMethod) {
+ Class>[] args = new Class[] { Request.class, HttpServletResponse.class, Principal.class, String.class,
+ String.class, String.class };
+ Class> superClass = getAuthenticatorBaseClass();
+ theSuperRegisterMethod = SecurityActions.getMethod(superClass, "register", args);
+ }
+ try {
+ if (theSuperRegisterMethod != null) {
+ Object[] callArgs = new Object[] { request, response, principal, arg3, arg4, arg5 };
+ theSuperRegisterMethod.invoke(this, callArgs);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // Try the older version
+ if (theSuperRegisterMethod == null) {
+ seekSuperRegisterMethod = false; // Don't try to seek super register
+ // method on next invocation
+ super.register(request, response, principal, arg3, arg4, arg5);
+ return;
+ }
+ }
+
+ /**
+ * Fall back on local authentication at the service provider side
+ *
+ * @param request
+ * @param response
+ * @param loginConfig
+ * @return
+ * @throws IOException
+ */
+ protected boolean localAuthentication(Request request, HttpServletResponse response) throws IOException {
+ if (request.getUserPrincipal() == null) {
+ logger.samlSPFallingBackToLocalFormAuthentication();// fallback
+ return super.authenticate(request, response);
+ } else
+ return true;
+ }
+
+ /**
+ * Return the SAML Binding that this authenticator supports
+ *
+ * @see {@link JBossSAMLURIConstants#SAML_HTTP_POST_BINDING}
+ * @see {@link JBossSAMLURIConstants#SAML_HTTP_REDIRECT_BINDING}
+ * @return
+ */
+ protected abstract String getBinding();
+
+ /**
+ * Attempt to process a metadata file available locally
+ */
+ protected void processIDPMetadataFile(String idpMetadataFile) {
+ ServletContext servletContext = context.getServletContext();
+ InputStream is = servletContext.getResourceAsStream(idpMetadataFile);
+ if (is == null)
+ return;
+
+ Object metadata = null;
+ try {
+ Document samlDocument = DocumentUtil.getDocument(is);
+ SAMLParser parser = new SAMLParser();
+ metadata = parser.parse(DocumentUtil.getNodeAsStream(samlDocument));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ IDPSSODescriptorType idpSSO = null;
+ if (metadata instanceof EntitiesDescriptorType) {
+ EntitiesDescriptorType entities = (EntitiesDescriptorType) metadata;
+ idpSSO = handleMetadata(entities);
+ } else {
+ idpSSO = handleMetadata((EntityDescriptorType) metadata);
+ }
+ if (idpSSO == null) {
+ logger.samlSPUnableToGetIDPDescriptorFromMetadata();
+ return;
+ }
+ List endpoints = idpSSO.getSingleSignOnService();
+ for (EndpointType endpoint : endpoints) {
+ String endpointBinding = endpoint.getBinding().toString();
+ if (endpointBinding.contains("HTTP-POST"))
+ endpointBinding = "POST";
+ else if (endpointBinding.contains("HTTP-Redirect"))
+ endpointBinding = "REDIRECT";
+ if (getBinding().equals(endpointBinding)) {
+ identityURL = endpoint.getLocation().toString();
+ break;
+ }
+ }
+ List keyDescriptors = idpSSO.getKeyDescriptor();
+ if (keyDescriptors.size() > 0) {
+ this.idpCertificate = MetaDataExtractor.getCertificate(keyDescriptors.get(0));
+ }
+ }
+
+ /**
+ * Process the configuration from the configuration file
+ */
+ @SuppressWarnings("deprecation")
+ protected void processConfiguration() {
+ ServletContext servletContext = context.getServletContext();
+ InputStream is = null;
+
+ if (isNullOrEmpty(this.configFile)) {
+ this.configFile = CONFIG_FILE_LOCATION;
+ is = servletContext.getResourceAsStream(this.configFile);
+ } else {
+ try {
+ is = new FileInputStream(this.configFile);
+ } catch (FileNotFoundException e) {
+ throw logger.samlIDPConfigurationError(e);
+ }
+ }
+
+ try {
+ // Work on the IDP Configuration
+ if (configProvider != null) {
+ try {
+ if (is == null) {
+ // Try the older version
+ is = servletContext.getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
+
+ // Additionally parse the deprecated config file
+ if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
+ ((AbstractSAMLConfigurationProvider) configProvider).setConfigFile(is);
+ }
+ } else {
+ // Additionally parse the consolidated config file
+ if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
+ ((AbstractSAMLConfigurationProvider) configProvider).setConsolidatedConfigFile(is);
+ }
+ }
+
+ picketLinkConfiguration = configProvider.getPicketLinkConfiguration();
+ spConfiguration = configProvider.getSPConfiguration();
+ } catch (ProcessingException e) {
+ throw logger.samlSPConfigurationError(e);
+ } catch (ParsingException e) {
+ throw logger.samlSPConfigurationError(e);
+ }
+ } else {
+ if (is != null) {
+ try {
+ picketLinkConfiguration = ConfigurationUtil.getConfiguration(is);
+ spConfiguration = (SPType) picketLinkConfiguration.getIdpOrSP();
+ } catch (ParsingException e) {
+ logger.trace(e);
+ throw logger.samlSPConfigurationError(e);
+ }
+ } else {
+ is = servletContext.getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
+ if (is == null)
+ throw logger.configurationFileMissing(configFile);
+ spConfiguration = ConfigurationUtil.getSPConfiguration(is);
+ }
+ }
+
+ if (this.picketLinkConfiguration != null) {
+ enableAudit = picketLinkConfiguration.isEnableAudit();
+
+ // See if we have the system property enabled
+ if (!enableAudit) {
+ String sysProp = SecurityActions.getSystemProperty(GeneralConstants.AUDIT_ENABLE, "NULL");
+ if (!"NULL".equals(sysProp)) {
+ enableAudit = Boolean.parseBoolean(sysProp);
+ }
+ }
+
+ if (enableAudit) {
+ if (auditHelper == null) {
+ String securityDomainName = PicketLinkAuditHelper.getSecurityDomainName(servletContext);
+
+ auditHelper = new PicketLinkAuditHelper(securityDomainName);
+ }
+ }
+ }
+
+ if (StringUtil.isNotNull(spConfiguration.getIdpMetadataFile())) {
+ processIDPMetadataFile(spConfiguration.getIdpMetadataFile());
+ } else {
+ this.identityURL = spConfiguration.getIdentityURL();
+ }
+ this.serviceURL = spConfiguration.getServiceURL();
+ this.canonicalizationMethod = spConfiguration.getCanonicalizationMethod();
+
+ logger.samlSPSettingCanonicalizationMethod(canonicalizationMethod);
+ XMLSignatureUtil.setCanonicalizationMethodType(canonicalizationMethod);
+
+ logger.trace("Identity Provider URL=" + this.identityURL);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected IDPSSODescriptorType handleMetadata(EntitiesDescriptorType entities) {
+ IDPSSODescriptorType idpSSO = null;
+
+ List entityDescs = entities.getEntityDescriptor();
+ for (Object entityDescriptor : entityDescs) {
+ if (entityDescriptor instanceof EntitiesDescriptorType) {
+ idpSSO = getIDPSSODescriptor(entities);
+ } else
+ idpSSO = handleMetadata((EntityDescriptorType) entityDescriptor);
+ if (idpSSO != null)
+ break;
+ }
+ return idpSSO;
+ }
+
+ protected IDPSSODescriptorType handleMetadata(EntityDescriptorType entityDescriptor) {
+ return CoreConfigUtil.getIDPDescriptor(entityDescriptor);
+ }
+
+ protected IDPSSODescriptorType getIDPSSODescriptor(EntitiesDescriptorType entities) {
+ List entityDescs = entities.getEntityDescriptor();
+ for (Object entityDescriptor : entityDescs) {
+
+ if (entityDescriptor instanceof EntitiesDescriptorType) {
+ return getIDPSSODescriptor((EntitiesDescriptorType) entityDescriptor);
+ }
+ return CoreConfigUtil.getIDPDescriptor((EntityDescriptorType) entityDescriptor);
+ }
+ return null;
+ }
+
+ protected void initializeHandlerChain() throws ConfigurationException, ProcessingException {
+ populateChainConfig();
+ SAML2HandlerChainConfig handlerChainConfig = new DefaultSAML2HandlerChainConfig(chainConfigOptions);
+
+ Set samlHandlers = chain.handlers();
+
+ for (SAML2Handler handler : samlHandlers) {
+ handler.initChainConfig(handlerChainConfig);
+ }
+ }
+
+ protected void populateChainConfig() throws ConfigurationException, ProcessingException {
+ chainConfigOptions.put(GeneralConstants.CONFIGURATION, spConfiguration);
+ chainConfigOptions.put(GeneralConstants.ROLE_VALIDATOR_IGNORE, "false"); // No
+ // validator
+ // as
+ // tomcat
+ // realm
+ // does
+ // validn
+
+ if (doSupportSignature()) {
+ chainConfigOptions.put(GeneralConstants.KEYPAIR, keyManager.getSigningKeyPair());
+ // If there is a need for X509Data in signedinfo
+ String certificateAlias = (String) keyManager.getAdditionalOption(GeneralConstants.X509CERTIFICATE);
+ if (certificateAlias != null) {
+ chainConfigOptions.put(GeneralConstants.X509CERTIFICATE, keyManager.getCertificate(certificateAlias));
+ }
+ }
+ }
+
+ protected void sendToLogoutPage(Request request, HttpServletResponse response, Session session) throws IOException,
+ ServletException {
+ // we are invalidated.
+ RequestDispatcher dispatch = context.getServletContext().getRequestDispatcher(this.getConfiguration().getLogOutPage());
+ if (dispatch == null)
+ logger.samlSPCouldNotDispatchToLogoutPage(this.getConfiguration().getLogOutPage());
+ else {
+ logger.trace("Forwarding request to logOutPage: " + this.getConfiguration().getLogOutPage());
+ session.expire();
+ try {
+ dispatch.forward(request, response);
+ } catch (Exception e) {
+ // JBAS5.1 and 6 quirkiness
+ dispatch.forward(request.getRequest(), response);
+ }
+ }
+ }
+
+ // Mock test purpose
+ public void testStart() throws LifecycleException {
+ this.saveRestoreRequest = false;
+ if (context == null)
+ throw new RuntimeException("Catalina Context not set up");
+ startPicketLink();
+ }
+
+ protected void startPicketLink() throws LifecycleException {
+ SystemPropertiesUtil.ensure();
+ Handlers handlers = null;
+
+ // Introduce a timer to reload configuration if desired
+ if (timerInterval > 0) {
+ if (timer == null) {
+ timer = new Timer();
+ }
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ // Clear the configuration
+ picketLinkConfiguration = null;
+ spConfiguration = null;
+
+ processConfiguration();
+ try {
+ initKeyProvider(context);
+ } catch (LifecycleException e) {
+ logger.trace(e.getMessage());
+ }
+ }
+ }, timerInterval, timerInterval);
+ }
+
+ // Get the chain from config
+ if (StringUtil.isNullOrEmpty(samlHandlerChainClass)) {
+ chain = SAML2HandlerChainFactory.createChain();
+ } else {
+ try {
+ chain = SAML2HandlerChainFactory.createChain(this.samlHandlerChainClass);
+ } catch (ProcessingException e1) {
+ throw new LifecycleException(e1);
+ }
+ }
+
+ ServletContext servletContext = context.getServletContext();
+
+ this.processConfiguration();
+
+ try {
+ if (picketLinkConfiguration != null) {
+ handlers = picketLinkConfiguration.getHandlers();
+ } else {
+ // Get the handlers
+ String handlerConfigFileName = GeneralConstants.HANDLER_CONFIG_FILE_LOCATION;
+ handlers = ConfigurationUtil.getHandlers(servletContext.getResourceAsStream(handlerConfigFileName));
+ }
+
+ chain.addAll(HandlerUtil.getHandlers(handlers));
+
+ this.initKeyProvider(context);
+ this.populateChainConfig();
+ this.initializeHandlerChain();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ if (this.picketLinkConfiguration == null) {
+ this.picketLinkConfiguration = new PicketLinkType();
+
+ this.picketLinkConfiguration.setIdpOrSP(getConfiguration());
+ this.picketLinkConfiguration.setHandlers(handlers);
+ }
+ }
+
+ /**
+ *
+ * Indicates if digital signatures/validation of SAML assertions are enabled.
+ * Subclasses that supports signature should override this method.
+ *
+ *
+ * @return
+ */
+ protected boolean doSupportSignature() {
+ if (spConfiguration != null) {
+ return spConfiguration.isSupportsSignature();
+ }
+ return false;
+ }
+
+ private Class> getAuthenticatorBaseClass() {
+ Class> myClass = getClass();
+ do {
+ myClass = myClass.getSuperclass();
+ } while (myClass != AuthenticatorBase.class);
+ return myClass;
+ }
+
+ protected abstract void initKeyProvider(Context context) throws LifecycleException;
+
+ public void setAuditHelper(PicketLinkAuditHelper auditHelper) {
+ this.auditHelper = auditHelper;
+ }
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SPUtil.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SPUtil.java
new file mode 100644
index 000000000..ea180f0a7
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SPUtil.java
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
+
+/**
+ * Common code useful for a SP
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Jan 9, 2009
+ */
+public class SPUtil {
+
+ private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+ /**
+ * Create a SAML2 auth request
+ *
+ * @param serviceURL URL of the service
+ * @param identityURL URL of the identity provider
+ * @return
+ * @throws ConfigurationException
+ */
+ public AuthnRequestType createSAMLRequest(String serviceURL, String identityURL) throws ConfigurationException {
+ if (serviceURL == null)
+ throw logger.nullArgumentError("serviceURL");
+ if (identityURL == null)
+ throw logger.nullArgumentError("identityURL");
+
+ SAML2Request saml2Request = new SAML2Request();
+ String id = IDGenerator.create("ID_");
+ return saml2Request.createAuthnRequestType(id, serviceURL, identityURL, serviceURL);
+ }
+
+ /**
+ * Create an instance of the {@link GenericPrincipal}
+ * @param request
+ * @param username
+ * @param roles
+ * @return
+ */
+ public Principal createGenericPrincipal(Request request, String username, List roles) {
+ Context ctx = request.getContext();
+ return new GenericPrincipal(username, roles);
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SecurityActions.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SecurityActions.java
new file mode 100644
index 000000000..263907781
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/SecurityActions.java
@@ -0,0 +1,156 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp;
+
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Privileged Blocks
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Dec 9, 2008
+ */
+class SecurityActions {
+
+ /**
+ *
+ * Loads a {@link Class} using the fullQualifiedName supplied. This method tries first to load from the
+ * specified {@link Class}, if not found it will try to load from using TCL.
+ *
Returns a system property value using the specified key. If not found the defaultValue will be returned.
+ *
+ * @param key
+ * @param defaultValue
+ * @return
+ */
+ static String getSystemProperty(final String key, final String defaultValue) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public String run() {
+ return System.getProperty(key, defaultValue);
+ }
+ });
+ } else {
+ return System.getProperty(key, defaultValue);
+ }
+ }
+
+ /**
+ * Use reflection to get the {@link Method} on a {@link Class} with the given parameter types
+ *
+ * @param clazz
+ * @param methodName
+ * @param parameterTypes
+ * @return
+ */
+ static Method getMethod(final Class> clazz, final String methodName, final Class>[] parameterTypes) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public Method run() {
+ try {
+ return clazz.getDeclaredMethod(methodName, parameterTypes);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ });
+ } else {
+ try {
+ return clazz.getDeclaredMethod(methodName, parameterTypes);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/holder/ServiceProviderSAMLContext.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/holder/ServiceProviderSAMLContext.java
new file mode 100644
index 000000000..63afb104b
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/holder/ServiceProviderSAMLContext.java
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp.holder;
+
+import org.picketlink.identity.federation.core.constants.PicketLinkFederationConstants;
+
+import java.util.List;
+
+/**
+ * A context of username/roles to be used by login modules
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Feb 13, 2009
+ */
+public class ServiceProviderSAMLContext {
+ public static final String EMPTY_PASSWORD = "EMPTY_STR";
+
+ private static ThreadLocal username = new ThreadLocal();
+ private static ThreadLocal> userRoles = new ThreadLocal>();
+
+ public static void push(String user, List roles) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(PicketLinkFederationConstants.RUNTIME_PERMISSION_CORE);
+ }
+ username.set(user);
+ userRoles.set(roles);
+ }
+
+ public static void clear() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(PicketLinkFederationConstants.RUNTIME_PERMISSION_CORE);
+ }
+ username.remove();
+ userRoles.remove();
+ }
+
+ public static String getUserName() {
+ return username.get();
+ }
+
+ public static List getRoles() {
+ return userRoles.get();
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/plugins/PropertiesAccountMapProvider.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/plugins/PropertiesAccountMapProvider.java
new file mode 100644
index 000000000..e8dcc533d
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/plugins/PropertiesAccountMapProvider.java
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+package org.picketlink.identity.federation.bindings.tomcat.sp.plugins;
+
+import org.picketlink.identity.federation.bindings.tomcat.sp.AbstractAccountChooserValve;
+
+import jakarta.servlet.ServletContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Implementation of
+ * {@link org.picketlink.identity.federation.bindings.tomcat.sp.AbstractAccountChooserValve.AccountIDPMapProvider} using a
+ * properties file
+ *
+ * @author Anil Saldhana
+ * @since January 23, 2014
+ */
+public class PropertiesAccountMapProvider implements AbstractAccountChooserValve.AccountIDPMapProvider {
+ private ClassLoader classLoader = null;
+
+ private ServletContext servletContext = null;
+
+ public static final String PROP_FILE_NAME = "idpmap.properties";
+
+ public static final String WEB_INF_PROP_FILE_NAME = "/WEB-INF/idpmap.properties";
+
+ @Override
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public void setServletContext(ServletContext servletContext){
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public Map getIDPMap() throws IOException {
+ Map idpmap = new HashMap();
+
+ InputStream inputStream = null;
+
+ Properties properties = new Properties();
+ if (classLoader != null) {
+ inputStream = classLoader.getResourceAsStream(PROP_FILE_NAME);
+ }
+ if (inputStream == null && servletContext != null) {
+ inputStream = servletContext.getResourceAsStream(PROP_FILE_NAME);
+ }
+ if (inputStream == null && servletContext != null) {
+ inputStream = servletContext.getResourceAsStream(WEB_INF_PROP_FILE_NAME);
+ }
+ if(inputStream == null){
+ inputStream = getClass().getResourceAsStream(PROP_FILE_NAME);
+ }
+ if (inputStream != null) {
+ properties.load(inputStream);
+ if (properties != null) {
+ Set keyset = properties.keySet();
+ for (Object key : keyset) {
+ idpmap.put((String) key, (String) properties.get(key));
+ }
+ }
+ }
+ return idpmap;
+ }
+}
\ No newline at end of file
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/ModuleUtils.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/ModuleUtils.java
new file mode 100644
index 000000000..911fe4272
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/ModuleUtils.java
@@ -0,0 +1,35 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.
+ */
+
+package org.picketlink.identity.federation.bindings.util;
+
+import org.picketlink.identity.federation.core.wstrust.STSClientConfig;
+
+/**
+ * Placeholder for utility class to work with modules.
+ *
+ * @author Peter Skopek
+ *
+ */
+public class ModuleUtils {
+
+ public static String getCurrentModuleId() {
+ return STSClientConfig.NO_MODULE;
+ }
+
+}
diff --git a/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/SecurityActions.java b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/SecurityActions.java
new file mode 100644
index 000000000..94c322eed
--- /dev/null
+++ b/sso-saml-plugin/src/main/java/org/picketlink/identity/federation/bindings/util/SecurityActions.java
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.picketlink.identity.federation.bindings.util;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Privileged Blocks
+ *
+ * @author Anil.Saldhana@redhat.com
+ * @since Dec 9, 2008
+ */
+class SecurityActions {
+ /**
+ *
Returns a system property value using the specified key. If not found the defaultValue will be returned.
Checks if the given {@link javax.servlet.http.HttpServletRequest} containes a SAML11 Target parameter. Usually this indicates that the given request is
++ *
Checks if the given {@link jakarta.servlet.http.HttpServletRequest} containes a SAML11 Target parameter. Usually this indicates that the given request is
+ * a SAML11 request.