InteractionManager.java revision ccf9d4a5c6453fa9f8b839baeee25147865fbb7d
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at opensso/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id: InteractionManager.java,v 1.5 2008/08/06 17:28:10 exu Exp $
*
* Portions Copyrighted 2016 ForgeRock AS.
*/
/**
* This class provides the interface and implementation for supporting
* resource owner interaction. <code>WSC</code> and <code>WSP</code> would
* collaborate with the singleton object instance of this class to provide
* and use resource owner interaction.
* @supported.api
*/
public class InteractionManager {
/**
*
* Name of URL query parameter to be used by <code>WSC</code> to include
* <code>returnToURL</code>, while redirecting user agent to
* <code>WSP</code>.
*
* @supported.all.api
*/
/**
* Name of suggested URL query parameter to be used by <code>WSC</code>
* to include an ID to refer to request message that led to user agent
* redirect.
*
* @supported.api
*/
/**
* Name of URL query parameter to be used by <code>WSC</code> to include
* <code>providerID</code> of <code>IDP</code>, that was used to
* authenticate user.
*
* @supported.api
*/
/**
* Name of URL query parameter to be used by <code>WSP</code> to include
* an ID to indicate that user agent is redirected back to
* <code>WSC</code> from <code>WSP</code>
*
* @supported.api
*/
/**
* Name space URI of interaction service
* @supported.api
*/
public static final String INTERACTION_NAMESPACE
= "urn:liberty:is:2003-08";
/**
* <code>QName</code> for <code>s:Server</code>
* <code>s</code> - soap name space prefix
*/
public static final QName QNAME_SERVER
/**
* <code>QName</code> for <code>is:interactIfNeeded</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACT_IF_NEEDED
/**
* <code>QName</code> for <code>is:doNotInteract</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_DO_NOT_INTERACT
/**
* <code>QName</code> for <code>is:doNotInteractForData</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_DO_NOT_INTERACT_FOR_DATA
/**
* <code>QName</code> for <code>is:interactionRequired</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACTION_REQUIRED
/**
* <code>QName</code> for <code>is:forData</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACTION_REQUIRED_FOR_DATA
/**
* <code>QName</code> for <code>is:timeNotSufficient</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACTION_TIME_NOT_SUFFICEINT
/**
* <code>QName</code> for <code>is:timeOut</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACTION_TIMED_OUT
/**
* <code>QName</code> for <code>is:interactIfNeeded</code>
* <code>is</code> - name space prefix for interaction service
*/
public static final QName QNAME_INTERACTION_CAN_NOT_DETERMINE_REQUEST_HOST
= new QName(INTERACTION_NAMESPACE,
"canNotDetermineRequestHostName");
/**
* Constant string to indicate generic server error
*/
private static final String FAULT_ACTOR
private InteractionConfig interactionConfig;
/**
* Gets singleton object instance of <code>InteractionManager</code>
* @return singleton object instance of <code>InteractionManager</code>
*
* @supported.api
*/
synchronized public static InteractionManager getInstance() {
if (interactionManager == null) {
interactionManager = new InteractionManager();
}
return interactionManager;
}
private InteractionManager() {
if (debug.messageEnabled()) {
"InteractionManager():constructed singleton instance");
}
}
/**
* Sends SOAP request to <code>WSP</code>.
* This would be invoked at <code>WSC</code> side.
*
* @param requestMessage request message.
* @param connectTo SOAP URL to which to send the SOAP request
* @param certAlias SOAP Client Certificate Alias
* @param soapAction SOAP Action Attribute
* @param returnToURL URL to which to redirect user agent after
* <code>WSP</code> - resource owner interactions
* @param httpRequest HTTP request object of current user agent request
* @param httpResponse HTTP response object of current user agent request
* @return response SOAP response message sent by <code>WSP</code>.
*
* @throws InteractionException for generic interaction error
* @throws InteractionRedirectException if user agent is redirected to
* <code>WSP</code> for resource owner interactions
* @throws SOAPBindingException for generic SOAP binding errors
* @throws SOAPFaultException if the response message has SOAP fault
*
* @supported.api
*/
if (debug.messageEnabled()) {
+ " entering with messageID="
+ ":refToMessageID="
+ ":requestMessage=" + requestMessage);
}
// construct and set UserInteraction element in requestMessage
while (locales.hasMoreElements()) {
}
if (debug.messageEnabled()) {
+ "Accept-Language specified by httpRequest="
+ acceptLanguages);
}
try {
id);
} catch (JAXBException je) {
+ "not setting userInteractionHeader:"
+ "can not convert JAXBObject to Element", je);
}
}
}
try {
if (debug.messageEnabled()) {
+ "invoking soap Client.sendRequest():"
+ ":requestMessage=" + requestMessage
+ ":connecTo=" + connectTo);
}
if (LogUtil.isLogEnabled()) {
.getMessageID();
}
} catch (SOAPFaultException sfe) {
if (debug.messageEnabled()) {
+ " catching SOAPFaultException="
+ sfe);
}
if(redirectURL == null) {
throw sfe;
}
}
if (debug.messageEnabled()) {
+ " returning response message=" + responseMessage);
}
if (LogUtil.isLogEnabled()) {
objs);
}
return responseMessage;
}
/**
* Resends a previously cached SOAP request message to <code>WSP</code>.
* This would be invoked at <code>WSC</code> side. Message ID for the cached
* message should be provided as value of <code>REQUEST_ID</code> query
* parameter in <code>httpRequest</code>.
*
* @param returnToURL URL to which to redirect user agent after
* <code>WSP</code> - resource owner interactions
* @param httpRequest HTTP request object of current user agent request
* @param httpResponse HTTP response object of current user agent request
* @return response SOAP message sent by <code>WSP</code>.
*
* @throws InteractionException for generic interaction error
* @throws InteractionRedirectException if user agent is redirected to
* <code>WSP</code> for resource owner interactions
* @throws SOAPBindingException if there are generic SOAP errors
* @throws SOAPFaultException if the response message has SOAP fault
*
* @see #REQUEST_ID
*
* @supported.api
*/
}
/**
* Resends a SOAP request message to <code>WSP</code>.
* This would be invoked at <code>WSC</code> side.
*
* @param returnToURL URL to which to redirect user agent after
* <code>WSP</code> - resource owner interactions
* @param httpRequest HTTP request object of current user agent request
* @param httpResponse HTTP response object of current user agent request
* @param requestMessage SOAP message to be resent.
* @return response SOAP message sent by <code>WSP</code>.
*
* @throws InteractionException for generic interaction error
* @throws InteractionRedirectException if user agent is redirected to
* <code>WSP</code> for resource owner interactions
* @throws SOAPBindingException for generic SOAP errors
* @throws SOAPFaultException if the response message has SOAP fault
*
* @supported.api
*/
if (debug.messageEnabled()) {
}
//check for RESEND_MESSAGE parameter
throw new InteractionException(INTERACTION_RB_NAME,
"missing_query_parameter", objs);
}
//check whether WSP advised not to resend
+ " resend not allowed in requestURL="
throw new InteractionException(INTERACTION_RB_NAME,
"wsp_advised_not_to_resend", null);
}
//check for original REQUEST_ID
throw new InteractionException(INTERACTION_RB_NAME,
"request_missing_query_parameter", objs);
}
+ " old connectTo not found for messageID="
+ messageID);
throw new InteractionException(INTERACTION_RB_NAME,
"old_connectTo_not_found", null);
}
if (requestMessage == null) {
if (debug.messageEnabled()) {
+ "invoking with null requestMessage:"
+ "old cached message would be used");
}
if (oldMessage == null) {
+ " old message not found for messageID="
+ messageID);
throw new InteractionException(INTERACTION_RB_NAME,
"old_message_not_found", null);
}
} else {
if (debug.messageEnabled()) {
+ "invoking with non null requestMessage");
}
}
if (debug.messageEnabled()) {
+ "invoking InteractionManager.sendRequest():"
+ "with requestMessage=" + requestMessage
+ ":returnToURL=" + returnToURL);
}
if (LogUtil.isLogEnabled()) {
}
if (debug.messageEnabled()) {
+ " returning responseMessage=" + responseMessage);
}
return responseMessage;
}
if (debug.messageEnabled()) {
+ "entering with redirectURL="
+ redirectURL);
}
//redirectURL should not have ReturnToURL parameter
"InteractionManager.handleRedirectRequest():"
+ "Invalid redirectURL - illegal parameter "
+ " in redirectURL=" + redirectURL);
throw new InteractionException(INTERACTION_RB_NAME,
"illegal_parameter_in_redirectURL", objs);
}
//redirectURL should not have IDP parameter
"InteractionManager.handleRedirectRequest():"
+ "Invalid redirectURL - illegal parameter:"
+ IDP
+ " in redirectURL=" + redirectURL);
throw new InteractionException(INTERACTION_RB_NAME,
"illegal_parameter_in_redirectURL", objs);
}
//redirectURL should be https
"InteractionManager.handleRedirectRequest():"
+ "Invalid Request "
+ " not https"
+ " in redirectURL=" + redirectURL);
throw new InteractionException(INTERACTION_RB_NAME,
"redirectURL_not_https", null);
}
//redirectURL should point to connectTo host
if (InteractionConfig.getInstance()
"InteractionManager.handleRedirectRequest():"
+ "Invalid Request redirectToHost differs from "
+ " connectToHost:"
+ " in redirectURL=" + redirectURL
+ ":connectTo=" + connectTo);
throw new InteractionException(INTERACTION_RB_NAME,
"redirectURL_differs_from_connectTo", null);
}
if (debug.messageEnabled()) {
+ " request message for messageID=" + messageID
+ ":requestID=" + requestID
}
if (debug.messageEnabled()) {
+ "redirecting user agent to redirectURL= "
+ redirectURL);
}
try {
+ " catching IOException", ioe);
throw new InteractionException(INTERACTION_RB_NAME,
"IOException_in_Interaction_Manager", null);
}
if (debug.messageEnabled()) {
+ "redirected user agent to redirectURL= "
+ redirectURL);
}
if (LogUtil.isLogEnabled()) {
}
throw new InteractionRedirectException(requestID);
//return returnMessage;
}
/**
* Handles resource owner interactions on behalf of <code>WSP</code>.
* This is invoked at <code>WSP</code> side.
*
* @param requestMessage SOAP request that requires resource
* owner interactions
* @param inquiryElement query that <code>WSP</code> wants to pose to
* resource owner.
* @return SOAP message that contains <code>InteractionResponse</code>,
* gathered by <code>InteractionManager</code>
*
* @throws InteractionException for generic interaction error
* @throws InteractionSOAPFaultException if a SOAP fault
* has to be returned to <code>WSC</code>
* @throws SOAPFaultException if the response message has SOAP fault
*
* @deprecated
*
*/
}
/**
* Handles resource owner interactions on behalf of <code>WSP</code>.
* This is invoked at <code>WSP</code> side.
*
* @param requestMessage SOAP request that requires resource
* owner interactions
* @param inquiryElement query that <code>WSP</code> wants to pose to
* resource owner
* @param language language in which the query page needs to be rendered
* @return SOAP message that contains <code>InteractionResponse</code>,
* gathered by <code>InteractionManager</code>
*
* @throws InteractionException for generic interaction error
* @throws InteractionSOAPFaultException if a SOAP fault
* has to be returned to <code>WSC</code>
* @throws SOAPFaultException if the response message has SOAP fault
*
* @supported.api
*/
throws InteractionException,
if (debug.messageEnabled()) {
}
//Check redirect is enabled for WSP
if (!interactionConfig.wspSupportsRedirect()) {
if (debug.warningEnabled()) {
+ " WSP requests for interaction:wspWillRedirect="
+ "throwing InteractionException");
}
throw new InteractionException(INTERACTION_RB_NAME,
"wsp_does_not_support_interaction", null);
}
//Check wsc provided UserInteraction header
if (debug.warningEnabled()) {
+ " WSP requests for interaction - WSC did not "
+ " provide UserInteractionHeader");
+ "throwing InteractionSOAPFaultException="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
}
//Check WSC is willing to redirect
if (ue.isRedirect() == false) {
if (debug.warningEnabled()) {
+ "WSP rquests for interaction - WSC "
+ " says redirect=false");
+ "throwing InteractionSOAPFaultException="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
}
//Check WSC allowed interaction
if (debug.warningEnabled()) {
+ "WSP rquests for interaction - WSC "
+ " UserInteractionHeader says doNotInteract");
+ "throwing InteractionSOAPFaultException="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
}
//Check WSC allowed interaction for data
if (debug.warningEnabled()) {
+ "WSP rquests interaction for data - WSC "
+ " UserInteractionHeader says doNotInteractForData");
+ "throwing InteractionSOAPFaultException="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
}
//Check WSP will not exceed maxInteractionTime specified by WSC
if (debug.warningEnabled()) {
+ "WSP inteaction time ="
+ " exceeds WSC maxInteractTime= "
+ ue.getMaxInteractTime());
+ "throwing InteractionSOAPFaultException="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
}
try {
//requestHost = inetAddress.getCanonicalHostName();
if (debug.messageEnabled()) {
+ " caching requestHost=" + requestHost
+ ", for redirectResponseID= "
}
} catch (UnknownHostException uhe) {
+ " can not resolve host name", uhe);
+ " throwing InteractionSOAPFaultException", sfe);
throw new InteractionSOAPFaultException(sfe1);
}
}
if (debug.messageEnabled()) {
+ " throwing InteractionSOAPFaultException "
+ " to redirect user agent="
+ sfe);
}
throw new InteractionSOAPFaultException(sfe);
//return responseMessage;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return inquiryElement;
}
if ( cacheEntry == null) {
}
}
/**
* Gets interaction response that was gathered from resource owner
* by <code>InteractionManager</code>
*
* @param requestMessage request message.
*
* @return interaction response that was gathered by
* <code>InteractionManager</code>.
*
* @throws InteractionException for interaction error
*
* @supported.api
*/
throws InteractionException {
if ( cacheEntry != null) {
}
if (debug.messageEnabled()) {
+ "responseElement="
+ (interactionResponseElement != null));
}
}
if (LogUtil.isLogEnabled()) {
objs);
}
return interactionResponseElement;
}
if ( cacheEntry == null) {
}
if (debug.messageEnabled()) {
+ " cached request message for messageID="
}
}
if ( cacheEntry != null) {
}
if (debug.messageEnabled()) {
+ " looking up request message for messageID="
}
return requestMessage;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return requestMessageID;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return returnToURL;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return requestHost;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return connectTo;
}
if ( cacheEntry != null) {
}
return clientCert;
}
if ( cacheEntry != null) {
}
return soapAction;
}
if ( cacheEntry == null) {
}
}
if ( cacheEntry != null) {
}
return language;
}
try {
} catch (JAXBException je) {
+ " can not create UserInteractionElement", je);
}
return ue;
}
try {
} catch (JAXBException je) {
+ "not able to get userInteractionElement:"
+ "can not convert Element to JAXBObject", je);
return null;
}
if (obj instanceof UserInteractionElement) {
break;
}
}
return ue;
}
try{
} catch (JAXBException je) {
+ " can not create RedirectRequestElement", je);
}
if(debug.messageEnabled()) {
+ "wspRedirectURL:" + wspRedirectUrl
+ ", lbRedirectUrl:" + lbRedirectUrl);
}
if (lbRedirectUrl == null) {
if(debug.messageEnabled()) {
+ "lbRedirectURL is null, rediectUrl:"
+ redirectUrl);
}
} else { //lbRedirectUrl defined
if(debug.messageEnabled()) {
+ "lbRedirectURL is not null, rediectUrl:"
+ redirectUrl);
}
}
try {
} catch (JAXBException je) {
+ " can not create newRedirectFault:"
+ " can not convert JAXBObject to Element", je);
}
return sfe;
}
try{
} catch (JAXBException je) {
+ " can not create StatusElement", je);
}
try {
} catch (JAXBException je) {
+ "can not create new RedirectFaultError:"
+ "can not convert JAXBObject to Element", je);
}
return sfe;
}
}
try {
} catch (JAXBException je) {
+ " can not get Redirect URL", je);
}
}
}
if (redirectURL == null) {
throw sfe;
}
return redirectURL;
}
boolean answer = false;
try {
answer = true;
}
} catch (MalformedURLException mfe) {
+ "redirectURL not a valid URL"
+ " :redirectURL=" + redirectURL
}
return answer;
}
+ "null CorrelationHeader in SOAPFaultException");
throw sfe;
}
if (responseID == null) {
+ "null messageID in SOAPFaultException");
throw sfe;
}
return responseID;
}
static class InteractionCache {
private static final boolean VERBOSE_MESSAGE = false;
InteractionCache() {
if (debug.messageEnabled()) {
+ " - entering constructor");
}
if (debug.messageEnabled()) {
+ " - starting sweeper thread");
+ " SWEEP_INTERVAL = " + SWEEP_INTERVAL);
+ " CACHE_ENTRY_MAX_IDLE_TIME = "
}
* 1000));
if (debug.messageEnabled()) {
+ " - returning from constructor");
}
}
+ "looking up cacheEntry for messageID=" + messageID);
}
}
return entry;
}
+ " cached cacheEntry for messageID=" + messageID);
}
}
}
static class CacheEntry {
}
this.requestMessage= message;
}
this.requestMessage = requestMessage;
}
return requestMessage;
}
this.requestMessageID = requestMessageID;
}
return requestMessageID;
}
this.inquiryElement = inquiryElement;
}
return inquiryElement;
}
}
return interactionResponseElement;
}
}
String getConnectTo() {
return connectTo;
}
}
String getClientCert() {
return certAlias;
}
this.soapAction = soapAction;
}
String getSoapAction() {
return soapAction;
}
this.returnToURL = returnToURL;
}
String getReturnToURL() {
return returnToURL;
}
this.requestHost = requestHost;
}
String getRequestHost() {
return requestHost;
}
}
String getLanguage() {
return language;
}
}
}