/*
* Copyright 1999-2008 University of Chicago
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.nimbustools.messaging.gt4_0_elastic.rpc;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.message.token.Timestamp;
import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceContext;
import org.globus.wsrf.ResourceContextException;
import org.globus.wsrf.ResourceException;
import org.globus.wsrf.impl.security.authentication.wssec.ReplayAttackFilter;
import org.globus.wsrf.impl.security.authentication.wssec.WSSecurityEngine;
import org.globus.wsrf.impl.security.authentication.wssec.WSSecurityException;
import org.globus.wsrf.impl.security.authentication.wssec.WSSecurityRequestEngine;
import org.globus.wsrf.impl.security.descriptor.SecurityPropertiesHelper;
import org.globus.wsrf.utils.ContextUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPHeaderElement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class RequestEngine extends WSSecurityRequestEngine {
// -------------------------------------------------------------------------
// STATIC VARIABLES
// -------------------------------------------------------------------------
private static final Log logger =
LogFactory.getLog(RequestEngine.class.getName());
protected static final SimpleDateFormat problematicFormat =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
private static WSSecurityEngine engine;
// -------------------------------------------------------------------------
// overrides WSSecurityRequestEngine
// -------------------------------------------------------------------------
public synchronized static WSSecurityEngine getEngine() {
if (engine == null) {
engine = new RequestEngine();
}
return engine;
}
// -------------------------------------------------------------------------
// overrides WSSecurityEngine
// -------------------------------------------------------------------------
/**
* Overrides processTimestampHeader in WSSecurityEngine
*
* The secmsg Timestamp constructor fails on timestamps with milliseconds
* in them.
*
* Since this is handled after the envelope integrity check, it's OK to
* replace the timestamp if necessary (this allows the the constructor to
* succeed and attack window analysis to proceed).
*
* @param givenTimestampElem timestampElem from soap
* @param msgCtx msgCtx
* @param messageIDHeader messageIDHeader
* @throws Exception problem
* @see #perhapsChangeTimestampFormat(Element);
*/
protected void processTimestampHeader(Element givenTimestampElem,
MessageContext msgCtx,
SOAPHeaderElement messageIDHeader)
throws Exception {
final String servicePath =
ContextUtils.getTargetServicePath(
(org.apache.axis.MessageContext)msgCtx);
if (servicePath == null) {
throw new Exception(i18n.getMessage("serviceNull"));
}
Resource resource = null;
try {
final ResourceContext resCtx = ResourceContext
.getResourceContext((SOAPMessageContext) msgCtx);
resource = resCtx.getResource();
} catch (ResourceContextException exp) {
// Not an issue
} catch (ResourceException exp) {
// Not an issue
}
if (givenTimestampElem != null) {
final Element timestampElem =
this.perhapsChangeTimestampFormat(givenTimestampElem);
final String propertyValue =
SecurityPropertiesHelper.getReplayAttackWindow(servicePath,
resource);
final ReplayAttackFilter replayFilter =
ReplayAttackFilter.getInstance(propertyValue);
if (messageIDHeader == null) {
final Timestamp timestamp =
new Timestamp(WSSConfig.getDefaultWSConfig(),
timestampElem);
final boolean stampOk =
verifyTimestamp(timestamp,
replayFilter.getMessageWindow());
if (!stampOk) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"timestampNotOk");
}
} else {
checkMessageValidity(replayFilter, timestampElem,
messageIDHeader);
}
} else {
final String propertyValue =
SecurityPropertiesHelper.getReplayAttackFilter(servicePath,
resource);
if (rejectMsgSansTimestampHeader(msgCtx, propertyValue)) {
logger.debug("Required time stamp header was not added.");
throw new WSSecurityException(WSSecurityException.FAILURE,
"timestampRequired");
}
}
}
// -------------------------------------------------------------------------
// TIMESTAMP CHANGES
// -------------------------------------------------------------------------
protected Element perhapsChangeTimestampFormat(Element timestampElem)
throws Exception {
if (timestampElem == null) {
throw new Exception("timestampElem may not be null");
}
final Node firstChild = timestampElem.getFirstChild();
if (firstChild == null) {
logger.warn("Timestamp but no data enclosed?");
return timestampElem;
}
final String firstChildLocal = firstChild.getLocalName();
if (firstChildLocal != null && firstChildLocal.equals("Created")) {
if (firstChildLocal.equals("Created")) {
this.handleCreatedOrExpires(firstChild, "Created");
} else {
logger.warn("Timestamp with something besides " +
"Created: '" + firstChildLocal + "'");
return timestampElem;
}
final Node sibling = firstChild.getNextSibling();
if (sibling == null) {
logger.warn("Timestamp with Created but no Expires?");
return timestampElem;
}
final String siblingLocal = sibling.getLocalName();
if (siblingLocal != null && siblingLocal.equals("Expires")) {
this.handleCreatedOrExpires(sibling, "Expires");
} else {
logger.warn("Timestamp with Created but no Expires?");
logger.warn("Timestamp with Created and then something " +
"besides Expires: '" + siblingLocal + "'");
return timestampElem;
}
}
return timestampElem;
}
protected void handleCreatedOrExpires(Node node, String name) {
final Node firstChild = node.getFirstChild();
final String firstChildNodeValue = firstChild.getNodeValue();
if (firstChildNodeValue == null) {
logger.warn("Timestamp with null value for " + name);
} else {
final String newValue = this.chop(firstChildNodeValue);
if (newValue != null) {
firstChild.setNodeValue(newValue);
}
}
}
protected String chop(String stamp) {
try {
problematicFormat.parse(stamp);
} catch (ParseException e) {
// not in the problematic format
return null; // *** EARLY RETURN ***
}
// remove milliseconds, Timestamp class wants "yyyy-MM-ddTHH:mm:ssZ"
// and the problematic one looks like "yyyy-MM-ddTHH:mm:ss.SSSZ"
// get first 19 characters then tack on Z
final String prefix = stamp.substring(0, 19);
return prefix + 'Z';
}
}