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: FSNameIdentifierMappingRequest.java,v 1.2 2008/06/25 05:46:44 qcheng Exp $
026 *
027 * Portions Copyrighted 2014-2016 ForgeRock AS.
028 */
029
030package com.sun.identity.federation.message;
031
032import static org.forgerock.openam.utils.Time.*;
033
034import com.sun.identity.shared.xml.XMLUtils;
035
036import com.sun.identity.shared.Constants;
037import com.sun.identity.shared.DateUtils;
038import com.sun.identity.common.SystemConfigurationUtil;
039
040import com.sun.identity.federation.common.FSUtils;
041import com.sun.identity.federation.common.IFSConstants;
042import com.sun.identity.federation.message.common.FSMsgException;
043
044import com.sun.identity.saml.assertion.NameIdentifier;
045import com.sun.identity.saml.common.SAMLConstants;
046import com.sun.identity.saml.common.SAMLUtils;
047import com.sun.identity.saml.common.SAMLException;
048import com.sun.identity.saml.common.SAMLResponderException;
049
050import com.sun.identity.saml.protocol.AbstractRequest;
051
052import com.sun.identity.saml.xmlsig.XMLSignatureManager;
053
054import java.util.Date;
055import java.util.List;
056
057import org.w3c.dom.Document;
058import org.w3c.dom.Element;
059import org.w3c.dom.Node;
060import org.w3c.dom.NodeList;
061
062/**
063 * The class <code>FSNameIdentifierMappingRequest</code> is used to
064 * create or parse <code>NameIdentifierMappingRequest<code>.
065 *
066 * @supported.all.api
067 * @deprecated since 12.0.0
068 */
069@Deprecated
070public class FSNameIdentifierMappingRequest extends AbstractRequest {
071    
072    private String providerID;
073    private NameIdentifier nameIdentifier;
074    private String targetNamespace;
075    private int minorVersion = IFSConstants.FF_12_PROTOCOL_MINOR_VERSION;
076    private String signatureString;
077    
078    /**
079     * Constructor to create <code> FSNameIdentifierMappingRequest<code>.
080     *
081     * @param providerID the requesting provider's ID
082     * @param nameIdentifier the <code>NameIdentifier</code> qualified by the
083     *        requesting service provider
084     * @param targetNamespace the provider ID of the other service provider
085     *                        which the requesting service provider would
086     *                        subsequently communicate with
087     * @throws FSMsgException if there is an error creating the object.
088     */
089    public FSNameIdentifierMappingRequest(String providerID,
090            NameIdentifier nameIdentifier, String targetNamespace)
091            throws FSMsgException {
092        this.providerID = providerID;
093        this.nameIdentifier = nameIdentifier;
094        this.targetNamespace = targetNamespace;
095        this.requestID = SAMLUtils.generateID();
096        setIssueInstant(newDate());
097    }
098    
099    /**
100     * Constructor to create <code> FSNameIdentifierMappingRequest<code> from
101     * the Document Element.
102     *
103     * @param root the <code>NameIdentifierMappingRequest</code> Document
104     *        element.
105     * @throws FSMsgException if there is an error.
106     */
107    public FSNameIdentifierMappingRequest(Element root) throws FSMsgException {
108        if (root == null) {
109            FSUtils.debug.message(
110                    "FSNameIdentifierMappingRequest: null element input.");
111            throw new FSMsgException("nullInputParameter",null);
112        }
113        String tag = null;
114        if (((tag = root.getLocalName()) == null) ||
115                (!tag.equals(IFSConstants.NAMEID_MAPPING_REQUEST))) {
116            FSUtils.debug.message(
117                    "FSNameIdentifierMappingRequest: wrong input");
118            throw new FSMsgException("wrongInput",null);
119        }
120        
121        // get IssueInstant
122        String instantString = root.getAttribute(IFSConstants.ISSUE_INSTANT);
123        if (instantString==null || instantString.length()==0) {
124            FSUtils.debug.error("FSNameIdentifierMappingRequest: " +
125                    "missing IssueInstant");
126            String[] args = { IFSConstants.ISSUE_INSTANT };
127            throw new FSMsgException("missingAttribute",args);
128        } else {
129            try {
130                issueInstant = DateUtils.stringToDate(instantString);
131            } catch (Exception e) {
132                FSUtils.debug.error("FSNameIdentifierMappingRequest: " +
133                        "could not parse IssueInstant.", e);
134                throw new FSMsgException("wrongInput",null);
135            }
136        }
137        
138        // get RequestID
139        requestID = root.getAttribute(IFSConstants.REQUEST_ID);
140        
141        // get and check versions
142        parseMajorVersion(root.getAttribute(IFSConstants.MAJOR_VERSION));
143        parseMinorVersion(root.getAttribute(IFSConstants.MINOR_VERSION));
144        
145        // get ProviderID, NameIdentifier & TargetNamespace
146        NodeList contentnl = root.getChildNodes();
147        Node child;
148        String nodeName;
149        int length = contentnl.getLength();
150        for (int i = 0; i < length; i++) {
151            child = contentnl.item(i);
152            if ((nodeName = child.getLocalName()) != null) {
153                if (nodeName.equals(IFSConstants.PROVIDER_ID)) {
154                    providerID = XMLUtils.getElementValue((Element) child);
155                } else if (nodeName.equals(IFSConstants.NAME_IDENTIFIER)) {
156                    try {
157                        nameIdentifier =
158                                new NameIdentifier((Element) child);
159                    } catch (SAMLException samle) {
160                        FSUtils.debug.error("FSNameIdentifierMappingRequest: " +
161                                "unable to initialize NameIdentifier", samle);
162                        throw new FSMsgException(
163                                "nameIdentifierCreateError",null,samle);
164                    }
165                } else if (nodeName.equals(IFSConstants.TARGET_NAME_SPACE)) {
166                    targetNamespace = XMLUtils.getElementValue((Element) child);
167                }
168            }
169        }
170        
171        // get signature
172        List signs = XMLUtils.getElementsByTagNameNS1(
173                root,
174                SAMLConstants.XMLSIG_NAMESPACE_URI,
175                SAMLConstants.XMLSIG_ELEMENT_NAME);
176        int signsSize = signs.size();
177        if (signsSize == 1) {
178            Element elem = (Element)signs.get(0);
179            setSignature(elem);
180            signed = true;
181        } else if (signsSize != 0) {
182            FSUtils.debug.error("FSNameIdentifierMappingRequest: " +
183                    "included more than one Signature element.");
184            throw new FSMsgException("moreElement",null);
185        }
186    }
187    
188    /**
189     * Returns <code>FSNameIdentifierMappingRequest</code> object. This
190     * object is created by parsing the <code>XML</code> string.
191     *
192     * @param xml <code>XML</code> String
193     * @return the <code>FSNameIdentifierMappingRequest</code> object.
194     * @throws FSMsgException if there is an error creating this object.
195     */
196    public static FSNameIdentifierMappingRequest parseXML(String xml)
197    throws FSMsgException {
198        Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
199        if (doc == null) {
200            FSUtils.debug.error("FSNameIdentifierMappingRequest.parseXML: " +
201                    "error while parsing input xml string");
202            throw new FSMsgException("parseError",null);
203        }
204        Element root = doc.getDocumentElement();
205        return new FSNameIdentifierMappingRequest(root);
206    }
207    
208    /**
209     * Returns the <code>ProviderID</code> attribute. This
210     * is the requesting Service Providers's identifier.
211     *
212     * @return the <code>ProviderID</code> attribute.
213     */
214    public String getProviderID() {
215        return providerID;
216    }
217    
218    /**
219     * Returns the <code>NameIdentifier</code> object  qualified by the
220     * requesting service provider .
221     *
222     * @return the <code>NameIdentifier</code> object  qualified by the
223     * requesting service provider .
224     */
225    public NameIdentifier getNameIdentifier() {
226        return nameIdentifier;
227    }
228    
229    /**
230     *  Returns the value of <code>TargetNamespace</code> attribute.
231     *
232     * @return the value of <code>TargetNamespace</code> attribute.
233     */
234    public String getTargetNamespace() {
235        return targetNamespace;
236    }
237    
238    /**
239     * Sets the <code>MajorVersion</code> by parsing the version string.
240     *
241     * @param majorVer a String representing the <code>MajorVersion</code> to
242     *        be set.
243     * @throws FSMsgException when the version mismatches.
244     */
245    private void parseMajorVersion(String version) throws FSMsgException {
246        try {
247            majorVersion = Integer.parseInt(version);
248        } catch (NumberFormatException e) {
249            if (FSUtils.debug.messageEnabled()) {
250                FSUtils.debug.message("FSNameIdentifierMappingRequest." +
251                        "parseMajorVersion: invalid MajorVersion: " + version, e);
252            }
253            throw new FSMsgException("wrongInput",null);
254        }
255        
256        if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) {
257            if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) {
258                if (FSUtils.debug.messageEnabled()) {
259                    FSUtils.debug.message("FSNameIdentifierMappingRequest." +
260                            "parseMajorVersion: MajorVersion is too high");
261                }
262                throw new FSMsgException("requestVersionTooHigh",null);
263            } else {
264                if (FSUtils.debug.messageEnabled()) {
265                    FSUtils.debug.message("FSNameIdentifierMappingRequest." +
266                            "parseMajorVersion: MajorVersion is too low");
267                }
268                throw new FSMsgException("requestVersionTooLow",null);
269            }
270        }
271    }
272    
273    /**
274     * Sets the <code>MinorVersion</code> by parsing the version string.
275     *
276     * @param minorVer a String representing the <code>MinorVersion</code> to
277     *        be set.
278     * @throws FSMsgException when the version mismatches.
279     */
280    private void parseMinorVersion(String version) throws FSMsgException {
281        try {
282            minorVersion = Integer.parseInt(version);
283        } catch (NumberFormatException e) {
284            if (FSUtils.debug.messageEnabled()) {
285                FSUtils.debug.message("FSNameIdentifierMappingRequest." +
286                        "parseMinorVersion: invalid MinorVersion: " + version, e);
287            }
288            throw new FSMsgException("wrongInput",null);
289        }
290
291        if (minorVersion > IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
292            if (FSUtils.debug.messageEnabled()) {
293                FSUtils.debug.message("FSNameIdentifierMappingRequest." +
294                    "parseMinorVersion: MinorVersion is too high");
295            }
296            throw new FSMsgException("requestVersionTooHigh",null);
297        } else if (minorVersion < IFSConstants.FF_11_PROTOCOL_MINOR_VERSION) {
298            if (FSUtils.debug.messageEnabled()) {
299                FSUtils.debug.message("FSNameIdentifierMappingRequest." +
300                    "parseMinorVersion: MinorVersion is too low");
301            }
302            throw new FSMsgException("requestVersionTooLow",null);
303        }
304    }
305    
306    /**
307     * Signs the XML document representing
308     * <code>NameIdentifierMappingRequest</code> using the certificate
309     * indicated by the property "com.sun.identity.saml.xmlsig.certalias"
310     * in AMConfig.properties file.
311     *
312     * @throws SAMLException if there is an error signing the XML document.
313     */
314    public void signXML() throws SAMLException {
315        String certAlias = SystemConfigurationUtil.getProperty(
316                Constants.SAML_XMLSIG_CERT_ALIAS);
317        signXML(certAlias);
318    }
319    
320    /**
321     * Signs the XML document representing
322     * <code>NameIdentifierMappingRequest</code> using the specified
323     * certificate.
324     *
325     * @param certAlias the alias (name) of the certificate used for signing
326     *                   the XML document
327     * @throws SAMLException it there is an error.
328     */
329    public void signXML(String certAlias) throws SAMLException {
330        FSUtils.debug.message("FSNameIdentifierMappingRequest.signXML");
331        if (signed) {
332            if (FSUtils.debug.messageEnabled()) {
333                FSUtils.debug.message("FSNameIdentifierMappingRequest.signXML: "
334                        + "the request is already signed.");
335            }
336            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
337                    "alreadySigned",null);
338        }
339        if (certAlias==null || certAlias.length()==0) {
340            if (FSUtils.debug.messageEnabled()) {
341                FSUtils.debug.message("FSNameIdentifierMappingRequest.signXML: "
342                        + "null certAlias");
343            }
344            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
345                    "cannotFindCertAlias",null);
346        }
347        try {
348            XMLSignatureManager manager = XMLSignatureManager.getInstance();
349            signatureString = manager.signXML(this.toXMLString(true, true),
350                    certAlias, (String) null, IFSConstants.REQUEST_ID,
351                    this.getRequestID(), false);
352            signature = XMLUtils.toDOMDocument(signatureString, FSUtils.debug)
353            .getDocumentElement();
354            signed = true;
355        } catch (Exception e){
356            FSUtils.debug.error("FSNameIdentifierMappingRequest.signXML: " +
357                    "unable to sign", e);
358            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
359                    "signFailed",null);
360            
361        }
362    }
363    
364    /**
365     * Returns the string representation of this object.
366     * This method translates the response to an XML document string.
367     *
368     * @return An XML String representing the response. NOTE: this is a
369     *         complete SAML response xml string with ResponseID,
370     *         MajorVersion, etc.
371     */
372    
373    public String toXMLString() throws FSMsgException {
374        return toXMLString(true, true);
375    }
376    
377    /**
378     * Returns the string representation of this object.
379     *
380     * @return An XML String representing the response.
381     * @throws FSMsgException if there is an error converting
382     *         this object ot a string.
383     */
384    public String toXMLString(boolean includeNS, boolean declareNS)
385    throws FSMsgException {
386        return toXMLString(includeNS, declareNS, false);
387    }
388    
389    /**
390     * Returns a String representation of the &lt;samlp:Response&gt; element.
391     *
392     * @param includeNS Determines whether or not the namespace qualifier
393     *        is prepended to the Element when converted
394     * @param declareNS Determines whether or not the namespace is declared
395     *        within the Element.
396     * @param includeHeader Determines whether the output include the xml
397     *        declaration header.
398     * @return a string containing the valid XML for this element
399     * @throws FSMsgException if there is an error converting
400     *         this object ot a string.
401     */
402    public String toXMLString(boolean includeNS, boolean declareNS,
403            boolean includeHeader) throws FSMsgException {
404        
405        String prefix = "";
406        String uriLIB = "";
407        String uriSAML = "";
408        if (includeNS) {
409            prefix = IFSConstants.LIB_PREFIX;
410        }
411        if (declareNS) {
412            uriLIB = IFSConstants.LIB_12_NAMESPACE_STRING;
413            uriSAML = IFSConstants.assertionDeclareStr;
414        }
415        String instantString = null;
416        try {
417            instantString = DateUtils.toUTCDateFormat(issueInstant);
418        } catch (Exception e) {
419            FSUtils.debug.error("FSNameIdentifierMappingRequest.toXMLString: " +
420                    "could not convert issueInstant to String.", e);
421        }
422        
423        // construct xml request
424        StringBuffer xml = new StringBuffer(1000);
425        if (includeHeader) {
426            xml.append(IFSConstants.XML_PREFIX)
427            .append(IFSConstants.DEFAULT_ENCODING)
428            .append(IFSConstants.QUOTE)
429            .append(IFSConstants.SPACE)
430            .append(IFSConstants.QUESTION_MARK)
431            .append(IFSConstants.RIGHT_ANGLE)
432            .append(IFSConstants.NL);
433        }
434        xml.append(IFSConstants.LEFT_ANGLE)
435        .append(prefix)
436        .append(IFSConstants.NAMEID_MAPPING_REQUEST)
437        .append(uriLIB).append(uriSAML)
438        .append(IFSConstants.SPACE)
439        .append(IFSConstants.REQUEST_ID)
440        .append(IFSConstants.EQUAL_TO)
441        .append(IFSConstants.QUOTE)
442        .append(requestID)
443        .append(IFSConstants.QUOTE)
444        .append(IFSConstants.SPACE)
445        .append(IFSConstants.SPACE)
446        .append(IFSConstants.MAJOR_VERSION)
447        .append(IFSConstants.EQUAL_TO)
448        .append(IFSConstants.QUOTE)
449        .append(majorVersion)
450        .append(IFSConstants.QUOTE)
451        .append(IFSConstants.SPACE)
452        .append(IFSConstants.SPACE)
453        .append(IFSConstants.MINOR_VERSION)
454        .append(IFSConstants.EQUAL_TO)
455        .append(IFSConstants.QUOTE)
456        .append(minorVersion)
457        .append(IFSConstants.QUOTE)
458        .append(IFSConstants.SPACE)
459        .append(IFSConstants.SPACE)
460        .append(IFSConstants.ISSUE_INSTANT)
461        .append(IFSConstants.EQUAL_TO)
462        .append(IFSConstants.QUOTE)
463        .append(instantString)
464        .append(IFSConstants.QUOTE)
465        .append(IFSConstants.SPACE)
466        .append(IFSConstants.RIGHT_ANGLE);
467        
468        if (signed) {
469            if (signatureString != null) {
470                xml.append(signatureString);
471            } else if (signature != null) {
472                signatureString = XMLUtils.print(signature);
473                xml.append(signatureString);
474            }
475        }
476        xml.append(IFSConstants.LEFT_ANGLE)
477        .append(prefix)
478        .append(IFSConstants.PROVIDER_ID)
479        .append(IFSConstants.RIGHT_ANGLE)
480        .append(providerID)
481        .append(IFSConstants.START_END_ELEMENT)
482        .append(prefix)
483        .append(IFSConstants.PROVIDER_ID)
484        .append(IFSConstants.RIGHT_ANGLE);
485        
486        if (nameIdentifier != null) {
487            xml.append(nameIdentifier.toString());
488        }
489        
490        xml.append(IFSConstants.LEFT_ANGLE)
491        .append(prefix)
492        .append(IFSConstants.TARGET_NAME_SPACE)
493        .append(IFSConstants.RIGHT_ANGLE)
494        .append(targetNamespace)
495        .append(IFSConstants.START_END_ELEMENT)
496        .append(prefix)
497        .append(IFSConstants.TARGET_NAME_SPACE)
498        .append(IFSConstants.RIGHT_ANGLE)
499        .append(IFSConstants.START_END_ELEMENT)
500        .append(prefix)
501        .append(IFSConstants.NAMEID_MAPPING_REQUEST)
502        .append(IFSConstants.RIGHT_ANGLE);
503        
504        return xml.toString();
505    }
506}