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: SubjectImpl.java,v 1.3 2008/06/25 05:48:13 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.xacml.context.impl;
030
031import com.sun.identity.shared.xml.XMLUtils;
032import com.sun.identity.xacml.common.XACMLConstants;
033import com.sun.identity.xacml.common.XACMLException;
034import com.sun.identity.xacml.common.XACMLSDKUtils;
035import com.sun.identity.xacml.context.Attribute;
036import com.sun.identity.xacml.context.ContextFactory;
037import com.sun.identity.xacml.context.Subject;
038
039import java.util.List;
040import java.net.URI;
041import java.util.ArrayList;
042import org.w3c.dom.Document;
043import org.w3c.dom.Element;
044import org.w3c.dom.Node;
045import org.w3c.dom.NodeList;
046
047/**
048 * The <code>Subject</code> element specifies information about a
049 * subject of the <code>Request</code> context by listing a 
050 * sequence of <code>Attribute</code> elements associated with the
051 * subject. A subject is an entity associated with the access request.
052 * <p>
053 * <pre>
054 * &lt;xs:complexType name="SubjectType">
055 *  &lt;xs:sequence>
056 *   &lt;xs:element ref="xacml-context:Attribute" minOccurs="0"
057 *      maxOccurs="unbounded"/>
058 * &lt;xs:sequence>
059 * &lt;xs:attribute name="SubjectCategory" type="xs:anyURI" 
060 *  default="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"/>
061 * &lt;xs:complexType>
062 * </pre>
063 *@supported.all.api
064 */
065public class SubjectImpl implements Subject {
066    private List  attributes ;
067    private URI subjectCategory;
068    private Attribute subjectCategoryAttribute;
069    private boolean isMutable = true;
070    private boolean needToCreateSubjectCategory = false;
071
072   /** 
073    * Default constructor
074    */
075    public SubjectImpl() {
076    }
077
078    /**
079     * This constructor is used to build <code>Subject</code> object from a
080     * XML string.
081     *
082     * @param xml A <code>java.lang.String</code> representing
083     *        a <code>Subject</code> object
084     * @exception XACMLException if it could not process the XML string
085     */
086    public SubjectImpl(String xml) throws XACMLException {
087        Document document = XMLUtils.toDOMDocument(xml, XACMLSDKUtils.debug);
088        if (document != null) {
089            Element rootElement = document.getDocumentElement();
090            processElement(rootElement);
091            makeImmutable();
092        } else {
093            XACMLSDKUtils.debug.error(
094                "SubjectImpl.processElement(): invalid XML input");
095            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
096                "errorObtainingElement"));
097        }
098    }
099
100    /**
101     * This constructor is used to build <code>Subject</code> object from a
102     * block of existing XML that has already been built into a DOM.
103     *
104     * @param element A <code>org.w3c.dom.Element</code> representing
105     *        DOM tree for <code>Subject</code> object
106     * @exception XACMLException if it could not process the Element
107     */
108    public SubjectImpl(Element element) throws XACMLException {
109        processElement(element);
110        makeImmutable();
111    }
112
113    private void processElement(Element element) throws XACMLException {
114        if (element == null) {
115            XACMLSDKUtils.debug.error(
116                "SubjectImpl.processElement(): invalid root element");
117            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
118                "invalid_element"));
119        }
120        String elemName = element.getLocalName(); 
121        if (elemName == null) {
122             XACMLSDKUtils.debug.error(
123                "SubjectImpl.processElement(): local name missing");
124            throw new XACMLException( XACMLSDKUtils.xacmlResourceBundle.getString(
125                "missing_local_name"));
126        }
127
128        if (!elemName.equals(XACMLConstants.SUBJECT)) {
129            XACMLSDKUtils.debug.error(
130                "SubjectImpl.processElement(): invalid local name " +
131                 elemName);
132            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
133                "invalid_local_name"));
134        }
135        // starts processing subelements
136        NodeList nodes = element.getChildNodes();
137        int numOfNodes = nodes.getLength();
138        if (numOfNodes >= 1) {
139            ContextFactory factory = ContextFactory.getInstance();
140            for (int nextElem = 0; nextElem < numOfNodes; nextElem++) {
141                Node child = (Node)nodes.item(nextElem);
142                if ((child.getNodeType() == Node.ELEMENT_NODE) ||
143                    (child.getNodeType() == Node.ATTRIBUTE_NODE )) {
144                    // The child nodes should be <Attribute> 
145                    // or <SubjectCategory>
146                    String attrChildName = child.getLocalName();
147                    if (attrChildName.equals(XACMLConstants.ATTRIBUTE)) {
148                        if (this.attributes == null) {
149                        this.attributes = new ArrayList();
150                        }
151                        Attribute attribute = factory.getInstance().
152                                createAttribute((Element)child);
153                        attributes.add(attribute);
154                    } else if (attrChildName.equals(
155                            XACMLConstants.SUBJECT_CATEGORY)) {
156                        try {
157                            subjectCategory = new URI (child.getNodeValue());
158                        } catch ( Exception e) {
159                            throw new XACMLException(
160                                XACMLSDKUtils.xacmlResourceBundle.getString( 
161                                    "attribute_not_uri"));
162                        }
163                    } else {
164                        XACMLSDKUtils.debug.error("RequestImpl."
165                            +"processElement(): Invalid element :"
166                            +attrChildName);
167                        throw new XACMLException(
168                            XACMLSDKUtils.xacmlResourceBundle.getString( 
169                                "invalid_element"));
170                    }
171                }
172            }
173         }
174    }
175    /**
176     * Returns zero to many <code>Attribute</code> elements of this object
177     * If no attributes and present, empty <code>List</code> will be returned.
178     * Typically a <code>Subject</code> element will contain an <code>
179     * Attribute</code> with an <code>AttributeId</code> of
180     * "urn:oasis:names:tc:xacml:1.0:subject:subject-id", containing 
181     * the identity of the <code>Subject</code>
182     *
183     * @return the <code>Attribute</code> elements of this object
184     */
185    public List getAttributes() { 
186        return attributes;
187    }
188
189    /**
190     * Sets the <code>Attribute</code> elements of this object
191     *
192     * @param attributes <code>Attribute</code> elements of this object
193     * attributes could be an empty <code>List</code>, if no attributes
194     * are present.
195     *
196     * @exception XACMLException if the object is immutable
197     * An object is considered <code>immutable</code> if <code>
198     * makeImmutable()</code> has been invoked on it. It can
199     * be determined by calling <code>isMutable</code> on the object.
200     */
201    public void setAttributes(List attributes) throws XACMLException {
202        if (!isMutable) {
203            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
204                "objectImmutable"));
205        }
206        if (attributes != null &&  !attributes.isEmpty()) {
207            if (this.attributes == null) {
208                this.attributes = new ArrayList();
209            }
210            this.attributes.addAll(attributes);
211        }
212    }
213
214    /**
215     * Returns the <code>SubjectCategory</code> of this object.
216     * This is optional so could be null if not defined.
217     * This attribute indicates the role that the parent <code>Subject</code> 
218     * played in the formation of the access request. If this attribute is not 
219     * present in the <code>Subject</code> element, then the
220     * default value of 
221     * urn:oasis:names:tc:xacml:1.0:subject-category:access-subject SHALL be
222     * used, indicating that the <code>Subject</code> represents the entity 
223     * ultimately responsible for initiating the access request.
224     *
225     * @return <code>URI</code> representing the 
226     * <code>SubjectCategory</code> of this  object.
227     */
228    public URI getSubjectCategory() {
229        try {
230            if (subjectCategory == null) {
231                subjectCategory = new URI(XACMLConstants.ACCESS_SUBJECT);
232            }
233        } catch (Exception e) { // cant do anything, return null
234        }
235        return subjectCategory;
236    }
237
238    /**
239     * Sets the <code>SubjectCategory</code> of this object
240     *
241     * @param subjectCategory <code>URI</code> 
242     *
243     * @exception XACMLException if the object is immutable
244     * An object is considered <code>immutable</code> if <code>
245     * makeImmutable()</code> has been invoked on it. It can
246     * be determined by calling <code>isMutable</code> on the object.
247     */
248    public void setSubjectCategory(URI subjectCategory) throws 
249        XACMLException 
250    {
251        if (!isMutable) {
252            throw new XACMLException(XACMLSDKUtils.xacmlResourceBundle.getString(
253                "objectImmutable"));
254        }
255        if (subjectCategory != null) {
256            this.subjectCategory = subjectCategory;
257        } /*else {
258            needToCreateSubjectCategory = true;
259             try {
260               subjectCategory = new URI(SUBJECT_CATEGORY_DEFAULT);
261               List values = new ArrayList();
262               values.add(subjectCategory.toString());
263                 subjectCategoryAttribute = 
264                     XACMLSDKUtils.createAttribute(values, new URI(
265                     SUBJECT_CATEGORY_ID), new URI(URI_DATATYPE), null);
266             } catch ( Exception e) {
267                 throw new XACMLException(e);
268             }
269        }*/
270    }
271
272   /**
273    * Returns a <code>String</code> representation of this object
274    * @param includeNSPrefix Determines whether or not the namespace qualifier
275    *        is prepended to the Element when converted
276    * @param declareNS Determines whether or not the namespace is declared
277    *        within the Element.
278    * @return a string representation of this object
279    * @exception XACMLException if conversion fails for any reason
280     */
281    public String toXMLString(boolean includeNSPrefix, boolean declareNS)
282            throws XACMLException
283    {
284        StringBuffer sb = new StringBuffer(2000);
285        StringBuffer NS = new StringBuffer(100);
286
287        //TODO: remove the 2 following line
288        includeNSPrefix = false;
289        declareNS = false;
290
291        String appendNS = "";
292        if (declareNS) {
293            NS.append(XACMLConstants.CONTEXT_NS_DECLARATION)
294                    .append(XACMLConstants.SPACE);
295            NS.append(XACMLConstants.XSI_NS_URI)
296                    .append(XACMLConstants.SPACE)
297                    .append(XACMLConstants.CONTEXT_SCHEMA_LOCATION);
298        }
299        if (includeNSPrefix) {
300            appendNS = XACMLConstants.CONTEXT_NS_PREFIX + ":";
301        }
302        sb.append("<").append(appendNS).append(XACMLConstants.SUBJECT)
303                .append(NS);
304        if (subjectCategory != null) {
305            sb.append(" ").append(XACMLConstants.SUBJECT_CATEGORY).append("=");
306            sb.append("\"").append(subjectCategory.toString()).append("\"");
307        }
308        sb.append(">");
309        int length = 0;
310        if (attributes != null) {
311            sb.append("\n");
312            length = attributes.size();
313            for (int i = 0; i < length; i++) {
314                Attribute attr = (Attribute)attributes.get(i);
315                sb.append(attr.toXMLString(includeNSPrefix, false));
316            }
317        }
318     /*   if (needToCreateSubjectCategory && subjectCategoryAttribute != null) {
319                sb.append(subjectCategoryAttribute.toXMLString(
320                    includeNSPrefix, false));
321        }// its already covered in the previous list of attrs.
322      */
323        sb.append("</").append(appendNS).append(XACMLConstants.SUBJECT);
324        sb.append(">\n");
325        return sb.toString();
326    }
327
328   /**
329    * Returns a string representation of this object
330    *
331    * @return a string representation of this object
332    * @exception XACMLException if conversion fails for any reason
333    */
334    public String toXMLString() throws XACMLException {
335        return toXMLString(true, false);
336    }
337
338   /**
339    * Makes the object immutable
340    */
341    public void makeImmutable() {}
342
343   /**
344    * Checks if the object is mutable
345    *
346    * @return <code>true</code> if the object is mutable,
347    *         <code>false</code> otherwise
348    */
349    public boolean isMutable() {
350        return isMutable;
351    }
352    
353}