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    
024    package microsoft.exchange.webservices.data.credential;
025    
026    import microsoft.exchange.webservices.data.core.EwsUtilities;
027    
028    import javax.xml.stream.XMLStreamException;
029    import javax.xml.stream.XMLStreamWriter;
030    
031    import java.net.URI;
032    import java.net.URISyntaxException;
033    import java.util.Calendar;
034    
035    /**
036     * WSSecurityBasedCredentials is the base class for all credential classes using
037     * WS-Security.
038     */
039    public 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    }