/* * Atricore IDBus * * Copyright (c) 2009, Atricore Inc. * * 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. */ /* * Atricore IDBus * * Copyright (c) 2009, Atricore Inc. * * 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.atricore.idbus.capabilities.sso.main.idp.producers; import oasis.names.tc.saml._1_0.assertion.AudienceRestrictionConditionType; import oasis.names.tc.saml._2_0.assertion.*; import oasis.names.tc.saml._2_0.assertion.SubjectType; import oasis.names.tc.saml._2_0.idbus.PreAuthenticatedAuthnRequestType; import oasis.names.tc.saml._2_0.idbus.SecTokenAuthnRequestType; import oasis.names.tc.saml._2_0.metadata.*; import oasis.names.tc.saml._2_0.protocol.AuthnRequestType; import oasis.names.tc.saml._2_0.protocol.RequestedAuthnContextType; import oasis.names.tc.saml._2_0.protocol.ResponseType; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.atricore.idbus.capabilities.sso.component.container.IdentityFlowContainer; import org.atricore.idbus.capabilities.sso.component.container.RouteRejectionException; import org.atricore.idbus.capabilities.sso.dsl.IdentityFlowResponse; import org.atricore.idbus.capabilities.sso.dsl.NoFurtherActionRequired; import org.atricore.idbus.capabilities.sso.dsl.RedirectToEndpoint; import org.atricore.idbus.capabilities.sso.main.SSOException; import org.atricore.idbus.capabilities.sso.main.claims.SSOCredentialClaimsRequest; import org.atricore.idbus.capabilities.sso.main.claims.SSOCredentialClaimsResponse; import org.atricore.idbus.capabilities.sso.main.common.AbstractSSOMediator; import org.atricore.idbus.capabilities.sso.main.common.producers.SSOProducer; import org.atricore.idbus.capabilities.sso.main.emitter.SamlR2SecurityTokenEmissionContext; import org.atricore.idbus.capabilities.sso.main.emitter.plans.SamlR2SecurityTokenToAuthnAssertionPlan; import org.atricore.idbus.capabilities.sso.main.idp.IdPSecurityContext; import org.atricore.idbus.capabilities.sso.main.idp.IdentityProviderConstants; import org.atricore.idbus.capabilities.sso.main.idp.SSOIDPMediator; import org.atricore.idbus.capabilities.sso.main.idp.plans.IDPInitiatedAuthnReqToSamlR2AuthnReqPlan; import org.atricore.idbus.capabilities.sso.main.idp.plans.SamlR2AuthnRequestToSamlR2ResponsePlan; import org.atricore.idbus.capabilities.sso.main.select.spi.EntitySelectorConstants; import org.atricore.idbus.capabilities.sso.support.SAMLR2Constants; import org.atricore.idbus.capabilities.sso.support.auth.AuthnCtxClass; import org.atricore.idbus.capabilities.sso.support.binding.SSOBinding; import org.atricore.idbus.capabilities.sso.support.core.*; import org.atricore.idbus.capabilities.sso.support.core.encryption.SamlR2Encrypter; import org.atricore.idbus.capabilities.sso.support.core.signature.SamlR2SignatureException; import org.atricore.idbus.capabilities.sso.support.core.signature.SamlR2SignatureValidationException; import org.atricore.idbus.capabilities.sso.support.core.signature.SamlR2Signer; import org.atricore.idbus.capabilities.sso.support.metadata.SSOService; import org.atricore.idbus.capabilities.sts.main.SecurityTokenAuthenticationFailure; import org.atricore.idbus.capabilities.sts.main.SecurityTokenEmissionException; import org.atricore.idbus.capabilities.sts.main.WSTConstants; import org.atricore.idbus.common.sso._1_0.protocol.*; import org.atricore.idbus.kernel.auditing.core.ActionOutcome; import org.atricore.idbus.kernel.auditing.core.AuditingServer; import org.atricore.idbus.kernel.main.authn.*; import org.atricore.idbus.kernel.main.federation.metadata.*; import org.atricore.idbus.kernel.main.mediation.*; import org.atricore.idbus.kernel.main.mediation.camel.AbstractCamelEndpoint; import org.atricore.idbus.kernel.main.mediation.camel.component.binding.CamelMediationExchange; import org.atricore.idbus.kernel.main.mediation.camel.component.binding.CamelMediationMessage; import org.atricore.idbus.kernel.main.mediation.channel.FederationChannel; import org.atricore.idbus.kernel.main.mediation.channel.SPChannel; import org.atricore.idbus.kernel.main.mediation.claim.*; import org.atricore.idbus.kernel.main.mediation.confirmation.IdentityConfirmationChannel; import org.atricore.idbus.kernel.main.mediation.confirmation.IdentityConfirmationRequest; import org.atricore.idbus.kernel.main.mediation.confirmation.IdentityConfirmationRequestImpl; import org.atricore.idbus.kernel.main.mediation.endpoint.IdentityMediationEndpoint; import org.atricore.idbus.kernel.main.mediation.policy.PolicyEnforcementRequest; import org.atricore.idbus.kernel.main.mediation.policy.PolicyEnforcementRequestImpl; import org.atricore.idbus.kernel.main.mediation.policy.PolicyEnforcementResponse; import org.atricore.idbus.kernel.main.mediation.provider.FederatedLocalProvider; import org.atricore.idbus.kernel.main.mediation.provider.FederatedProvider; import org.atricore.idbus.kernel.main.mediation.provider.IdentityProvider; import org.atricore.idbus.kernel.main.session.SSOSessionManager; import org.atricore.idbus.kernel.main.session.exceptions.NoSuchSessionException; import org.atricore.idbus.kernel.main.store.SSOIdentityManager; import org.atricore.idbus.kernel.main.util.UUIDGenerator; import org.atricore.idbus.kernel.monitoring.core.MonitoringServer; import org.atricore.idbus.kernel.planning.*; import org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.AttributedString; import org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.BinarySecurityTokenType; import org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.PasswordString; import org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.UsernameTokenType; import org.xmlsoap.schemas.ws._2005._02.trust.RequestSecurityTokenResponseType; import org.xmlsoap.schemas.ws._2005._02.trust.RequestSecurityTokenType; import org.xmlsoap.schemas.ws._2005._02.trust.RequestedSecurityTokenType; import org.xmlsoap.schemas.ws._2005._02.trust.wsdl.SecurityTokenService; import javax.security.auth.Subject; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import java.math.BigInteger; import java.security.Principal; import java.util.*; /** * @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a> * @version $Id: SingleSignOnProducer.java 1359 2009-07-19 16:57:57Z sgonzalez $ */ public class SingleSignOnProducer extends SSOProducer { private static final Log logger = LogFactory.getLog(SingleSignOnProducer.class); private UUIDGenerator uuidGenerator = new UUIDGenerator(); public SingleSignOnProducer(AbstractCamelEndpoint<CamelMediationExchange> endpoint) throws Exception { super(endpoint); } @Override protected void doProcess(CamelMediationExchange exchange) throws Exception { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); Object content = in.getMessage().getContent(); // May be used later by HTTP-Redirect binding! AbstractSSOMediator mediator = (AbstractSSOMediator) channel.getIdentityMediator(); in.getMessage().getState().setAttribute("SAMLR2Signer", mediator.getSigner()); long s = System.currentTimeMillis(); String metric = mediator.getMetricsPrefix() + "/Sso/Transactions/"; try { String thread = Thread.currentThread().getName(); if (content instanceof PreAuthenticatedIDPInitiatedAuthnRequestType) { // New Pre-authenticated IDP Initiated Single Sign-On metric += "doProcessPreAuthenticatedIDPInitiantedSSO"; doProcessPreAuthenticatedIDPInitiantedSSO(exchange, (PreAuthenticatedIDPInitiatedAuthnRequestType) content); } else if (content instanceof IDPInitiatedAuthnRequestType) { // New IDP Initiated Single Sign-On metric += "doProcessIDPInitiatedSSO"; doProcessIDPInitiatedSSO(exchange, (IDPInitiatedAuthnRequestType) content); } else if (content instanceof SecTokenAuthnRequestType) { // New Assert Identity with Basic authentication metric += "doProcessAssertIdentityWithBasicAuth"; doProcessAssertIdentityWithBasicAuth(exchange, (SecTokenAuthnRequestType) content); } else if (content instanceof AuthnRequestType) { metric += "doProcessAuthnRequest"; // New SP Initiated Single SignOn doProcessAuthnRequest(exchange, (AuthnRequestType) content, in.getMessage().getRelayState()); } else if (content instanceof SSOCredentialClaimsResponse) { metric += "doProcessClaimsResponse"; // Processing Claims to create authn resposne doProcessClaimsResponse(exchange, (SSOCredentialClaimsResponse) content); } else if (content instanceof PolicyEnforcementResponse) { metric += "doProcessPolicyEnforcementResponse"; // Process policy enforcement response doProcessPolicyEnforcementResponse(exchange, (PolicyEnforcementResponse) content); } else if (content instanceof SPAuthnResponseType) { metric += "doProcessProxyACSResponse"; // Process proxy responses doProcessProxyACSResponse(exchange, (SPAuthnResponseType) content); } else { metric += "Unknown"; throw new IdentityMediationFault(StatusCode.TOP_RESPONDER.getValue(), null, StatusDetails.UNKNOWN_REQUEST.getValue(), content == null ? "<null>" : content.getClass().getName(), null); } } catch (SSORequestException e) { throw new IdentityMediationFault( e.getTopLevelStatusCode() != null ? e.getTopLevelStatusCode().getValue() : StatusCode.TOP_RESPONDER.getValue(), e.getSecondLevelStatusCode() != null ? e.getSecondLevelStatusCode().getValue() : null, e.getStatusDtails() != null ? e.getStatusDtails().getValue() : StatusDetails.UNKNOWN_REQUEST.getValue(), e.getErrorDetails() != null ? e.getErrorDetails() : content.getClass().getName(), e); } catch (SSOException e) { throw new IdentityMediationFault(StatusCode.TOP_RESPONDER.getValue(), null, StatusDetails.INTERNAL_ERROR.getValue(), content.getClass().getName(), e); } finally { MonitoringServer mServer = mediator.getMonitoringServer(); long e = System.currentTimeMillis(); mServer.recordResponseTimeMetric(metric, e - s); } } /** * This procedure will handle an IdP-initiated (aka IdP unsolicited response) request. */ protected void doProcessIDPInitiatedSSO(CamelMediationExchange exchange, IDPInitiatedAuthnRequestType idpInitiatedAuthnRequest) throws SSOException { logger.debug("Processing IDP Initiated Single Sign-On with " + idpInitiatedAuthnRequest.getPreferredResponseFormat() + " preferred Response Format" ); try { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); String relayState = in.getMessage().getRelayState(); // ------------------------------------------------------ // Resolve target IDP for relaying the Authentication Request // ------------------------------------------------------ in.getMessage().getState().setLocalVariable( "urn:org:atricore:idbus:sso:protocol:responseMode", "unsolicited"); in.getMessage().getState().setLocalVariable( "urn:org:atricore:idbus:sso:protocol:responseFormat", idpInitiatedAuthnRequest.getPreferredResponseFormat()); CircleOfTrustMemberDescriptor idp = this.resolveIdp(exchange); logger.debug("Using IdP " + idp.getAlias()); // Select endpoint, must be a SingleSingOnService endpoint from a IDPSSORoleD EndpointType idpSsoEndpoint = resolveIdpSsoEndpoint(idp); EndpointDescriptor ed = new EndpointDescriptorImpl( "IDPSSOEndpoint", "SingleSignOnService", idpSsoEndpoint.getBinding(), idpSsoEndpoint.getLocation(), idpSsoEndpoint.getResponseLocation()); // ------------------------------------------------------ // Create AuthnRequest using identity plan // ------------------------------------------------------ // Get SPInitiated authn request, if any! IDPInitiatedAuthnRequestType ssoAuthnRequest = (IDPInitiatedAuthnRequestType) ((CamelMediationMessage) exchange.getIn()).getMessage().getContent(); AuthnRequestType authnRequest = buildIdPInitiatedAuthnRequest(exchange, ssoAuthnRequest, idp, ed, (FederationChannel) channel); // ------------------------------------------------------ // Send Authn Request to IDP // ------------------------------------------------------ in.getMessage().getState().setLocalVariable( SAMLR2Constants.SAML_PROTOCOL_NS + ":AuthnRequest", authnRequest); // Send SAMLR2 Message back CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(), authnRequest, "AuthnRequest", relayState, ed, in.getMessage().getState())); exchange.setOut(out); } catch (Exception e) { throw new SSOException(e); } } /* * This procedure will handle preauthenticated IdP-initiated (aka IdP unsolicited response) requests. */ protected void doProcessPreAuthenticatedIDPInitiantedSSO(CamelMediationExchange exchange, PreAuthenticatedIDPInitiatedAuthnRequestType PreAuthIdpInitiatedAuthnRequest) throws SSOException { logger.debug("Processing PreAuthenticated IDP Initiated Single Sign-On with " + PreAuthIdpInitiatedAuthnRequest.getPreferredResponseFormat() + " preferred Response Format" ); try { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); String relayState = in.getMessage().getRelayState(); // ------------------------------------------------------ // Resolve target IDP for relaying the Authentication Request // ------------------------------------------------------ logger.debug("Received Security Token [" + PreAuthIdpInitiatedAuthnRequest.getSecurityToken() + "]"); in.getMessage().getState().setLocalVariable( "urn:org:atricore:idbus:sso:protocol:responseMode", "unsolicited"); in.getMessage().getState().setLocalVariable( "urn:org:atricore:idbus:sso:protocol:responseFormat", PreAuthIdpInitiatedAuthnRequest.getPreferredResponseFormat()); CircleOfTrustMemberDescriptor idp = this.resolveIdp(exchange); logger.debug("Using IdP " + idp.getAlias()); // Select endpoint, must be a SingleSingOnService endpoint from a IDPSSORoleD EndpointType idpSsoEndpoint = resolveIdpSsoEndpoint(idp); EndpointDescriptor ed = new EndpointDescriptorImpl( "IDPSSOEndpoint", "SingleSignOnService", idpSsoEndpoint.getBinding(), idpSsoEndpoint.getLocation(), idpSsoEndpoint.getResponseLocation()); // ------------------------------------------------------ // Create PreAuthenticatedAuthnRequest using identity plan // ------------------------------------------------------ PreAuthenticatedAuthnRequestType preauthAuthnRequest = buildPreAuthIdPInitiatedAuthnRequest(exchange, idp, ed, (FederationChannel) channel); // ------------------------------------------------------ // Send Authn Request to IDP // ------------------------------------------------------ in.getMessage().getState().setLocalVariable( SAMLR2Constants.SAML_IDBUS_NS + ":PreAuthenticatedAuthnRequest", preauthAuthnRequest); // Send SAMLR2 Message back CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(), preauthAuthnRequest, "PreAuthenticatedAuthnRequest", relayState, ed, in.getMessage().getState())); exchange.setOut(out); } catch (Exception e) { throw new SSOException(e); } } /** * This procedure will process an authn request. * <p/> * <p/> * If we already stablished identity for the 'presenter' (user) of the request, we'll generate * an assertion using the authn statement stored in session as security token. * The assertion will be sent to the SP in a new Response. * <p/> * <p/> * If we don't have user identity yet, we have to decide if we're handling the request or we are proxying it to a * different IDP. * If we handle the request, we'll search for a claims endpoint and start collecting claims. If no claims endpoint * are available, we're sending a status error response. (we could look for a different IDP here!) */ protected void doProcessAuthnRequest(CamelMediationExchange exchange, AuthnRequestType authnRequest, String relayState) throws Exception { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); MediationState mediationState = in.getMessage().getState(); String varName = getProvider().getName().toUpperCase() + "_SECURITY_CTX"; IdPSecurityContext secCtx = (IdPSecurityContext) mediationState.getLocalVariable(varName); String responseMode = (String) mediationState.getLocalVariable("urn:org:atricore:idbus:sso:protocol:responseMode"); String responseFormat = (String) mediationState.getLocalVariable("urn:org:atricore:idbus:sso:protocol:responseFormat"); if (responseMode != null && responseMode.equalsIgnoreCase("unsolicited")) { logger.debug("Response Mode for Authentication Request " + authnRequest.getID() + " is unsolicited"); logger.debug("Response Format for Authentication Request " + authnRequest.getID() + " is " + responseFormat); } else { logger.debug("Response Mode for Authentication Request " + authnRequest.getID() + " is NOT unsolicited"); } SSOSessionManager sessionMgr = ((SPChannel) channel).getSessionManager(); // Validate AuthnRequest validateRequest(authnRequest, in.getMessage().getRawContent(), in.getMessage().getState()); // ----------------------------------------------------------------------------- // Keep track of request IDs // ----------------------------------------------------------------------------- AbstractSSOMediator mediator = (AbstractSSOMediator) channel.getIdentityMediator(); if (mediator.isVerifyUniqueIDs()) mediator.getIdRegistry().register(authnRequest.getID()); // ----------------------------------------------------------------------------- // Validate SSO Session // ----------------------------------------------------------------------------- boolean isSsoSessionValid = false; if (secCtx != null && secCtx.getSessionIndex() != null) { try { sessionMgr.accessSession(secCtx.getSessionIndex()); isSsoSessionValid = true; if (logger.isDebugEnabled()) logger.debug("SSO Session is valid : " + secCtx.getSessionIndex()); } catch (NoSuchSessionException e) { if (logger.isDebugEnabled()) logger.debug("SSO Session is not valid : " + secCtx.getSessionIndex() + " " + e.getMessage(), e); } } // IF SSO Session is not valid, create a new authn state object AuthenticationState authnState = null; if (!isSsoSessionValid) { if (logger.isTraceEnabled()) logger.trace("Creating new AuthnState"); authnState = newAuthnState(exchange); } else { if (logger.isTraceEnabled()) logger.trace("Using existing AuthnState, if any"); authnState = getAuthnState(exchange); } authnState.setAuthnRequest(authnRequest); authnState.setReceivedRelayState(relayState); authnState.setResponseMode(responseMode); authnState.setResponseFormat(responseFormat); if (authnRequest.getForceAuthn() != null && authnRequest.getForceAuthn()) { if (logger.isDebugEnabled()) logger.debug("Forcing authentication for request " + authnRequest.getID()); isSsoSessionValid = false; // Discard current SSO Session } if (!isSsoSessionValid) { SPChannel spChannel = (SPChannel) channel; // ------------------------------------------------------ // Handle proxy mode // ------------------------------------------------------ if (spChannel.isProxyModeEnabled()) { Channel proxyChannel = spChannel.getProxy(); EndpointDescriptor proxyEndpoint = resolveSPInitiatedSSOProxyEndpointDescriptor(exchange, proxyChannel); logger.debug("Proxying SP-Initiated SSO Request to " + proxyChannel.getLocation() + proxyEndpoint.getLocation()); SPInitiatedAuthnRequestType authnProxyRequest = buildAuthnProxyRequest(authnRequest); in.getMessage().getState().setLocalVariable( "urn:org:atricore:idbus:sso:protocol:SPInitiatedAuthnRequest", authnProxyRequest); out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(), authnProxyRequest, "AuthnProxyRequest", relayState, proxyEndpoint, in.getMessage().getState())); exchange.setOut(out); } else { // ------------------------------------------------------------------------------ // Handle Invalid SSO Session // ------------------------------------------------------------------------------ // Ask for credentials, use claims channel logger.debug("No SSO Session found, asking for credentials"); // TODO : Verify max sessions per user, etc! ClaimChannel claimChannel = selectNextClaimsEndpoint(authnState, exchange); IdentityMediationEndpoint claimEndpoint = authnState.getCurrentClaimsEndpoint(); if (claimEndpoint == null) { // Auth failed, no more endpoints available if (logger.isDebugEnabled()) logger.debug("No claims endpoint found for authn request : " + authnRequest.getID()); // Send failure response CircleOfTrustMemberDescriptor sp = resolveProviderDescriptor(authnRequest.getIssuer()); EndpointDescriptor ed = resolveSpAcsEndpoint(exchange, authnRequest); ResponseType response = buildSamlResponse(exchange, authnState, null, sp, ed); out.setMessage(new MediationMessageImpl(response.getID(), response, "Response", relayState, ed, in.getMessage().getState())); exchange.setOut(out); return; } // TODO : Use a plan authnreq to claimsreq logger.debug("Selected claims endpoint : " + claimEndpoint); // Create Claims Request SSOCredentialClaimsRequest claimsRequest = null; if (authnRequest instanceof PreAuthenticatedAuthnRequestType) { PreAuthenticatedAuthnRequestType preAuthnRequest = (PreAuthenticatedAuthnRequestType) authnRequest; claimsRequest = new SSOCredentialClaimsRequest( authnRequest.getID(), channel, endpoint, claimChannel, uuidGenerator.generateId(), preAuthnRequest.getSecurityToken()); } else { claimsRequest = new SSOCredentialClaimsRequest( authnRequest.getID(), channel, endpoint, claimChannel, uuidGenerator.generateId()); } // Send our state ID as relay claimsRequest.setRelayState(mediationState.getLocalState().getId()); // Send SP relay state claimsRequest.setTargetRelayState(in.getMessage().getRelayState()); if (authnRequest.getIssuer() != null) claimsRequest.setSpAlias(authnRequest.getIssuer().getValue()); // Set requested authn class claimsRequest.setRequestedAuthnCtxClass(authnRequest.getRequestedAuthnContext()); // -------------------------------------------------------------------- // Send claims request // -------------------------------------------------------------------- EndpointDescriptor ed = new EndpointDescriptorImpl(claimEndpoint.getBinding(), claimEndpoint.getType(), claimEndpoint.getBinding(), claimEndpoint.getLocation().startsWith("/") ? claimChannel.getLocation() + claimEndpoint.getLocation() : claimEndpoint.getLocation(), claimEndpoint.getResponseLocation()); logger.debug("Collecting claims using endpoint " + claimEndpoint); SSOBinding edBinding = SSOBinding.asEnum(ed.getBinding()); if (!edBinding.isFrontChannel()) { SSOCredentialClaimsResponse cr = (SSOCredentialClaimsResponse) channel.getIdentityMediator().sendMessage(claimsRequest, ed, claimChannel); // TODO : in and out may not be what doProcessClaimsResponse is expecting! doProcessClaimsResponse(exchange, cr); } else { out.setMessage(new MediationMessageImpl(claimsRequest.getId(), claimsRequest, "ClaimsRequest", null, ed, in.getMessage().getState())); exchange.setOut(out); } } } else { // ------------------------------------------------------------------------------ // Handle Valid SSO Session // ------------------------------------------------------------------------------ if (logger.isDebugEnabled()) logger.debug("Found valid SSO Session for AuthnRequest " + authnRequest.getID()); EndpointDescriptor ed = resolveSpAcsEndpoint(exchange, authnRequest); SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx = new SamlR2SecurityTokenEmissionContext(); // Send extra information to STS, using the emission context securityTokenEmissionCtx.setMember(resolveProviderDescriptor(authnRequest.getIssuer())); // TODO !!! : securityTokenEmissionCtx.setRoleMetadata(null); authnState.setAuthnRequest(authnRequest); securityTokenEmissionCtx.setAuthnState(authnState); securityTokenEmissionCtx.setSessionIndex(secCtx.getSessionIndex()); securityTokenEmissionCtx.setSsoSession(sessionMgr.getSession(secCtx.getSessionIndex())); securityTokenEmissionCtx.setIssuerMetadata(((SPChannel) channel).getMember().getMetadata()); securityTokenEmissionCtx.setIdentityPlanName(getSTSPlanName()); securityTokenEmissionCtx.setSpAcs(ed); // Add any proxy principals available if (secCtx.getProxyPrincipals() != null) securityTokenEmissionCtx.getProxyPrincipals().addAll(secCtx.getProxyPrincipals()); securityTokenEmissionCtx = emitAssertionFromPreviousSession(exchange, securityTokenEmissionCtx, authnRequest, secCtx); if (logger.isDebugEnabled()) logger.debug("Created SAMLR2 Assertion " + securityTokenEmissionCtx.getAssertion().getID() + " for AuthnRequest " + authnRequest.getID()); // Register SP in SSO List secCtx.register(authnRequest.getIssuer(), authnState.getReceivedRelayState()); CircleOfTrustMemberDescriptor sp = resolveProviderDescriptor(authnRequest.getIssuer()); ResponseType response = buildSamlResponse(exchange, authnState, securityTokenEmissionCtx.getAssertion(), sp, ed); if (responseFormat != null && responseFormat.equals("urn:oasis:names:tc:SAML:1.1")) { oasis.names.tc.saml._1_0.protocol.ResponseType saml11Response; saml11Response = transformSamlR2ResponseToSaml11(response); out.setMessage(new MediationMessageImpl(saml11Response.getResponseID(), saml11Response, "Response", relayState, ed, in.getMessage().getState())); } else { // SAML R2 is used by default out.setMessage(new MediationMessageImpl(response.getID(), response, "Response", relayState, ed, in.getMessage().getState())); } exchange.setOut(out); } } /** * @deprecated Use pre-authentication instead * * @param exchange * @param authnRequest * @throws Exception */ @Deprecated public void doProcessAssertIdentityWithBasicAuth(CamelMediationExchange exchange, SecTokenAuthnRequestType authnRequest) throws Exception { if (true) throw new UnsupportedOperationException("Please, use pre-authentication service instead"); CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); AuthenticationState authnState = this.getAuthnState(exchange); NameIDType issuer = authnRequest.getIssuer(); CircleOfTrustMemberDescriptor sp = resolveProviderDescriptor(issuer); // Resolve SP endpoint EndpointDescriptor ed = this.resolveSpAcsEndpoint(exchange, authnRequest); // ------------------------------------------------------- // Build STS Context // ------------------------------------------------------- // The context will act as an alternative communication exchange between this producer (IDP) and the STS. // It will transport back the Subject wich is not supported by the WST protocol SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx = new SamlR2SecurityTokenEmissionContext(); // Send extra information to STS, using the emission context securityTokenEmissionCtx.setMember(sp); // TODO : Resolve SP SAMLR2 Role springmetadata securityTokenEmissionCtx.setRoleMetadata(null); securityTokenEmissionCtx.setAuthnState(authnState); securityTokenEmissionCtx.setSessionIndex(uuidGenerator.generateId()); securityTokenEmissionCtx.setIssuerMetadata(sp.getMetadata()); securityTokenEmissionCtx.setIdentityPlanName(getSTSPlanName()); securityTokenEmissionCtx.setSpAcs(ed); UsernameTokenType usernameToken = new UsernameTokenType(); AttributedString usernameString = new AttributedString(); usernameString.setValue(authnRequest.getUsername()); usernameToken.setUsername(usernameString); usernameToken.getOtherAttributes().put(new QName(Constants.PASSWORD_NS), authnRequest.getPassword()); usernameToken.getOtherAttributes().put(new QName(AuthnCtxClass.PASSWORD_AUTHN_CTX.getValue()), "TRUE"); CredentialClaim credentialClaim = new CredentialClaimImpl(AuthnCtxClass.PASSWORD_AUTHN_CTX.getValue(), usernameToken); ClaimSet claims = new ClaimSetImpl(); claims.addClaim(credentialClaim); SamlR2SecurityTokenEmissionContext cxt = emitAssertionFromClaims(exchange, securityTokenEmissionCtx, claims, sp); AssertionType assertion = cxt.getAssertion(); Subject authnSubject = cxt.getSubject(); logger.debug("New Assertion " + assertion.getID() + " emitted form request " + (authnRequest != null ? authnRequest.getID() : "<NULL>")); // Create a new SSO Session IdPSecurityContext secCtx = createSecurityContext(exchange, authnSubject, assertion, null); // Associate the SP with the new session, including relay state! // TODO : Instead of authnRequest, use metadata to get issuer! secCtx.register(authnRequest.getIssuer(), authnState.getReceivedRelayState()); // Build a response for the SP ResponseType response = buildSamlResponse(exchange, authnState, assertion, sp, ed); // Set the SSO Session var // State not supported in SOAP yet in.getMessage().getState().setLocalVariable(channel.getFederatedProvider().getName().toUpperCase() + "_SECURITY_CTX", secCtx); // State not supported in SOAP yet in.getMessage().getState().getLocalState().addAlternativeId("ssoSessionId", secCtx.getSessionIndex()); // -------------------------------------------------------------------- // Send Authn Response to SP // -------------------------------------------------------------------- CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); out.setMessage(new MediationMessageImpl(response.getID(), response, "Response", authnState.getReceivedRelayState(), ed, null)); exchange.setOut(out); } /** * This will emit an assertion using the received claims. If the process is successful, a SAML Response will * be issued to the original SP. * If an error occurs, the procedure will decide to retry collecting claims with the las * claims endpoint selected or collect claims using a new claims endpoint. * <p/> * If no more claim endpoints are available, this will send an status error response to the SP. * * @param exchange * @param claimsResponse * @throws Exception */ protected void doProcessClaimsResponse(CamelMediationExchange exchange, SSOCredentialClaimsResponse claimsResponse) throws Exception { //------------------------------------------------------------ // Process a claims response //------------------------------------------------------------ CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); MediationState state = in.getMessage().getState(); AuthenticationState authnState = getAuthnState(exchange); AuthnRequestType authnRequest = authnState.getAuthnRequest(); IdentityMediationEndpoint prevClaimsEndpoint = authnState.getCurrentClaimsEndpoint(); NameIDType issuer = authnRequest.getIssuer(); CircleOfTrustMemberDescriptor sp = resolveProviderDescriptor(issuer); String responseMode = authnState.getResponseMode(); String responseFormat = authnState.getResponseFormat(); if (responseMode != null && responseMode.equalsIgnoreCase("unsolicited")) { logger.debug("Response Mode for Claim Response " + authnRequest.getID() + " is unsolicited"); logger.debug("Response Format for Claim Response " + authnRequest.getID() + " is " + responseFormat); } else { logger.debug("Response Mode for Claim Response " + authnRequest.getID() + " is NOT unsolicited"); } // ---------------------------------------------------- // Emit new assertion // ---------------------------------------------------- try { authnState.setSsoAttepmts(authnState.getSsoAttepmts() + 1); // Resolve SP endpoint EndpointDescriptor ed = this.resolveSpAcsEndpoint(exchange, authnRequest); // ------------------------------------------------------- // Build STS Context // ------------------------------------------------------- // The context will act as an alternative communication exchange between this producer (IDP) and the STS. // It will transport back the Subject which is not supported by the WST protocol SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx = new SamlR2SecurityTokenEmissionContext(); // Send extra information to STS, using the emission context securityTokenEmissionCtx.setIssuerMetadata(((SPChannel) channel).getMember().getMetadata()); securityTokenEmissionCtx.setMember(sp); securityTokenEmissionCtx.setIdentityPlanName(getSTSPlanName()); // TODO : Resolve SP SAMLR2 Role springmetadata securityTokenEmissionCtx.setRoleMetadata(null); securityTokenEmissionCtx.setAuthnState(authnState); securityTokenEmissionCtx.setSessionIndex(uuidGenerator.generateId()); securityTokenEmissionCtx.setSpAcs(ed); // ---------------------------------------------------------------------------------------- // Authenticate the user, send a RequestSecurityToken to the Security Token Service (STS) // and emit a SAML 2.0 Assertion // ---------------------------------------------------------------------------------------- securityTokenEmissionCtx = emitAssertionFromClaims(exchange, securityTokenEmissionCtx, claimsResponse.getClaimSet(), sp); AssertionType assertion = securityTokenEmissionCtx.getAssertion(); Subject authnSubject = securityTokenEmissionCtx.getSubject(); SimplePrincipal principal = authnSubject.getPrincipals(SimplePrincipal.class).iterator().next(); if (logger.isDebugEnabled()) logger.debug("New Assertion " + assertion.getID() + " emitted form request " + (authnRequest != null ? authnRequest.getID() : "<NULL>")); // Generate audit trail AbstractSSOMediator mediator = (AbstractSSOMediator) channel.getIdentityMediator(); AuditingServer aServer = mediator.getAuditingServer(); Properties auditProps = new Properties(); auditProps.put("attempt", authnState.getSsoAttepmts() + ""); if (authnState.getCurrentAuthnCtxClass() != null) auditProps.put("authnCtx", authnState.getCurrentAuthnCtxClass().getValue()); recordInfoAuditTrail("SSO", ActionOutcome.SUCCESS, principal != null ? principal.getName() : null, exchange, auditProps); if (((IdentityProvider)getProvider()).isIdentityConfirmationEnabled()) { // -------------------------------------------------------------------- // Confirm user's identity in case needed // -------------------------------------------------------------------- logger.debug("Confirming user's identity with claims [" + claimsResponse.getClaimSet().getClaims() + "] and " + "subject [" + securityTokenEmissionCtx.getSubject() + "]"); UsernameTokenType usernameToken = new UsernameTokenType (); AttributedString usernameString = new AttributedString(); usernameString.setValue( principal.getName() ); usernameToken.setUsername(usernameString); Claim principalClaim = new CredentialClaimImpl("", usernameToken); claimsResponse.getClaimSet().addClaim(principalClaim); IdentityConfirmationChannel idConfChannel = selectNextIdentityConfirmationEndpoint( authnState, exchange, claimsResponse.getClaimSet()); IdentityMediationEndpoint idConfEndpoint = authnState.getCurrentIdConfirmationEndpoint(); if (idConfEndpoint != null) { logger.debug("Selected identity confirmation endpoint : " + idConfEndpoint); // Create Claims Request IdentityConfirmationRequest idConfRequest = new IdentityConfirmationRequestImpl( (SPChannel)channel, authnRequest.getIssuer().getValue() ); for (Claim claim : claimsResponse.getClaimSet().getClaims()) { idConfRequest.getClaims().add(claim); } // generate our own claims idConfRequest.getClaims().add(new UserClaimImpl("", "sourceIpAddress", state.getTransientVariable("RemoteAddress"))); idConfRequest.getClaims().add(new UserClaimImpl("", "emailAddress", "gbrigand@gmail.com")); // -------------------------------------------------------------------- // Submit identity confirmation request // -------------------------------------------------------------------- EndpointDescriptor idConfEp = new EndpointDescriptorImpl(idConfEndpoint.getBinding(), idConfEndpoint.getType(), idConfEndpoint.getBinding(), idConfEndpoint.getLocation().startsWith("/") ? idConfChannel.getLocation() + idConfEndpoint.getLocation() : idConfEndpoint.getLocation(), idConfEndpoint.getResponseLocation()); logger.debug("Confirming user identity using endpoint " + idConfEp); clearAuthnState(exchange); out.setMessage(new MediationMessageImpl(idConfRequest.getId(), idConfRequest, "IdentityConfirmationRequest", null, idConfEp, in.getMessage().getState())); exchange.setOut(out); return; } else { logger.debug("There is no endpoint available for identity confirmation. Skipping."); } } if (logger.isDebugEnabled()) logger.debug("New Assertion " + assertion.getID() + " emitted form request " + (authnRequest != null ? authnRequest.getID() : "<NULL>")); // Create a new SSO Session IdPSecurityContext secCtx = createSecurityContext(exchange, authnSubject, assertion, claimsResponse.getClaimSet()); // Associate the SP with the new session, including relay state! // We already validated authn request issuer, so we can use it. secCtx.register(authnRequest.getIssuer(), authnState.getReceivedRelayState()); // Build a response for the SP ResponseType saml2Response = buildSamlResponse(exchange, authnState, assertion, sp, ed); oasis.names.tc.saml._1_0.protocol.ResponseType saml11Response = null; // Set the SSO Session var in.getMessage().getState().setLocalVariable(getProvider().getName().toUpperCase() + "_SECURITY_CTX", secCtx); in.getMessage().getState().getLocalState().addAlternativeId(IdentityProviderConstants.SEC_CTX_SSOSESSION_KEY, secCtx.getSessionIndex()); // -------------------------------------------------------------------- // Send Authn Response to SP // -------------------------------------------------------------------- if (responseFormat != null && responseFormat.equals("urn:oasis:names:tc:SAML:1.1")) { saml11Response = transformSamlR2ResponseToSaml11(saml2Response); SamlR2Signer signer = ((SSOIDPMediator) channel.getIdentityMediator()).getSigner(); saml11Response = signer.sign(saml11Response); } // Clear the current authentication state clearAuthnState(exchange); // If subject contains SSOPolicy enforcement principals, we need to show them to the user before moving on ... List<SSOPolicyEnforcementStatement> stmts = getPolicyEnforcementStatements(assertion); if (stmts != null && stmts.size() > 0) { if (logger.isDebugEnabled()) logger.debug("Processing " + stmts.size() + " SSO Policy Enforcement Statements"); // Store: Authn Response and Endpoint Descriptor in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponse", saml11Response != null ? saml11Response : saml2Response); in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseEndpoint", ed); in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseRelayState", authnState.getReceivedRelayState()); // 2. Send artifact to warning endpoint w/ policies EndpointDescriptor pweEd = new EndpointDescriptorImpl("PolicyEnforcementWarningService", "PolicyEnforcementWarningService", SSOBinding.SSO_ARTIFACT.getValue(), channel.getIdentityMediator().getWarningUrl(), null); EndpointDescriptor replyTo = resolveIdpSsoContinueEndpoint(); PolicyEnforcementRequest per = new PolicyEnforcementRequestImpl(uuidGenerator.generateId(), replyTo); per.getStatements().addAll(stmts); out.setMessage(new MediationMessageImpl( per.getId(), per, "PolicyEnforcementWarning", null, pweEd, in.getMessage().getState())); return; } if (responseFormat != null && responseFormat.equals("urn:oasis:names:tc:SAML:1.1")) { if (logger.isDebugEnabled()) logger.debug("Sending SAML 1.1 Response"); out.setMessage(new MediationMessageImpl(saml11Response.getResponseID(), saml11Response, "Response", authnState.getReceivedRelayState(), ed, in.getMessage().getState())); } else { if (logger.isDebugEnabled()) logger.debug("Sending SAML 2.0 Response"); // SAML R2 is used by default out.setMessage(new MediationMessageImpl(saml2Response.getID(), saml2Response, "Response", authnState.getReceivedRelayState(), ed, in.getMessage().getState())); } exchange.setOut(out); } catch (SecurityTokenAuthenticationFailure e) { if (logger.isDebugEnabled()) logger.debug("Security Token authentication failure : " + e.getMessage(), e); // Generate audit trail Properties auditProps = new Properties(); auditProps.put("attempt", authnState.getSsoAttepmts() + ""); if (authnState.getCurrentAuthnCtxClass() != null) auditProps.put("authnCtx", authnState.getCurrentAuthnCtxClass().getValue()); recordInfoAuditTrail("SSO", ActionOutcome.FAILURE, e.getPrincipalName(), exchange, auditProps); // The authentication failed, let's see what needs to be done. // If the request was set to 'Passive', keep trying with passive claim endponits only! // If not, keep trying with other endpoints. // Set of policies enforced during authentication Set<SSOPolicyEnforcementStatement> ssoPolicyEnforcements = e.getSsoPolicyEnforcements(); // Ask for more claims, using other auth schemes ClaimChannel claimChannel = selectNextClaimsEndpoint(authnState, exchange); IdentityMediationEndpoint claimEndpoint = authnState.getCurrentClaimsEndpoint(); // No more claim endpoints available, the authentication process is over. if (claimEndpoint == null) { // Authentication failure, no more endpoints available, consider proxying to another IDP. if (logger.isDebugEnabled()) logger.error("No claims endpoint found for authn request : " + authnRequest.getID()); // Send failure response EndpointDescriptor ed = resolveSpAcsEndpoint(exchange, authnRequest); // This could be a response to a passive request ... ResponseType response = buildSamlResponse(exchange, authnState, null, sp, ed); out.setMessage(new MediationMessageImpl(response.getID(), response, "Response", authnState.getReceivedRelayState(), ed, in.getMessage().getState())); exchange.setOut(out); return; } // We have another Claim endpoint to try, let's send the request. if (logger.isDebugEnabled()) logger.debug("Selecting claims endpoint : " + endpoint.getName()); SSOCredentialClaimsRequest claimsRequest = new SSOCredentialClaimsRequest(authnRequest.getID(), channel, endpoint, claimChannel, uuidGenerator.generateId()); // We're retrying the same endpoint type, mark the authentication as failed if (prevClaimsEndpoint != null && prevClaimsEndpoint.getType().equals(claimEndpoint.getType())) claimsRequest.setLastErrorId("AUTHN_FAILED"); claimsRequest.setLastErrorMsg(e.getMessage()); claimsRequest.getSsoPolicyEnforcements().addAll(ssoPolicyEnforcements); // Update authentication state claimsRequest.setRequestedAuthnCtxClass(authnRequest.getRequestedAuthnContext()); authnState.setAuthnRequest(authnRequest); // Set SP information if (authnRequest.getIssuer() != null) claimsRequest.setSpAlias(authnRequest.getIssuer().getValue()); // -------------------------------------------------------------------- // Send claims request // -------------------------------------------------------------------- EndpointDescriptor ed = new EndpointDescriptorImpl(claimEndpoint.getBinding(), claimEndpoint.getType(), claimEndpoint.getBinding(), claimChannel.getLocation() + claimEndpoint.getLocation(), claimEndpoint.getResponseLocation()); logger.debug("Collecting claims using endpoint " + claimEndpoint.getName() + " [" + ed.getLocation() + "]"); out.setMessage(new MediationMessageImpl(claimsRequest.getId(), claimsRequest, "ClaimsRequest", null, ed, in.getMessage().getState())); exchange.setOut(out); } } /** * This will emit an assertion using the claims conveyed in the proxy response. If the process is successful, * a SAML Response will be issued to the original SP. * If an error occurs, the error condition will be notified back to the requesting IDP. * * @param exchange * @param proxyResponse * @throws Exception */ protected void doProcessProxyACSResponse(CamelMediationExchange exchange, SPAuthnResponseType proxyResponse) throws Exception { //------------------------------------------------------------ // Process a proxy response //------------------------------------------------------------ CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); AuthenticationState authnState = getAuthnState(exchange); AuthnRequestType authnRequest = authnState.getAuthnRequest(); // This is IDP-Initiated , but we're acting as proxy CircleOfTrustMemberDescriptor sp = null; if (authnRequest == null) { // Now authn-request, this is IDP initiated, the authnState is probably new. SPChannel spChannel = (SPChannel) channel; sp = resolveProviderDescriptor(spChannel.getTargetProvider()); CircleOfTrustMemberDescriptor idpProxy = spChannel.getMember(); EndpointDescriptor destination = new EndpointDescriptorImpl(endpoint); IDPInitiatedAuthnRequestType idpInitReq = new IDPInitiatedAuthnRequestType(); idpInitReq.setID(uuidGenerator.generateId()); idpInitReq.setPreferredResponseFormat("urn:oasis:names:tc:SAML:2.0"); RequestAttributeType a = new RequestAttributeType(); a.setName("atricore_sp_alias"); a.setValue(sp.getAlias()); idpInitReq.getRequestAttribute().add(a); // This builds an authn request type in behalf of the original SP authnRequest = buildIdPInitiatedAuthnRequest(exchange, idpInitReq, idpProxy, destination, spChannel); authnState.setResponseMode("unsolicited"); authnState.setAuthnRequest(authnRequest); } else { NameIDType issuer = authnRequest.getIssuer(); sp = resolveProviderDescriptor(issuer); } String responseMode = authnState.getResponseMode(); String responseFormat = authnState.getResponseFormat(); if (responseMode != null && responseMode.equalsIgnoreCase("unsolicited")) { logger.debug("Response Mode for Proxy Response is unsolicited [" + (authnRequest != null ? authnRequest.getID() : "<NO-AUTHN-REQUEST>") + "]"); logger.debug("Response Format for Proxy Response is " + responseFormat + "[" + (authnRequest != null ? authnRequest.getID() : "<NO-AUTHN-REQUEST>") + "]"); } else { logger.debug("Response Mode for Proxy Response is NOT unsolicited [" + (authnRequest != null ? authnRequest.getID() : "<NO-AUTHN-REQUEST>") + "]"); } // ---------------------------------------------------- // Emit new SAML Assertion, only if authn succeeded // ---------------------------------------------------- try { // Resolve SP endpoint EndpointDescriptor ed = this.resolveSpAcsEndpoint(exchange, authnRequest); AbstractSSOMediator mediator = (AbstractSSOMediator) channel.getIdentityMediator(); List<SSOPolicyEnforcementStatement> stmts = null; AssertionType assertion = null; if (proxyResponse.getSubject() == null) { // The authentication failed! if (logger.isDebugEnabled()) logger.debug("Authentication failed, no subject received"); } else { // ------------------------------------------------------- // Build STS Context // ------------------------------------------------------- // The context will act as an alternative communication exchange between this producer (IDP) and the STS. // It will transport back the Subject wich is not supported by the WST protocol SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx = new SamlR2SecurityTokenEmissionContext(); // Send extra information to STS, using the emission context securityTokenEmissionCtx.setIssuerMetadata(((SPChannel) channel).getMember().getMetadata()); securityTokenEmissionCtx.setMember(sp); securityTokenEmissionCtx.setIdentityPlanName(getSTSPlanName()); // TODO : Resolve SP SAMLR2 Role springmetadata securityTokenEmissionCtx.setRoleMetadata(null); securityTokenEmissionCtx.setAuthnState(authnState); securityTokenEmissionCtx.setSessionIndex(uuidGenerator.generateId()); securityTokenEmissionCtx.setSpAcs(ed); // in order to request a security token we need to map the claims sent by the proxy to // STS claims List<AbstractPrincipalType> proxySubjectPrincipals = proxyResponse.getSubject().getAbstractPrincipal(); List<AbstractPrincipalType> proxyPrincipals = new ArrayList<AbstractPrincipalType>(); AuthnCtxClass authnCtx = null; if (proxyResponse.getSubjectAttributes() != null) { for (SubjectAttributeType attr : proxyResponse.getSubjectAttributes()) { if (attr.getName().equals("authnCtxClass")) { try { authnCtx = AuthnCtxClass.asEnum(attr.getValue()); if (logger.isDebugEnabled()) logger.debug("Using authnCtxClass " + attr.getValue()); break; } catch (Exception e) { logger.error("Unknonw AuthnCtxClass type " + attr.getValue()); } } } } // Just in case if (authnCtx == null) { authnCtx = AuthnCtxClass.PASSWORD_AUTHN_CTX; } ClaimSet claims = new ClaimSetImpl(); UsernameTokenType usernameToken = new UsernameTokenType(); for (Iterator<AbstractPrincipalType> iterator = proxySubjectPrincipals.iterator(); iterator.hasNext(); ) { AbstractPrincipalType next = iterator.next(); if (next instanceof SubjectNameIDType) { // TODO : Perform some kind of identity mapping if necessary, email -> username, etc. SubjectNameIDType nameId = (SubjectNameIDType) next; AttributedString usernameString = new AttributedString(); usernameString.setValue(nameId.getName()); usernameToken.setUsername(usernameString); usernameToken.getOtherAttributes().put(new QName(Constants.PASSWORD_NS), nameId.getName()); // TODO : This is not accurate // TODO : We should honor the provided authn. context if any usernameToken.getOtherAttributes().put(new QName(authnCtx.getValue()), "TRUE"); // Also update authentication state authnState.setAuthnCtxClass(authnCtx); usernameToken.getOtherAttributes().put(new QName(Constants.PROXY_NS), "TRUE"); CredentialClaim credentialClaim = new CredentialClaimImpl(authnCtx.getValue(), usernameToken); claims.addClaim(credentialClaim); } else { securityTokenEmissionCtx.getProxyPrincipals().add(next); } } // Now, add all proxy principals stored in the response, different proxies may use different mechanisms if (proxyResponse.getSubjectAttributes() != null) securityTokenEmissionCtx.getProxyPrincipals().addAll(proxyResponse.getSubjectAttributes()); if (proxyResponse.getSubjectRoles() != null) securityTokenEmissionCtx.getProxyPrincipals().addAll(proxyResponse.getSubjectRoles()); SamlR2SecurityTokenEmissionContext stsCtx = emitAssertionFromClaims(exchange, securityTokenEmissionCtx, claims, sp); assertion = stsCtx.getAssertion(); Subject authnSubject = stsCtx.getSubject(); logger.debug("New Assertion " + assertion.getID() + " emitted form request " + (authnRequest != null ? authnRequest.getID() : "<NULL>")); // Create a new SSO Session IdPSecurityContext secCtx = createSecurityContext(exchange, authnSubject, assertion, null); secCtx.setPRoxyPrincipals(stsCtx.getProxyPrincipals()); // Associate the SP with the new session, including relay state! // We already validated authn request issuer, so we can use it. secCtx.register(authnRequest.getIssuer(), authnState.getReceivedRelayState()); // TODO : If subject contains SSOPolicy enforcement principals, we need to show them to the user before moving on ... stmts = getPolicyEnforcementStatements(assertion); // Set the SSO Session var in.getMessage().getState().setLocalVariable(getProvider().getName().toUpperCase() + "_SECURITY_CTX", secCtx); in.getMessage().getState().getLocalState().addAlternativeId(IdentityProviderConstants.SEC_CTX_SSOSESSION_KEY, secCtx.getSessionIndex()); } // Build a response for the SP ResponseType saml2Response = buildSamlResponse(exchange, authnState, assertion, sp, ed); oasis.names.tc.saml._1_0.protocol.ResponseType saml11Response = null; // -------------------------------------------------------------------- // Send Authn Response to SP // -------------------------------------------------------------------- if (responseFormat != null && responseFormat.equals("urn:oasis:names:tc:SAML:1.1")) { saml11Response = transformSamlR2ResponseToSaml11(saml2Response); SamlR2Signer signer = ((SSOIDPMediator) channel.getIdentityMediator()).getSigner(); saml11Response = signer.sign(saml11Response); } clearAuthnState(exchange); if (stmts != null && stmts.size() > 0) { if (logger.isDebugEnabled()) logger.debug("Processing " + stmts.size() + " SSO Policy Enforcement Statements"); // Store: Authn Response and Endpoint Descriptor in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponse", saml11Response != null ? saml11Response : saml2Response); in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseEndpoint", ed); in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseRelayState", authnState.getReceivedRelayState()); // 2. Send artifact to warning endpoint w/ policies EndpointDescriptor pweEd = new EndpointDescriptorImpl("PolicyEnforcementWarningService", "PolicyEnforcementWarningService", SSOBinding.SSO_ARTIFACT.getValue(), channel.getIdentityMediator().getWarningUrl(), null); EndpointDescriptor replyTo = resolveIdpSsoContinueEndpoint(); PolicyEnforcementRequest per = new PolicyEnforcementRequestImpl(uuidGenerator.generateId(), replyTo); per.getStatements().addAll(stmts); out.setMessage(new MediationMessageImpl( per.getId(), per, "PolicyEnforcementWarning", null, pweEd, in.getMessage().getState())); return; } if (responseFormat != null && responseFormat.equals("urn:oasis:names:tc:SAML:1.1")) { out.setMessage(new MediationMessageImpl(saml11Response.getResponseID(), saml11Response, "Response", authnState.getReceivedRelayState(), ed, in.getMessage().getState())); } else { // SAML R2 is used by default out.setMessage(new MediationMessageImpl(saml2Response.getID(), saml2Response, "Response", authnState.getReceivedRelayState(), ed, in.getMessage().getState())); } exchange.setOut(out); } catch (SecurityTokenAuthenticationFailure e) { if (logger.isDebugEnabled()) logger.debug("Security Token authentication failure : " + e.getMessage(), e); } } protected void doProcessPolicyEnforcementResponse(CamelMediationExchange exchange, PolicyEnforcementResponse response) throws Exception { // Recover SSO Artifacts: CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); CamelMediationMessage out = (CamelMediationMessage) exchange.getOut(); // Recover: Authn Response, RelayState and Endpoint Descriptor Object saml11OrSaml2AuthnResponse = in.getMessage().getState().getLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponse"); EndpointDescriptor acs = (EndpointDescriptor) in.getMessage().getState().getLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseEndpoint"); String relayState = (String) in.getMessage().getState().getLocalVariable("urn:org:atricore:idbus:samlr2:idp:pendingAuthnResponseRelayState"); if (saml11OrSaml2AuthnResponse instanceof oasis.names.tc.saml._1_0.protocol.ResponseType) { oasis.names.tc.saml._1_0.protocol.ResponseType saml11Response = (oasis.names.tc.saml._1_0.protocol.ResponseType) saml11OrSaml2AuthnResponse; out.setMessage(new MediationMessageImpl(saml11Response.getResponseID(), saml11Response, "Response", relayState, acs, in.getMessage().getState())); } else { // SAML R2 is used by default ResponseType saml2Response = (ResponseType) saml11OrSaml2AuthnResponse; out.setMessage(new MediationMessageImpl(saml2Response.getID(), saml2Response, "Response", relayState, acs, in.getMessage().getState())); } exchange.setOut(out); } // ----------------------------------------------------------------------------------- // Utils // ----------------------------------------------------------------------------------- protected void validateRequest(AuthnRequestType request, String originalRequest, MediationState state) throws SSORequestException, SSOException { AbstractSSOMediator mediator = (AbstractSSOMediator) channel.getIdentityMediator(); SamlR2Signer signer = mediator.getSigner(); SamlR2Encrypter encrypter = mediator.getEncrypter(); // Metadata from the IDP SPSSODescriptorType saml2SpMd = null; IDPSSODescriptorType saml2IdpMd = null; try { // Lookup SP SAML MD String spAlias = request.getIssuer().getValue(); MetadataEntry spMd = getCotManager().findEntityRoleMetadata(spAlias, "urn:oasis:names:tc:SAML:2.0:metadata:SPSSODescriptor"); saml2SpMd = (SPSSODescriptorType) spMd.getEntry(); // Lookup IDP SAML MD MetadataEntry idpMd = getCotManager().findEntityRoleMetadata(getCotMemberDescriptor().getAlias(), "urn:oasis:names:tc:SAML:2.0:metadata:IDPSSODescriptor"); saml2IdpMd = (IDPSSODescriptorType) idpMd.getEntry(); } catch (CircleOfTrustManagerException e) { throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, null, request.getIssuer().getValue(), e); } // SAML Want AuthnRequest signed has precedence over want requests signed boolean validateSignature = mediator.isValidateRequestsSignature(); if (saml2IdpMd.getWantAuthnRequestsSigned() != null) validateSignature = saml2IdpMd.getWantAuthnRequestsSigned(); // XML Signature, saml2 core, section 5 if (validateSignature) { if (!endpoint.getBinding().equals(SSOBinding.SAMLR2_REDIRECT.getValue())) { // If no signature is present, throw an exception! if (request.getSignature() == null) throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.INVALID_REQUEST_SIGNATURE); try { if (originalRequest != null) signer.validateDom(saml2SpMd, originalRequest); else signer.validate(saml2SpMd, request); } catch (SamlR2SignatureValidationException e) { throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.INVALID_RESPONSE_SIGNATURE, e); } catch (SamlR2SignatureException e) { //other exceptions like JAXB, xml parser... throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.INVALID_RESPONSE_SIGNATURE, e); } } else { // HTTP-Redirect binding signature validation ! try { signer.validateQueryString(saml2SpMd, state.getTransientVariable("SAMLRequest"), state.getTransientVariable("RelayState"), state.getTransientVariable("SigAlg"), state.getTransientVariable("Signature"), false); } catch (SamlR2SignatureValidationException e) { throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.INVALID_RESPONSE_SIGNATURE, e); } catch (SamlR2SignatureException e) { //other exceptions like JAXB, xml parser... throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.INVALID_RESPONSE_SIGNATURE, e); } } } if (mediator.isVerifyUniqueIDs() && mediator.getIdRegistry().isUsed(request.getID())) { if (logger.isDebugEnabled()) logger.debug("Duplicated SAML ID " + request.getID()); throw new SSORequestException(request, StatusCode.TOP_REQUESTER, StatusCode.REQUEST_DENIED, StatusDetails.DUPLICATED_ID ); } // TODO : Validate destination, etc!!! } /** * This has the logic to select endpoints for claims collecting. */ protected ClaimChannel selectNextClaimsEndpoint(AuthenticationState status, CamelMediationExchange exchange) { SSOIDPMediator idpMediator = (SSOIDPMediator) channel.getIdentityMediator(); IdentityFlowContainer ifc = idpMediator.getIdentityFlowContainer(); try { IdentityFlowResponse response = ifc.dispatch( idpMediator.getClaimEndpointSelection(), exchange, getProvider(), channel, endpoint, null ); if (response.statusCode() instanceof RedirectToEndpoint) { logger.debug("Got redirect response : " + response); RedirectToEndpoint redirect = (RedirectToEndpoint) response.statusCode(); return (ClaimChannel) redirect.channel(); } } catch (RouteRejectionException e) { logger.debug(e.getMessage(), e); return null; } return null; } /** * This has the logic to select endpoints for identity confirmation. */ protected IdentityConfirmationChannel selectNextIdentityConfirmationEndpoint(AuthenticationState status, CamelMediationExchange exchange, ClaimSet claims) { IdentityProvider idp = (IdentityProvider)getProvider(); SSOIDPMediator idpMediator = (SSOIDPMediator) channel.getIdentityMediator(); IdentityFlowContainer ifc = idpMediator.getIdentityFlowContainer(); try { IdentityFlowResponse response = ifc.dispatch( idp.getIdentityConfirmationPolicy(), exchange, getProvider(), channel, endpoint, claims ); if (response.statusCode() instanceof RedirectToEndpoint) { logger.debug("Got redirect response : " + response); RedirectToEndpoint redirect = (RedirectToEndpoint) response.statusCode(); return (IdentityConfirmationChannel) redirect.channel(); } else if (response.statusCode() instanceof NoFurtherActionRequired) { logger.debug("Skipping identity confirmation"); } } catch (RouteRejectionException e) { logger.debug(e.getMessage(), e); return null; } return null; } protected SamlR2SecurityTokenEmissionContext emitAssertionFromPreviousSession(CamelMediationExchange exchange, SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx, AuthnRequestType authnRequest, IdPSecurityContext secCtx) throws Exception { // TODO : We need to use the STS ..., and get ALL the required tokens again. // TODO : Set in assertion AuthnCtxClass.PREVIOUS_SESSION_AUTHN_CTX ClaimSet claims = new ClaimSetImpl(); UsernameTokenType usernameToken = new UsernameTokenType(); for (Iterator<Principal> iterator = secCtx.getSubject().getPrincipals().iterator(); iterator.hasNext(); ) { Principal next = iterator.next(); if (next instanceof SimplePrincipal) { SimplePrincipal principal = (SimplePrincipal) next; // Get previously used authn-ctx class AuthnCtxClass authnCtx = null; List<JAXBElement<?>> c = secCtx.getAuthnStatement().getAuthnContext().getContent(); if (c != null && c.size() > 0) { for (JAXBElement e : c) { if (e.getName().getLocalPart().equals("AuthnContextClassRef")) { authnCtx = AuthnCtxClass.asEnum((String) e.getValue()); break; } } } if (authnCtx == null) { logger.warn("No previous authentication context class, forcing Password"); authnCtx = AuthnCtxClass.PASSWORD_AUTHN_CTX; } AttributedString usernameString = new AttributedString(); usernameString.setValue(principal.getName()); usernameToken.setUsername(usernameString); usernameToken.getOtherAttributes().put(new QName(Constants.PASSWORD_NS), principal.getName()); usernameToken.getOtherAttributes().put(new QName(authnCtx.getValue()), "TRUE"); usernameToken.getOtherAttributes().put(new QName(Constants.PREVIOUS_SESSION_NS), "TRUE"); RequestedAuthnContextType reqAuthn = authnRequest.getRequestedAuthnContext(); if (reqAuthn != null) { // TODO : We should honor the originally requested authentication context! logger.warn("Requested Authentication context class ignored !!!! " + reqAuthn); } //CredentialClaim credentialClaim = new CredentialClaimImpl(AuthnCtxClass.PASSWORD_AUTHN_CTX.getValue(), usernameToken); CredentialClaim credentialClaim = new CredentialClaimImpl(authnCtx.getValue(), usernameToken); claims.addClaim(credentialClaim); } } securityTokenEmissionCtx = emitAssertionFromClaims(exchange, securityTokenEmissionCtx, claims, securityTokenEmissionCtx.getMember()); AssertionType assertion = securityTokenEmissionCtx.getAssertion(); //Subject authnSubject = securityTokenEmissionCtx.getSubject(); logger.debug("New Assertion " + assertion.getID() + " emitted form request " + (authnRequest != null ? authnRequest.getID() : "<NULL>")); return securityTokenEmissionCtx; } /** * This will return an emission context with both, the required SAMLR2 Assertion and the associated Subject. * * @return SamlR2 Security emission context containing SAMLR2 Assertion and Subject. */ protected SamlR2SecurityTokenEmissionContext emitAssertionFromClaims(CamelMediationExchange exchange, SamlR2SecurityTokenEmissionContext securityTokenEmissionCtx, ClaimSet receivedClaims, CircleOfTrustMemberDescriptor sp) throws Exception { MessageQueueManager aqm = getArtifactQueueManager(); // ------------------------------------------------------- // Emit a new security token // ------------------------------------------------------- // TODO : Improve communication mechanism between STS and IDP! // Queue this contenxt and send the artifact as RST context information Artifact emitterCtxArtifact = aqm.pushMessage(securityTokenEmissionCtx); SecurityTokenService sts = ((SPChannel) channel).getSecurityTokenService(); // Send artifact id as RST context information, similar to relay state. RequestSecurityTokenType rst = buildRequestSecurityToken(receivedClaims, emitterCtxArtifact.getContent()); if (logger.isDebugEnabled()) logger.debug("Requesting Security Token (RST) w/context " + rst.getContext()); // Send request to STS RequestSecurityTokenResponseType rstrt = sts.requestSecurityToken(rst); if (logger.isDebugEnabled()) logger.debug("Received Request Security Token Response (RSTR) w/context " + rstrt.getContext()); // Recover emission context, to get Subject information securityTokenEmissionCtx = (SamlR2SecurityTokenEmissionContext) aqm.pullMessage(ArtifactImpl.newInstance(rstrt.getContext())); /// Obtain assertion from STS Response JAXBElement<RequestedSecurityTokenType> token = (JAXBElement<RequestedSecurityTokenType>) rstrt.getAny().get(1); Subject subject = (Subject) rstrt.getAny().get(2); // Hard-coded subject position in response AssertionType assertion = (AssertionType) token.getValue().getAny(); if (logger.isDebugEnabled()) logger.debug("Generated SamlR2 Assertion " + assertion.getID()); securityTokenEmissionCtx.setAssertion(assertion); securityTokenEmissionCtx.setSubject(subject); SSOUser ssoUser = null; Set<SimplePrincipal> p = subject.getPrincipals(SimplePrincipal.class); if (p != null && p.size() > 0) { // We have a simple princiapl, Look for an SSOUser instance SimplePrincipal user = p.iterator().next(); SSOIdentityManager identityMgr = ((SPChannel) channel).getIdentityManager(); if (identityMgr != null) ssoUser = identityMgr.findUser(user.getName()); } else { Set<SSOUser> ssoUsers = subject.getPrincipals(SSOUser.class); if (ssoUsers != null && ssoUsers.size() > 0) { // We already have an SSOUser instance ssoUser = ssoUsers.iterator().next(); } } if (ssoUser != null) { // Make some validations on the SSO user for (SSONameValuePair nvp : ssoUser.getProperties()) { if (nvp.getName().equalsIgnoreCase("accountDisabled")) { boolean disabled = Boolean.parseBoolean(nvp.getValue()); if (disabled) { throw new SecurityTokenAuthenticationFailure("Account disabled"); } } } } // Return context with Assertion and Subject return securityTokenEmissionCtx; } protected IdPSecurityContext createSecurityContext(CamelMediationExchange exchange, Subject authnSubject, AssertionType assertion, ClaimSet claims) throws Exception { // ------------------------------------------------------- // Create the SSO Session, using authnStatusment as security token // ------------------------------------------------------- // The security token must store the AuthnStatmenet only, the rest of the assertion must be generated per request. AuthnStatementType authnStmt = null; for (StatementAbstractType stmt : assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement()) { if (stmt instanceof AuthnStatementType) { authnStmt = (AuthnStatementType) stmt; break; } } if (authnStmt == null) throw new SSOException("Assertion MUST contain an AuthnStatement"); // Create session security token, use the sesionIndex as token ID SecurityToken<AuthnStatementType> st = new SecurityTokenImpl<AuthnStatementType>(authnStmt.getSessionIndex(), authnStmt); // Get SSO User information (stored as simple principal!) Principal userId = authnSubject.getPrincipals(SimplePrincipal.class).iterator().next(); if (logger.isDebugEnabled()) logger.debug("Using username : " + userId.getName()); // Initiate SSO Session String ssoSessionId = ((SPChannel) channel).getSessionManager().initiateSession(userId.getName(), st); assert ssoSessionId.equals(st.getId()) : "SSO Session Manager MUST use security token ID as session ID"; boolean rememberMe = false; if (claims != null) { for (Claim c : claims.getClaims()) { if (c instanceof CredentialClaim) { CredentialClaim cc = (CredentialClaim) c; if (cc.getValue() instanceof UsernameTokenType) { UsernameTokenType ut = (UsernameTokenType) cc.getValue(); String rememberMeStr = ut.getOtherAttributes().get(new QName(Constants.REMEMBERME_NS)); if (rememberMeStr != null) rememberMe = Boolean.parseBoolean(rememberMeStr); } } } } if (rememberMe) { if (logger.isDebugEnabled()) logger.debug("Creating remember-me security contenxt information"); String preAuthnTokenId = null; for (StatementAbstractType stmt : assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement()) { if (stmt instanceof AttributeStatementType) { AttributeStatementType attrStmt = (AttributeStatementType) stmt; for (Object o : attrStmt.getAttributeOrEncryptedAttribute()) { // TODO : What if assertion is encrypted ?! (improve!) if (o instanceof AttributeType) { AttributeType attr = (AttributeType) o; if (attr.getName().equals(WSTConstants.WST_OAUTH2_TOKEN_TYPE + "_ID")) { preAuthnTokenId = (String) attr.getAttributeValue().get(0); } } } } } if (preAuthnTokenId != null) { String varName = getProvider().getStateManager().getNamespace().toUpperCase() + "_" + getProvider().getName().toUpperCase() + "_RM"; if (logger.isDebugEnabled()) logger.debug("Creating remote variable (" + varName + ") with token id " + preAuthnTokenId); CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); MediationState state = in.getMessage().getState(); state.setRemoteVariable(varName, preAuthnTokenId, System.currentTimeMillis() + (1000L * 60L * 60L * 24L * 30L)); // TODO : Configure! } } return new IdPSecurityContext(authnSubject, ssoSessionId, authnStmt); } /** * Creates a new SAML Response for the given assertion */ protected ResponseType buildSamlResponse(CamelMediationExchange exchange, AuthenticationState authnState, AssertionType assertion, CircleOfTrustMemberDescriptor sp, EndpointDescriptor spEndpoint) throws Exception { // Build authnresponse IdentityPlan identityPlan = findIdentityPlanOfType(SamlR2AuthnRequestToSamlR2ResponsePlan.class); IdentityPlanExecutionExchange idPlanExchange = createIdentityPlanExecutionExchange(); // Publish SP springmetadata idPlanExchange.setProperty(VAR_DESTINATION_COT_MEMBER, sp); idPlanExchange.setProperty(VAR_SAMLR2_ASSERTION, assertion); idPlanExchange.setProperty(VAR_DESTINATION_ENDPOINT_DESCRIPTOR, spEndpoint); idPlanExchange.setProperty(VAR_REQUEST, authnState.getAuthnRequest()); idPlanExchange.setProperty(VAR_RESPONSE_MODE, authnState.getResponseMode()); // Create in/out artifacts IdentityArtifact<AuthnRequestType> in = new IdentityArtifactImpl<AuthnRequestType>(new QName(SAMLR2Constants.SAML_PROTOCOL_NS, "AuthnRequest"), authnState.getAuthnRequest()); idPlanExchange.setIn(in); IdentityArtifact<ResponseType> out = new IdentityArtifactImpl<ResponseType>(new QName(SAMLR2Constants.SAML_PROTOCOL_NS, "Response"), new ResponseType()); idPlanExchange.setOut(out); // Prepare execution identityPlan.prepare(idPlanExchange); // Perform execution identityPlan.perform(idPlanExchange); if (!idPlanExchange.getStatus().equals(IdentityPlanExecutionStatus.SUCCESS)) { throw new SecurityTokenEmissionException("Identity plan returned : " + idPlanExchange.getStatus()); } if (idPlanExchange.getOut() == null) throw new SecurityTokenEmissionException("Plan Exchange OUT must not be null!"); return (ResponseType) idPlanExchange.getOut().getContent(); } /** * Build an AuthnRequest for the target SP to which IDP's unsollicited response needs to be pushed to. */ protected AuthnRequestType buildIdPInitiatedAuthnRequest(CamelMediationExchange exchange, IDPInitiatedAuthnRequestType ssoAuthnRequest, CircleOfTrustMemberDescriptor idp, EndpointDescriptor ed, FederationChannel spChannel ) throws IdentityPlanningException, SSOException { IdentityPlan identityPlan = findIdentityPlanOfType(IDPInitiatedAuthnReqToSamlR2AuthnReqPlan.class); IdentityPlanExecutionExchange idPlanExchange = createIdentityPlanExecutionExchange(); // Publish IdP Metadata idPlanExchange.setProperty(VAR_DESTINATION_COT_MEMBER, idp); idPlanExchange.setProperty(VAR_DESTINATION_ENDPOINT_DESCRIPTOR, ed); idPlanExchange.setProperty(VAR_COT_MEMBER, spChannel.getMember()); idPlanExchange.setProperty(VAR_RESPONSE_CHANNEL, spChannel); // Create in/out artifacts IdentityArtifact in = new IdentityArtifactImpl(new QName("urn:org:atricore:idbus:sso:protocol", "IDPInitiatedAuthnRequest"), ssoAuthnRequest); idPlanExchange.setIn(in); IdentityArtifact<AuthnRequestType> out = new IdentityArtifactImpl<AuthnRequestType>(new QName(SAMLR2Constants.SAML_PROTOCOL_NS, "AuthnRequest"), new AuthnRequestType()); idPlanExchange.setOut(out); // Prepare execution identityPlan.prepare(idPlanExchange); // Perform execution identityPlan.perform(idPlanExchange); if (!idPlanExchange.getStatus().equals(IdentityPlanExecutionStatus.SUCCESS)) { throw new SecurityTokenEmissionException("Identity plan returned : " + idPlanExchange.getStatus()); } if (idPlanExchange.getOut() == null) throw new SecurityTokenEmissionException("Plan Exchange OUT must not be null!"); return (AuthnRequestType) idPlanExchange.getOut().getContent(); } /** * Build an AuthnRequest for the target SP to which IDP's unsollicited response needs to be pushed to. */ protected PreAuthenticatedAuthnRequestType buildPreAuthIdPInitiatedAuthnRequest(CamelMediationExchange exchange, CircleOfTrustMemberDescriptor idp, EndpointDescriptor ed, FederationChannel spChannel ) throws IdentityPlanningException, SSOException { IdentityPlan identityPlan = findIdentityPlanOfType(IDPInitiatedAuthnReqToSamlR2AuthnReqPlan.class); IdentityPlanExecutionExchange idPlanExchange = createIdentityPlanExecutionExchange(); // Publish IdP Metadata idPlanExchange.setProperty(VAR_DESTINATION_COT_MEMBER, idp); idPlanExchange.setProperty(VAR_DESTINATION_ENDPOINT_DESCRIPTOR, ed); idPlanExchange.setProperty(VAR_COT_MEMBER, spChannel.getMember()); idPlanExchange.setProperty(VAR_RESPONSE_CHANNEL, spChannel); // Get SPInitiated authn request, if any! PreAuthenticatedIDPInitiatedAuthnRequestType ssoAuthnRequest = (PreAuthenticatedIDPInitiatedAuthnRequestType) ((CamelMediationMessage) exchange.getIn()).getMessage().getContent(); // Create in/out artifacts IdentityArtifact in = new IdentityArtifactImpl(new QName("urn:org:atricore:idbus:sso:protocol", "PreAuthenticatedIDPInitiatedAuthnRequest"), ssoAuthnRequest); idPlanExchange.setIn(in); IdentityArtifact<AuthnRequestType> out = new IdentityArtifactImpl<AuthnRequestType>(new QName(SAMLR2Constants.SAML_PROTOCOL_NS, "PreAuthenticatedAuthnRequest"), new PreAuthenticatedAuthnRequestType()); idPlanExchange.setOut(out); // Prepare execution identityPlan.prepare(idPlanExchange); // Perform execution identityPlan.perform(idPlanExchange); if (!idPlanExchange.getStatus().equals(IdentityPlanExecutionStatus.SUCCESS)) { throw new SecurityTokenEmissionException("Identity plan returned : " + idPlanExchange.getStatus()); } if (idPlanExchange.getOut() == null) throw new SecurityTokenEmissionException("Plan Exchange OUT must not be null!"); return (PreAuthenticatedAuthnRequestType) idPlanExchange.getOut().getContent(); } protected CircleOfTrustMemberDescriptor resolveProviderDescriptor(FederatedProvider sp) { FederatedLocalProvider spl = (FederatedLocalProvider) sp; for (FederationChannel fChannel : spl.getChannels()) { if (fChannel.getTargetProvider() != null) { if (fChannel.getTargetProvider().getName().equals(((SPChannel) channel).getFederatedProvider().getName())) { if (logger.isTraceEnabled()) logger.trace("Selected SP Channel " + fChannel.getName() + " from provider " + sp); return fChannel.getMember(); } } } if (logger.isTraceEnabled()) logger.trace("Selected SP Channel " + spl.getChannel().getName() + " from provider " + sp); // Use default channel return spl.getChannel().getMember(); } protected CircleOfTrustMemberDescriptor resolveProviderDescriptor(NameIDType issuer) { if (issuer.getFormat() != null && !issuer.getFormat().equals(NameIDFormat.ENTITY.getValue())) { logger.warn("Invalid issuer format for entity : " + issuer.getFormat()); return null; } return getCotManager().lookupMemberByAlias(issuer.getValue()); } protected EndpointDescriptor resolveSpAcsEndpoint(CamelMediationExchange exchange, AuthnRequestType authnRequest) throws SSOException { try { String requestedBinding = authnRequest.getProtocolBinding(); if (logger.isDebugEnabled()) logger.debug("Requested binding/service" + authnRequest.getProtocolBinding() + "/" + authnRequest.getAssertionConsumerServiceURL()); CircleOfTrust cot = this.getCot(); CircleOfTrustMemberDescriptor sp = resolveProviderDescriptor(authnRequest.getIssuer()); CircleOfTrustManager cotMgr = ((SPChannel) channel).getFederatedProvider().getCotManager(); MetadataEntry md = cotMgr.findEntityRoleMetadata(sp.getAlias(), "urn:oasis:names:tc:SAML:2.0:metadata:SPSSODescriptor"); SPSSODescriptorType samlr2sp = (SPSSODescriptorType) md.getEntry(); IndexedEndpointType acEndpoint = null; IndexedEndpointType defaultAcEndpoint = null; IndexedEndpointType postAcEndpoint = null; IndexedEndpointType artifactAcEndpoint = null; for (IndexedEndpointType ac : samlr2sp.getAssertionConsumerService()) { if (authnRequest.getAssertionConsumerServiceIndex() != null && authnRequest.getAssertionConsumerServiceIndex() >= 0) { if (ac.getIndex() == authnRequest.getAssertionConsumerServiceIndex()) { if (ac.getBinding().equals(SSOBinding.SAMLR2_REDIRECT.getValue())) { logger.warn("Invalid requested ACS location at " + ac.getLocation() + ", Ignoring endpoint" ); } else { acEndpoint = ac; break; } } } if (authnRequest.getAssertionConsumerServiceURL() != null) { if (ac.getLocation().equals(authnRequest.getAssertionConsumerServiceURL())) { if (ac.getBinding().equals(SSOBinding.SAMLR2_REDIRECT.getValue())) { logger.warn("Invalid requested ACS location at " + ac.getLocation() + ", Ignoring endpoint" ); } else { acEndpoint = ac; break; } } } if (ac.getIsDefault() != null && ac.getIsDefault()) { if (ac.getBinding().equals(SSOBinding.SAMLR2_REDIRECT.getValue())) { logger.warn("Invalid default SP ACS Binding at " + ac.getLocation() + ", Ignoring endpoint" ); } else { defaultAcEndpoint = ac; } } if (ac.getBinding().equals(SSOBinding.SAMLR2_POST.getValue())) postAcEndpoint = ac; if (ac.getBinding().equals(SSOBinding.SAMLR2_ARTIFACT.getValue())) artifactAcEndpoint = ac; if (requestedBinding != null && ac.getBinding().equals(requestedBinding)) { if (ac.getBinding().equals(SSOBinding.SAMLR2_REDIRECT.getValue())) { logger.warn("Invalid Requested ACS Binding at " + ac.getLocation() + ", Ignoring endpoint" ); } else { acEndpoint = ac; } } } if (acEndpoint == null) acEndpoint = defaultAcEndpoint; if (acEndpoint == null) acEndpoint = artifactAcEndpoint; if (acEndpoint == null) acEndpoint = postAcEndpoint; if (acEndpoint == null) throw new SSOException("Cannot resolve response SP SSO endpoint for " + sp.getAlias()); if (logger.isTraceEnabled()) logger.trace("Resolved ACS endpoint " + acEndpoint.getLocation() + "/" + acEndpoint.getBinding()); return new EndpointDescriptorImpl(acEndpoint.getBinding(), SSOService.AssertionConsumerService.toString(), acEndpoint.getBinding(), acEndpoint.getLocation(), acEndpoint.getResponseLocation()); } catch (CircleOfTrustManagerException e) { throw new SSOException(e); } } protected CircleOfTrustMemberDescriptor resolveIdp(CamelMediationExchange exchange) throws SSOException { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); IDPInitiatedAuthnRequestType ssoAuthnReq = (IDPInitiatedAuthnRequestType) in.getMessage().getContent(); // TODO : The way to resolve the IDP may vary from deployment to deployment, user intervention may be required String idpAlias = null; CircleOfTrustMemberDescriptor idp = null; // -------------------------------------------------------------- // Try with the received IdP alias, if any // -------------------------------------------------------------- for (int i = 0; i < ssoAuthnReq.getRequestAttribute().size(); i++) { RequestAttributeType a = ssoAuthnReq.getRequestAttribute().get(i); // TODO : [ENTITY-SEL] CHECK BASE 64 ENCODING AND ENTITY SELECTOR USAGE! if (a.getName().equals(EntitySelectorConstants.REQUESTED_IDP_ALIAS_ATTR)) idpAlias = new String(Base64.decodeBase64(a.getValue().getBytes())); } if (idpAlias != null) { if (logger.isDebugEnabled()) logger.debug("Using IdP alias from request attribute " + idpAlias); idp = getCotManager().lookupMemberByAlias(idpAlias); if (idp == null) { throw new SSOException("No IDP found in circle of trust for received alias [" + idpAlias + "], verify your setup."); } } if (idp != null) return idp; // -------------------------------------------------------------- // Try with the preferred idp alias, if any // -------------------------------------------------------------- SSOIDPMediator mediator = (SSOIDPMediator) channel.getIdentityMediator(); idpAlias = mediator.getPreferredIdpAlias(); if (idpAlias != null) { if (logger.isDebugEnabled()) logger.debug("Using preferred IdP alias " + idpAlias); idp = getCotManager().lookupMemberByAlias(idpAlias); if (idp == null) { throw new SSOException("No IDP found in circle of trust for preferred alias [" + idpAlias + "], verify your setup."); } } if (idp != null) return idp; // -------------------------------------------------------------- // Fallback to the local IdP definition for this SP Channel // -------------------------------------------------------------- return ((FederationChannel) channel).getMember(); } protected FederationChannel resolveIdpChannel(CircleOfTrustMemberDescriptor idpDescriptor) { // Resolve IdP channel, then look for the ACS endpoint SPChannel spChannel = (SPChannel) channel; FederatedLocalProvider sp = spChannel.getFederatedProvider(); FederationChannel idpChannel = sp.getChannel(); for (FederationChannel fChannel : sp.getChannels()) { FederatedProvider idp = fChannel.getTargetProvider(); for (CircleOfTrustMemberDescriptor member : idp.getMembers()) { if (member.getAlias().equals(idpDescriptor.getAlias())) { if (logger.isDebugEnabled()) logger.debug("Selected IdP channel " + fChannel.getName() + " for provider " + idp.getName()); idpChannel = fChannel; break; } } } return idpChannel; } protected EndpointType resolveIdpSsoEndpoint(CircleOfTrustMemberDescriptor idp) throws SSOException { SSOIDPMediator mediator = (SSOIDPMediator) channel.getIdentityMediator(); SSOBinding preferredBinding = mediator.getPreferredIdpSSOBindingValue(); MetadataEntry idpMd = idp.getMetadata(); if (idpMd == null || idpMd.getEntry() == null) throw new SSOException("No metadata descriptor found for IDP " + idp); if (idpMd.getEntry() instanceof EntityDescriptorType) { EntityDescriptorType md = (EntityDescriptorType) idpMd.getEntry(); for (RoleDescriptorType role : md.getRoleDescriptorOrIDPSSODescriptorOrSPSSODescriptor()) { if (role instanceof IDPSSODescriptorType) { IDPSSODescriptorType idpSsoRole = (IDPSSODescriptorType) role; EndpointType defaultEndpoint = null; for (EndpointType idpSsoEndpoint : idpSsoRole.getSingleSignOnService()) { SSOBinding b = SSOBinding.asEnum(idpSsoEndpoint.getBinding()); if (b.equals(preferredBinding)) return idpSsoEndpoint; if (b.equals(SSOBinding.SAMLR2_ARTIFACT)) defaultEndpoint = idpSsoEndpoint; if (defaultEndpoint == null) defaultEndpoint = idpSsoEndpoint; } return defaultEndpoint; } } } else { throw new SSOException("Unknown metadata descriptor type " + idpMd.getEntry().getClass().getName()); } logger.debug("No IDP Endpoint supporting binding : " + preferredBinding); throw new SSOException("IDP does not support preferred binding " + preferredBinding); } protected EndpointDescriptor resolveIdpSsoContinueEndpoint() { // Sso 'continue' endpoint for policy enforcement responses String location = endpoint.getLocation(); if (location.startsWith("/")) location = channel.getLocation() + location; EndpointDescriptor ed = new EndpointDescriptorImpl(endpoint.getName(), endpoint.getType(), SSOBinding.SSO_ARTIFACT.getValue(), location, null); if (logger.isTraceEnabled()) logger.trace("Resolved IDP SSO 'Continue' endpoint to " + ed); return ed; } /** * Create a new RSTR based on the received claims. * * @param claims the claims sent by the user. * @param context the context string used in the request. */ protected RequestSecurityTokenType buildRequestSecurityToken(ClaimSet claims, String context) throws Exception { if (logger.isDebugEnabled()) logger.debug("generating RequestSecurityToken..."); org.xmlsoap.schemas.ws._2005._02.trust.ObjectFactory of = new org.xmlsoap.schemas.ws._2005._02.trust.ObjectFactory(); RequestSecurityTokenType rstRequest = new RequestSecurityTokenType(); rstRequest.getAny().add(of.createTokenType(WSTConstants.WST_SAMLR2_TOKEN_TYPE)); rstRequest.getAny().add(of.createRequestType(WSTConstants.WST_ISSUE_REQUEST)); org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.ObjectFactory ofwss = new org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.ObjectFactory(); for (Claim c : claims.getClaims()) { if (!(c instanceof CredentialClaim)) { if (logger.isTraceEnabled()) logger.trace("Ignoring non-credential claim " + c); continue; } CredentialClaim credentialClaim = (CredentialClaim) c; if (logger.isDebugEnabled()) logger.debug("Adding Claim : " + credentialClaim.getQualifier() + " of type " + credentialClaim.getValue().getClass().getName()); Object claimObj = credentialClaim.getValue(); if (claimObj instanceof UsernameTokenType) { rstRequest.getAny().add(ofwss.createUsernameToken((UsernameTokenType) credentialClaim.getValue())); } else if (claimObj instanceof BinarySecurityTokenType) { rstRequest.getAny().add(ofwss.createBinarySecurityToken((BinarySecurityTokenType) credentialClaim.getValue())); } else if (claimObj instanceof PasswordString) { rstRequest.getAny().add(ofwss.createPassword((PasswordString) credentialClaim.getValue())); } else { throw new SSOException("Claim type not supported " + claimObj.getClass().getName()); } } if (context != null) rstRequest.setContext(context); if (logger.isDebugEnabled()) logger.debug("generated RequestSecurityToken [" + rstRequest + "]"); return rstRequest; } protected MessageQueueManager getArtifactQueueManager() { SSOIDPMediator mediator = (SSOIDPMediator) channel.getIdentityMediator(); return mediator.getArtifactQueueManager(); } protected AuthenticationState newAuthnState(CamelMediationExchange exchange) { logger.debug("Creating new AuthenticationState"); AuthenticationState state = new AuthenticationState(); CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:samlr2:idp:authn-state", state); return state; } protected AuthenticationState getAuthnState(CamelMediationExchange exchange) { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); AuthenticationState state = null; try { state = (AuthenticationState) in.getMessage().getState().getLocalVariable("urn:org:atricore:idbus:samlr2:idp:authn-state"); if (logger.isTraceEnabled()) logger.trace("Using existing AuthnState " + state); } catch (IllegalStateException e) { // This binding does not support provider state ... if (logger.isDebugEnabled()) logger.debug("Provider state not supported " + e.getMessage()); if (logger.isTraceEnabled()) logger.trace(e.getMessage(), e); state = new AuthenticationState(); } if (state == null) { state = newAuthnState(exchange); } return state; } protected void clearAuthnState(CamelMediationExchange exchange) { CamelMediationMessage in = (CamelMediationMessage) exchange.getIn(); in.getMessage().getState().removeLocalVariable("urn:org:atricore:idbus:samlr2:idp:authn-state"); in.getMessage().getState().removeLocalVariable("urn:org:atricore:idbus:sso:protocol:responseMode"); in.getMessage().getState().removeLocalVariable("urn:org:atricore:idbus:sso:protocol:responseFormat"); } protected oasis.names.tc.saml._1_0.protocol.ResponseType transformSamlR2ResponseToSaml11(ResponseType responseType) { oasis.names.tc.saml._1_0.assertion.ObjectFactory saml11AssertionObjectFactory = new oasis.names.tc.saml._1_0.assertion.ObjectFactory(); oasis.names.tc.saml._1_0.protocol.ResponseType saml11Response = new oasis.names.tc.saml._1_0.protocol.ResponseType(); // Response envelope saml11Response.setIssueInstant(responseType.getIssueInstant()); saml11Response.setMinorVersion(BigInteger.valueOf(1)); saml11Response.setMajorVersion(BigInteger.valueOf(1)); saml11Response.setRecipient(responseType.getDestination()); saml11Response.setResponseID(responseType.getID()); saml11Response.setInResponseTo(responseType.getInResponseTo()); // Status oasis.names.tc.saml._1_0.protocol.StatusType saml11ResponseStatus = new oasis.names.tc.saml._1_0.protocol.StatusType(); oasis.names.tc.saml._1_0.protocol.StatusCodeType saml11ResponseStatusCode = new oasis.names.tc.saml._1_0.protocol.StatusCodeType(); if (responseType.getStatus().getStatusCode().getValue().equals("urn:oasis:names:tc:SAML:2.0:status:Success")) { saml11ResponseStatusCode.setValue(new QName("urn:oasis:names:tc:SAML:1.0:protocol", "Success")); } // TODO: map the complete set of supported status codes saml11ResponseStatus.setStatusCode(saml11ResponseStatusCode); saml11Response.setStatus(saml11ResponseStatus); // Assertion for (Object aoe : responseType.getAssertionOrEncryptedAssertion()) { if (aoe instanceof oasis.names.tc.saml._2_0.assertion.AssertionType) { AssertionType saml2Assertion = (oasis.names.tc.saml._2_0.assertion.AssertionType) aoe; oasis.names.tc.saml._1_0.assertion.AssertionType saml11Assertion = new oasis.names.tc.saml._1_0.assertion.AssertionType(); saml11Assertion.setMinorVersion(BigInteger.valueOf(1)); saml11Assertion.setMajorVersion(BigInteger.valueOf(1)); saml11Assertion.setAssertionID(saml2Assertion.getID()); saml11Assertion.setIssuer(saml2Assertion.getIssuer().getValue()); saml11Assertion.setIssueInstant(saml2Assertion.getIssueInstant()); // Assertion's conditions ConditionsType saml2Conditions = saml2Assertion.getConditions(); oasis.names.tc.saml._1_0.assertion.ConditionsType saml11Conditions = new oasis.names.tc.saml._1_0.assertion.ConditionsType(); saml11Conditions.setNotBefore(saml2Conditions.getNotBefore()); saml11Conditions.setNotOnOrAfter(saml2Conditions.getNotOnOrAfter()); saml11Assertion.setConditions(saml11Conditions); for (ConditionAbstractType cond : saml2Conditions.getConditionOrAudienceRestrictionOrOneTimeUse()) { if (cond instanceof AudienceRestrictionType) { AudienceRestrictionType saml2ar = (AudienceRestrictionType) cond; oasis.names.tc.saml._1_0.assertion.AudienceRestrictionConditionType saml11arc = new AudienceRestrictionConditionType(); for (String audience : saml2ar.getAudience()) { saml11arc.getAudience().add(audience); } saml11Conditions.getAudienceRestrictionConditionOrDoNotCacheConditionOrCondition().add(saml11arc); break; } // TODO: transform remaining conditions } // Assertion's authentication statement for (StatementAbstractType s : saml2Assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement()) { if (s instanceof AuthnStatementType) { AuthnStatementType saml2authnStatement = (AuthnStatementType) s; oasis.names.tc.saml._1_0.assertion.AuthenticationStatementType saml11authnStatement = new oasis.names.tc.saml._1_0.assertion.AuthenticationStatementType(); oasis.names.tc.saml._1_0.assertion.AttributeStatementType saml11attrStatement = new oasis.names.tc.saml._1_0.assertion.AttributeStatementType(); // Subject goes at the authn statement level instead of the assertion one saml11authnStatement.setAuthenticationInstant(saml2authnStatement.getAuthnInstant()); // extract Subject's Authentication Context and map it to the Subject's Authn Method AuthnContextType saml2AuthnContext = saml2authnStatement.getAuthnContext(); if (saml2AuthnContext.getContent().size() > 0) { JAXBElement acc = saml2AuthnContext.getContent().get(0); String saml2authnCtxClassRef = (String) acc.getValue(); if (saml2authnCtxClassRef.equals(AuthnCtxClass.PASSWORD_AUTHN_CTX.getValue())) { saml11authnStatement.setAuthenticationMethod("urn:oasis:names:tc:SAML:1.0:am:password"); } // TODO: map remaining authentication context classes types } else { saml11authnStatement.setAuthenticationMethod("urn:oasis:names:tc:SAML:1.0:am:unspecified"); } // Embed Assertion's Subject within Authentication Statement SubjectType saml2Subject = saml2Assertion.getSubject(); oasis.names.tc.saml._1_0.assertion.SubjectType saml11Subject = new oasis.names.tc.saml._1_0.assertion.SubjectType(); for (JAXBElement sc : saml2Subject.getContent()) { Object scv = sc.getValue(); if (scv instanceof NameIDType) { NameIDType saml2nameid = (NameIDType) scv; oasis.names.tc.saml._1_0.assertion.NameIdentifierType saml11nameid = new oasis.names.tc.saml._1_0.assertion.NameIdentifierType(); //TODO: map nameid formats saml11nameid.setNameQualifier(saml2nameid.getNameQualifier()); saml11nameid.setValue(saml2nameid.getValue()); saml11Subject.getContent().add(saml11AssertionObjectFactory.createNameIdentifier(saml11nameid)); } else if (scv instanceof SubjectConfirmationType) { SubjectConfirmationType saml2subjectConfirmation = (SubjectConfirmationType) scv; oasis.names.tc.saml._1_0.assertion.SubjectConfirmationType saml11subjectConfirmation = new oasis.names.tc.saml._1_0.assertion.SubjectConfirmationType(); // Subject Confirmation Methods if (saml2subjectConfirmation.getMethod().equals("urn:oasis:names:tc:SAML:2.0:cm:bearer")) { saml11subjectConfirmation.getConfirmationMethod().add("urn:oasis:names:tc:SAML:1.0:cm:bearer"); saml11Subject.getContent().add(saml11AssertionObjectFactory.createSubjectConfirmation(saml11subjectConfirmation)); } } } // create attr statement's attribute saml11authnStatement.setSubject(saml11Subject); saml11Assertion.getStatementOrSubjectStatementOrAuthenticationStatement().add(saml11authnStatement); } // TODO: map remaining statement types (e.g. Attribute Statements) } saml11Response.getAssertion().add(saml11Assertion); } } return saml11Response; } protected List<SSOPolicyEnforcementStatement> getPolicyEnforcementStatements(AssertionType assertion) { List<SSOPolicyEnforcementStatement> policyStatements = new ArrayList<SSOPolicyEnforcementStatement>(); List<StatementAbstractType> stmts = assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement(); if (stmts == null) return policyStatements; for (StatementAbstractType stmt : stmts) { if (stmt instanceof AttributeStatementType) { AttributeStatementType attrStmt = (AttributeStatementType) stmt; if (attrStmt.getAttributeOrEncryptedAttribute() == null) continue; for (Object attrOrEncAttr : attrStmt.getAttributeOrEncryptedAttribute()) { if (attrOrEncAttr == null) continue; if (attrOrEncAttr instanceof AttributeType) { // TODO : Make this 'dynamic' to decouple from specific policy types AttributeType attr = (AttributeType) attrOrEncAttr; SSOPasswordPolicyEnforcement policy = null; if (attr.getName().startsWith(PasswordPolicyEnforcementWarning.NAMESPACE)) { if (logger.isTraceEnabled()) logger.trace("Processing Password Policy Warning statement : " + attr.getFriendlyName()); policy = new PasswordPolicyEnforcementWarning(PasswordPolicyWarningType.fromName(attr.getFriendlyName())); if (attr.getAttributeValue() != null) { if (logger.isTraceEnabled()) logger.trace("Processing password policy warning statement values, total " + attr.getAttributeValue().size()); policy.getValues().addAll(attr.getAttributeValue()); } } else if (attr.getName().startsWith(PasswordPolicyEnforcementError.NAMESPACE)) { if (logger.isTraceEnabled()) logger.trace("Processing password policy error statement : " + attr.getFriendlyName()); policy = new PasswordPolicyEnforcementError(PasswordPolicyErrorType.fromName(attr.getFriendlyName())); } else { // What other policies can we handle ?!? if (logger.isTraceEnabled()) logger.trace("Ignoring non-password policy statement : " + attr.getName()); } if (policy != null) policyStatements.add(policy); } else { logger.warn("Unsupported Attribute Type " + attrOrEncAttr.getClass().getName()); } } } } return policyStatements; } protected String getSTSPlanName() throws SSOException { Map<String, SamlR2SecurityTokenToAuthnAssertionPlan> stsPlans = applicationContext.getBeansOfType(SamlR2SecurityTokenToAuthnAssertionPlan.class); SamlR2SecurityTokenToAuthnAssertionPlan stsPlan = null; for (IdentityPlan plan : endpoint.getIdentityPlans()) { if (plan instanceof SamlR2SecurityTokenToAuthnAssertionPlan) { stsPlan = (SamlR2SecurityTokenToAuthnAssertionPlan) plan; break; } } if (stsPlan == null) throw new SSOException("No valid STS Plan found, looking for SamlR2SecurityTokenToAuthnAssertionPlan instances"); for (String planName : stsPlans.keySet()) { SamlR2SecurityTokenToAuthnAssertionPlan registeredStsPlan = stsPlans.get(planName); // We need to know that it is the same instance! if (registeredStsPlan == stsPlan) { if (logger.isTraceEnabled()) logger.trace("Using STS plan : " + planName); return planName; } } logger.warn("No STS plan found for endpoint : " + endpoint.getName()); return null; } private EndpointDescriptor resolveSPInitiatedSSOProxyEndpointDescriptor(CamelMediationExchange exchange, Channel proxyChannel) throws SSOException { try { if (logger.isDebugEnabled()) logger.debug("Looking for " + SSOService.SPInitiatedSingleSignOnServiceProxy.toString() + " at " + proxyChannel.getName()); for (IdentityMediationEndpoint endpoint : proxyChannel.getEndpoints()) { if (logger.isTraceEnabled()) logger.trace("Processing endpoint : " + endpoint.getType() + "[" + endpoint.getBinding() + "] from " + proxyChannel.getName()); if (endpoint.getType().equals(SSOService.SPInitiatedSingleSignOnServiceProxy.toString())) { if (endpoint.getBinding().equals(SSOBinding.SSO_ARTIFACT.getValue())) { if (logger.isDebugEnabled()) logger.debug("Found SP Initiated SSO Service endpoint : " + endpoint.getName()); // This is the endpoint we're looking for return proxyChannel.getIdentityMediator().resolveEndpoint(proxyChannel, endpoint); } } } } catch (IdentityMediationException e) { throw new SSOException(e); } throw new SSOException("No SP Initiated SSO Proxy endpoint found for SP Initiated SSO using SSO Artifact binding for channel " + proxyChannel.getName()); } /** * Creates an Authentication Proxy Request which is essentially - at least as the current release - * * @return */ protected SPInitiatedAuthnRequestType buildAuthnProxyRequest(AuthnRequestType source) { SPInitiatedAuthnRequestType target = new SPInitiatedAuthnRequestType(); target.setID(uuidGenerator.generateId()); target.setPassive(source.getIsPassive() != null ? source.getIsPassive() : false); target.setForceAuthn(source.getForceAuthn() != null ? source.getForceAuthn() : false); if (source.getIssuer() != null) { NameIDType issuer = source.getIssuer(); // TODO : Other issuer values may be useful here target.setIssuer(issuer.getValue()); } // Set our proxy endpoint here! EndpointDescriptor idpAcsProxy = resolveIdPProxyAcsEndpoint(); if (idpAcsProxy != null) target.setReplyTo(idpAcsProxy.getResponseLocation() != null ? idpAcsProxy.getResponseLocation() : idpAcsProxy.getLocation()); else logger.error("No ProxyAssertionConsumerService endpoint found for IDP channel " + channel.getName()); return target; } protected EndpointDescriptor resolveIdPProxyAcsEndpoint() { for (IdentityMediationEndpoint ided : channel.getEndpoints()) { if (ided.getType().equals(SSOService.ProxyAssertionConsumerService.toString()) && ided.getBinding().equals(SSOBinding.SSO_ARTIFACT.getValue())) { return new EndpointDescriptorImpl(ided.getName(), ided.getType(), ided.getBinding(), channel.getLocation() + ided.getLocation(), ided.getResponseLocation() != null ? channel.getLocation() + ided.getResponseLocation() : null); } } return null; } }