Java tutorial
/* * Licensed to the University Corporation for Advanced Internet Development, * Inc. (UCAID) under one or more contributor license agreements. See the * NOTICE file distributed with this work for additional information regarding * copyright ownership. The UCAID licenses this file to You 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.opensaml.soap.wssecurity.messaging.impl; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.shibboleth.utilities.java.support.component.ComponentSupport; import org.joda.time.DateTime; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.util.XMLObjectSupport; import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.context.navigate.ContextDataLookupFunction; import org.opensaml.messaging.handler.AbstractMessageHandler; import org.opensaml.messaging.handler.MessageHandlerException; import org.opensaml.soap.messaging.SOAPMessagingSupport; import org.opensaml.soap.wssecurity.Created; import org.opensaml.soap.wssecurity.Expires; import org.opensaml.soap.wssecurity.Security; import org.opensaml.soap.wssecurity.Timestamp; import org.opensaml.soap.wssecurity.messaging.WSSecurityContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Handler implementation that adds a wsse:Timestamp header to the wsse:Security header * of the outbound SOAP envelope. */ public class AddTimestampHandler extends AbstractMessageHandler { /** Logger. */ private Logger log = LoggerFactory.getLogger(AddTimestampHandler.class); /** Context lookup function for the Created time. */ private ContextDataLookupFunction<MessageContext, DateTime> createdLookup; /** Context lookup function for the Expires time. */ private ContextDataLookupFunction<MessageContext, DateTime> expiresLookup; /** Flag indicating whether to use the current time as the Created time, if no value * is explicitly supplied by the other supported mechanisms. */ private boolean useCurrentTimeAsDefaultCreated; /** Parameter indicating the offset from Created, in milliseconds, used to calculate the Expires time, * if no Expires value is explicitly supplied via the other supported mechanisms. */ private Long expiresOffsetFromCreated; /** The effective Created value to use. */ private DateTime createdValue; /** The effective Expires value to use. */ private DateTime expiresValue; /** * Get the context lookup function for the Created time. * * @return the lookup function */ @Nullable public ContextDataLookupFunction<MessageContext, DateTime> getCreatedLookup() { return createdLookup; } /** * Set the context lookup function for the Created time. * * @param lookup the lookup function */ public void setCreatedLookup(@Nullable final ContextDataLookupFunction<MessageContext, DateTime> lookup) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); ComponentSupport.ifDestroyedThrowDestroyedComponentException(this); createdLookup = lookup; } /** * Get the context lookup function for the Expires time. * * @return the lookup function */ @Nullable public ContextDataLookupFunction<MessageContext, DateTime> getExpiresLookup() { return expiresLookup; } /** * Set the context lookup function for the Expires time. * * @param lookup the lookup function */ public void setExpiresLookup(@Nullable final ContextDataLookupFunction<MessageContext, DateTime> lookup) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); ComponentSupport.ifDestroyedThrowDestroyedComponentException(this); expiresLookup = lookup; } /** * Get the flag indicating whether to use the current time as the Created time, if no value * is explicitly supplied by the other supported mechanisms. * * @return true if should use current time, false if not */ public boolean isUseCurrentTimeAsDefaultCreated() { return useCurrentTimeAsDefaultCreated; } /** * Set the flag indicating whether to use the current time as the Created time, if no value * is explicitly supplied by the other supported mechanisms. * * @param flag true if should use currnet time, false if not */ public void setUseCurrentTimeAsDefaultCreated(boolean flag) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); ComponentSupport.ifDestroyedThrowDestroyedComponentException(this); useCurrentTimeAsDefaultCreated = flag; } /** * Get the parameter indicating the offset from Created, in milliseconds, used to calculate the Expires time, * if no Expires value is explicitly supplied via the other supported mechanisms. * * @return the expires offset, or null */ @Nullable public Long getExpiresOffsetFromCreated() { return expiresOffsetFromCreated; } /** * Set the parameter indicating the offset from Created, in milliseconds, used to calculate the Expires time, * if no Expires value is explicitly supplied via the other supported mechanisms. * * @param value the expires off set, or null */ public void setExpiresOffsetFromCreated(@Nullable final Long value) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); ComponentSupport.ifDestroyedThrowDestroyedComponentException(this); expiresOffsetFromCreated = value; } /** {@inheritDoc} */ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException { createdValue = getCreatedValue(messageContext); expiresValue = getExpiresValue(messageContext, createdValue); if (createdValue == null && expiresValue == null) { log.debug("No WS-Security Timestamp Created or Expires values available, skipping further processing"); return false; } else { return super.doPreInvoke(messageContext); } } /** {@inheritDoc} */ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException { log.debug("Processing addition of outbound WS-Security Timestamp"); Timestamp timestamp = (Timestamp) XMLObjectSupport.buildXMLObject(Timestamp.ELEMENT_NAME); if (createdValue != null) { log.debug("WS-Security Timestamp Created value added was: {}", createdValue); Created created = (Created) XMLObjectSupport.buildXMLObject(Created.ELEMENT_NAME); created.setDateTime(createdValue); timestamp.setCreated(created); } if (expiresValue != null) { log.debug("WS-Security Timestamp Expires value added was: {}", createdValue); Expires expires = (Expires) XMLObjectSupport.buildXMLObject(Expires.ELEMENT_NAME); expires.setDateTime(expiresValue); timestamp.setExpires(expires); } Security security = getSecurityHeader(messageContext); if (security == null) { log.debug("Security header was null, building"); security = (Security) XMLObjectSupport.buildXMLObject(Security.ELEMENT_NAME); //TODO probably should add Security/@mustUnderstand=1, // but need helper and/or other config (e.g. core SOAP processing context) to know SOAP version SOAPMessagingSupport.addHeaderBlock(messageContext, security); } security.getUnknownXMLObjects().add(timestamp); } /** * Get the Created value. * * @param messageContext the current message context * * @return the effective Created DateTime value to use */ @Nullable protected DateTime getCreatedValue(@Nonnull final MessageContext messageContext) { DateTime value = null; WSSecurityContext security = messageContext.getSubcontext(WSSecurityContext.class, false); if (security != null) { value = security.getTimestampCreated(); } if (value == null && getCreatedLookup() != null) { value = getCreatedLookup().apply(messageContext); } if (value == null) { if (isUseCurrentTimeAsDefaultCreated()) { value = new DateTime(); } } return value; } /** * Get the Expires value. * * @param messageContext the current message context * @param created the created value, if any * * @return the effective Expires DateTime value to use */ @Nullable protected DateTime getExpiresValue(@Nonnull final MessageContext messageContext, @Nullable final DateTime created) { DateTime value = null; WSSecurityContext security = messageContext.getSubcontext(WSSecurityContext.class, false); if (security != null) { value = security.getTimestampExpires(); } if (value == null && getExpiresLookup() != null) { value = getExpiresLookup().apply(messageContext); } if (value == null) { if (getExpiresOffsetFromCreated() != null && created != null) { return created.plus(getExpiresOffsetFromCreated()); } } return value; } /** * Get the Security header block. * * @param messageContext the current message context * * @return the Security header block if present, or null */ protected Security getSecurityHeader(@Nonnull final MessageContext messageContext) { List<XMLObject> securityHeaders = SOAPMessagingSupport.getOutboundHeaderBlock(messageContext, Security.ELEMENT_NAME); if (securityHeaders != null && !securityHeaders.isEmpty()) { return (Security) securityHeaders.get(0); } return null; } }