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: XACMLAuthzDecisionQueryImpl.java,v 1.4 2008/06/25 05:48:15 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.xacml.saml2.impl;
030
031import com.sun.identity.saml2.assertion.AssertionFactory;
032import com.sun.identity.saml2.common.SAML2Exception;
033import com.sun.identity.saml2.protocol.impl.RequestAbstractImpl;
034import com.sun.identity.saml2.protocol.ProtocolFactory;
035import com.sun.identity.shared.xml.XMLUtils;
036import com.sun.identity.shared.DateUtils;
037import com.sun.identity.xacml.common.XACMLException;
038import com.sun.identity.xacml.common.XACMLConstants;
039import com.sun.identity.xacml.common.XACMLSDKUtils;
040import com.sun.identity.xacml.context.Request;
041import com.sun.identity.xacml.saml2.XACMLAuthzDecisionQuery;
042import com.sun.identity.xacml.context.ContextFactory;
043import org.w3c.dom.Attr;
044import org.w3c.dom.Document;
045import org.w3c.dom.Element;
046import org.w3c.dom.NamedNodeMap;
047import org.w3c.dom.Node;
048import org.w3c.dom.NodeList;
049import java.text.ParseException;
050
051/**
052 * The <code>XACMLAuthzDecisionQueryImpl</code> is an impelmentation
053 * of <code>XACMLAuthzDecisionQuery</code> interface.
054 *
055 * The <code>XACMLAuthzDecisionQuery</code> element is a SAML Query that 
056 * extends SAML Protocol schema type <code>RequestAbstractType</code>.
057 * It allows an XACML PEP to submit an XACML Request Context in a  SAML
058 * Query along with other information. This element is an alternative to 
059 * SAML defined <code><samlp:AuthzDecisionQuery></code> that allows an 
060 * XACML PEP  to communicate with an XACML PDP using SAML2 protocol.
061 * <p>
062 * <pre>
063 *&lt;xs:element name="XACMLAuthzDecisionQuery"
064 *         type="XACMLAuthzDecisionQueryType"/>
065 *&lt;xs:complexType name="XACMLAuthzDecisionQueryType">
066 *  &lt;xs:complexContent>
067 *    &lt;xs:extension base="samlp:RequestAbstractType">
068 *      &lt;xs:sequence>
069 *        &lt;xs:element ref="xacml-context:Request"/>
070 *      &lt;xs:sequence>
071 *      &lt;xs:attribute name="InputContextOnly"
072 *                    type="boolean"
073 *                    use="optional"
074 *                    default="false"/>
075 *      &lt;xs:attribute name="ReturnContext"
076 *                    type="boolean"
077 *                    use="optional"
078 *                    default="false"/>
079 *    &lt;xs:extension>
080 *  &lt;xs:complexContent>
081 *&lt;xs:complexType>
082 * </pre>
083 *
084 * Schema for Base:
085 * <pre>
086 *  &lt;complexType name="RequestAbstractType" abstract="true">
087 *      &lt;sequence>
088 *          &lt;element ref="saml:Issuer" minOccurs="0"/>
089 *          &lt;element ref="ds:Signature" minOccurs="0"/>
090 *          &lt;element ref="samlp:Extensions" minOccurs="0"/>
091 *      &lt;sequence>
092 *      &lt;attribute name="ID" type="ID" use="required"/>
093 *      &lt;attribute name="Version" type="string" use="required"/>
094 *      &lt;attribute name="IssueInstant" type="dateTime" use="required"/>
095 *      &lt;attribute name="Destination" type="anyURI" use="optional"/>
096 *      &lt;attribute name="Consent" type="anyURI" use="optional"/>
097 *  &lt;complexType>
098 * </pre>
099 *@supported.all.api
100 */
101public class XACMLAuthzDecisionQueryImpl extends RequestAbstractImpl 
102        implements XACMLAuthzDecisionQuery {
103    
104    //TODO: need to reimplement toXML, toXML, process,
105    //makeImmutable, isMutable methods
106    private boolean inputContextOnly = false;
107    private boolean returnContext = false;
108    private Request request;
109
110    private String xmlString;
111
112    /**
113     * Default constructor
114     */
115    public XACMLAuthzDecisionQueryImpl() {
116        isMutable = true;
117    }
118    
119    /**
120     * This constructor is used to build <code>XACMLAuthzDecisionQuery</code> 
121     * object from a block of existing XML that has already been built into a
122     * DOM.
123     *
124     * @param element A <code>org.w3c.dom.Element</code> representing
125     *        DOM tree for <code>XACMLAuthzDecisionQuery</code> object
126     * @exception SAML2Exception if it could not process the Element
127     */
128    public XACMLAuthzDecisionQueryImpl(Element element) throws SAML2Exception {
129        parseDOMElement(element);
130        if (isSigned) {
131            signedXMLString = XMLUtils.print(element);
132        }
133    }
134    
135    /**
136     * This constructor is used to build <code>XACMLAuthzDecisionQuery</code>
137     * object from a XML string.
138     *
139     * @param xml A <code>java.lang.String</code> representing
140     *        an <code>XACMLAuthzDecisionQuery</code> object
141     * @exception XACMLException if it could not process the XML string
142     */
143    public XACMLAuthzDecisionQueryImpl(String xml) throws SAML2Exception {
144        Document document = XMLUtils.toDOMDocument(xml, XACMLSDKUtils.debug);
145        if (document != null) {
146            Element rootElement = document.getDocumentElement();
147            parseDOMElement(rootElement);
148            this.xmlString = xml;
149            if(isSigned) {
150                signedXMLString = xml;
151            }
152        } else {
153            XACMLSDKUtils.debug.error(
154                    "XACMLAuthzDecisionQueryImpl.processElement(): invalid XML "
155                     +"input");
156            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
157                    "errorObtainingElement"));
158        }
159    }
160    
161    
162    /**
163     * Returns the XML attribute boolean value which governs the
164     * source of information that the PDP is allowed to use in
165     * making an authorization decision. If this attribute is "true"
166     * then it indiactes that the authorization decision has been made
167     * solely on the basis of information contained in the <code>
168     * XACMLAuthzDecisionQuery</code>; no external attributes have been
169     * used. If this value is "false" then the decision may have been made
170     * on the basis of external attributes not conatined in the <code>
171     * XACMLAuthzDecisionQuery</code>.
172     * @return <code>boolean</code> indicating the value
173     * of this attribute.
174     */
175    public boolean getInputContextOnly() {
176        return inputContextOnly;
177    }
178    
179    
180    /**
181     * Sets the XML attribute boolean value which governs the
182     * source of information that the PDP is allowed to use in
183     * making an authorization decision. If this attribute is "true"
184     * then it indicates to the PDP  that the authorization decision has to be 
185     * made solely on the basis of information contained in the <code>
186     * XACMLAuthzDecisionQuery</code>; no external attributes may be
187     * used. If this value is "false" then the decision can be  made
188     * on the basis of external attributes not conatined in the <code>
189     * XACMlAuthzDecisionQuery</code>.
190     * @param inputContextOnly <code>boolean</code> indicating the value
191     * of this attribute.
192     *
193     * @exception XACMLException if the object is immutable
194     * An object is considered <code>immutable</code> if <code>
195     * makeImmutable()</code> has been invoked on it. It can
196     * be determined by calling <code>isMutable</code> on the object.
197     */
198    public void setInputContextOnly(boolean inputContextOnly) throws
199            XACMLException 
200    {
201        this.inputContextOnly = inputContextOnly;
202    }
203    
204    
205    /**
206     * Returns the XML attribute boolean value which provides means
207     * to PEP to request that an <code>xacml-context>Request</code>
208     * element be included in the <code>XACMlAuthzdecisionStatement</code>
209     * resulting from the request. It also governs the contents of that
210     * <code.Request</code> element. If this attribite is "true" then the
211     * PDP SHALL include the <code>xacml-context:Request</code> element in the
212     * <code>XACMLAuthzDecisionStatement</code> element in the 
213     * <code>XACMLResponse</code>.
214     * The <code>xacml-context:Request</code> SHALL include all the attributes 
215     * supplied by the PEP in the <code>AuthzDecisionQuery</code> which were 
216     * used in making the authz decision. Other addtional attributes which may 
217     * have been used by the PDP may be included.
218     * If this attribute is "false" then the PDP SHALL NOT include the
219     * <code>xacml-context:Request</code> element in the 
220     * <code>XACMLAuthzDecisionStatement<code>.
221     *
222     * @return <code>boolean</code> indicating the value
223     * of this attribute.
224     */
225    public boolean getReturnContext() {
226        return returnContext;
227    }
228    
229    /**
230     * Sets the boolean value for this XML attribute
231     *
232     * @param returnContext <code>boolean</code> indicating the value
233     * of this attribute.
234     *
235     * @exception XACMLException if the object is immutable
236     * An object is considered <code>immutable</code> if <code>
237     * makeImmutable()</code> has been invoked on it. It can
238     * be determined by calling <code>isMutable</code> on the object.
239     *
240     * @see #getReturnContext()
241     */
242    public void setReturnContext(boolean returnContext) throws XACMLException {
243        this.returnContext = returnContext;
244    }
245    
246    /**
247     * Returns the <code>xacml-context:Request</code> element of this object
248     *
249     * @return the <code>xacml-context:Request</code> elements of this object
250     */
251    public Request getRequest() {
252        return request;
253    }
254    
255    /**
256     * Sets the <code>xacml-context:Request</code> element of this object
257     *
258     * @param request the <code>xacml-context:Request</code> element of this
259     * object.
260     *
261     * @exception XACMLException if the object is immutable
262     * An object is considered <code>immutable</code> if <code>
263     * makeImmutable()</code> has been invoked on it. It can
264     * be determined by calling <code>isMutable</code> on the object.
265     */
266    public void setRequest(Request request) throws XACMLException {
267        if (request == null) {
268            throw new XACMLException(
269                    XACMLSDKUtils.xacmlResourceBundle.getString(
270                    "null_not_valid")); 
271        }
272        this.request = request;
273    }
274    
275    /**
276     * Returns a string representation of this object
277     *
278     * @return a string representation of this object
279     * @exception XACMLException if conversion fails for any reason
280     */
281    public String toXMLString() throws XACMLException {
282        //top level element
283        return toXMLString(true, true);
284    }
285
286    /**
287     * Returns a <code>String</code> representation of this object
288     * @param includeNSPrefix Determines whether or not the namespace qualifier
289     *        is prepended to the Element when converted
290     * @param declareNS Determines whether or not the namespace is declared
291     *        within the Element.
292     * @return a string representation of this object
293     * @exception XACMLException if conversion fails for any reason
294     */
295    public String toXMLString(boolean includeNSPrefix, boolean declareNS)
296    throws XACMLException {
297        if (isSigned && signedXMLString != null) {
298            return signedXMLString;
299        }
300
301        //validateData();
302        StringBuffer sb = new StringBuffer(1000);
303        String nsPrefix = "";
304        String nsDeclaration = "";
305        if (declareNS) {
306            nsDeclaration = XACMLConstants.SAMLP_NS_DECLARATION;
307        }
308        if (includeNSPrefix) {
309            nsPrefix = XACMLConstants.SAMLP_NS_PREFIX;
310        }
311
312        sb.append("\n<")
313                .append(XACMLConstants.SAMLP_NS_PREFIX)
314                .append(XACMLConstants.REQUEST_ABSTRACT)
315                .append(XACMLConstants.SAMLP_NS_DECLARATION)
316                .append(XACMLConstants.XSI_TYPE_XACML_AUTHZ_DECISION_QUERY)
317                .append(XACMLConstants.XSI_NS_DECLARATION)
318                .append(XACMLConstants.XACML_SAMLP_NS_DECLARATION)
319            .append(XACMLConstants.SPACE)
320            .append(XACMLConstants.XACML_SAMLP_NS_PREFIX)
321            .append(XACMLConstants.INPUT_CONTEXT_ONLY).append("=")
322            .append(XACMLSDKUtils.quote(Boolean.toString(inputContextOnly)))
323            .append(XACMLConstants.SPACE)
324            .append(XACMLConstants.XACML_SAMLP_NS_PREFIX)
325            .append(XACMLConstants.RETURN_CONTEXT).append("=")
326            .append(XACMLSDKUtils.quote(Boolean.toString(returnContext)))
327            .append(XACMLConstants.SPACE)
328            .append("ID").append("=")
329            .append(XACMLSDKUtils.quote(requestId))
330            .append(XACMLConstants.SPACE)
331            .append("Version").append("=")
332            .append(XACMLSDKUtils.quote(version))
333            .append(XACMLConstants.SPACE)
334            .append("IssueInstant").append("=")
335            .append(XACMLSDKUtils.quote(DateUtils.toUTCDateFormat(
336                    issueInstant)));
337        if (destinationURI != null && destinationURI.trim().length() != 0) {
338            sb.append(" Destination=\"").append(destinationURI).
339                append("\"");
340        }
341        if (consent != null && consent.trim().length() != 0) {
342            sb.append(" Consent=\"").append(consent).append("\"");
343        }
344        sb.append(">\n");
345        try {
346        if (nameID != null) {
347            sb.append(nameID.toXMLString(includeNSPrefix, declareNS));
348        }
349        if (signatureString != null) {
350            sb.append(signatureString);
351        }
352        if (extensions != null) {
353            sb.append(extensions.toXMLString(includeNSPrefix, declareNS));
354        }
355        } catch (Exception e) {
356        }
357
358        if (request != null) {
359            sb.append(request.toXMLString(true, true)).append("\n");
360        }
361
362        sb.append("\n</")
363                .append(XACMLConstants.SAMLP_NS_PREFIX)
364                .append(XACMLConstants.REQUEST_ABSTRACT)
365                .append(">\n");
366        return  sb.toString();
367    }
368    
369    
370    protected void parseDOMElement(Element element) throws SAML2Exception {
371        //TODO: fix
372        String value = null;
373        if (element == null) {
374            XACMLSDKUtils.debug.error(
375                    "XACMLAuthzDecisionQueryImpl.processElement(): "
376                    + "invalid root element");
377            throw new XACMLException( XACMLSDKUtils.xacmlResourceBundle.getString(
378                    "invalid_element"));
379        }
380        
381        // First check that we're really parsing an XACMLAuthzDecisionQuery
382        if (! element.getLocalName().equals(
383            XACMLConstants.REQUEST_ABSTRACT)) {
384            XACMLSDKUtils.debug.error(
385                    "XACMLAuthzDecisionQueryImpl.processElement(): "
386                    + "invalid root element");
387            throw new XACMLException( XACMLSDKUtils.xacmlResourceBundle.getString(
388                    "missing_local_name"));
389        }
390        
391        //TODO: check for xsi:type=
392        
393        // now we get the request
394        NodeList nodes = element.getChildNodes();
395        ContextFactory factory = ContextFactory.getInstance();
396        for (int i = 0; i < nodes.getLength(); i++) {
397            Node node = nodes.item(i);
398            if ((node.getNodeType() == Node.ELEMENT_NODE) ||
399                    (node.getNodeType() == Node.ATTRIBUTE_NODE)) {
400                if (node.getLocalName().equals(XACMLConstants.REQUEST)) {
401                    if (request != null) {
402                        //validation error, throw error
403                    } else {
404                        request = factory.getInstance().createRequest(
405                                (Element)node);
406                    }
407                }
408            }
409        }
410
411        // make sure we got a request
412        if (request == null) {
413            //throw new XACMLException(
414             //       XACMLSDKUtils.xacmlResourceBundle.getString(
415             //       "null_not_valid"));
416        }
417        
418        System.out.println("ReturnContex:" + element.getAttributeNS(
419                XACMLConstants.XACML_SAMLP_NS_URI,
420                XACMLConstants.RETURN_CONTEXT));
421        System.out.println("InputContextOnly:" + element.getAttributeNS(
422                XACMLConstants.XACML_SAMLP_NS_URI,
423                XACMLConstants.INPUT_CONTEXT_ONLY));
424        String returnContextString = element.getAttributeNS(
425                XACMLConstants.XACML_SAMLP_NS_URI,
426                XACMLConstants.RETURN_CONTEXT);
427        if (returnContextString != null) {
428            returnContext = Boolean.valueOf(returnContextString).booleanValue();
429        }
430
431        String inputContextOnlyString = element.getAttributeNS(
432                XACMLConstants.XACML_SAMLP_NS_URI,
433                XACMLConstants.INPUT_CONTEXT_ONLY);
434        if (inputContextOnlyString != null) {
435            inputContextOnly = Boolean.valueOf(inputContextOnlyString)
436                    .booleanValue();
437        }
438
439        NamedNodeMap attrs = element.getAttributes();
440
441        //TODO: change the baseclass impl and call super.parse...
442
443        //parse the attributes of base class RequestAbstract
444        NamedNodeMap atts = ((Node)element).getAttributes();
445        if (atts != null) {
446            int length = atts.getLength();
447            for (int i = 0; i < length; i++) {
448                Attr attr = (Attr) atts.item(i);
449                String attrName = attr.getName();
450                String attrValue = attr.getValue().trim();
451                if (attrName.equals("ID")) {
452                    requestId = attrValue;
453                } else if (attrName.equals("Version")) {
454                    version = attrValue;
455                } else if (attrName.equals("IssueInstant")) {
456                    try {
457                        issueInstant = DateUtils.stringToDate(attrValue);
458                    } catch (ParseException pe) {
459                        throw new XACMLException(pe.getMessage());
460                    }
461                } else if (attrName.equals("Destination")) {
462                    destinationURI = attrValue;
463                }
464            }
465        }
466
467        //parse the elements of base class RequestAbstract
468        NodeList nl = element.getChildNodes();
469        Node child;
470        String childName;
471        int length = nl.getLength();
472        for (int i = 0; i < length; i++) {
473            child = nl.item(i);
474            if ((childName = child.getLocalName()) != null) {
475                if (childName.equals("Issuer")) {
476                    if (nameID != null) {
477                        if (XACMLSDKUtils.debug.messageEnabled()) {
478                            XACMLSDKUtils.debug.message(
479                                "ArtifactResolveImpl.parse"
480                                + "Element: included more than one Issuer.");
481                        }
482                        throw new XACMLException(
483                            XACMLSDKUtils.xacmlResourceBundle.getString(
484                            "invalid_duplicate_element"));
485                    }
486                    if (signatureString != null ||
487                        extensions != null )
488                    {
489                        if (XACMLSDKUtils.debug.messageEnabled()) {
490                            XACMLSDKUtils.debug.message(
491                                    "ArtifactResolveImpl.parse" 
492                                    + "Element:wrong sequence.");
493                        }
494                        throw new XACMLException(
495                            XACMLSDKUtils.xacmlResourceBundle.getString(
496                            "schemaViolation"));
497                    }
498                    nameID = AssertionFactory.getInstance().createIssuer(
499                        (Element) child);
500                } else if (childName.equals("Signature")) {
501                    if (signatureString != null) {
502                        if (XACMLSDKUtils.debug.messageEnabled()) {
503                            XACMLSDKUtils.debug.message(
504                                "ArtifactResolveImpl.parse"
505                                + "Element:included more than one Signature.");
506                        }
507                        throw new XACMLException(
508                            XACMLSDKUtils.xacmlResourceBundle.getString(
509                            "invalid_duplicate_element"));
510                    }
511                    if (extensions != null ) {
512                        if (XACMLSDKUtils.debug.messageEnabled()) {
513                            XACMLSDKUtils.debug.message(
514                                    "ArtifactResolveImpl.parse" 
515                                    + "Element:wrong sequence.");
516                        }
517                        throw new XACMLException(
518                            XACMLSDKUtils.xacmlResourceBundle.getString(
519                            "schemaViolation"));
520                    }
521                    signatureString = XMLUtils.print((Element) child);
522                    isSigned = true;
523                } else if (childName.equals("Extensions")) {
524                    if (extensions != null) {
525                        if (XACMLSDKUtils.debug.messageEnabled()) {
526                            XACMLSDKUtils.debug.message(
527                                "ArtifactResolveImpl.parse"
528                                + "Element:included more than one Extensions.");
529                        }
530                        throw new XACMLException(
531                            XACMLSDKUtils.xacmlResourceBundle.getString(
532                            "invalid_duplicate_element"));
533                    }
534                    extensions = ProtocolFactory.getInstance().createExtensions(
535                        (Element) child);
536                } else if (childName.equals("Request")) {
537                    //no action, it has been processd already
538                } else {
539                    if (XACMLSDKUtils.debug.messageEnabled()) {
540                        XACMLSDKUtils.debug.message(
541                            "XACMLAuthzDecisionQueryImpl.parseDOMElement"
542                            + "Element: Invalid element:" + childName);
543                    }
544                    throw new XACMLException(
545                        XACMLSDKUtils.xacmlResourceBundle.getString(
546                        "invalidElement"));
547                }
548            }
549        }
550
551        validateData();
552        
553    }
554    
555    /**
556     * Makes the object immutable
557     */
558    public void makeImmutable() {
559        //TODO: fix
560    }
561    
562    protected void validateData() throws SAML2Exception {
563        //TODO: fix or remove?
564        super.validateData();
565    }
566
567}