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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractGenericHeaderAuthenticator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractGenericHeaderAuthenticator.java
new file mode 100644
index 000000000..5f2008726
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractPicketLinkAuthenticator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/AbstractPicketLinkAuthenticator.java
new file mode 100644
index 000000000..48d4074a1
--- /dev/null
+++ b/picketlink-saml/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.
+ *
+ * @param request
+ * @param response
+ * @param loginConfig
+ * @return
+ * @throws IOException
+ */
+ protected boolean performAuthentication(Request request, Response response, LoginConfig loginConfig) throws IOException {
+ logger.trace("Authenticating user");
+
+ Principal principal = request.getUserPrincipal();
+ if (principal != null) {
+ logger.trace("Already authenticated '" + principal.getName() + "'");
+ return true;
+ }
+
+ Session session = request.getSessionInternal(true);
+ String userName = UUID.randomUUID().toString();
+ String password = userName;
+ Realm realm = context.getRealm();
+
+ principal = realm.authenticate(userName, password);
+ Principal originalPrincipal = principal;
+
+ if (principal != null) {
+ if (needSubjectPrincipalSubstitution) {
+ principal = getSubjectPrincipal();
+ if (principal == null)
+ throw new RuntimeException("Principal from subject is null");
+ principal = realm.authenticate(principal.getName(), password);
+ }
+ session.setNote(Constants.SESS_USERNAME_NOTE, principal.getName());
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+ doRegister(request, response, principal, password);
+ if (originalPrincipal != null && needSubjectPrincipalSubstitution) {
+ subjectInteraction.cleanup(originalPrincipal);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SSLValve.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SSLValve.java
new file mode 100644
index 000000000..863bb30e0
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SecurityActions.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SecurityActions.java
new file mode 100644
index 000000000..9e5cb808d
--- /dev/null
+++ b/picketlink-saml/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.
+ *
+ *
+ * @param theClass
+ * @param fullQualifiedName
+ * @return
+ */
+ static Class> loadClass(final Class> theClass, final String fullQualifiedName) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ public Class> run() {
+ ClassLoader classLoader = theClass.getClassLoader();
+
+ Class> clazz = loadClass(classLoader, fullQualifiedName);
+ if (clazz == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ clazz = loadClass(classLoader, fullQualifiedName);
+ }
+ return clazz;
+ }
+ });
+ } else {
+ ClassLoader classLoader = theClass.getClassLoader();
+
+ Class> clazz = loadClass(classLoader, fullQualifiedName);
+ if (clazz == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ clazz = loadClass(classLoader, fullQualifiedName);
+ }
+ return clazz;
+ }
+ }
+
+ /**
+ *
+ * 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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SubjectSecurityInteraction.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/SubjectSecurityInteraction.java
new file mode 100644
index 000000000..5cb5a26ef
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatAttributeManager.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatAttributeManager.java
new file mode 100644
index 000000000..00470c117
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatRoleGenerator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/TomcatRoleGenerator.java
new file mode 100644
index 000000000..112d9338f
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractAccountChooserValve.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractAccountChooserValve.java
new file mode 100644
index 000000000..78ce6625b
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSAML11SPRedirectFormAuthenticator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSAML11SPRedirectFormAuthenticator.java
new file mode 100644
index 000000000..a5f7497ac
--- /dev/null
+++ b/picketlink-saml/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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSPFormAuthenticator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/AbstractSPFormAuthenticator.java
new file mode 100644
index 000000000..6154aa660
--- /dev/null
+++ b/picketlink-saml/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.
+ *
+ *
+ * @author Anil Saldhana
+ * @author Pedro Silva
+ */
+public abstract class AbstractSPFormAuthenticator extends BaseFormAuthenticator {
+
+ /**
+ * The previously authenticated principal (if caching is disabled).
+ */
+ public static final String FORM_PRINCIPAL_NOTE = "org.apache.catalina.authenticator.PRINCIPAL";
+
+ public static final String FORM_METHOD = "FORM";
+
+ 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
+ * @param response
+ * @param request
+ * @param willSendRequest are we sending Request or Response to IDP
+ * @param destinationQueryStringWithSignature used only with Redirect binding
+ * and with signature enabled.
+ * @throws ProcessingException
+ * @throws ConfigurationException
+ * @throws IOException
+ */
+ protected void sendRequestToIDP(String destination,
+ Document samlDocument,
+ String relayState,
+ Request request,
+ HttpServletResponse 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
+ * @param relayState
+ * @param response
+ * @param willSendRequest
+ * @param destinationQueryStringWithSignature
+ * @throws IOException
+ * @throws UnsupportedEncodingException
+ * @throws ConfigurationException
+ * @throws ProcessingException
+ */
+ protected void sendHttpRedirectRequest(String destination,
+ Document samlDocument,
+ String relayState,
+ HttpServletResponse 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
+ * @param samlDocument
+ * @param relayState
+ * @param response
+ * @param willSendRequest
+ * @throws TrustKeyProcessingException
+ * @throws ProcessingException
+ * @throws IOException
+ * @throws ConfigurationException
+ */
+ protected void sendHttpPostBindingRequest(String destination,
+ Document samlDocument,
+ String relayState,
+ HttpServletResponse 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
+ * @throws LifecycleException
+ */
+ 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());
+ }
+
+ /**
+ * Authenticate the request
+ *
+ * @param request
+ * @param response
+ * @param config
+ * @return
+ * @throws IOException
+ * @throws {@link RuntimeException} when the response is not of type catalina
+ * response object
+ */
+ public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
+ return doAuthenticate(request, response);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.apache.catalina.authenticator.FormAuthenticator#authenticate(org.apache
+ * .catalina.connector.Request, org.apache.catalina.connector.Response,
+ * org.apache.tomcat.util.descriptor.web.LoginConfig)
+ */
+ @Override
+ protected boolean doAuthenticate(Request request, HttpServletResponse response) 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,
+ FORM_METHOD,
+ (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);
+ }
+
+ // Handle a SAML Response from IDP
+ if (isNotNull(samlResponse)) {
+ return handleSAMLResponse(request, response);
+ }
+
+ // Handle SAML Requests from IDP
+ if (isNotNull(samlRequest)) {
+ return handleSAMLRequest(request, response);
+ } // end if
+
+ return localAuthentication(request, response);
+ } 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
+ * @return
+ */
+ 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
+ * @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/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/BaseFormAuthenticator.java b/picketlink-saml/src/main/java/org/picketlink/identity/federation/bindings/tomcat/sp/BaseFormAuthenticator.java
new file mode 100644
index 000000000..1d38fbf7e
--- /dev/null
+++ b/picketlink-saml/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