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: RequestImpl.java,v 1.4 2008/11/10 22:57:05 veiming Exp $
026 *
027 */
028
029package com.sun.identity.xacml.context.impl;
030
031import com.sun.identity.xacml.common.XACMLSDKUtils;
032import com.sun.identity.shared.xml.XMLUtils;
033import com.sun.identity.xacml.common.XACMLConstants;
034import com.sun.identity.xacml.common.XACMLException;
035import com.sun.identity.xacml.context.Attribute;
036import com.sun.identity.xacml.context.ContextFactory;
037import com.sun.identity.xacml.context.Resource;
038import com.sun.identity.xacml.context.Request;
039import com.sun.identity.xacml.context.Subject;
040import com.sun.identity.xacml.context.Action;
041import com.sun.identity.xacml.context.Environment;
042import com.sun.identity.xacml.context.impl.ActionImpl;
043import com.sun.identity.xacml.context.impl.EnvironmentImpl;
044import java.util.ArrayList;
045import java.util.HashSet;
046
047import java.util.List;
048import java.util.Set;
049import org.w3c.dom.Document;
050import org.w3c.dom.Element;
051import org.w3c.dom.Node;
052import org.w3c.dom.NodeList;
053
054/**
055 * The <code>Request</code> element is the top-level element in the XACML
056 * context schema. Its an abstraction layer used by the policy language.
057 * It contains <code>Subject</code>, <code>Resource</code>, <code>Action
058 * </code> and <code>Environment<code> elements.
059 * <p>
060 * <pre>
061 * &lt;xs:complexType name="RequestType">
062 *   &lt;xs:sequence>
063 *     &lt;xs:element ref="xacml-context:Subject" maxOccurs="unbounded"/>
064 *     &lt;xs:element ref="xacml-context:Resource" maxOccurs="unbounded"/>
065 *     &lt;xs:element ref="xacml-context:Action"/>
066 *     &lt;xs:element ref="xacml-context:Environment"/>
067 *   &lt;xs:sequence>
068 * &lt;xs:complexType>
069 * </pre>
070 *@supported.all.api
071 */
072public class RequestImpl implements Request {
073
074    private List subjects = new ArrayList();
075    private List resources = new ArrayList();
076    private Action action = null;
077    private Environment env = null;
078    private boolean isMutable = true;
079    
080    private  static Set supportedSubjectCategory = new HashSet();
081    static {
082        supportedSubjectCategory.add(XACMLConstants.ACCESS_SUBJECT);
083        supportedSubjectCategory.add(XACMLConstants.
084            INTERMEDIARY_SUBJECT);
085    };
086   /** 
087    * Default constructor
088    */
089    public RequestImpl() {
090    }
091
092    /**
093     * This constructor is used to build <code>Request</code> object from a
094     * XML string.
095     *
096     * @param xml A <code>java.lang.String</code> representing
097     *        a <code>Request</code> object
098     * @exception XACMLException if it could not process the XML string
099     */
100    public RequestImpl(String xml) throws XACMLException {
101        Document document = XMLUtils.toDOMDocument(xml, XACMLSDKUtils.debug);
102        if (document != null) {
103            Element rootElement = document.getDocumentElement();
104            processElement(rootElement);
105            makeImmutable();
106        } else {
107            XACMLSDKUtils.debug.error(
108                "RequestImpl.processElement(): invalid XML input");
109            throw new XACMLException(
110                XACMLSDKUtils.xacmlResourceBundle.getString(
111                "errorObtainingElement"));
112        }
113    }
114
115    /**
116     * This constructor is used to build <code>Request</code> object from a
117     * block of existing XML that has already been built into a DOM.
118     *
119     * @param element A <code>org.w3c.dom.Element</code> representing
120     *        DOM tree for <code>Request</code> object
121     * @exception XACML2Exception if it could not process the Element
122     */
123    public  RequestImpl(Element element) throws XACMLException {
124        processElement(element);
125        makeImmutable();
126    }
127
128    private void processElement(Element element) throws XACMLException {
129        if (element == null) {
130            XACMLSDKUtils.debug.error(
131                "RequestImpl.processElement(): invalid root element");
132            throw new XACMLException( 
133                XACMLSDKUtils.xacmlResourceBundle.getString(
134                "invalid_element"));
135        }
136        String elemName = element.getLocalName(); 
137        if (elemName == null) {
138             XACMLSDKUtils.debug.error(
139                "RequestImpl.processElement(): local name missing");
140            throw new XACMLException( 
141                XACMLSDKUtils.xacmlResourceBundle.getString(
142                "missing_local_name"));
143        }
144
145        if (!elemName.equals(XACMLConstants.REQUEST)) {
146            XACMLSDKUtils.debug.error(
147                "RequestImpl.processElement(): invalid local name " +
148                 elemName);
149            throw new XACMLException(
150                XACMLSDKUtils.xacmlResourceBundle.getString(
151                "invalid_local_name"));
152        }
153
154        // starts processing subelements
155        NodeList nodes = element.getChildNodes();
156        int numOfNodes = nodes.getLength();
157        if (numOfNodes < 1) {
158            XACMLSDKUtils.debug.error(
159                "RequestImpl.processElement(): request has no subelements");
160            throw new XACMLException(
161                XACMLSDKUtils.xacmlResourceBundle.getString(
162                "missing_subelements"));
163        }
164   
165        ContextFactory factory = ContextFactory.getInstance();
166        List children = new ArrayList();
167        int i = 0;
168        Node child;
169        while ( i < numOfNodes) {
170            child = (Node)nodes.item(i);
171            if (child.getNodeType() == Node.ELEMENT_NODE) {
172                children.add(child);
173            }
174            i++;
175        }
176        if (children.isEmpty()) {
177            XACMLSDKUtils.debug.error("RequestImpl.processElement():"
178                + " request has no subelements");
179            throw new XACMLException(
180                XACMLSDKUtils.xacmlResourceBundle.getString(
181                "missing_subelements"));
182        }
183        child = (Node)children.get(0);
184        // The first subelement should be <Subject>
185        String childName = child.getLocalName();
186        if ((childName == null) || (!childName.
187            equals(XACMLConstants.SUBJECT))) {
188            XACMLSDKUtils.debug.error("RequestImpl.processElement():"+
189                " the first element is not <Subject>");
190        throw new XACMLException(
191            XACMLSDKUtils.xacmlResourceBundle.getString(
192            "missing_subelement_subject"));
193        }
194        Subject subject = factory.getInstance().createSubject((Element)child);
195        if (!supportedSubjectCategory.contains(
196            subject.getSubjectCategory().toString())) 
197        {
198            XACMLSDKUtils.debug.error("RequestImpl.processElement():subject "
199                +"category in subject not supported");
200            throw new XACMLException(
201                XACMLSDKUtils.xacmlResourceBundle.getString(
202                "unsupported_subject_category")); 
203        }
204        subjects.add(subject);
205        boolean resourceFound = false;
206        boolean actionFound = false;
207        boolean envFound = false;
208        for ( int j = 1; j < children.size(); j++) {
209            child = (Node)children.get(j);
210            // so far <Resource> is not encountered
211            // Go through next sub elements for <Subject> and <Resource>
212            // The next subelement may be <Resource> or <Subject>
213            childName = child.getLocalName();
214            if ((childName != null) &&
215                (childName.equals(XACMLConstants.RESOURCE) || childName.
216                equals(XACMLConstants.SUBJECT))) {
217                    if (resourceFound) {
218                        if (childName.equals(XACMLConstants.SUBJECT)) {
219                            // all <Subject> should be before <Resource>
220                            XACMLSDKUtils.debug.error("RequestImpl."
221                                +"processElement(): <Subject> should be "
222                                + "before <Resource>");
223                            throw new XACMLException(
224                                XACMLSDKUtils.xacmlResourceBundle.getString(
225                                    "element_out_of_place"));
226                        } else { // found another resource
227                            Resource resource = factory.getInstance()
228                                    .createResource((
229                                    Element)child);
230                            resources.add(resource);
231                        }
232                    } else if (childName.equals(XACMLConstants.SUBJECT)) {
233                            subject = factory.getInstance().createSubject(
234                                (Element)child);
235                            subjects.add(subject);
236                    } else { // childname is resource
237                            resourceFound = true;
238                            Resource resource = factory.getInstance()
239                                    .createResource((
240                                    Element)child);
241                            resources.add(resource);
242                    }
243            } else if ((childName != null) && (childName.
244                equals(XACMLConstants.ACTION))) {
245                if (!resourceFound) {
246                    XACMLSDKUtils.debug.error("RequestImpl."
247                        +"processElement(): <Resource> should be "
248                        + "before <Action>");
249                    throw new XACMLException(
250                        XACMLSDKUtils.xacmlResourceBundle.getString(
251                            "element_out_of_place"));
252                } else {
253                    actionFound = true;
254                    action = factory.createAction((Element)child);                                     
255                }
256            } else if ((childName != null) && (childName.
257                equals(XACMLConstants.ENVIRONMENT))) {
258                if (!resourceFound || !actionFound){
259                    XACMLSDKUtils.debug.error("RequestImpl."
260                        +"processElement(): <Resource> and "
261                        +"Action should be before <Environment>");
262                        throw new XACMLException(
263                            XACMLSDKUtils.xacmlResourceBundle.getString(
264                                "element_out_of_place"));
265                } else {
266                    envFound = true;
267                    env = factory.createEnvironment((Element) child);
268                }
269            }
270        }
271        if (XACMLSDKUtils.debug.messageEnabled()) {
272            XACMLSDKUtils.debug.message("resourceFound:"+resourceFound);
273            XACMLSDKUtils.debug.message("actionFound:"+actionFound);
274            XACMLSDKUtils.debug.message("envFound:"+envFound);
275        }
276        if (!resourceFound || !actionFound || !envFound) {
277            XACMLSDKUtils.debug.error("RequestImpl.processElement(): Some"
278                +"of required elements are missing");
279            throw new XACMLException(
280                XACMLSDKUtils.xacmlResourceBundle.getString(
281                    "missing_subelements"));
282        }
283    }
284        
285
286    /**
287     * Returns the one to many <code>Subject</code> elements of this object
288     *
289     * @return the <code>Subject</code> elements of this object
290     */
291    public List getSubjects() {
292        return subjects;
293    }
294
295    /**
296     * Sets the one to many <code>Subject</code> elements of this object
297     *
298     * @param subjects the one to many <code>Subject</code> elements of this 
299     * object
300     *
301     * @exception XACMLException if the object is immutable
302     * An object is considered <code>immutable</code> if <code>
303     * makeImmutable()</code> has been invoked on it. It can
304     * be determined by calling <code>isMutable</code> on the object.
305     */
306    public void setSubjects(List subjects) throws XACMLException {
307        if (!isMutable) {
308            throw new XACMLException(
309                XACMLSDKUtils.xacmlResourceBundle.getString(
310                "objectImmutable"));
311        }
312        if (subjects == null || subjects.isEmpty()) {
313            throw new XACMLException(
314                XACMLSDKUtils.xacmlResourceBundle.getString(
315                    "null_not_valid"));
316        }
317        this.subjects.addAll(subjects);
318    }
319    
320
321    /**
322     * Returns the one to many <code>Resource</code> elements of this object
323     *
324     * @return the <code>Resource</code> elements of this object
325     */
326    public List getResources() {
327        return resources;
328    }
329
330    /**
331     * Sets the one to many <code>Resource</code> elements of this object
332     *
333     * @param resources the one to many <code>Resource</code> elements of this 
334     * object
335     *
336     * @exception XACMLException if the object is immutable
337     * An object is considered <code>immutable</code> if <code>
338     * makeImmutable()</code> has been invoked on it. It can
339     * be determined by calling <code>isMutable</code> on the object.
340     */
341    public void setResources(List resources) throws XACMLException {
342        if (!isMutable) {
343            throw new XACMLException(
344                XACMLSDKUtils.xacmlResourceBundle.getString(
345                "objectImmutable"));
346        } 
347        if (resources == null || resources.isEmpty()) {
348            throw new XACMLException(
349                XACMLSDKUtils.xacmlResourceBundle.getString("null_not_valid"));
350        }
351        this.resources.addAll(resources);
352    }
353
354    /**
355     * Returns the instance of <code>Action</code> element
356     *
357     * @return the instance of <code>Action</code>.
358     */
359    public Action getAction() {
360        return action;
361    }
362
363    /**
364     * Sets the instance of <code>Action</code>
365     *
366     * @param argAction instance of  <code>Action</code>.
367     * 
368     * @exception XACMLException if the object is immutable
369     * An object is considered <code>immutable</code> if <code>
370     * makeImmutable()</code> has been invoked on it. It can
371     * be determined by calling <code>isMutable</code> on the object.
372     */
373    public void setAction(Action argAction) throws XACMLException {
374        if (!isMutable) {
375            throw new XACMLException(
376                XACMLSDKUtils.xacmlResourceBundle.getString(
377                "objectImmutable"));
378        }
379        
380        if (argAction == null) {
381            throw new XACMLException(
382                XACMLSDKUtils.xacmlResourceBundle.getString("null_not_valid")); 
383        }
384        action = argAction;
385        
386    }
387
388    /**
389     * Returns the instance of <code>Environment</code> element.
390     *
391     * @return the instance of <code>Environment</code>.
392     */
393    public Environment getEnvironment() {
394        return env;
395    }
396
397    /**
398     * Sets the instance of the <code>Environment</code>
399     *
400     * @param argEnv instance of <code>Environment</code>.
401     * @throws XACMLException if the object is immutable
402     *         An object is considered <code>immutable</code> if <code>
403     *         makeImmutable()</code> has been invoked on it. It can
404     *         be determined by calling <code>isMutable</code> on the object.
405     */
406    public void setEnvironment(Environment argEnv) throws XACMLException {
407        if (!isMutable) {
408            throw new XACMLException(
409                XACMLSDKUtils.xacmlResourceBundle.getString(
410                "objectImmutable"));
411        } 
412        if (argEnv == null ) {
413            throw new XACMLException(
414                XACMLSDKUtils.xacmlResourceBundle.getString("null_not_valid")); 
415        }
416        env = argEnv;
417    }
418
419   /**
420    * Returns a <code>String</code> representation of this object
421    * @param includeNSPrefix Determines whether or not the namespace qualifier
422    *        is prepended to the Element when converted
423    * @param declareNS Determines whether or not the namespace is declared
424    *        within the Element.
425    * @return a string representation of this object
426    * @exception XACMLException if conversion fails for any reason
427     */
428    public String toXMLString(boolean includeNSPrefix, boolean declareNS)
429            throws XACMLException {
430        StringBuffer sb = new StringBuffer(2000);
431        StringBuffer namespaceBuffer = new StringBuffer(100);
432        String nsDeclaration = "";
433        if (declareNS) {
434            namespaceBuffer.append(XACMLConstants.CONTEXT_NS_DECLARATION).
435                append(XACMLConstants.SPACE);
436            namespaceBuffer.append(XACMLConstants.XSI_NS_URI).
437                append(XACMLConstants.SPACE).append(XACMLConstants.
438                CONTEXT_SCHEMA_LOCATION);
439        }
440        if (includeNSPrefix) {
441            nsDeclaration = XACMLConstants.CONTEXT_NS_PREFIX + ":";
442        }
443        sb.append("\n<").append(nsDeclaration).append(XACMLConstants.REQUEST).
444            append(namespaceBuffer).append(">\n");
445        int length = 0;
446        if (subjects != null && !subjects.isEmpty()) {
447            length = subjects.size();
448            for (int i = 0; i < length; i++) {
449                Subject sub = (Subject)subjects.get(i);
450                sb.append(sub.toXMLString(includeNSPrefix, false));
451            }
452        }
453        if (resources != null && !resources.isEmpty()) {
454            length = resources.size();
455            for (int i = 0; i < length; i++) {
456                Resource resource = (Resource)resources.get(i);
457                sb.append(resource.toXMLString(includeNSPrefix, false));
458            }
459        }
460        if (action != null) {
461            sb.append(action.toXMLString(includeNSPrefix, false));
462        }
463        if (env != null) {
464            sb.append(env.toXMLString(includeNSPrefix, false));
465        }
466        sb.append("</").append(nsDeclaration).append(XACMLConstants.REQUEST).
467        append(">\n");
468        return sb.toString();
469    }
470
471   /**
472    * Returns a string representation of this object
473    *
474    * @return a string representation of this object
475    * @exception XACMLException if conversion fails for any reason
476    */
477    public String toXMLString() throws XACMLException {
478        return this.toXMLString(true, false);
479    }
480
481   /**
482    * Makes the object immutable
483    */
484    public void makeImmutable() {}
485
486   /**
487    * Checks if the object is mutable
488    *
489    * @return <code>true</code> if the object is mutable,
490    *         <code>false</code> otherwise
491    */
492    public boolean isMutable() {
493        return isMutable;
494    }
495}