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: SASLResponse.java,v 1.2 2008/06/25 05:47:08 qcheng Exp $
026 *
027 */
028
029
030package com.sun.identity.liberty.ws.authnsvc.protocol;
031
032import java.util.ArrayList;
033import java.util.Iterator;
034import java.util.List;
035
036import org.w3c.dom.Document;
037import org.w3c.dom.Element;
038import org.w3c.dom.Node;
039import org.w3c.dom.NodeList;
040
041import javax.xml.namespace.QName;
042
043import com.sun.identity.shared.xml.XMLUtils;
044import com.sun.identity.shared.encode.Base64;
045import com.sun.identity.liberty.ws.authnsvc.AuthnSvcConstants;
046import com.sun.identity.liberty.ws.authnsvc.AuthnSvcException;
047import com.sun.identity.liberty.ws.authnsvc.AuthnSvcUtils;
048import com.sun.identity.liberty.ws.disco.common.DiscoConstants;
049import com.sun.identity.liberty.ws.disco.ResourceOffering;
050import com.sun.identity.liberty.ws.soapbinding.Utils;
051
052/**
053 * The <code>SASLResponse</code> class represents <code>SASLResponse</code>
054 * element defined in Authentication Service schema.
055 *
056 * @supported.all.api
057 */
058public class SASLResponse {
059    
060    /**
061     * Continue status where the server expects the client to send another
062     * <code>SASLRequest</code>
063     */ 
064    public static final String CONTINUE = "continue";
065
066    /**
067     * Abort status where the server is aborting the authentication exchange.
068     */
069    public static final String ABORT = "abort";
070
071    /**
072     * OK status where the server considers the authentication exchange to have
073     * successfully completed.
074     */
075    public static final String OK = "OK";
076
077    private String statusCode = null;
078    private PasswordTransforms passwordTransforms = null;
079    private byte[] data = null;
080    private ResourceOffering resourceOffering = null;
081    private List credentials = null;
082    private String serverMechanism = null;
083    private String id = null;
084    private String messageID = null;
085    private String refToMessageID = null;
086
087    /**
088     * Constructs a <code>SASLResponse</code> instance.
089     *
090     * @param statusCode Status Code.
091     */
092    public SASLResponse(String statusCode) {
093        this.statusCode = statusCode;
094    }
095
096    /**
097     * Constructs a <code>SASLResponse</code> with a 
098     * <code>org.w3c.dom.Element</code>.
099     * @param element a <code>SASLResponse</code> element
100     * @exception AuthnSvcException if an error occurs while parsing the
101     *            <code>SASLResponse</code> element
102     */
103    public SASLResponse(Element element) throws AuthnSvcException {
104        Element statusE = null;
105        Element ptE = null;
106        Element dataE = null;
107        Element roE = null;
108        Element credentialsE = null;
109
110        NodeList nl = element.getChildNodes();
111        int length = nl.getLength();
112
113        int i;
114        for(i = 0; i < length; i++) {
115            Node child = nl.item(i);
116            if (child.getNodeType() == Node.ELEMENT_NODE) {
117                Element childElement = (Element)child;
118                String localName = childElement.getLocalName();
119                String namespaceURI = childElement.getNamespaceURI();
120
121                if (AuthnSvcConstants.NS_AUTHN_SVC.equals(namespaceURI) &&
122                    AuthnSvcConstants.TAG_STATUS.equals(localName)){
123                        statusE = childElement;
124                    break;
125                } else {
126                    throw new AuthnSvcException("missingStatus");
127                }
128            }
129        }
130
131        String statusCodeStr = XMLUtils.getNodeAttributeValue(
132                                    statusE,
133                                    AuthnSvcConstants.ATTR_CODE);
134        QName  statusCodeQN = Utils.convertStringToQName(statusCodeStr,
135                                                         statusE);
136        if (!AuthnSvcConstants.NS_AUTHN_SVC
137                               .equals(statusCodeQN.getNamespaceURI())) {
138            throw new AuthnSvcException("invalidStatusCodeNS");
139        }
140
141        statusCode = statusCodeQN.getLocalPart();
142
143        for(i = i + 1; i < length; i++) {
144            Node child = nl.item(i);
145            if (child.getNodeType() == Node.ELEMENT_NODE) {
146                Element childElement = (Element)child;
147                String localName = childElement.getLocalName();
148                String namespaceURI = childElement.getNamespaceURI();
149                if (AuthnSvcConstants.NS_AUTHN_SVC.equals(namespaceURI)) {
150                    if (AuthnSvcConstants.TAG_STATUS.equals(localName)) {
151                        throw new AuthnSvcException("tooManyStatus");
152                    } else if(AuthnSvcConstants.TAG_PASSWORD_TRANSFORMS
153                                        .equals(localName)){
154                        if (ptE != null) {
155                            throw new AuthnSvcException("tooManyPT");
156                        } else if (dataE != null || roE != null ||
157                                   credentialsE != null) {
158                            throw new AuthnSvcException("invalidSeq");
159                        }
160                        ptE = childElement;
161                    } else if(AuthnSvcConstants.TAG_DATA.equals(localName)){
162                        if (dataE != null) {
163                            throw new AuthnSvcException("tooManyData");
164                        } else if (roE != null || credentialsE != null) {
165                            throw new AuthnSvcException("invalidSeq");
166                        }
167                        dataE = childElement;
168                    } else if(AuthnSvcConstants.TAG_CREDENTIALS
169                                               .equals(localName)){
170                        if (credentialsE != null) {
171                            throw new AuthnSvcException("tooManyCr");
172                        }
173                        credentialsE = childElement;
174                    } else {
175                        throw new AuthnSvcException("invalidChild");
176                    }
177                } else if (DiscoConstants.DISCO_NS.equals(namespaceURI) &&
178                           AuthnSvcConstants.TAG_RESOURCE_OFFERING
179                                            .equals(localName)) {
180                    if (roE != null) {
181                        throw new AuthnSvcException("tooManyRO");
182                    } else if (credentialsE != null) {
183                        throw new AuthnSvcException("invalidSeq");
184                    }
185                    roE = childElement;
186                } else {
187                    throw new AuthnSvcException("invalidChild");
188                }
189            }
190        }
191
192        if (ptE != null) {
193            passwordTransforms = new PasswordTransforms(ptE);
194        }
195
196        data = AuthnSvcUtils.decodeDataElement(dataE);
197
198        if (roE != null) {
199            try {
200                resourceOffering = new ResourceOffering(roE);
201            } catch (Exception ex) {
202                throw new AuthnSvcException(ex);
203            }
204        }
205
206        if (credentialsE != null) {
207            credentials = new ArrayList();
208            nl = credentialsE.getChildNodes();
209            for(i = 0; i < nl.getLength(); i++) {
210                Node child = nl.item(i);
211                if (child.getNodeType() == Node.ELEMENT_NODE) {
212                    credentials.add(child);
213                }
214            }
215        }
216
217        serverMechanism = XMLUtils.getNodeAttributeValue(
218                                element,
219                                AuthnSvcConstants.ATTR_SERVER_MECHANISM);
220
221        id = XMLUtils.getNodeAttributeValue(element,
222                                            AuthnSvcConstants.ATTR_id);
223
224    }
225
226    /**
227     * Returns value of attribute 'code' of Element 'Status'.
228     * @return value of attribute 'code' of Element 'Status'
229     * @see #setStatusCode(String)
230     */
231    public String getStatusCode()
232    {
233        return statusCode;
234    }
235
236    /**
237     * Returns child Element 'PasswordTransforms'.
238     * @return child Element 'PasswordTransforms'
239     * @see #setPasswordTransforms(PasswordTransforms)
240     */
241    public PasswordTransforms getPasswordTransforms()
242    {
243        return passwordTransforms;
244    }
245
246    /**
247     * Returns value of Element 'Data'.
248     * @return value of Element 'Data'
249     * @see #setData(byte[])
250     */
251    public byte[] getData()
252    {
253        return data;
254    }
255
256    /**
257     * Returns Element <code>ResourceOffering</code>.
258     * @return Element <code>ResourceOffering</code>.
259     * @see #setResourceOffering(ResourceOffering)
260     */
261    public ResourceOffering getResourceOffering() {
262        return resourceOffering;
263    }
264
265    /**
266     * Returns a list of child Element of 'Credentials' Element.
267     * @return a list of child Element of 'Credentials' Element
268     * @see #setCredentials(List)
269     */
270    public List getCredentials() {
271        return credentials;
272    }
273
274    /**
275     * Returns value of <code>serverMechanism</code> attribute.
276     * @return value of <code>serverMechanism</code> attribute
277     * @see #setServerMechanism(String)
278     */
279    public String getServerMechanism() {
280        return serverMechanism;
281    }
282
283    /**
284     * Returns value of <code>id</code> attribute.
285     * @return value of <code>id</code> attribute
286     * @see #setId(String)
287     */
288    public String getId() {
289        return id;
290    }
291
292    /**
293     * Returns value of <code>messageID</code> attribute of
294     * <code>CorrelationHeader</code>.
295     * @return value of <code>messageID</code> attribute
296     * @see #setMessageID(String)
297     */
298    public String getMessageID() {
299        return messageID;
300    }
301
302    /**
303     * Returns value of <code>refToMessageID</code> attribute of
304     * <code>CorrelationHeader</code>.
305     * @return value of <code>refToMessageID</code> attribute
306     * @see #setRefToMessageID(String)
307     */
308    public String getRefToMessageID() {
309        return refToMessageID;
310    }
311
312    /**
313     * Sets value of attribute 'code' of Element 'Status'.
314     * @param statusCode value of attribute 'code' of Element 'Status'
315     * @see #getStatusCode()
316     */
317    public void setStatusCode(String statusCode) {
318        this.statusCode = statusCode;
319    }
320
321    /**
322     * Sets child Element 'PasswordTransforms'
323     * @param passwordTransforms Element 'PasswordTransforms'
324     * @see #getPasswordTransforms()
325     */
326    public void setPasswordTransforms(PasswordTransforms passwordTransforms)
327    {
328        this.passwordTransforms = passwordTransforms;
329    }
330
331    /**
332     * Sets value of Element 'Data'.
333     * @param data value of Element 'Data'
334     * @see #getData()
335     */
336    public void setData(byte[] data) {
337        this.data = data;
338    }
339
340    /**
341     * Sets Element <code>ResourceOffering</code>.
342     * @param resourceOffering Element <code>ResourceOffering</code>
343     * @see #getResourceOffering()
344     */
345    public void setResourceOffering(ResourceOffering resourceOffering) {
346        this.resourceOffering = resourceOffering;
347    }
348
349    /**
350     * Sets a list of child Elements of 'Credentials' Element.
351     * @param credentials a list of child Elements of 'Credentials' Element
352     * @see #getCredentials()
353     */
354    public void setCredentials(List credentials) {
355        this.credentials = credentials;
356    }
357
358    /**
359     * Sets value of <code>mechanism</code> attribute.
360     * @param serverMechanism value of <code>mechanism</code> attribute
361     * @see #getServerMechanism()
362     */
363    public void setServerMechanism(String serverMechanism) {
364        this.serverMechanism = serverMechanism;
365    }
366
367    /**
368     * Sets value of <code>id</code> attribute.
369     * @param id value of <code>id</code> attribute
370     * @see #getId()
371     */
372    public void setId(String id) {
373        this.id = id;
374    }
375
376    /**
377     * Sets value of <code>messageID</code> attribute of
378     * <code>CorrelationHeader</code>.
379     * @param messageID value of <code>messageID</code> attribute
380     * @see #getMessageID()
381     */
382    public void setMessageID(String messageID) {
383        this.messageID = messageID;
384    }
385
386    /**
387     * Sets value of <code>refToMessageID</code> attribute of
388     * <code>CorrelationHeader</code>.
389     * @param refToMessageID value of <code>refToMessageID</code> attribute
390     * @see #getRefToMessageID()
391     */
392    public void setRefToMessageID(String refToMessageID) {
393        this.refToMessageID = refToMessageID;
394    }
395
396    /**
397     * Returns <code>SASLResponse</code> in <code>org.w3c.dom.Element</code>
398     * format.
399     *
400     * @return <code>SASLResponse</code> in <code>org.w3c.dom.Element</code>
401     *         format.
402     * @exception AuthnSvcException if an error occurs while creating the
403     *            <code>SASLResponse</code> element
404     */
405    public Element toElement() throws AuthnSvcException {
406        Document doc = null;
407        try {
408            doc = XMLUtils.newDocument();
409        } catch (Exception ex) {
410            AuthnSvcUtils.debug.error("SASLResponse:toElement", ex);
411            throw new AuthnSvcException(ex.getMessage());
412        }
413
414        Element saslRespE = doc.createElementNS(AuthnSvcConstants.NS_AUTHN_SVC,
415                                         AuthnSvcConstants.PTAG_SASL_RESPONSE);
416        saslRespE.setAttributeNS(AuthnSvcConstants.NS_XML,
417                                 AuthnSvcConstants.XMLNS_AUTHN_SVC,
418                                 AuthnSvcConstants.NS_AUTHN_SVC);
419        saslRespE.setAttributeNS(AuthnSvcConstants.NS_XML,
420                                 AuthnSvcConstants.XMLNS_DISCO,
421                                 DiscoConstants.DISCO_NS);
422
423        Element statusE = doc.createElementNS(AuthnSvcConstants.NS_AUTHN_SVC,
424                                         AuthnSvcConstants.PTAG_STATUS);
425        statusE.setAttributeNS(null, AuthnSvcConstants.ATTR_CODE,
426                       AuthnSvcConstants.PREFIX_AUTHN_SVC + ":" + statusCode);
427        saslRespE.appendChild(statusE);
428
429        if (passwordTransforms != null) {
430            passwordTransforms.addToParent(saslRespE);
431        }
432
433        if (data != null) {
434            Element dataE = doc.createElementNS(AuthnSvcConstants.NS_AUTHN_SVC,
435                                         AuthnSvcConstants.PTAG_DATA);
436            dataE.appendChild(doc.createTextNode(Base64.encode(data)));
437            saslRespE.appendChild(dataE);
438        }
439
440        if (resourceOffering != null) {
441            Document roDoc =
442                        XMLUtils.toDOMDocument(resourceOffering.toString(),
443                                               AuthnSvcUtils.debug);
444            if (roDoc == null) {
445                throw new AuthnSvcException("invalidRO");
446            }
447            saslRespE.appendChild(doc.importNode(roDoc.getDocumentElement(),
448                                                 true));
449        }
450
451        if (credentials != null && !credentials.isEmpty()) {
452            Element credentialsE =
453                     doc.createElementNS(AuthnSvcConstants.NS_AUTHN_SVC,
454                                         AuthnSvcConstants.PTAG_CREDENTIALS);
455            Iterator iter = credentials.iterator();
456            while (iter.hasNext()) {
457                credentialsE.appendChild(doc.importNode((Element)iter.next(),
458                                                        true));
459            }
460            saslRespE.appendChild(credentialsE);
461        }
462
463        if (serverMechanism != null) {
464            saslRespE.setAttributeNS(null,
465                                     AuthnSvcConstants.ATTR_SERVER_MECHANISM,
466                                     serverMechanism);
467        }
468
469        if (id != null) {
470            saslRespE.setAttributeNS(null, AuthnSvcConstants.ATTR_id, id);
471        }
472
473        doc.appendChild(saslRespE);
474        return doc.getDocumentElement();
475    }
476}