001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: FSSAMLRequest.java,v 1.2 2008/06/25 05:46:45 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.federation.message;
030
031import java.text.ParseException;
032import java.util.List;
033import java.util.Collections;
034import java.util.ArrayList;
035
036import org.w3c.dom.Element;
037import org.w3c.dom.Node;
038import org.w3c.dom.NodeList;
039import com.sun.identity.shared.xml.XMLUtils;
040import com.sun.identity.shared.DateUtils;
041import com.sun.identity.saml.protocol.AssertionArtifact;
042import com.sun.identity.saml.protocol.Request;
043import com.sun.identity.saml.common.SAMLConstants;
044import com.sun.identity.saml.common.SAMLException;
045import com.sun.identity.saml.common.SAMLUtils;
046import com.sun.identity.saml.common.SAMLRequestVersionTooHighException;
047import com.sun.identity.saml.common.SAMLRequestVersionTooLowException;
048import com.sun.identity.saml.common.SAMLRequesterException;
049import com.sun.identity.saml.xmlsig.XMLSignatureManager;
050import com.sun.identity.federation.common.*;
051
052/**
053 * This class had methods to create a <code>SAML</code> Request
054 * object from a Document Element and to create Request message
055 * from this object.
056 *
057 * @supported.all.api
058 */
059
060public class FSSAMLRequest extends Request {
061    
062    /*
063     * Default Constructor.
064     */
065    protected FSSAMLRequest() {}
066    
067    /**
068     * Constructor creates <code>FSSAMLRequest</code> from
069     * the Document Element.
070     *
071     * @param root the Document Element.
072     * @throws SAMLException if there is an error creating this object.
073     */
074    public FSSAMLRequest(Element root) throws SAMLException {
075        // Make sure this is a Request
076        String tag = null;
077        if (root == null) {
078            SAMLUtils.debug.message("FSSAMLRequest(Element): null input.");
079            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
080                    "nullInput",null);
081        }
082        if (((tag = root.getLocalName()) == null) ||
083                (!tag.equals("Request"))) {
084            SAMLUtils.debug.message("FSSAMLRequest(Element): wrong input");
085            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
086                    "wrongInput",null);
087        }
088        
089        // Attribute MajorVersion
090        parseMajorVersion(root.getAttribute("MajorVersion"));
091        
092        // Attribute MinorVersion
093        parseMinorVersion(root.getAttribute("MinorVersion"));
094        
095        List signs = XMLUtils.getElementsByTagNameNS1(root,
096                SAMLConstants.XMLSIG_NAMESPACE_URI,
097                SAMLConstants.XMLSIG_ELEMENT_NAME);
098        int signsSize = signs.size();
099        if (signsSize == 1) {
100            XMLSignatureManager manager = XMLSignatureManager.getInstance();
101            if (minorVersion ==
102                    IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) {
103                valid = manager.verifyXMLSignature(root);
104            } else {
105                valid = manager.verifyXMLSignature(root,
106                        IFSConstants.REQUEST_ID, null);
107            }
108            if (!valid) {
109                if (SAMLUtils.debug.messageEnabled()) {
110                    SAMLUtils.debug.message("FSSAMLRequest(Element): couldn't"
111                            + " verify Request's signature.");
112                }
113            }
114            xmlString = XMLUtils.print(root);
115            signed = true;
116        } else if (signsSize != 0) {
117            if (SAMLUtils.debug.messageEnabled()) {
118                SAMLUtils.debug.message("FSSAMLRequest(Element): included more "
119                        + "than one Signature element.");
120            }
121            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
122                    "moreElement",null);
123        }
124        
125        // Attribute RequestID
126        requestID = root.getAttribute("RequestID");
127        if ((requestID == null) || (requestID.length() == 0)) {
128            if (SAMLUtils.debug.messageEnabled()) {
129                SAMLUtils.debug.message("FSSAMLRequest(Element): Request "
130                        + "does not have a RequestID.");
131            }
132            String[] args = { IFSConstants.REQUEST_ID };
133            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
134                    "missingAttribute",args);
135        }
136        
137        // Attribute IssueInstant
138        String instantString = root.getAttribute("IssueInstant");
139        if ((instantString == null) || (instantString.length() == 0)) {
140            SAMLUtils.debug.message("FSSAMLRequest(Element): "
141                    + " missing IssueInstant");
142            String[] args = { IFSConstants.ISSUE_INSTANT };
143            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
144                    "missingAttribute",args);
145        } else {
146            try {
147                issueInstant = DateUtils.stringToDate(instantString);
148            } catch (ParseException e) {
149                SAMLUtils.debug.message(
150                        "FSSAMLRequest(Element): could not parse IssueInstant",
151                        e);
152                throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
153                        "wrongInput",null);
154            }
155        }
156        
157        // get the contents of the request
158        NodeList contentnl = root.getChildNodes();
159        Node child;
160        String nodeName;
161        String respondWith;
162        for (int i = 0, length = contentnl.getLength(); i < length; i++) {
163            child = contentnl.item(i);
164            if ((nodeName = child.getLocalName()) != null) {
165                if (nodeName.equals("RespondWith")) {
166                    respondWith = XMLUtils.getElementValue((Element) child);
167                    if (respondWith.length() == 0) {
168                        if (SAMLUtils.debug.messageEnabled()) {
169                            SAMLUtils.debug.message("FSSAMLRequest(Element): "
170                                    + "wrong RespondWith value.");
171                        }
172                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
173                                "wrongInput",null);
174                    }
175                    if (respondWiths == Collections.EMPTY_LIST) {
176                        respondWiths = new ArrayList();
177                    }
178                    respondWiths.add(respondWith);
179                } else if (nodeName.equals("Signature")) {
180                    signature = (Element) child;
181                } else if (nodeName.equals("AssertionArtifact")) {
182                    // make sure the content has no other elements assigned
183                    if ((contentType != NOT_SUPPORTED) &&
184                            (contentType != ASSERTION_ARTIFACT)) {
185                        if (SAMLUtils.debug.messageEnabled()) {
186                            SAMLUtils.debug.message("FSSAMLRequest(Element): "
187                                    + "contained mixed contents.");
188                        }
189                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
190                                "wrongInput",null);
191                    }
192                    contentType = ASSERTION_ARTIFACT;
193                    if (artifacts == Collections.EMPTY_LIST) {
194                        artifacts = new ArrayList();
195                    }
196                    try{
197                        AssertionArtifact newArt = new FSAssertionArtifact(
198                                XMLUtils.getElementValue((Element) child));
199                        artifacts.add(newArt);
200                    }catch (Exception e){
201                        SAMLUtils.debug.error("FSSAMLRequest(Element): ", e);
202                    }
203                } else {
204                    if (SAMLUtils.debug.messageEnabled()) {
205                        SAMLUtils.debug.message("FSSAMLRequest(Element):invalid"
206                                + " node" + nodeName);
207                    }
208                    throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
209                            "wrongInput",null);
210                } // check nodeName
211            } // if nodeName != null
212        } // done for the nodelist loop
213        
214        if (contentType == NOT_SUPPORTED) {
215            SAMLUtils.debug.message("Request: empty content.");
216            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
217                    "wrongInput",null);
218        }
219    }
220    
221    
222    /**
223     * Sets the <code>MajorVersion</code> by parsing the version string.
224     *
225     * @param majorVer a String representing the <code>MajorVersion</code> to
226     *        be set.
227     * @throws SAMLException when the version mismatches.
228     */
229    private void parseMajorVersion(String majorVer) throws SAMLException {
230        try {
231            majorVersion = Integer.parseInt(majorVer);
232        } catch (NumberFormatException e) {
233            if (SAMLUtils.debug.messageEnabled()) {
234                SAMLUtils.debug.message("FSSAMLRequest(Element): invalid "
235                        + "MajorVersion", e);
236            }
237            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
238                    "wrongInput",null);
239        }
240        
241        if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) {
242            if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) {
243                if (SAMLUtils.debug.messageEnabled()) {
244                    SAMLUtils.debug.message("FSSAMLRequest(Element): "
245                            + "MajorVersion of the Request is too high.");
246                }
247                throw new SAMLRequestVersionTooHighException(
248                        FSUtils.BUNDLE_NAME,"requestVersionTooHigh",null);
249            } else {
250                if (SAMLUtils.debug.messageEnabled()) {
251                    SAMLUtils.debug.message("FSSAMLRequest(Element): "
252                            + "MajorVersion of the Request is too low.");
253                }
254                throw new SAMLRequestVersionTooLowException(FSUtils.BUNDLE_NAME,
255                        "requestVersionTooLow",null);
256            }
257        }
258        
259    }
260    
261    /**
262     * Sets the <code>MinorVersion</code> by parsing the version string.
263     *
264     * @param minorVer a String representing the <code>MinorVersion</code> to
265     *        be set.
266     * @throws SAMLException when the version mismatches.
267     */
268    private void parseMinorVersion(String minorVer) throws SAMLException {
269        try {
270            minorVersion = Integer.parseInt(minorVer);
271        } catch (NumberFormatException e) {
272            if (FSUtils.debug.messageEnabled()) {
273                FSUtils.debug.message("Request(Element): invalid "
274                        + "MinorVersion", e);
275            }
276            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
277                    "wrongInput",null);
278        }
279        
280        if(minorVersion > IFSConstants.FF_12_SAML_PROTOCOL_MINOR_VERSION) {
281            FSUtils.debug.error("Request(Element): MinorVersion"
282                    + " of the Request is too high.");
283            throw new SAMLRequestVersionTooHighException(FSUtils.BUNDLE_NAME,
284                    "requestVersionTooHigh",null);
285        } else if (minorVersion <
286                IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) {
287            FSUtils.debug.error("Request(Element): MinorVersion"
288                    + " of the Request is too low.");
289            throw new SAMLRequestVersionTooLowException(FSUtils.BUNDLE_NAME,
290                    "requestVersionTooLow",null);
291        }
292    }
293    
294    /**
295     * Returns the <code>MinorVersion</code> attribute.
296     *
297     * @return the Minor Version.
298     * @see #setMinorVersion(int)
299     */
300    public int getMinorVersion() {
301        return minorVersion;
302    }
303    
304    /**
305     * Sets the <code>MinorVersion</code>.
306     *
307     * @param version the minor version in the assertion.
308     * @see #setMinorVersion(int)
309     */
310    public void setMinorVersion(int version) {
311        minorVersion = version;
312    }
313}