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: DiscoveryClient.java,v 1.5 2008/12/16 01:48:31 exu Exp $
026 *
027 */
028
029package com.sun.identity.liberty.ws.disco;
030
031import java.util.List;
032import java.util.ArrayList;
033import java.util.Iterator;
034
035import org.w3c.dom.Element;
036
037import com.sun.identity.liberty.ws.common.wsse.BinarySecurityToken;
038import com.sun.identity.liberty.ws.soapbinding.Message;
039import com.sun.identity.liberty.ws.soapbinding.ProviderHeader;
040import com.sun.identity.liberty.ws.soapbinding.Client;
041import com.sun.identity.liberty.ws.soapbinding.SOAPBindingConstants;
042import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException;
043import com.sun.identity.liberty.ws.soapbinding.Utils;
044import com.sun.identity.liberty.ws.disco.common.*;
045import com.sun.identity.liberty.ws.security.*;
046import com.sun.identity.shared.configuration.SystemPropertiesManager;
047
048/**
049 * The class <code>DiscoveryClient</code> provides methods to send  
050 * Discovery Service query and modify.
051 * Note: Current implementation uses <code>JAXB</code> objects and no wrapper
052 * classes are used.
053 * @supported.all.api
054 */
055public class DiscoveryClient {
056
057    private String connectTo = null;
058    private int clientMech = Message.ANONYMOUS;
059    private ResourceID resID = null;
060    private EncryptedResourceID encResID = null;
061    private String certAlias = null;
062    private String providerID = null;
063    private boolean clientAuth = false;
064    private SecurityAssertion assertion = null;
065    private List assertions = null;
066    private BinarySecurityToken token = null;
067    private ResourceOffering offering = null;
068    private boolean processed = true;
069    private String soapAction = null;
070    private Object session = null;
071    private String wsfVersion = Utils.getDefaultWSFVersion();
072
073    /**
074     * Constructor, connects to Discovery Service without web service security
075     * token.
076     *
077     * @param soapURI URI of the SOAP end point for this discovery 
078     *                       service instance
079     * @param providerID ID of the web service client.
080     */
081    public DiscoveryClient (String soapURI, String providerID) {
082        connectTo = soapURI;
083        this.providerID = providerID;
084    }
085
086    /**
087     * Constructor, connects to Discovery Service  using <code>WSS</code> SAML
088     * Token.
089     *
090     * @param assertion <code>WSS</code> SAML Token
091     * @param soapURI URI of the SOAP end point for this discovery 
092     *                       service instance
093     * @param providerID ID of the web service client.
094     */
095    public DiscoveryClient (SecurityAssertion assertion,
096                            String soapURI,
097                            String providerID)
098    {
099        connectTo = soapURI;
100        if ((assertion != null) && (assertion.isBearer())) {
101            clientMech = Message.BEARER_TOKEN;
102        } else {
103            clientMech = Message.SAML_TOKEN;
104        }
105        this.assertion = assertion;
106        this.providerID = providerID;
107    }
108
109    /**
110     * Constructor, connects to Discovery Service using <code>WSS X509</code>
111     * Token.
112     * @param token <code>WSS X.509</code> Certificate Token
113     * @param soapURI URI of the SOAP end point for this discovery 
114     *        service instance.
115     * @param providerID ID of the web service client.
116     */
117    public DiscoveryClient (BinarySecurityToken token,
118                            String soapURI,
119                            String providerID)
120    {
121        connectTo = soapURI;
122        clientMech = Message.X509_TOKEN;
123        this.token = token;
124        this.providerID = providerID;
125    }
126
127    /**
128     * Constructor, connects to Discovery Service specified by the resource
129     * offering, security mechanism/SOAP endpoint defined in the 
130     * <code>ResourceOffering</code> will be used.
131     *
132     * @param resourceOffering resource offering for this 
133     *        discovery service instance 
134     * @param session session of the  <code>WSC</code>
135     * @param providerID ID of the web service client.
136     */
137    public DiscoveryClient(ResourceOffering resourceOffering,
138                            Object session,
139                            String providerID)
140    {
141        offering = resourceOffering;
142        processed = false;
143        this.session = session;
144        this.providerID = providerID;
145    }
146
147    /**
148     * Constructor, connects to Discovery Service specified by the resource
149     * offering, security mechanism/SOAP endpoint defined in the 
150     * <code>ResourceOffering</code> will be used.
151     *
152     * @param resourceOffering resource offering for this 
153     *        discovery service instance 
154     * @param session session of the  <code>WSC</code>
155     * @param providerID ID of the web service client.
156     * @param assertions List of assertions.
157     */
158    public DiscoveryClient(ResourceOffering resourceOffering,
159                            Object session,
160                            String providerID,
161                            List assertions)
162    {
163        offering = resourceOffering;
164        processed = false;
165        this.session = session;
166        this.providerID = providerID;
167        this.assertions = assertions;
168    }
169
170    private void processResourceOffering() throws DiscoveryException {
171        ServiceInstance instance = offering.getServiceInstance();
172        if (!(instance.getServiceType().equals(DiscoConstants.DISCO_NS))) {
173            DiscoSDKUtils.debug.error("DiscoveryClient.processResourceOffering: " +
174            "ServiceType in ResourceOffering is not discovery service type.");
175            throw new DiscoveryException(
176                        DiscoSDKUtils.bundle.getString("notDiscoServiceType"));
177        }
178        resID = offering.getResourceID();
179        encResID = offering.getEncryptedResourceID();
180        List descriptions = instance.getDescription();
181        /*
182         * Iterate through supported security profiles until we find one
183         * that we support (and we should always do so if the spec is
184         * being complied with).  They should be in decreasing order of
185         * preference...
186         */
187        // TODO: support wsdl form
188        Iterator i = descriptions.iterator();
189        while (i.hasNext()) {
190            Description desc = (Description) i.next();
191            connectTo = desc.getEndpoint();
192            soapAction = desc.getSoapAction();
193            Iterator j = desc.getSecurityMechID().iterator();
194            while (j.hasNext()) {
195                String mech = (String) j.next();
196                if ((mech.equals(Message.NULL_NULL)) ||
197                    (mech.equals(Message.TLS_NULL)) ||
198                    (mech.equals(Message.CLIENT_TLS_NULL)))
199                {
200                    clientMech = Message.ANONYMOUS;
201                    DiscoSDKUtils.debug.message("DiscoClient: null");
202                    if (mech.equals(Message.CLIENT_TLS_NULL)) {
203                        clientAuth = true;
204                        DiscoSDKUtils.debug.message("DiscoClient: clientAuth on");
205                    }
206                    return;
207                } else if ((mech.equals(Message.NULL_X509)) ||
208                    (mech.equals(Message.TLS_X509)) ||
209                    (mech.equals(Message.CLIENT_TLS_X509)) ||
210                    (mech.equals(Message.NULL_X509_WSF11)) ||
211                    (mech.equals(Message.TLS_X509_WSF11)) ||
212                    (mech.equals(Message.CLIENT_TLS_X509_WSF11)))
213                {
214                    clientMech = Message.X509_TOKEN;
215                    if (mech.equals(Message.NULL_X509) ||
216                        mech.equals(Message.TLS_X509) ||
217                        mech.equals(Message.CLIENT_TLS_X509)) {
218                        wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
219                    } else {
220                        wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
221                    }
222                    DiscoSDKUtils.debug.message("DiscoClient: x509");
223                    try {
224                        SecurityTokenManagerClient stm =
225                            new SecurityTokenManagerClient(session);
226                        if (certAlias == null) {
227                            certAlias = SystemPropertiesManager.get(
228                                "com.sun.identity.liberty.ws.wsc.certalias");
229                        }
230                        stm.setCertAlias(certAlias);
231                        token = stm.getX509CertificateToken();
232                        token.setWSFVersion(wsfVersion);
233                    } catch (Exception e) {
234                        DiscoSDKUtils.debug.error("DiscoveryClient.processResource"
235                            + "Offering: couldn't generate X509 token: ", e);
236                        throw new DiscoveryException(e.getMessage());
237                    }
238                    if (mech.equals(Message.CLIENT_TLS_X509) ||
239                        mech.equals(Message.CLIENT_TLS_X509_WSF11)) {
240                        clientAuth = true;
241                        DiscoSDKUtils.debug.message("DiscoClient: clientAuth on");
242                    }
243                    return;
244                } else if ((mech.equals(Message.NULL_SAML)) ||
245                    (mech.equals(Message.TLS_SAML)) ||
246                    (mech.equals(Message.CLIENT_TLS_SAML)) ||
247                    (mech.equals(Message.NULL_SAML_WSF11)) ||
248                    (mech.equals(Message.TLS_SAML_WSF11)) ||
249                    (mech.equals(Message.CLIENT_TLS_SAML_WSF11)))
250                {
251                    clientMech = Message.SAML_TOKEN;
252                    if (mech.equals(Message.NULL_SAML) ||
253                        mech.equals(Message.TLS_SAML) ||
254                        mech.equals(Message.CLIENT_TLS_SAML)) {
255                        wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
256                    } else {
257                        wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
258                    }
259                    DiscoSDKUtils.debug.message("DiscoClient: saml token");
260                    List credRefs = desc.getCredentialRef();
261                    if ((credRefs == null) || (credRefs.size() == 0)) {
262                        throw new DiscoveryException(
263                                DiscoSDKUtils.bundle.getString("noCredential"));
264                    } else {
265                        String credID = (String) credRefs.get(0);
266                        if (assertions == null) {
267                            throw new DiscoveryException(
268                                DiscoSDKUtils.bundle.getString("noCredential"));
269                        } else {
270                            Iterator iter1 = assertions.iterator();
271                            while (iter1.hasNext()) {
272                                SecurityAssertion sassert = (SecurityAssertion)
273                                                iter1.next();
274                                if (credID.equals(sassert.getAssertionID())) {
275                                    assertion = sassert;
276                                    break;
277                                }
278                            }
279                            if (assertion == null) {
280                                throw new DiscoveryException(
281                                DiscoSDKUtils.bundle.getString("noCredential"));
282                            }
283                        }
284                    }
285                    if (mech.equals(Message.CLIENT_TLS_SAML) ||
286                        mech.equals(Message.CLIENT_TLS_SAML_WSF11)) {
287                        clientAuth = true;
288                        DiscoSDKUtils.debug.message("DiscoClient: clientAuth on");
289                    }
290                    return;
291                } else if ((mech.equals(Message.NULL_BEARER)) ||
292                    (mech.equals(Message.TLS_BEARER)) ||
293                    (mech.equals(Message.CLIENT_TLS_BEARER)) ||
294                    (mech.equals(Message.NULL_BEARER_WSF11)) ||
295                    (mech.equals(Message.TLS_BEARER_WSF11)) ||
296                    (mech.equals(Message.CLIENT_TLS_BEARER_WSF11)))
297                {
298                    clientMech = Message.BEARER_TOKEN;
299                    if (mech.equals(Message.NULL_BEARER) ||
300                        mech.equals(Message.TLS_BEARER) ||
301                        mech.equals(Message.CLIENT_TLS_BEARER)) {
302                        wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
303                    } else {
304                        wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
305                    }
306                    DiscoSDKUtils.debug.message("DiscoClient: bearer token");
307                    List credRefs = desc.getCredentialRef();
308                    if ((credRefs == null) || (credRefs.size() == 0)) {
309                        throw new DiscoveryException(
310                                DiscoSDKUtils.bundle.getString("noCredential"));
311                    } else {
312                        String credID = (String) credRefs.get(0);
313                        if (credID == null || assertions == null) {
314                            throw new DiscoveryException(
315                                DiscoSDKUtils.bundle.getString("noCredential"));
316                        } else {
317                            Iterator iter2 = assertions.iterator();
318                            while (iter2.hasNext()) {
319                                SecurityAssertion sassert = (SecurityAssertion)
320                                                iter2.next();
321                                if (credID.equals(sassert.getAssertionID())) {
322                                    assertion = sassert;
323                                    break;
324                                }
325                            }
326                            if (assertion == null) {
327                                throw new DiscoveryException(
328                                DiscoSDKUtils.bundle.getString("noCredential"));
329                            }
330                        }
331                    }
332                    if (mech.equals(Message.CLIENT_TLS_BEARER) ||
333                        mech.equals(Message.CLIENT_TLS_BEARER_WSF11)) {
334                        clientAuth = true;
335                        DiscoSDKUtils.debug.message("DiscoClient: clientAuth on");
336                    }
337                    return;
338                }
339            }
340        }
341        // still here? couldn't find supported mech id
342        
343        DiscoSDKUtils.debug.error("DiscoveryClient.processResourceOffering: " +
344            "Couldn't find supported SecurityMechID from ResourceOffering.");
345        throw new DiscoveryException(
346                DiscoSDKUtils.bundle.getString("noSupportedSecuMechID"));
347    }
348 
349    /**
350     * Sets the alias for the client certificate. If none is set, a default
351     * client certificate will be used.
352     * @param certAlias certificate alias name
353     */ 
354    public void setClientCert(String certAlias) {
355        this.certAlias = certAlias;
356    }
357
358    /**
359     * Sets flag to indicate whether the connection is SSL/TLS with client
360     * authentication. When this flag is set to true, the message will not be
361     * signed according to the spec. If you want to sign the message always,
362     * do not set this flag to true, even when the connection is SSL/TLS with
363     * client authentication.
364     *
365     * @param value The flag value to be set
366     */
367    public void setClientAuthentication(boolean value) {
368        clientAuth = value;
369    }
370
371    /**
372     * Sets the resource ID to be accessed.
373     * @param resourceID resource ID
374     */
375    public void setResourceID(String resourceID) {
376        resID = new ResourceID(resourceID);
377    }
378
379    /**
380     * Sets the encrypted resource ID to be accessed.
381     *
382     * @param resourceID encrypted resource ID.
383     */
384    public void setResourceID(EncryptedResourceID resourceID) {
385        encResID = resourceID;
386    }
387 
388    /**
389     * Sets the provider ID.
390     *
391     * @param providerID ID of the web service client.
392     */
393    public void setProviderID(String providerID) {
394        this.providerID = providerID;
395    }
396
397    /**
398     * Queries discovery service for <code>ResourceOffering</code> given list of
399     * service types.
400     * 
401     * @param serviceTypes List of <code>serviceTypes</code> as
402     *        <code>java.lang.String</code> to be queried 
403     * @return Query response Element corresponding to the query
404     * @exception DiscoveryException if error occurs
405     */
406    public QueryResponse getResourceOffering(java.util.List serviceTypes) 
407        throws DiscoveryException 
408    {
409        if (!processed) {
410            processResourceOffering();
411            processed = true;
412        }
413        Query query = null;
414            Iterator i = serviceTypes.iterator();
415            List serviceList = new ArrayList();
416            while (i.hasNext()) {
417                serviceList.add(new RequestedService(null, (String) i.next()));
418            }
419        if (resID != null) {
420            query = new Query(resID, serviceList);
421        } else {
422            query = new Query(encResID, serviceList);
423        }
424
425        return getResourceOffering(query);
426    }
427
428    /**
429     * Queries discovery service for resource offering.
430     * @param query discovery query object 
431     * @return Query response Element corresponding to the query
432     * @exception DiscoveryException if error occurs
433     */
434    public QueryResponse getResourceOffering(Query query) 
435        throws DiscoveryException 
436    {
437        Message req = createRequest();
438        req.setSOAPBody(DiscoSDKUtils.parseXML(query.toString()));
439        return new QueryResponse(getResponse(req));
440    }
441
442
443    private Message createRequest() throws DiscoveryException {
444        if (!processed) {
445            processResourceOffering();
446            processed = true;
447        }
448        // create new Message according to different secuMechID
449        Message req = null;
450        ProviderHeader provH = null;
451        if (providerID != null) {
452            try {
453                provH = new ProviderHeader(providerID);
454            } catch (SOAPBindingException sbe) {
455                throw new DiscoveryException(sbe.getMessage());
456            }
457        }
458        if (clientMech == Message.X509_TOKEN) {
459            DiscoSDKUtils.debug.message(
460                "DiscoveryClient.createRequest: mech=x509");
461            try {
462                 req = new Message(provH, token);
463            } catch (SOAPBindingException sbe) {
464                throw new DiscoveryException(sbe.getMessage());
465            }
466        } else if ((clientMech == Message.SAML_TOKEN) ||
467                    (clientMech == Message.BEARER_TOKEN)) {
468            if (DiscoSDKUtils.debug.messageEnabled()) {
469                DiscoSDKUtils.debug.message("DiscoveryClient.createRequest: "
470                        + "mech=saml or bearer");
471            }
472            try {
473                req = new Message(provH, assertion);
474            } catch (SOAPBindingException sbe) {
475                throw new DiscoveryException(sbe.getMessage());
476            }
477        } else {
478            if (DiscoSDKUtils.debug.messageEnabled()) {
479                DiscoSDKUtils.debug.message("DiscoveryClient.createRequest: "
480                    + "mech=anon");
481            }
482            try {
483                req = new Message(provH);
484            } catch (SOAPBindingException sbe) {
485                throw new DiscoveryException(sbe.getMessage());
486            }
487        }
488        if (clientAuth) {
489            req.setClientAuthentication(clientAuth);
490        }
491        req.setWSFVersion(wsfVersion);
492        return req;
493    }
494
495    private Element getResponse(Message req) throws DiscoveryException {
496        Message resp = null;
497        try {
498            resp = Client.sendRequest(req, connectTo, certAlias, soapAction);
499        } catch (Exception e) {
500            DiscoSDKUtils.debug.error("DiscoveryClient.getResponse:", e);
501            throw new DiscoveryException(e.getMessage());
502        }
503        List bodies = resp.getBodies();
504        if (!(bodies.size() == 1)) {
505            DiscoSDKUtils.debug.error("DiscoveryClient.getResponse: SOAP Response "
506                + "didn't contain one SOAPBody.");
507            throw new DiscoveryException(
508                                DiscoSDKUtils.bundle.getString("oneBody"));
509        }
510        return ((Element) bodies.iterator().next());
511
512    }
513
514    /**
515     * Modifies discovery resource offering.
516     * @param modify List of Modify object
517     * @return List of <code>ModifyResponse</code> object
518     * @exception DiscoveryException if error occurs
519     */
520    public ModifyResponse modify(Modify modify) 
521        throws DiscoveryException
522    {
523        Message req = createRequest();
524        req.setSOAPBody(DiscoSDKUtils.parseXML(modify.toString()));
525
526        return new ModifyResponse(getResponse(req));
527    }
528
529    /**
530     * Sets the web services version.
531     *
532     * @param wsfVersion the web services version that should be used.
533     */
534    public void setWSFVersion(String wsfVersion) {
535        this.wsfVersion = wsfVersion;
536    }
537}