View Javadoc

1   /*
2    * The MIT License
3    *
4    * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5    *
6    * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   *
26   * $HeadURL$
27   * $Id$
28   */
29  
30  package dk.sosi.seal.model;
31  
32  import dk.sosi.seal.model.constants.*;
33  import dk.sosi.seal.modelbuilders.ModelBuildException;
34  import dk.sosi.seal.pki.Federation;
35  import dk.sosi.seal.xml.XmlUtil;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  import org.w3c.dom.Node;
39  import org.w3c.dom.NodeList;
40  
41  import java.text.ParseException;
42  import java.util.Date;
43  import java.util.LinkedList;
44  import java.util.List;
45  
46  /**
47   * @author $LastChangedBy:$ $LastChangedDate:$
48   * @version $Revision:$
49   */
50  public class LibertyRequest extends AbstractDOMInfoExtractor {
51  
52      public LibertyRequest(Federation federation, Document doc) {
53          this.dom = doc.getDocumentElement();
54          final IdentityToken identityToken = getIdentityToken();
55          identityToken.validateSignature(federation);
56          //TODO always validate signature? Or only when the token is expired? Make this a parameter?
57          validateLibertySignature(federation);
58  
59          final Date now = new Date();
60          if (now.before(identityToken.getNotBefore()) || ! now.before(identityToken.getNotOnOrAfter())) {
61              validateLibertyTimestamp(identityToken);
62          }
63      }
64  
65      public IdentityToken getIdentityToken() {
66          final Element assertion = getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, SAMLTags.assertion);
67          if (assertion == null) {
68              throw new ModelBuildException("Could not find SAML assertion element");
69          }
70          return new IdentityToken(assertion);
71      }
72  
73      public Date getCreatedTimestamp() {
74          final Element createdTimestamp = getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, WSUTags.timestamp, WSUTags.created);
75          if (createdTimestamp == null) {
76              throw new ModelBuildException("Could not find wsu:Created element");
77          }
78          try {
79              return XmlUtil.fromXMLTimeStamp(createdTimestamp.getTextContent());
80          } catch (ParseException e) {
81              throw new ModelBuildException("Could not parse wsu:Created element", e);
82          }
83      }
84  
85      public String getMessageID() {
86          final Element messageID = getTag(SOAPTags.envelope, SOAPTags.header, WSATags.messageID);
87          if (messageID == null) {
88              throw new ModelBuildException("Could not find " + NameSpaces.WSA_1_0_SCHEMA + "#MessageID element");
89          }
90          return messageID.getTextContent();
91      }
92  
93      private void validateLibertySignature(Federation federation) {
94          final Element signatureElement = getLibertySignatureElement();
95          if ( ! SignatureUtil.validate(signatureElement, federation, null, true)) {
96             throw new ModelBuildException("Liberty signature could not be validated");
97          }
98  
99          final NodeList references = signatureElement.getElementsByTagNameNS(NameSpaces.DSIG_SCHEMA, DSTags.REFERENCE);
100         final List<Element> dereferencedSignedElements = dereference(references);
101 
102         final Element messageID = getWSAddressingMessageIDElement();
103         validateIsReferenced(messageID, dereferencedSignedElements);
104 
105         final Element action = getWSAddressingActionElement();
106         validateIsReferenced(action, dereferencedSignedElements);
107 
108         final Element to = getWSAddressingToElement();
109         if (to != null) {
110             validateIsReferenced(to, dereferencedSignedElements);
111         }
112 
113         final Element framework = getLibertyFrameworkElement();
114         validateIsReferenced(framework, dereferencedSignedElements);
115 
116         final Element timestamp = getTimestampElement();
117         validateIsReferenced(timestamp, dereferencedSignedElements);
118 
119         final Element securityTokenReference = getSecurityTokenReferenceElement();
120         validateIsReferenced(securityTokenReference, dereferencedSignedElements);
121         validateReferenceToIdentityToken(securityTokenReference);
122 
123         final Element bodyElement = getBodyElement();
124         validateIsReferenced(bodyElement, dereferencedSignedElements);
125     }
126 
127     private List<Element> dereference(NodeList references) {
128         final LinkedList<Element> elements = new LinkedList<Element>();
129         for (int i = 0; i < references.getLength(); i++) {
130             final Element reference = (Element) references.item(i);
131             final String uri = reference.getAttribute(DSAttributes.URI);
132             final Element element = (Element) XmlUtil.getElementByIdExtended(dom, uri.substring(1)); // Strip '#'
133             if (element != null) {
134                 elements.add(element);
135             }
136         }
137         return elements;
138     }
139 
140     private void validateReferenceToIdentityToken(Element securityTokenReference) {
141         final Element keyIdentifier = XmlUtil.getFirstChildElementNS(securityTokenReference, NameSpaces.WSSE_SCHEMA, WSSETags.KEY_IDENTIFIER);
142         if (keyIdentifier == null) {
143             throw new ModelBuildException("Could not find KeyIdentifier element in SecurityTokenReference");
144         }
145         final Node token = XmlUtil.getElementByIdExtended(dom, keyIdentifier.getTextContent());
146         if (! getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, SAMLTags.assertion).equals(token)) {
147             throw new ModelBuildException("SecurityTokenReference is not referencing IdentityToken");
148         }
149     }
150 
151     private void validateLibertyTimestamp(IdentityToken identityToken) {
152         if (getCreatedTimestamp().before(identityToken.getNotBefore()) || getCreatedTimestamp().after(identityToken.getNotOnOrAfter())) {
153             throw new ModelBuildException("Liberty timestamp (wsu:Created) is not within the Identity Tokens validity period");
154         }
155     }
156 
157     private void validateIsReferenced(Element element, List<Element> references) {
158         if (! references.contains(element)) {
159             throw new ModelBuildException("Missing Liberty signature on element " + element.getNamespaceURI() + "#" + element.getLocalName());
160         }
161     }
162 
163     private Element getLibertySignatureElement() {
164         final Element signature = getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, DSTags.signature);
165         if (signature == null) {
166             throw new ModelBuildException("Could not find Liberty signature element");
167         }
168         return signature;
169     }
170 
171     private Element getWSAddressingMessageIDElement() {
172         final Element messageID = getTag(SOAPTags.envelope, SOAPTags.header, WSATags.messageID);
173         if (messageID == null) {
174             throw new ModelBuildException("Could not find WS-Addressing 1.0 MessageID element");
175         }
176         return messageID;
177     }
178 
179 
180     private Element getWSAddressingActionElement() {
181         final Element action = getTag(SOAPTags.envelope, SOAPTags.header, WSATags.action);
182         if (action == null) {
183             throw new ModelBuildException("Could not find WS-Addressing 1.0 Action element");
184         }
185         return action;
186     }
187 
188     private Element getWSAddressingToElement() {
189         return getTag(SOAPTags.envelope, SOAPTags.header, WSATags.to);
190     }
191 
192     private Element getLibertyFrameworkElement() {
193         final Element framework = getTag(SOAPTags.envelope, SOAPTags.header, LibertyTags.framework);
194         if (framework == null) {
195             throw new ModelBuildException("Could not find Liberty Framework element");
196         }
197         return framework;
198     }
199 
200     private Element getTimestampElement() {
201         final Element timestamp = getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, WSUTags.timestamp);
202         if (timestamp == null) {
203             throw new ModelBuildException("Could not find Timestamp element");
204         }
205         return timestamp;
206     }
207 
208     private Element getSecurityTokenReferenceElement() {
209         final Element securityTokenReference = getTag(SOAPTags.envelope, SOAPTags.header, WSSETags.security, WSSETags.securityTokenReference);
210         if (securityTokenReference == null) {
211             throw new ModelBuildException("Could not find SecurityTokenReference element");
212         }
213         return securityTokenReference;
214     }
215 
216     private Element getBodyElement() {
217         final Element body = getTag(SOAPTags.envelope, SOAPTags.body);
218         if (body == null) {
219             throw new ModelBuildException("Could not find Body element");
220         }
221         return body;
222     }
223 
224 }