001/*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024package microsoft.exchange.webservices.data.credential;
025
026import microsoft.exchange.webservices.data.core.EwsUtilities;
027
028import javax.xml.stream.XMLStreamException;
029import javax.xml.stream.XMLStreamWriter;
030
031import java.net.URI;
032import java.net.URISyntaxException;
033import java.util.Calendar;
034
035/**
036 * WSSecurityBasedCredentials is the base class for all credential classes using
037 * WS-Security.
038 */
039public abstract class WSSecurityBasedCredentials extends ExchangeCredentials {
040
041  /**
042   * The security token.
043   */
044  private String securityToken;
045
046  /**
047   * The ews url.
048   */
049  private URI ewsUrl;
050
051  protected static final String wsuTimeStampFormat =
052      "<wsu:Timestamp>" +
053          "<wsu:Created>{0:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}</wsu:Created>" +
054          "<wsu:Expires>{1:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}</wsu:Expires>" +
055          "</wsu:Timestamp>";
056  //kavi-start
057  // WS-Security SecExt 1.0 Namespace (and the namespace prefix we will use
058  // for it).
059  /** The Constant WSSecuritySecExt10NamespacePrefix. */
060  //protected static final String WSSecuritySecExt10NamespacePrefix = "wsse";
061
062  /** The Constant WSSecuritySecExt10Namespace. */
063  //protected static final String WSSecuritySecExt10Namespace =
064  //    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
065
066  // WS-Addressing 1.0 Namespace (and the namespace prefix we will use for
067  // it).
068
069
070  /** The Constant WSAddressing10NamespacePrefix. */
071  //protected static final String WSAddressing10NamespacePrefix = "wsa";
072
073  /** The Constant WSAddressing10Namespace. */
074  //protected static final String WSAddressing10Namespace =
075  //    "http://www.w3.org/2005/08/addressing";
076
077  //kavi end
078
079  // The WS-Addressing headers format string to use for adding the
080  // WS-Addressing headers.
081  // Fill-Ins: %s = Web method name; %s = EWS URL
082  /**
083   * The Constant WsAddressingHeadersFormat.
084   */
085  protected static final String wsAddressingHeadersFormat =
086      "<wsa:Action soap:mustUnderstand='1'>http://schemas.microsoft.com/exchange/services/2006/messages/%s</wsa:Action>"
087          +
088          "<wsa:ReplyTo><wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>" +
089          "</wsa:ReplyTo>" +
090          "<wsa:To soap:mustUnderstand='1'>%s</wsa:To>";
091
092  // The WS-Security header format string to use for adding the WS-Security
093  // header.
094  // Fill-Ins:
095  // %s = EncryptedData block (the token)
096  /**
097   * The Constant WsSecurityHeaderFormat.
098   */
099  protected static final String wsSecurityHeaderFormat =
100      "<wsse:Security soap:mustUnderstand='1'>" +
101          "  %s" + // EncryptedData (token)
102          "</wsse:Security>";
103
104  private boolean addTimestamp;
105
106  // / Path suffix for WS-Security endpoint.
107  /**
108   * The Constant WsSecurityPathSuffix.
109   */
110  protected static final String wsSecurityPathSuffix = "/wssecurity";
111
112  /**
113   * Initializes a new instance of the WSSecurityBasedCredentials class.
114   */
115  protected WSSecurityBasedCredentials() {
116  }
117
118  /**
119   * Initializes a new instance of the WSSecurityBasedCredentials class.
120   *
121   * @param securityToken The security token.
122   */
123  protected WSSecurityBasedCredentials(String securityToken) {
124    this.securityToken = securityToken;
125  }
126
127  /**
128   * Initializes a new instance of the WSSecurityBasedCredentials class.
129   *
130   * @param securityToken The security token.
131   * @param addTimestamp  Timestamp should be added.
132   */
133  protected WSSecurityBasedCredentials(String securityToken, boolean addTimestamp) {
134    this.securityToken = securityToken;
135    this.addTimestamp = addTimestamp;
136  }
137
138  /**
139   * This method is called to pre-authenticate credential before a service
140   * request is made.
141   */
142  @Override public void preAuthenticate() {
143    // Nothing special to do here.
144  }
145
146  /**
147   * Emit the extra namespace aliases used for WS-Security and WS-Addressing.
148   *
149   * @param writer the writer
150   * @throws XMLStreamException the XML stream exception
151   */
152  @Override public void emitExtraSoapHeaderNamespaceAliases(XMLStreamWriter writer)
153      throws XMLStreamException {
154    writer.writeAttribute(
155        "xmlns",
156        "",
157        EwsUtilities.WSSecuritySecExtNamespacePrefix,
158        EwsUtilities.WSSecuritySecExtNamespace);
159    writer.writeAttribute(
160        "xmlns",
161        "",
162        EwsUtilities.WSAddressingNamespacePrefix,
163        EwsUtilities.WSAddressingNamespace);
164  }
165
166  /**
167   * Serialize the WS-Security and WS-Addressing SOAP headers.
168   *
169   * @param writer the writer
170   * @param webMethodName the Web method being called
171   * @throws XMLStreamException the XML stream exception
172   */
173  @Override public void serializeExtraSoapHeaders(XMLStreamWriter writer, String webMethodName) throws XMLStreamException {
174    this.serializeWSAddressingHeaders(writer, webMethodName);
175    this.serializeWSSecurityHeaders(writer);
176  }
177
178  /**
179   * Creates the WS-Addressing headers necessary to send with an outgoing request.
180   *
181   * @param xmlWriter the XML writer to serialize the headers to
182   * @param webMethodName the Web method being called
183   * @throws XMLStreamException the XML stream exception
184   */
185  private void serializeWSAddressingHeaders(XMLStreamWriter xmlWriter,
186      String webMethodName) throws XMLStreamException {
187    EwsUtilities.ewsAssert(webMethodName != null,
188        "WSSecurityBasedCredentials.SerializeWSAddressingHeaders",
189        "Web method name cannot be null!");
190
191    EwsUtilities.ewsAssert(this.ewsUrl != null,
192        "WSSecurityBasedCredentials.SerializeWSAddressingHeaders",
193        "EWS Url cannot be null!");
194
195    // Format the WS-Addressing headers.
196    String wsAddressingHeaders = String.format(
197        WSSecurityBasedCredentials.wsAddressingHeadersFormat,
198        webMethodName, this.ewsUrl);
199
200    // And write them out...
201    xmlWriter.writeCharacters(wsAddressingHeaders);
202  }
203
204  /**
205   * Creates the WS-Security header necessary to send with an outgoing request.
206   *
207   * @param xmlWriter The XML writer to serialize the headers to
208   * @throws XMLStreamException the XML stream exception
209   */
210  @Override public void serializeWSSecurityHeaders(XMLStreamWriter xmlWriter)
211      throws XMLStreamException {
212    EwsUtilities.ewsAssert(this.securityToken != null,
213        "WSSecurityBasedCredentials.SerializeWSSecurityHeaders",
214        "Security token cannot be null!");
215
216    // <wsu:Timestamp wsu:Id="_timestamp">
217    //   <wsu:Created>2007-09-20T01:13:10.468Z</wsu:Created>
218    //   <wsu:Expires>2007-09-20T01:18:10.468Z</wsu:Expires>
219    // </wsu:Timestamp>
220    //
221    String timestamp = null;
222    if (this.addTimestamp) {
223      Calendar utcNow = Calendar.getInstance();
224      utcNow.add(Calendar.MINUTE, 5);
225      timestamp = String.format(WSSecurityBasedCredentials.wsuTimeStampFormat, utcNow, utcNow);
226
227    }
228
229    // Format the WS-Security header based on all the information we have.
230    String wsSecurityHeader = String.format(
231        WSSecurityBasedCredentials.wsSecurityHeaderFormat,
232        timestamp + this.securityToken);
233
234    // And write the header out...
235    xmlWriter.writeCharacters(wsSecurityHeader);
236  }
237
238  /**
239   * Adjusts the URL based on the credential.
240   *
241   * @param url The URL.
242   * @return Adjust URL.
243   * @throws java.net.URISyntaxException the uRI syntax exception
244   */
245  @Override public URI adjustUrl(URI url) throws URISyntaxException {
246    return new URI(getUriWithoutWSSecurity(url) + WSSecurityBasedCredentials.wsSecurityPathSuffix);
247  }
248
249  /**
250   * Gets the security token.
251   */
252  protected String getSecurityToken() {
253    return this.securityToken;
254  }
255
256  /**
257   * Sets the security token.
258   */
259  protected void setSecurityToken(String value) {
260    securityToken = value;
261  }
262
263  /**
264   * Gets the EWS URL.
265   */
266  protected URI getEwsUrl() {
267    return this.ewsUrl;
268  }
269
270  /**
271   * Sets the EWS URL.
272   */
273  protected void setEwsUrl(URI value) {
274    ewsUrl = value;
275  }
276}