package org.atricore.idbus.capabilities.oauth2.main.sso.producers;
import org.apache.camel.Endpoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atricore.idbus.capabilities.oauth2.common.OAuth2Constants;
import org.atricore.idbus.capabilities.oauth2.main.OAuth2AuthnContext;
import org.atricore.idbus.capabilities.oauth2.main.OAuth2BPMediator;
import org.atricore.idbus.capabilities.oauth2.main.OAuth2Exception;
import org.atricore.idbus.capabilities.oauth2.main.ResourceServer;
import org.atricore.idbus.capabilities.sso.support.binding.SSOBinding;
import org.atricore.idbus.capabilities.sso.support.metadata.SSOService;
import org.atricore.idbus.common.sso._1_0.protocol.RequestAttributeType;
import org.atricore.idbus.common.sso._1_0.protocol.SPInitiatedAuthnRequestType;
import org.atricore.idbus.kernel.main.authn.util.CipherUtil;
import org.atricore.idbus.kernel.main.federation.metadata.CircleOfTrust;
import org.atricore.idbus.kernel.main.federation.metadata.CircleOfTrustMemberDescriptor;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptor;
import org.atricore.idbus.kernel.main.mediation.IdentityMediationException;
import org.atricore.idbus.kernel.main.mediation.MediationMessageImpl;
import org.atricore.idbus.kernel.main.mediation.binding.BindingChannel;
import org.atricore.idbus.kernel.main.mediation.camel.AbstractCamelProducer;
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.claim.ClaimChannel;
import org.atricore.idbus.kernel.main.mediation.endpoint.IdentityMediationEndpoint;
import org.atricore.idbus.kernel.main.mediation.provider.FederatedLocalProvider;
import org.atricore.idbus.kernel.main.mediation.provider.Provider;
import org.atricore.idbus.kernel.main.mediation.provider.ServiceProvider;
import org.atricore.idbus.kernel.main.util.UUIDGenerator;
import java.net.URLDecoder;
/**
* This is useful when accessing an OAuth2 application directly from Front-Channel (NON-SOA)
*
* @author <a href=mailto:sgonzalez@atricore.org>Sebastian Gonzalez Oyuela</a>
*/
public class SingleSignOnProducer extends AbstractCamelProducer<CamelMediationExchange> {
private static final Log logger = LogFactory.getLog(SingleSignOnProducer.class);
private UUIDGenerator uuidGenerator = new UUIDGenerator();
public SingleSignOnProducer(Endpoint endpoint) {
super(endpoint);
}
/**
* Acts as SP initiated SSO service
*/
@Override
protected void doProcess(CamelMediationExchange exchange) throws Exception {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
BindingChannel bChannel = (BindingChannel) channel;
OAuth2BPMediator mediator = (OAuth2BPMediator) bChannel.getIdentityMediator();
ResourceServer rServer = mediator.getResourceServer();
// TODO : Implement a request object instead of reading vars from the state
String idpAlias = null;
String idpAliasB64 = in.getMessage().getState().getTransientVariable(OAuth2Constants.OAUTH2_IDPALIAS_VAR);
boolean passive = false;
String passiveStr = in.getMessage().getState().getTransientVariable(OAuth2Constants.OAUTH2_PASSIVE_VAR);
if (passiveStr != null) {
passive = Boolean.parseBoolean(passiveStr);
}
if (logger.isDebugEnabled())
logger.debug("Passive login : " + passiveStr);
OAuth2AuthnContext authnCtx = (OAuth2AuthnContext) in.getMessage().getState().getLocalVariable("urn:org:atricore:idbus:capabilities:oauth2:authnCtx");
// Decode IDP Alias, if any
if (idpAliasB64 != null) {
idpAlias = URLDecoder.decode(new String(CipherUtil.decodeBase64(idpAliasB64)), "UTF-8");
if (logger.isDebugEnabled())
logger.debug("Using received idp alias " + idpAlias);
}
if (idpAlias == null) {
idpAlias = authnCtx != null ? authnCtx.getIdpAlias() : null;
if (logger.isDebugEnabled())
logger.debug("Using previous idp alias " + idpAlias);
}
if (logger.isDebugEnabled())
logger.debug("Starting OAuth2 SSO, resource server [" + rServer.getName() + "] " +
"resource ["+rServer.getResourceLocation()+"] idpAlias ["+idpAlias+"]");
BindingChannel spChannel = resolveSpBindingChannel(bChannel);
EndpointDescriptor destination = resolveSPInitiatedSSOEndpointDescriptor(exchange, spChannel);
// Create SP AuthnRequest
// TODO : Support on_error ?
SPInitiatedAuthnRequestType request = buildAuthnRequest(exchange, idpAlias, passive);
// Create context information
authnCtx = new OAuth2AuthnContext();
authnCtx.setIdpAlias(idpAlias);
authnCtx.setAuthnRequest(request);
// Store state
in.getMessage().getState().setLocalVariable("urn:org:atricore:idbus:capabilities:josso:authnCtx", authnCtx);
CamelMediationMessage out = (CamelMediationMessage) exchange.getOut();
out.setMessage(new MediationMessageImpl(request.getID(),
request,
"SSOAuthnRequest",
null,
destination,
in.getMessage().getState()));
exchange.setOut(out);
}
/**
* @return
*/
protected SPInitiatedAuthnRequestType buildAuthnRequest(CamelMediationExchange exchange, String idpAlias, boolean passive) {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
SPInitiatedAuthnRequestType req = new SPInitiatedAuthnRequestType();
req.setID(uuidGenerator.generateId());
req.setPassive(passive);
RequestAttributeType idpAliasAttr = new RequestAttributeType();
idpAliasAttr.setName("atricore_idp_alias");
idpAliasAttr.setValue(idpAlias);
req.getRequestAttribute().add(idpAliasAttr);
// Send all transient vars to SP
for (String tvarName : in.getMessage().getState().getTransientVarNames()) {
RequestAttributeType a = new RequestAttributeType();
a.setName(tvarName);
a.setValue(in.getMessage().getState().getTransientVariable(tvarName));
}
return req;
}
protected EndpointDescriptor resolveSPInitiatedSSOEndpointDescriptor(CamelMediationExchange exchange,
BindingChannel sp) throws OAuth2Exception {
try {
if (logger.isDebugEnabled())
logger.debug("Looking for " + SSOService.SPInitiatedSingleSignOnService.toString());
for (IdentityMediationEndpoint endpoint : sp.getEndpoints()) {
if (logger.isDebugEnabled())
logger.debug("Processing endpoint : " + endpoint.getType() + "["+endpoint.getBinding()+"]");
if (endpoint.getType().equals(SSOService.SPInitiatedSingleSignOnService.toString())) {
if (endpoint.getBinding().equals(SSOBinding.SSO_ARTIFACT.getValue())) {
// This is the endpoint we're looking for
return sp.getIdentityMediator().resolveEndpoint(sp, endpoint);
}
}
}
} catch (IdentityMediationException e) {
throw new OAuth2Exception(e);
}
throw new OAuth2Exception("No SP endpoint found for SP Initiated SSO using SSO Artifact binding");
}
protected BindingChannel resolveSpBindingChannel(BindingChannel bChannel) throws OAuth2Exception {
String spAlias = ((OAuth2BPMediator)bChannel.getIdentityMediator()).getSpAlias();
CircleOfTrust cot = getFederatedProvider().getCircleOfTrust();
for (Provider p : cot.getProviders()) {
if (p instanceof ServiceProvider) {
ServiceProvider sp = (ServiceProvider)p;
for (CircleOfTrustMemberDescriptor m : sp.getMembers()) {
if (m.getAlias().equals(spAlias)) {
if (logger.isDebugEnabled())
logger.debug("Found Service Provider " + p.getName() + " for alias " + spAlias);
return ((ServiceProvider) p).getBindingChannel();
}
}
}
}
if (logger.isDebugEnabled())
logger.debug("No Service Provider found for alias " + spAlias);
return null;
}
protected FederatedLocalProvider getFederatedProvider() {
if (channel instanceof FederationChannel) {
return ((FederationChannel) channel).getFederatedProvider();
} else if (channel instanceof BindingChannel) {
return ((BindingChannel) channel).getFederatedProvider();
} else if (channel instanceof ClaimChannel) {
return ((ClaimChannel) channel).getFederatedProvider();
} else {
throw new IllegalStateException("Configured channel does not support Federated Provider : " + channel);
}
}
}