package org.atricore.idbus.capabilities.sso.main.select.producers;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atricore.idbus.capabilities.sso.main.SSOException;
import org.atricore.idbus.capabilities.sso.main.common.producers.SSOProducer;
import org.atricore.idbus.capabilities.sso.support.metadata.SSOMetadataConstants;
import org.atricore.idbus.common.sso._1_0.protocol.*;
import org.atricore.idbus.kernel.main.mediation.claim.*;
import org.atricore.idbus.capabilities.sso.main.select.internal.EntitySelectionState;
import org.atricore.idbus.kernel.main.mediation.claim.UserClaimsRequestImpl;
import org.atricore.idbus.capabilities.sso.main.select.spi.*;
import org.atricore.idbus.capabilities.sso.main.select.SSOEntitySelectorMediator;
import org.atricore.idbus.capabilities.sso.support.binding.SSOBinding;
import org.atricore.idbus.capabilities.sso.support.core.StatusCode;
import org.atricore.idbus.capabilities.sso.support.core.StatusDetails;
import org.atricore.idbus.kernel.main.federation.metadata.CircleOfTrustMemberDescriptor;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptor;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptorImpl;
import org.atricore.idbus.kernel.main.mediation.IdentityMediationFault;
import org.atricore.idbus.kernel.main.mediation.MediationMessageImpl;
import org.atricore.idbus.kernel.main.mediation.MediationState;
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.select.SelectorChannel;
import org.atricore.idbus.kernel.main.util.UUIDGenerator;
import java.util.List;
/**
*
*/
public class IdPSelectorProducer extends SSOProducer {
protected UUIDGenerator uuidGenerator = new UUIDGenerator();
private static final Log logger = LogFactory.getLog(IdPSelectorProducer.class);
public IdPSelectorProducer(AbstractCamelEndpoint<CamelMediationExchange> endpoint) {
super(endpoint);
}
@Override
protected void doProcess(CamelMediationExchange exchange) throws Exception {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
MediationState state = in.getMessage().getState();
Object content = in.getMessage().getContent();
try {
// TODO : Keep track of Selected IdP (session)
// TODO : Remember Selected IdP (persistente cookie)
// ------------------------------------------------------------------------------------------
// Select Entity
// ------------------------------------------------------------------------------------------
if (content instanceof SelectEntityRequestType) {
SelectEntityRequestType request = (SelectEntityRequestType) content;
if (logger.isDebugEnabled())
logger.debug("Starting IdP selection for " + endpointRef);
doProcessSelectEntityRequest(exchange, state, request);
} else if (content instanceof UserClaimsResponse) {
if (logger.isDebugEnabled())
logger.debug("Processing claims response for " + endpointRef);
// Claims collected from the user, to make a selection decision.
doProcessUserClaimsResponse(exchange, state, (UserClaimsResponse) content);
} else if (content instanceof CurrentEntityRequestType) {
if (logger.isDebugEnabled())
logger.debug("Processing claims response for " + endpointRef);
// Claims collected from the user, to make a selection decision.
doProcessCurrentEntityRequest(exchange, state, (CurrentEntityRequestType) content);
} else {
logger.error("Unknown message type : " + content);
if (content != null)
throw new IdentityMediationFault(StatusCode.TOP_RESPONDER.getValue(),
null,
StatusDetails.UNKNOWN_REQUEST.getValue(),
content.getClass().getName(),
null);
throw new IdentityMediationFault(StatusCode.TOP_RESPONDER.getValue(),
null,
StatusDetails.UNKNOWN_REQUEST.getValue(),
"No content received",
null);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new IdentityMediationFault(StatusCode.TOP_RESPONDER.getValue(),
null,
StatusDetails.INTERNAL_ERROR.getValue(),
content.getClass().getName(),
e);
}
}
protected void doProcessCurrentEntityRequest(CamelMediationExchange exchange, MediationState state, CurrentEntityRequestType request) throws SSOException {
// Do we need to collect more information to make a decision ?!
SSOEntitySelectorMediator mediator = (SSOEntitySelectorMediator) channel.getIdentityMediator();
CircleOfTrustMemberDescriptor idp = getCotManager().lookupMemberByAlias(request.getEntityId());
if (logger.isDebugEnabled())
logger.debug("Storing selected entity " + idp);
state.setLocalVariable(getProvider().getName().toUpperCase() + "_SELECTED_ENTITY", idp);
EndpointDescriptor destination = new EndpointDescriptorImpl("idpSelectorCallback",
SSOMetadataConstants.IdPSelectorCallbackService_QNAME.getLocalPart(),
SSOBinding.SSO_ARTIFACT.getValue(), request.getReplyTo(), null);
SSOResponseType response = new SSOResponseType();
response.setID(uuidGenerator.generateId());
response.setInReplayTo(request.getID());
// Send User Claims request
CamelMediationMessage out = (CamelMediationMessage) exchange.getOut();
out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(),
response,
"SSOResponse",
null,
destination,
state));
exchange.setOut(out);
}
protected void doProcessSelectEntityRequest(CamelMediationExchange exchange, MediationState state, SelectEntityRequestType request) throws SSOException {
// Do we need to collect more information to make a decision ?!
SSOEntitySelectorMediator mediator = (SSOEntitySelectorMediator) channel.getIdentityMediator();
EntitySelectorManager entitySelectorMgr = mediator.getSelectorManager();
CircleOfTrustMemberDescriptor previouslySelectedCotMember = (CircleOfTrustMemberDescriptor)
state.getLocalVariable(getProvider().getName().toUpperCase() + "_SELECTED_ENTITY");
if (logger.isDebugEnabled())
logger.debug("Found previously selected entity " + previouslySelectedCotMember);
// Information to make a decision can be obtain in the following ways:
// 1. In the request (preferred IdP, requested IdP, etc)
// 2. As provider state variables (user IP, user-agent, etc)
// 3. Provided by additional endpoints as User Claims
EntitySelectionState selectionState = new EntitySelectionState();
ClaimSet userClaims = new ClaimSetImpl();
if (request.getRequestAttribute() != null) {
for (RequestAttributeType attrType : request.getRequestAttribute()) {
if (attrType.getValue() != null) {
UserClaim attr = new UserClaimImpl(attrType.getName(), attrType.getValue());
userClaims.addClaim(attr);
}
}
selectionState.setUserClaims(userClaims);
if (previouslySelectedCotMember != null)
selectionState.setPreviousCotMember(previouslySelectedCotMember.getAlias());
}
selectionState.setRequest(request);
EntitySelectionContext ctx = new EntitySelectionContext(state,
selectionState,
getCotManager(),
selectionState.getUserClaims(),
request);
// We already have some attributes in the selection state
//List<EndpointDescriptor> endpoints = entitySelectorMgr.resolveUserClaimsEndpoints(ctx, (SelectorChannel) channel, mediator.getPreferredStrategy());
List<EntitySelector> selectors = entitySelectorMgr.resolveSelectors(ctx, (SelectorChannel) channel, mediator.getPreferredStrategy());
// Store selection state
state.setLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE", selectionState);
// Try to find an IdP
CircleOfTrustMemberDescriptor entity = null;
EntitySelector selector = processNextSelector(exchange, selectors, ctx);
while (selector != null && entity == null) {
if (logger.isDebugEnabled())
logger.debug("Processing Selector: " + selector);
// This will process the next selector endpoint, if it returns true, it means that a claims endpoint was used and we'll wait for a response
if (processNextSelectorEndpoint(exchange, selector, ctx))
return;
// If we get here, it means that there are now more endpoints to process for this selector, try to select
// an entity now.
entity = entitySelectorMgr.selectEntity(mediator.getPreferredStrategy(), selector, ctx, (SelectorChannel) channel);
selector = processNextSelector(exchange, selectors, ctx);
}
if (logger.isDebugEnabled())
logger.debug("Selected IdP " + (entity != null ? entity.getAlias() : "NULL"));
// Send the selected entity, if any
sendSelectionResponse(exchange, state, selectionState.getRequest(), entity);
// Clear selection state
state.removeLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE");
if (entity != null) {
if (logger.isDebugEnabled())
logger.debug("Storing selected entity " + entity.getAlias());
state.setLocalVariable(getProvider().getName().toUpperCase() + "_SELECTED_ENTITY", entity);
}
}
protected void doProcessUserClaimsResponse(CamelMediationExchange exchange, MediationState state, UserClaimsResponse userClaimsResp) throws SSOException {
// Do we need to collect more information to make a decision ?!
SSOEntitySelectorMediator mediator = (SSOEntitySelectorMediator) channel.getIdentityMediator();
EntitySelectorManager entitySelectorMgr = mediator.getSelectorManager();
// Get selection state
EntitySelectionState selectionState = (EntitySelectionState) state.getLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE");
for (Claim c : userClaimsResp.getClaimSet().getClaims()) {
if (logger.isDebugEnabled())
logger.debug("Storing selection claim: " + c);
selectionState.getUserClaims().addClaim(c);
}
EntitySelectionContext ctx = new EntitySelectionContext(state,
selectionState,
getCotManager(),
selectionState.getUserClaims(),
selectionState.getRequest());
CircleOfTrustMemberDescriptor entity = null;
List<EntitySelector> selectors = entitySelectorMgr.resolveSelectors(ctx, (SelectorChannel) channel, mediator.getPreferredStrategy());
// Current selector
EntitySelector selector = selectors.get(selectionState.getNextSelectorIdx() - 1);
while (selector != null && entity == null) {
// This will process the next selector endpoint, if it returs true, it means that an endpoint was used
if (processNextSelectorEndpoint(exchange, selector, ctx))
return;
// If we get here, it means that there are no more endpoints to process for this selector, try to select
// an entity now.
entity = entitySelectorMgr.selectEntity(mediator.getPreferredStrategy(), selector, ctx, (SelectorChannel) channel);
selector = processNextSelector(exchange, selectors, ctx);
}
if (logger.isDebugEnabled())
logger.debug("Selected IdP " + (entity != null ? entity.getAlias() : "NULL"));
// Send the selected entity, if any
sendSelectionResponse(exchange, state, selectionState.getRequest(), entity);
// Clear selection state
state.removeLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE");
if (entity != null) {
if (logger.isDebugEnabled())
logger.debug("Storing selected entity " + entity.getAlias());
state.setLocalVariable(getProvider().getName().toUpperCase() + "_SELECTED_ENTITY", entity);
}
}
protected EntitySelector processNextSelector(CamelMediationExchange exchange, List<EntitySelector> selectors, EntitySelectionContext ctx) {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
MediationState state = in.getMessage().getState();
EntitySelectionState selectionState = (EntitySelectionState) state.getLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE");
int selectorIdx = selectionState.getNextSelectorIdx();
if (logger.isDebugEnabled())
logger.debug("Processing selector [" + selectorIdx + "] ");
EntitySelector selector = null;
if (selectorIdx < selectors.size()) {
selector = selectors.get(selectorIdx);
while (selector != null && !selector.canHandle(ctx)) {
selectorIdx ++;
if (selectorIdx < selectors.size())
selector = selectors.get(selectorIdx);
else
selector = null;
}
}
selectionState.setNextSelectorIdx(selectorIdx + 1);
// Reset endpoints index
selectionState.setNextSelectorEndpointIdx(0);
if (logger.isDebugEnabled())
logger.debug("Using selector [" + selectorIdx + "] " + selector);
return selector;
}
protected boolean processNextSelectorEndpoint(CamelMediationExchange exchange,
EntitySelector selector,
EntitySelectionContext ctx) {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
MediationState state = in.getMessage().getState();
EntitySelectionState selectionState = (EntitySelectionState) state.getLocalVariable(getProvider().getName().toUpperCase() + "_SELECTION_STATE");
List<EndpointDescriptor> endpoints = selector.getUserClaimsEndpoints(ctx, (SelectorChannel) channel);
if (endpoints == null) {
if (logger.isDebugEnabled())
logger.debug("No endpoints for selector " + selector);
return false;
}
// Send Attributes Request
int selectorEndpointIdx = selectionState.getNextSelectorEndpointIdx();
if (logger.isDebugEnabled())
logger.debug("Processing IDX ["+selectorEndpointIdx+"] for Endpoints [" + endpoints.size() + "] from Selector: " + selector);
if (selectorEndpointIdx < endpoints.size()) {
EndpointDescriptor ed = endpoints.get(selectorEndpointIdx);
selectionState.setNextSelectorEndpointIdx(selectorEndpointIdx + 1);
if (logger.isDebugEnabled())
logger.debug("Sending User Claims Request to " + ed);
// We need to keep track of the endpoints, for now only one supported !!!
UserClaimsRequest userClaimsReq = new UserClaimsRequestImpl(
uuidGenerator.generateId(),
channel,
endpoint,
state.getLocalState().getId());
userClaimsReq.setAttribute("ServiceProvider", ctx.getRequest().getIssuer());
// Send User Claims request
CamelMediationMessage out = (CamelMediationMessage) exchange.getOut();
out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(),
userClaimsReq,
"UserClaimsRequest",
null,
ed,
state));
exchange.setOut(out);
return true;
}
if (logger.isDebugEnabled())
logger.debug("No more endpoints found for " + selector);
return false;
}
protected void sendSelectionResponse(CamelMediationExchange exchange ,
MediationState state,
SelectEntityRequestType request,
CircleOfTrustMemberDescriptor selectedCotMembery) throws SSOException {
if (logger.isDebugEnabled())
logger.debug("Sending selection response with entity " + selectedCotMembery != null ? (selectedCotMembery.getId() + " " + selectedCotMembery.getAlias()) : "NULL");
// Do something with the outcome
SelectEntityResponseType response = new SelectEntityResponseType();
response.setInReplayTo(request.getID());
response.setID(uuidGenerator.generateId());
if (selectedCotMembery != null)
response.setEntityId(selectedCotMembery.getId());
String location = request.getReplyTo();
if (location == null)
throw new SSOException("Reply-To attribute is required for select entity request " + request.getID());
// For now, artifact binding is required.
EndpointDescriptor ed = new EndpointDescriptorImpl(
"IDPSelectorResponseEndpoint",
"EntitySelectorResponse",
SSOBinding.SSO_ARTIFACT.toString(),
request.getReplyTo(),
null);
// Send SAMLR2 Message back
CamelMediationMessage out = (CamelMediationMessage) exchange.getOut();
out.setMessage(new MediationMessageImpl(uuidGenerator.generateId(),
response,
"SelectEntityResponse",
null,
ed,
state));
exchange.setOut(out);
}
}