001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 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: ServiceManager.java,v 1.27 2009/10/28 04:24:26 hengming Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted 2012 ForgeRock Inc
031 */
032
033package com.sun.identity.sm;
034
035import com.iplanet.services.util.AMEncryption;
036import com.iplanet.sso.SSOException;
037import com.iplanet.sso.SSOToken;
038import com.iplanet.sso.SSOTokenManager;
039import com.iplanet.ums.IUMSConstants;
040import com.sun.identity.authentication.util.ISAuthConstants;
041import com.sun.identity.common.CaseInsensitiveHashMap;
042import com.sun.identity.common.configuration.ServerConfiguration;
043import com.sun.identity.idm.IdConstants;
044import com.sun.identity.shared.debug.Debug;
045import com.sun.identity.shared.xml.XMLUtils;
046import com.sun.identity.security.AdminTokenAction;
047import com.sun.identity.security.DecodeAction;
048import com.sun.identity.security.EncodeAction;
049import java.io.InputStream;
050import java.io.UnsupportedEncodingException;
051import java.security.AccessController;
052import java.text.MessageFormat;
053import java.util.ArrayList;
054import java.util.Collections;
055import java.util.HashMap;
056import java.util.HashSet;
057import java.util.Iterator;
058import java.util.List;
059import java.util.Map;
060import java.util.Set;
061import org.w3c.dom.Document;
062import org.w3c.dom.DocumentType;
063import org.w3c.dom.Node;
064import org.w3c.dom.NodeList;
065
066/**
067 * The <code>ServiceManager</code> class provides methods to register/remove
068 * services and to list currently registered services. It also provides methods
069 * to obtain an instance of <code>ServiceSchemaManager</code> and an instance
070 * of <code>ServiceConfigManager</code>.
071 *
072 * @supported.api
073 */
074public class ServiceManager {
075
076    // Initialization parameters
077    private static boolean initialized;
078
079    private static boolean loadedAuthServices;
080
081    protected static final String serviceDN = SMSEntry.SERVICES_RDN
082            + "," + SMSEntry.baseDN;
083
084    // For realms and co-existance support
085    protected static final String COEXISTENCE_ATTR_NAME = "coexistenceMode";
086
087    protected static HashMap serviceNameDefaultVersion =
088        new CaseInsensitiveHashMap();
089
090    protected static final String REALM_ATTR_NAME = "realmMode";
091
092    public static final String REALM_SERVICE = 
093        "sunidentityrepositoryservice";
094
095    protected static final String DEFAULT_SERVICES_FOR_REALMS = 
096        "serviceNamesForAutoAssignment";
097
098    protected static final String SERVICE_VERSION = "1.0";
099
100    protected static final String REALM_ENTRY = "ou=" + SERVICE_VERSION
101            + ",ou=" + REALM_SERVICE + "," + serviceDN;
102
103    protected static final String PLATFORM_SERVICE = "iPlanetAMPlatformService";
104
105    protected static final String ATTR_SERVER_LIST = 
106        "iplanet-am-platform-server-list";
107
108    private static boolean realmCache;
109
110    private static boolean coexistenceCache = true;
111
112    private static boolean ditUpgradedCache;
113    
114    protected static Set requiredServices;
115
116    protected static Set defaultServicesToLoad;
117
118    // constants for IdRepo management
119    private static final String SERVICE_OC_ATTR_NAME = "serviceObjectClasses";
120
121    private static final String ALL_SERVICES = "null";
122
123    private static Map serviceNameAndOCs = new CaseInsensitiveHashMap();
124
125    // List of sub-services
126    protected static SMSEntry smsEntry;
127
128    protected static CachedSubEntries serviceNames;
129
130    protected static HashMap serviceVersions = new CaseInsensitiveHashMap();
131
132    protected static Set accessManagerServers;
133
134    // SSOToken of the caller
135    private SSOToken token;
136
137    private CachedSubEntries subEntries = null;
138
139    // Debug & I18n
140    private static Debug debug = SMSEntry.debug;
141
142    private static boolean amsdkChecked;
143
144    private static boolean isAMSDKEnabled;
145
146    /**
147     * Creates an instance of <code>ServiceManager</code>.
148     * The <code>SSOToken</code> is used to identify the user performing
149     * service operations.
150     * 
151     * @param token
152     *            the authenticated single sign on token.
153     * @throws SSOException
154     *             if the user's single sign on token is invalid or expired
155     * @throws SMSException
156     *             if an error occurred while performing the operation
157     *
158     * @supported.api
159     */
160    public ServiceManager(SSOToken token) throws SSOException, SMSException {
161        // Initilaize the static variables and caches
162        initialize(token);
163
164        // Validate SSOToken
165        SSOTokenManager.getInstance().validateToken(token);
166        this.token = token;
167    }
168
169    /**
170     * Returns the <code>ServiceSchemaManager</code> for
171     * the given service name and version.
172     * 
173     * @param serviceName
174     *            the name of the service
175     * @param version
176     *            the version of the service
177     * @return the <code>ServiceSchemaManager</code> for the given service
178     *         name and version
179     * @throws SSOException
180     *             if the user's single sign on token is invalid or expired
181     * @throws SMSException
182     *             if an error occurred while performing the operation
183     *
184     * @supported.api
185     */
186    public ServiceSchemaManager getSchemaManager(String serviceName,
187            String version) throws SMSException, SSOException {
188        return (new ServiceSchemaManager(token, serviceName, version));
189    }
190
191    /**
192     * Returns the <code>ServiceConfigManager</code> for
193     * the given service name and version.
194     * 
195     * @param serviceName
196     *            the name of the service
197     * @param version
198     *            the version of the service
199     * @return the <code>ServiceConfigManager</code> for the given service
200     *         name and version.
201     * @throws SSOException
202     *             if the user's single sign on token is invalid or expired
203     * @throws SMSException
204     *             if an error occurred while performing the operation
205     *
206     * @supported.api
207     */
208    public ServiceConfigManager getConfigManager(String serviceName,
209            String version) throws SMSException, SSOException {
210        return (new ServiceConfigManager(token, serviceName, version));
211    }
212
213    /**
214     * Returns the <code>OrganizationConfigManager</code> for the given
215     * organization name. If the <code>orgName</code> either <code>
216     * null</code>
217     * or empty or "/", the organization configuration for the root organization
218     * will be returned.
219     * 
220     * @param orgName
221     *            the name of the organization
222     * @return the <code>OrganizationConfigManager</code> for the given
223     *         organization name
224     * 
225     * @throws SSOException
226     *             if the user's single sign on token is invalid or expired
227     * @throws SMSException
228     *             if an error occurred while performing the operation
229     */
230    public OrganizationConfigManager getOrganizationConfigManager(
231        String orgName) throws SMSException, SSOException {
232        return (new OrganizationConfigManager(token, orgName));
233    }
234
235    /**
236     * Returns all the service names that have been
237     * registered.
238     * 
239     * @return the set of names of services that have been registered
240     * @throws SMSException
241     *             if an error occurred while performing the operation
242     *
243     * @supported.api
244     */
245    public Set getServiceNames() throws SMSException {
246        try {
247            if (serviceNames == null) {
248                serviceNames = CachedSubEntries.getInstance(token, serviceDN);
249            }
250            return (serviceNames.getSubEntries(token));
251        } catch (SSOException s) {
252            debug.error("ServiceManager: Unable to get service names", s);
253            throw (new SMSException(s, "sms-service-not-found"));
254        }
255    }
256
257    /**
258     * Returns a map of service names and the related object classes for the
259     * given <code>schemaType</code>.
260     * 
261     * @param schemaType
262     *            name of the schema
263     * @return Map of service names and objectclasses
264     */
265    public Map getServiceNamesAndOCs(String schemaType) {
266        if (schemaType == null) {
267            schemaType = ALL_SERVICES;
268        } else if (schemaType.equalsIgnoreCase("realm")) {
269            schemaType = "filteredrole";
270        }
271        Map answer = (Map) serviceNameAndOCs.get(schemaType);
272        if (answer == null) {
273            try {
274                answer = new HashMap();
275                Set sNames = getServiceNames();
276                if (sNames != null && !sNames.isEmpty()) {
277                    Iterator it = sNames.iterator();
278                    while (it.hasNext()) {
279                        try {
280                            String service = (String) it.next();
281                            ServiceSchemaManagerImpl ssm;
282                            if (isCoexistenceMode()) {
283                                // For backward compatibility, get the
284                                // version from the service.
285                                // no hardcoding to '1.0', even if it
286                                // improves performance in OpenSSO.
287                                // Otherwise, it breaks for services like
288                                // iplanetAMProviderConfigService with
289                                // '1.1' as version.
290                                ssm = ServiceSchemaManagerImpl.getInstance(
291                                    token, service, serviceDefaultVersion(
292                                    token, service));
293                            } else {
294                                ssm = ServiceSchemaManagerImpl.getInstance(
295                                    token, service, 
296                                    ServiceManager.getVersion(service));
297                            }
298                            if (ssm != null) {
299                                // Check if service has schemaType
300                                if (schemaType != null &&
301                                        ssm.getSchema(new SchemaType(
302                                                schemaType)) == null) {
303                                    // If the schema type is "User"
304                                    // check for "Dynamic" also
305                                    if (schemaType.equalsIgnoreCase(
306                                            SMSUtils.USER_SCHEMA)
307                                         && ssm.getSchema(SchemaType.DYNAMIC) 
308                                         == null) 
309                                    {
310                                        continue;
311                                    }
312                                    // If the schema type is "Role:
313                                    // check for "Dynamic" also
314                                    if (schemaType.toLowerCase()
315                                            .indexOf("role") != -1
316                                            && ssm.getSchema(SchemaType.DYNAMIC)
317                                            == null) 
318                                    {
319                                        continue;
320                                    }
321                                }
322                                ServiceSchemaImpl ss = ssm
323                                        .getSchema(SchemaType.GLOBAL);
324                                if (ss != null) {
325                                    Map attrs = ss.getAttributeDefaults();
326                                    if (attrs.containsKey(SERVICE_OC_ATTR_NAME))
327                                    {
328                                        answer.put(service, attrs
329                                                .get(SERVICE_OC_ATTR_NAME));
330                                    }
331                                }
332                            }
333                        } catch (SMSException smse) {
334                            // continue with next service. Best effort to get
335                            // all service names.
336                            if (debug.messageEnabled()) {
337                                debug.message(
338                                        "ServiceManager.getServiceNamesandOCs"
339                                          + " caught SMSException ", smse);
340                            }
341                        }
342                    }
343                }
344                serviceNameAndOCs.put(schemaType, answer);
345            } catch (SMSException smse) {
346                // ignore
347                if (debug.messageEnabled()) {
348                    debug.message("ServiceManager.getServiceNamesandOCs"
349                            + " caught SMSException ", smse);
350                }
351            } catch (SSOException ssoe) {
352                // ignore
353                if (debug.messageEnabled()) {
354                    debug.message("ServiceManager.getServiceNamesandOCs"
355                            + " caught SSOException ", ssoe);
356                }
357            }
358        }
359        return (SMSUtils.copyAttributes(answer));
360    }
361
362    /**
363     * Returns all versions supported by the service.
364     * 
365     * @param serviceName
366     *            service name.
367     * @return the set of versions supported by the service
368     * @throws SMSException
369     *             if an error occurred while performing the operation
370     *
371     * @supported.api
372     */
373    public Set getServiceVersions(String serviceName) throws SMSException {
374        try {
375            return (getVersions(token, serviceName));
376        } catch (SSOException s) {
377            debug.error("ServiceManager: Unable to get service versions", s);
378            throw (new SMSException(s, "sms-version-not-found"));
379        }
380    }
381
382    /**
383     * Registers one or more services, defined by the XML
384     * input stream that follows the SMS DTD.
385     * 
386     * @param xmlServiceSchema
387     *            the input stream of service metadata in XML conforming to SMS
388     *            DTD.
389     * @return set of registered service names.
390     * @throws SMSException if an error occurred while performing the operation.
391     * @throws SSOException if the user's single sign on token is invalid or 
392     *         expired.
393     *
394     * @supported.api
395     */
396    public Set registerServices(InputStream xmlServiceSchema)
397        throws SMSException, SSOException {
398        return registerServices(xmlServiceSchema, null);
399    }
400    
401    /**
402     * Registers one or more services, defined by the XML
403     * input stream that follows the SMS DTD.
404     *
405     * @param xmlServiceSchema
406     *        the input stream of service metadata in XML conforming to SMS
407     *        DTD.
408     * @param decryptObj Object to decrypt the password in the XML.
409     * @return set of registered service names.
410     * @throws SMSException if an error occurred while performing the operation
411     * @throws SSOException if the user's single sign on token is invalid or
412     *         expired.
413     */
414    public Set registerServices(
415        InputStream xmlServiceSchema,
416        AMEncryption decryptObj
417    ) throws SMSException, SSOException {
418        // Validate SSO Token
419        SMSEntry.validateToken(token);
420        Set sNames = new HashSet();
421        List serviceNodes = new ArrayList();
422        // Get the XML document and get the list of service nodes
423        Document doc = SMSSchema.getXMLDocument(xmlServiceSchema);
424
425        if (!validSMSDtdDocType(doc)) {
426            throw new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
427                    IUMSConstants.SMS_xml_invalid_doc_type, null);
428        }
429        
430        // Before validating service schema, we need to check
431        // for AttributeSchema having the syntax of "password"
432        // and if present, encrypt the DefaultValues if any
433        checkAndEncryptPasswordSyntax(doc, true, decryptObj);
434
435        // Create service schema
436        NodeList nodes = doc.getElementsByTagName(SMSUtils.SERVICE);
437        for (int i = 0; (nodes != null) && (i < nodes.getLength()); i++) {
438            Node serviceNode = nodes.item(i);
439            String name = XMLUtils.getNodeAttributeValue(serviceNode,
440                    SMSUtils.NAME);
441            String version = XMLUtils.getNodeAttributeValue(serviceNode,
442                    SMSUtils.VERSION);
443
444            // Obtain the SMSSchema for Schema and PluginSchema
445            SMSSchema smsSchema = new SMSSchema(name, version, doc);
446
447            // Check if the schema element exists
448            if (XMLUtils.getChildNode(serviceNode, SMSUtils.SCHEMA) != null) {
449                validateServiceSchema(serviceNode);
450                ServiceSchemaManager.createService(token, smsSchema);
451
452                // Update the service name and version cached SMSEntry
453                if (serviceNames == null) {
454                    serviceNames = CachedSubEntries.getInstance(token,
455                            serviceDN);
456                }
457                serviceNames.add(name);
458                CachedSubEntries sVersions = (CachedSubEntries) serviceVersions
459                        .get(name);
460                if (sVersions == null) {
461                    // Not present, hence create it and add it
462                    sVersions = CachedSubEntries.getInstance(token,
463                            getServiceNameDN(name));
464                    serviceVersions.put(name, sVersions);
465                }
466                sVersions.add(version);
467                sNames.add(name);
468            }
469
470            // Check if PluginSchema nodes exists
471            for (Iterator pluginNodes = XMLUtils.getChildNodes(serviceNode,
472                    SMSUtils.PLUGIN_SCHEMA).iterator(); pluginNodes.hasNext();)
473            {
474                Node pluginNode = (Node) pluginNodes.next();
475                PluginSchema.createPluginSchema(token, pluginNode, smsSchema);
476            }
477
478            if (XMLUtils.getChildNode(serviceNode, SMSUtils.CONFIGURATION) 
479                != null) {
480                serviceNodes.add(serviceNode);
481            }
482        }
483
484        if (serviceNodes.size() > 0) {
485            clearCache();
486        }
487        /*
488         * Need to do this after all the schema has been loaded
489         */
490        for (Iterator i = serviceNodes.iterator(); i.hasNext(); ) {
491            Node svcNode = (Node)i.next();
492            String name = XMLUtils.getNodeAttributeValue(svcNode,
493                SMSUtils.NAME);
494            String version = XMLUtils.getNodeAttributeValue(svcNode,
495                SMSUtils.VERSION);
496            Node configNode = XMLUtils.getChildNode(svcNode,
497                SMSUtils.CONFIGURATION);
498            /*
499             * Store the configuration, will throw exception if
500             * the service configuration already exists
501             */
502            CreateServiceConfig.createService(this, name, version,
503                configNode, true, decryptObj);
504        }
505        return sNames;
506    }
507    
508    public Document parseServicesFile(InputStream xmlServiceSchema)
509    throws SMSException, SSOException {
510        return parseServicesFile(xmlServiceSchema, null);
511    }
512    
513    public Document parseServicesFile(
514        InputStream xmlServiceSchema,
515        AMEncryption decryptObj
516    ) throws SMSException, SSOException {
517        // Validate SSO Token
518        SMSEntry.validateToken(token);
519        //Set<SMSSchema> smsSchemas = new HashSet<SMSSchema>();
520        Map<String, SMSSchema> smsSchemas = new HashMap<String, SMSSchema>();
521        List serviceNodes = new ArrayList();
522        // Get the XML document and get the list of service nodes
523        Document doc = SMSSchema.getXMLDocument(xmlServiceSchema);
524
525        return doc;
526    }
527    
528    /*
529        if (!validSMSDtdDocType(doc)) {
530            throw new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
531                    IUMSConstants.SMS_xml_invalid_doc_type, null);
532        }
533        
534        // Before validating service schema, we need to check
535        // for AttributeSchema having the syntax of "password"
536        // and if present, encrypt the DefaultValues if any
537        checkAndEncryptPasswordSyntax(doc, true, decryptObj);
538
539        // Create service schema
540        NodeList nodes = doc.getElementsByTagName(SMSUtils.SERVICE);
541        for (int i = 0; (nodes != null) && (i < nodes.getLength()); i++) {
542            Node serviceNode = nodes.item(i);
543            String name = XMLUtils.getNodeAttributeValue(serviceNode,
544                    SMSUtils.NAME);
545            String version = XMLUtils.getNodeAttributeValue(serviceNode,
546                    SMSUtils.VERSION);
547
548            // Obtain the SMSSchema for Schema and PluginSchema
549            SMSSchema smsSchema = new SMSSchema(name, version, doc);
550            smsSchemas.put(name, smsSchema);
551            
552        }
553
554            // Check if the schema element exists
555            /*if (XMLUtils.getChildNode(serviceNode, SMSUtils.SCHEMA) != null) {
556                validateServiceSchema(serviceNode);
557                ServiceSchemaManager.createService(token, smsSchema);
558
559                // Update the service name and version cached SMSEntry
560                if (serviceNames == null) {
561                    serviceNames = CachedSubEntries.getInstance(token,
562                            serviceDN);
563                }
564                serviceNames.add(name);
565                CachedSubEntries sVersions = (CachedSubEntries) serviceVersions
566                        .get(name);
567                if (sVersions == null) {
568                    // Not present, hence create it and add it
569                    sVersions = CachedSubEntries.getInstance(token,
570                            getServiceNameDN(name));
571                    serviceVersions.put(name, sVersions);
572                }
573                sVersions.add(version);
574                sNames.add(name);
575            }
576
577            // Check if PluginSchema nodes exists
578            for (Iterator pluginNodes = XMLUtils.getChildNodes(serviceNode,
579                    SMSUtils.PLUGIN_SCHEMA).iterator(); pluginNodes.hasNext();)
580            {
581                Node pluginNode = (Node) pluginNodes.next();
582                PluginSchema.createPluginSchema(token, pluginNode, smsSchema);
583            }
584
585            if (XMLUtils.getChildNode(serviceNode, SMSUtils.CONFIGURATION) 
586                != null) {
587                serviceNodes.add(serviceNode);
588            }
589            
590        }
591
592        if (serviceNodes.size() > 0) {
593            clearCache();
594        }
595        /*
596         * Need to do this after all the schema has been loaded
597         */
598            /*
599        for (Iterator i = serviceNodes.iterator(); i.hasNext(); ) {
600            Node svcNode = (Node)i.next();
601            String name = XMLUtils.getNodeAttributeValue(svcNode,
602                SMSUtils.NAME);
603            String version = XMLUtils.getNodeAttributeValue(svcNode,
604                SMSUtils.VERSION);
605            Node configNode = XMLUtils.getChildNode(svcNode,
606                SMSUtils.CONFIGURATION);
607            /*
608             * Store the configuration, will throw exception if
609             * the service configuration already exists
610             */
611            /*
612            CreateServiceConfig.createService(this, name, version,
613                configNode, true, decryptObj);
614        }
615        return sNames;
616        return smsSchemas;
617    }*/
618    
619    private boolean validSMSDtdDocType(Document doc) {
620        boolean valid = false;
621        DocumentType docType = doc.getDoctype();
622
623        if (docType != null) {
624            String dtdPath = docType.getSystemId();
625            if (dtdPath != null) {
626                int idx = dtdPath.lastIndexOf('/');
627                if (idx != -1) {
628                    dtdPath = dtdPath.substring(idx + 1);
629                }
630                valid = dtdPath.equals("sms.dtd");
631            }
632        }
633
634        return valid;
635    }
636
637    /**
638     * Adds a new plugin schema to an existing service
639     *
640     * @param pluginDoc 
641     * @throws SMSException if an error occurred while performing the operation
642     * @throws SSOException if the user's single sign on token is invalid or
643     *         expired.
644     */
645    public void addPluginSchema(Document pluginDoc)
646    throws SMSException, SSOException {
647        // Validate SSO Token
648        SMSEntry.validateToken(token);
649
650        Node serviceNode = XMLUtils.getRootNode(pluginDoc, SMSUtils.SERVICE);
651        String serviceName = XMLUtils.getNodeAttributeValue(serviceNode,
652                SMSUtils.NAME);
653        ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName, token);
654        Document schemaDoc = ssm.getDocumentCopy();
655
656        Node pluginSchemaDoc = XMLUtils.getRootNode(pluginDoc, SMSUtils.PLUGIN_SCHEMA);
657
658        // Obtain the SMSSchema for Schema and PluginSchema
659        SMSSchema smsSchema = new SMSSchema(schemaDoc);
660        PluginSchema.createPluginSchema(token, pluginSchemaDoc, smsSchema);
661    }
662
663    /**
664     * Removes a plugin schema from a service
665     *
666     * @param serviceName The name of the service
667     * @param interfaceName The name of the plugin interface
668     * @param pluginName The name of the plugin schema
669     * @throws SMSException if an error occurred while performing the operation
670     * @throws SSOException if the user's single sign on token is invalid or
671     *         expired.
672     */
673    public void removePluginSchema(String serviceName,
674                               String interfaceName,
675                               String pluginName)
676    throws SMSException, SSOException {
677        ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName, token);
678        String version = ssm.getVersion();
679
680        // Check if PluginSchema nodes exists
681        Set pluginSchemaNames = ssm.getPluginSchemaNames(interfaceName, null);
682
683        // if they match, delete
684        if (pluginSchemaNames.contains(pluginName)) {
685            StringBuilder sb = new StringBuilder(100);
686
687            // Construct the DN and get CachedSMSEntry
688            sb.append("ou=").append(pluginName).append(",").append("ou=").append(
689                interfaceName).append(",").append(
690                CreateServiceConfig.PLUGIN_CONFIG_NODE).append("ou=").append(
691                version).append(",").append("ou=").append(serviceName).append(
692                ",").append(SMSEntry.SERVICES_RDN).append(",");
693
694            CachedSMSEntry ce;
695
696            try {
697                ce = CachedSMSEntry.getInstance(token, sb.toString()
698                        + SMSEntry.baseDN);
699                SMSEntry smsEntry = ce.getClonedSMSEntry();
700                smsEntry.forceDelete(token);
701                ce.refresh(smsEntry);
702            } catch (SSOException ssoe) {
703                throw (new SMSException(ssoe, "sms-INVALID_SSO_TOKEN"));
704            }
705        } else {
706            throw new SMSException("Condition does not exist");
707        }
708
709        if (debug.messageEnabled()) {
710            debug.message("removePluginSchema: remove plugin " + pluginName +
711                    "from service " + serviceName);
712        }
713    }
714
715    /**
716     * Removes the service schema and configuration for
717     * the given service name.
718     * 
719     * @param serviceName
720     *            the name of the service
721     * @param version
722     *            the version of the service
723     * @throws SMSException
724     *             if an error occurred while performing the operation
725     * @throws SSOException
726     *             if the user's single sign on token is invalid or expired
727     *
728     * @supported.api
729     */
730    public void removeService(String serviceName, String version)
731            throws SMSException, SSOException {
732        // Find all service entries that have the DN
733        // Search for (&(ou=<serviceName>)(objectclass=top))
734        // construct the rdn with the given version, look for the entry
735        // in iDS and if entry exists(service with that version), delete.
736        if (serviceName.equalsIgnoreCase(IdConstants.REPO_SERVICE) ||
737            serviceName.equalsIgnoreCase(ISAuthConstants.AUTH_SERVICE_NAME)) {
738            Object args[] = { serviceName };
739            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
740                    "sms-SERVICE_CORE_CANNOT_DELETE", args));
741        }
742        SMSEntry.validateToken(token);
743        String[] objs = { serviceName };
744        Iterator results = SMSEntry.search(token, SMSEntry.baseDN,
745            MessageFormat.format(SMSEntry.FILTER_PATTERN, (Object[])objs),
746            0, 0, false, false).iterator();
747        while (results.hasNext()) {
748            String dn = (String) results.next();
749            String configdn = SMSEntry.PLACEHOLDER_RDN + SMSEntry.EQUALS
750                    + version + SMSEntry.COMMA + dn;
751            CachedSMSEntry configsmse = CachedSMSEntry.getInstance(token,
752                    configdn);
753            if (configsmse.isDirty()) {
754                configsmse.refresh();
755            }
756            SMSEntry confige = configsmse.getClonedSMSEntry();
757            if (!confige.isNewEntry()) {
758                confige.delete(token);
759                configsmse.refresh(confige);
760            }
761            // If there are no other service version nodes for that service,
762            // delete that node(schema).
763            CachedSMSEntry smse = CachedSMSEntry.getInstance(token, dn);
764            if (smse.isDirty()) {
765                smse.refresh();
766            }
767            SMSEntry e = smse.getSMSEntry();
768            Iterator versions = 
769                e.subEntries(token, "*", 0, false, false).iterator();
770            if (!versions.hasNext()) {
771                e.delete(token);
772                smse.refresh(e);
773            }
774        }
775    }
776
777    /**
778     * Deletes only the schema for the given service name. This is provided only
779     * for backward compatibility for DSAME 5.0 and will be deprecated in the
780     * future release. Alternative is to use
781     * <code>ServiceSchemaManager.replaceSchema()</code>.
782     * 
783     * @param serviceName
784     *            Name of service to be deleted.
785     * @throws SMSException
786     *             if an error occurred while performing the operation
787     * @throws SSOException
788     *             if the user's single sign on token is invalid or expired
789     */
790    public void deleteService(String serviceName) throws SMSException,
791            SSOException {
792        if (serviceName.equalsIgnoreCase(IdConstants.REPO_SERVICE) ||
793            serviceName.equalsIgnoreCase(ISAuthConstants.AUTH_SERVICE_NAME)) {
794            Object args[] = { serviceName };
795            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
796                    "sms-SERVICE_CORE_CANNOT_DELETE", args));
797        }
798
799        Iterator versions = getServiceVersions(serviceName).iterator();
800        while (versions.hasNext()) {
801            String version = (String) versions.next();
802            CachedSMSEntry ce = CachedSMSEntry.getInstance(token,
803                    getServiceNameDN(serviceName, version));
804            if (ce.isDirty()) {
805                ce.refresh();
806            }
807            SMSEntry e = ce.getClonedSMSEntry();
808            String[] values = { SMSSchema.getDummyXML(serviceName, version) };
809            e.setAttribute(SMSEntry.ATTR_SCHEMA, values);
810            e.save(token);
811            ce.refresh(e);
812        }
813    }
814
815    /**
816     * Returns the base DN (or root DN) that was set in
817     * <code>serverconfig.xml</code> at install time.
818     */
819    public static String getBaseDN() {
820        return (SMSEntry.baseDN);
821    }
822
823    /**
824     * Returns all AM Server instance. Read the configured servers from platform
825     * service's <code>iplanet-am-platform-server-list</code>
826     */
827    public static Set getAMServerInstances() {
828        // Check cache
829        if (accessManagerServers == null) {
830            // Get AdminToken
831            try {
832                SSOToken token = (SSOToken) AccessController
833                        .doPrivileged(AdminTokenAction.getInstance());
834                accessManagerServers = ServerConfiguration.getServers(token);
835                if (debug.messageEnabled()) {
836                    debug.message("ServiceManager.getAMServerInstances: "
837                        + "server list: " + accessManagerServers);
838                }
839            } catch (SMSException e) {
840                if (debug.warningEnabled()) {
841                    debug.warning("ServiceManager.getAMServerInstances: " +
842                        "Unable to get server list", e);
843                }
844            } catch (SSOException e) {
845                if (debug.warningEnabled()) {
846                    debug.warning("ServiceManager.getAMServerInstances: " +
847                        "Unable to get server list", e);
848                }
849            }
850        }
851        return (accessManagerServers == null ? new HashSet() : new HashSet(
852                accessManagerServers));
853    }
854
855    /**
856     * Returns organization names that match the given attribute name and
857     * values. Only exact matching is supported, and if more than one value is
858     * provided the organization must have all these values for the attribute.
859     * Basically an AND is performed for attribute values for searching.
860     * 
861     * @param serviceName
862     *            service name under which the attribute is to be sought.
863     * @param attrName
864     *            name of the attribute to search.
865     * @param values
866     *            set of attribute values to search.
867     * @return organizations that match the attribute name and values.
868     * @throws SMSException
869     *             if an error occurred while performing the operation.
870     * @throws SSOException
871     *             if the user's single sign on token is invalid or expired.
872     */
873    public Set searchOrganizationNames(String serviceName, String attrName,
874            Set values) throws SMSException, SSOException {
875
876        try {
877            if (subEntries == null) {
878                subEntries = CachedSubEntries.getInstance(token,
879                        SMSEntry.SERVICES_RDN + SMSEntry.COMMA
880                                + SMSEntry.baseDN);
881            }
882            return (subEntries.searchOrgNames(token, serviceName.toLowerCase(),
883                    attrName, values));
884        } catch (SSOException ssoe) {
885            debug.error("OrganizationConfigManagerImpl: Unable to "
886                    + "get sub organization names", ssoe);
887            throw (new SMSException(SMSEntry.bundle
888                    .getString("sms-INVALID_SSO_TOKEN"),
889                    "sms-INVALID_SSO_TOKEN"));
890        }
891    }
892
893    /**
894     * Removes all the SMS cached entries. This method
895     * should be called to clear the cache for example, if ACIs for the SMS
896     * entries are changed in the directory. Also, this clears the SMS entries
897     * only in this JVM instance. If multiple instances (of JVM) are running
898     * this method must be called within each instance.
899     *
900     * @supported.api
901     */
902    public synchronized void clearCache() {
903        // Clear the local caches
904        serviceNameAndOCs = new CaseInsensitiveHashMap();
905        serviceVersions = new CaseInsensitiveHashMap();
906        serviceNameDefaultVersion = new CaseInsensitiveHashMap();
907        accessManagerServers = null;
908        amsdkChecked = false;
909
910        // Call respective Impl classes
911        CachedSMSEntry.clearCache();
912        CachedSubEntries.clearCache();
913        // ServiceSchemaManagerImpl.clearCache();
914        PluginSchemaImpl.clearCache();
915        ServiceInstanceImpl.clearCache();
916        ServiceConfigImpl.clearCache();
917        ServiceConfigManagerImpl.clearCache();
918        OrganizationConfigManagerImpl.clearCache();
919        OrgConfigViaAMSDK.clearCache();
920
921        // Re-initialize the flags
922        try {
923            checkFlags(token);
924            OrganizationConfigManager.initializeFlags();
925            DNMapper.clearCache();
926        } catch (Exception e) {
927            debug.error("ServiceManager::clearCache unable to " +
928                "re-initialize global flags", e);
929        }
930    }
931
932    /**
933     * Returns the flag which lets IdRepo and SM know that we are running in the
934     * co-existence mode.
935     * 
936     * @return true or false depending on if the coexistence flag is enabled or
937     *         not.
938     */
939    public static boolean isCoexistenceMode() {
940        isRealmEnabled();
941        return (coexistenceCache);
942    }
943
944    /**
945     * Returns the version for a service. This is to handle the co-existence
946     * of OpenSSO and AM 7.1 in realm mode. The co-existence of OpenSSO and
947     * AM 7.1 in legacy mode is handled by the call to isCoexistenceMode() 
948     * method. There is a special service named "iPlanetAMProviderConfigService"
949     * used in AM 7.x code for ID-FF metadata, the version for the service
950     * is "1.1", all the rest of service is "1.0" right now. This method can 
951     * be removed if no need to support Co-existence of OpenSSO and AM 7.x 
952     * any more.
953     * @param serviceName Name of the service.
954     * @return version of the service, the value will be 1.0 or 1.1.
955     */
956    protected static String getVersion(String serviceName) {
957        if ("iPlanetAMProviderConfigService".equals(serviceName)) {
958            return "1.1";
959        } else {
960            return "1.0";
961        }
962    }
963 
964    /**
965     * Returns <code>true</code> if current service
966     * configuration uses the realm model to store the configuration data.
967     * 
968     * @return <code>true</code> is realm model is used for storing
969     *         configuration data; <code>false</code> otherwise.
970     *
971     * @supported.api
972     */
973    public static boolean isRealmEnabled() {
974        if (!initialized) {
975            try {
976                initialize((SSOToken) AccessController
977                        .doPrivileged(AdminTokenAction.getInstance()));
978            } catch (Exception ssme) {
979                debug.error("ServiceManager::isRealmEnabled unable to "
980                        + "initialize", ssme);
981            }
982        }
983        return (realmCache);
984    }
985    
986    
987    /**
988     * Returns <code>true</code> if AMSDK IdRepo plugin is
989     * configured in any of the realms
990     */
991    public static boolean isAMSDKConfigured() throws SMSException {
992        if (!isRealmEnabled() || OrgConfigViaAMSDK.isAMSDKConfigured("/")) {
993            // Legacy mode, AMSDK is configured by default
994            return (true);
995        }
996
997        // Iterate through all the realms to check if AMSDK is configured
998        SSOToken token = (SSOToken) AccessController
999            .doPrivileged(AdminTokenAction.getInstance());
1000        Set realms = (new OrganizationConfigManager(token, "/"))
1001            .getSubOrganizationNames("*", true);
1002        for (Iterator items = realms.iterator(); items.hasNext();) {
1003            String realm = items.next().toString();
1004            if (OrgConfigViaAMSDK.isAMSDKConfigured(realm)) {
1005                return (true);
1006            }
1007        }
1008        return (false);
1009    }
1010    
1011    /**
1012     * Returns <code>true</code> if configuration data has been migrated to
1013     * Access Manager 7.0. Else <code>false</code> otherwise.
1014     * 
1015     * @return <code>true</code> if configuration data has been migrated to AM
1016     *         7.0; <code>false</code> otherwise
1017     */
1018    public static boolean isConfigMigratedTo70() {
1019        isRealmEnabled();
1020        return (ditUpgradedCache);
1021    }
1022
1023    // ------------------------------------------------------------
1024    // Protected methods
1025    // ------------------------------------------------------------
1026
1027    // Called by CreateServiceConfig.java to create LDAP entries
1028    SSOToken getSSOToken() {
1029        return (token);
1030    }
1031
1032    protected static String getCacheIndex(String serviceName, String version) {
1033        StringBuilder sb = new StringBuilder(20);
1034        return (
1035            sb.append(serviceName).append(version).toString().toLowerCase());
1036    }
1037
1038    protected static String getServiceNameDN(String serviceName) {
1039        StringBuilder sb = new StringBuilder(100);
1040        sb.append(SMSEntry.PLACEHOLDER_RDN).append(SMSEntry.EQUALS).append(
1041                serviceName).append(SMSEntry.COMMA).append(serviceDN);
1042        return (sb.toString());
1043    }
1044
1045    protected static String getServiceNameDN(String serviceName, String version)
1046    {
1047        StringBuilder sb = new StringBuilder(100);
1048        sb.append(SMSEntry.PLACEHOLDER_RDN).append(SMSEntry.EQUALS).append(
1049                version).append(SMSEntry.COMMA).append(
1050                getServiceNameDN(serviceName));
1051        return (sb.toString());
1052    }
1053
1054    protected static Set getVersions(SSOToken token, String serviceName)
1055            throws SMSException, SSOException {
1056        CachedSubEntries sVersions = (CachedSubEntries) serviceVersions
1057                .get(serviceName);
1058        if (sVersions == null) {
1059            sVersions = CachedSubEntries.getInstance(token,
1060                    getServiceNameDN(serviceName));
1061            if (sVersions == null || sVersions.getSMSEntry().isNewEntry()
1062                    || sVersions.getSubEntries(token).isEmpty()) {
1063                String[] msgs = { serviceName };
1064                throw (new ServiceNotFoundException(
1065                        IUMSConstants.UMS_BUNDLE_NAME,
1066                        IUMSConstants.SMS_service_does_not_exist, msgs));
1067            }
1068            serviceVersions.put(serviceName, sVersions);
1069        }
1070        return (sVersions.getSubEntries(token));
1071    }
1072
1073    protected static void checkAndEncryptPasswordSyntax(Document doc,
1074        boolean encrypt
1075    ) throws SMSException {
1076         checkAndEncryptPasswordSyntax(doc, encrypt, null);
1077    }
1078
1079    protected static void checkAndEncryptPasswordSyntax(
1080        Document doc,
1081        boolean encrypt,
1082        AMEncryption encryptObj
1083    ) throws SMSException {
1084        // Get the node list of all AttributeSchema
1085        NodeList nl = doc.getElementsByTagName(SMSUtils.SCHEMA_ATTRIBUTE);
1086        for (int i = 0; i < nl.getLength(); i++) {
1087            Node node = nl.item(i);
1088            // Check if the "syntax" attribute is "password"
1089            String syntax = XMLUtils.getNodeAttributeValue(node,
1090                    SMSUtils.ATTRIBUTE_SYNTAX);
1091            if (syntax.equals(AttributeSchema.Syntax.PASSWORD.toString())) {
1092                if (debug.messageEnabled()) {
1093                    debug.message("ServiceManager: encrypting password syntax");
1094                }
1095                // Get the DefaultValues and encrypt then
1096                Node defaultNode;
1097                if ((defaultNode = XMLUtils.getChildNode(node,
1098                        SMSUtils.ATTRIBUTE_DEFAULT_ELEMENT)) != null) {
1099                    // Get NodeList of "Value" nodes and encrypt them
1100                    for (Iterator items = XMLUtils.getChildNodes(defaultNode,
1101                            SMSUtils.ATTRIBUTE_VALUE).iterator(); items
1102                            .hasNext();) {
1103                        Node valueNode = (Node) items.next();
1104                        String value = XMLUtils.getValueOfValueNode(valueNode);
1105                        String encValue;
1106                        
1107                        // skip empty passwords
1108                        if (value.equals("null")) {
1109                            continue;
1110                        }
1111
1112                        if (encrypt) {
1113                            if (encryptObj != null) {
1114                                value = (String)AccessController
1115                                    .doPrivileged(new DecodeAction(
1116                                        value, encryptObj));
1117                                if (value.equals("&amp;#160;")) {
1118                                    try {
1119                                        byte[] b = new byte[1];
1120                                        b[0] = -96;
1121                                        value = new String(b, "ISO-8859-1");
1122                                    } catch (UnsupportedEncodingException e) {
1123                                        //ignore
1124                                    }
1125                                }
1126                            }
1127                            encValue = (String)AccessController.doPrivileged(
1128                                new EncodeAction(value));
1129                        } else {
1130                            encValue = (String)AccessController.doPrivileged(
1131                                new DecodeAction(value));
1132                            
1133                            try {
1134                                //this is catch the whitespace for password
1135                                byte[] b = encValue.getBytes("ISO-8859-1");
1136                                if ((b.length == 1) && (b[0] == -96)) {
1137                                    encValue = "&amp;#160;";
1138                                }
1139                            } catch (UnsupportedEncodingException e) {
1140                                //ignore
1141                            }
1142                            if (encryptObj != null) {
1143                                encValue = (String)AccessController
1144                                    .doPrivileged(new EncodeAction(
1145                                        encValue, encryptObj));
1146                            }
1147                        }
1148
1149                        // Construct the encrypted "Value" node
1150                        StringBuilder sb = new StringBuilder(100);
1151                        sb.append(AttributeSchema.VALUE_BEGIN).append(encValue)
1152                          .append(AttributeSchema.VALUE_END);
1153                        Document newDoc = SMSSchema.getXMLDocument(
1154                            sb.toString(), false);
1155                        Node newValueNode = XMLUtils.getRootNode(newDoc,
1156                                SMSUtils.ATTRIBUTE_VALUE);
1157                        // Replace the node
1158                        Node nValueNode = doc.importNode(newValueNode, true);
1159                        defaultNode.replaceChild(nValueNode, valueNode);
1160                    }
1161                }
1162            }
1163        }
1164    }
1165
1166    protected static boolean validateServiceSchema(Node serviceNode)
1167            throws SMSException {
1168        Node schemaRoot = XMLUtils.getChildNode(serviceNode, SMSUtils.SCHEMA);
1169        String[] schemaNames = { SMSUtils.GLOBAL_SCHEMA, SMSUtils.ORG_SCHEMA,
1170                SMSUtils.DYNAMIC_SCHEMA, SMSUtils.USER_SCHEMA,
1171                SMSUtils.POLICY_SCHEMA, SMSUtils.GROUP_SCHEMA,
1172                SMSUtils.DOMAIN_SCHEMA };
1173        for (int i = 0; i < schemaNames.length; i++) {
1174            Node childNode = XMLUtils.getChildNode(schemaRoot, schemaNames[i]);
1175            if (childNode != null) {
1176                ServiceSchemaImpl ssi = new ServiceSchemaImpl(null, childNode);
1177                Map attrs = ssi.getAttributeDefaults();
1178                ssi.validateAttributes(attrs, false);
1179            }
1180        }
1181        return (true);
1182    }
1183
1184    // Gets called by OrganizationConfigManager when service schema has changed
1185    protected static void schemaChanged() {
1186        // Reset the service names and OCs used by IdRepo
1187        serviceNameAndOCs = new CaseInsensitiveHashMap();
1188        // Reset the schema types and service names
1189        // Reset the service names
1190        serviceNames = null;
1191    }
1192
1193    protected static String serviceDefaultVersion(SSOToken token,
1194    String serviceName) throws SMSException, SSOException {
1195        String version = (String) serviceNameDefaultVersion.get(serviceName);
1196        if (version == null) {
1197            Iterator iter = getVersions(token, serviceName).iterator();
1198            if (iter.hasNext()) {
1199                version = (String) iter.next();
1200            } else {
1201                String msgs[] = { serviceName };
1202                throw (new ServiceNotFoundException(
1203                    IUMSConstants.UMS_BUNDLE_NAME,
1204                    IUMSConstants.SMS_service_does_not_exist,
1205                    msgs));
1206            }
1207            serviceNameDefaultVersion.put(serviceName, version);
1208        }
1209        return (version);
1210    }
1211
1212    /**
1213     * Returns service names that will be assigned to a realm during creation.
1214     */
1215    public static Set servicesAssignedByDefault() {
1216        if (!loadedAuthServices) {
1217            AuthenticationServiceNameProvider provider = 
1218                AuthenticationServiceNameProviderFactory.getProvider();
1219            defaultServicesToLoad.addAll(provider
1220                    .getAuthenticationServiceNames());
1221            if (debug.messageEnabled()) {
1222                debug.message("ServiceManager::servicesAssignedByDefault:"
1223                        + "defaultServicesToLoad = " + defaultServicesToLoad);
1224            }
1225            loadedAuthServices = true;
1226            defaultServicesToLoad = Collections
1227                    .unmodifiableSet(defaultServicesToLoad);
1228        }
1229        return (defaultServicesToLoad);
1230    }
1231    
1232    /**
1233     * Returns service names configured via IdRepo service to be
1234     * added as required services
1235     */
1236    static Set requiredServices() {
1237        return (requiredServices);
1238    }
1239
1240    static void initialize(SSOToken token) throws SMSException, SSOException {
1241        // Validate SSOToken
1242        SMSEntry.validateToken(token);
1243
1244        // Check if already initialized
1245        if (initialized)
1246            return;
1247        // Initilaize the parameters
1248        try {
1249            // Get the service names and cache it
1250            serviceNames = CachedSubEntries.getInstance(token, serviceDN);
1251            if (serviceNames.getSMSEntry().isNewEntry()) {
1252                if (debug.warningEnabled()) {
1253                    debug.warning("SeviceManager:: Root service node "
1254                            + "does not exists: " + serviceDN);
1255                }
1256                String[] msgs = new String[1];
1257                msgs[0] = serviceDN;
1258                throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
1259                        IUMSConstants.SMS_services_node_does_not_exist, msgs));
1260            }
1261        } catch (SMSException e) {
1262            debug.error("ServiceManager::unable to get " + "services node: "
1263                    + serviceDN, e);
1264            throw (e);
1265        }
1266        // Check if realm is enabled and set appropriate flags
1267        checkFlags(token);
1268        initialized = true;
1269    }
1270
1271    static void checkFlags(SSOToken token) throws SMSException, SSOException {
1272        try {
1273            CachedSMSEntry entry = CachedSMSEntry.getInstance(token,
1274                REALM_ENTRY);
1275            if (entry.isDirty()) {
1276                entry.refresh();
1277            }
1278            if (!entry.isNewEntry()) {
1279                ditUpgradedCache = true;
1280                ServiceConfigManagerImpl ssm = ServiceConfigManagerImpl
1281                        .getInstance(token, REALM_SERVICE, SERVICE_VERSION);
1282                ServiceConfigImpl sc = null;
1283                Map map = null;
1284                if (ssm == null
1285                        || (sc = ssm.getGlobalConfig(token, null)) == null
1286                        || (map = sc.getAttributes()) == null) {
1287                    return;
1288                }
1289                Set coexistEntry = (Set) map.get(COEXISTENCE_ATTR_NAME);
1290                if (coexistEntry != null && coexistEntry.contains("false")) {
1291                    coexistenceCache = false;
1292                }
1293                Set realmEntry = (Set) map.get(REALM_ATTR_NAME);
1294                if (realmEntry != null && realmEntry.contains("true")) {
1295                    realmCache = true;
1296                }
1297                // Get the default services to be loaded
1298                requiredServices = (Set) map
1299                        .get(DEFAULT_SERVICES_FOR_REALMS);
1300                defaultServicesToLoad = new HashSet();
1301                defaultServicesToLoad.addAll(requiredServices);
1302
1303                // Make this flag false, for always the union of 
1304                // auto assignment services from idRepoService.xml and
1305                // auth services from AMAuthenticationManager code
1306                // should be returned for deep copy for a newly created
1307                // sub realm.
1308                loadedAuthServices = false;
1309            }
1310            if (debug.messageEnabled()) {
1311                debug.message("ServiceManager::checkFlags:realmEnabled="
1312                        + realmCache);
1313                debug.message("ServiceManager::checkFlags:coexistenceMode="
1314                        + coexistenceCache);
1315            }
1316        } catch (SMSException e) {
1317            debug.error("ServiceManager::unable to check "
1318                    + "if Realm is enabled: ", e);
1319            throw (e);
1320        }
1321    }
1322    
1323    public String toXML(AMEncryption encryptObj)
1324        throws SMSException, SSOException
1325    {
1326        StringBuilder buff = new StringBuilder();
1327        buff.append(SMSSchema.XML_ENC)
1328            .append("\n")
1329            .append("<!DOCTYPE ServicesConfiguration\n")
1330            .append(
1331       "PUBLIC \"=//iPlanet//Service Management Services (SMS) 1.0 DTD//EN\"\n")
1332            .append("\"jar://com/sun/identity/sm/sms.dtd\">\n\n");
1333        buff.append("<ServicesConfiguration>\n");
1334
1335        Set serviceNames = getServiceNames();
1336        
1337        for (Iterator i = serviceNames.iterator(); i.hasNext(); ) {
1338            String serviceName = (String)i.next();
1339            Set versions = getServiceVersions(serviceName);
1340        
1341            for (Iterator j = versions.iterator(); j.hasNext(); ) {
1342                String version = (String)j.next();
1343                ServiceSchemaManager ssm = new 
1344                    ServiceSchemaManager(token, serviceName, version);
1345                String xml = ssm.toXML(encryptObj);
1346                ServiceConfigManager scm = new ServiceConfigManager(
1347                    serviceName, token);
1348                int idx = xml.lastIndexOf("</" + SMSUtils.SERVICE + ">");
1349                xml = xml.substring(0, idx) + scm.toXML(encryptObj) + "</" + 
1350                    SMSUtils.SERVICE + ">";
1351                buff.append(xml).append("\n");
1352            }
1353        }
1354
1355        buff.append("</ServicesConfiguration>\n");
1356        return buff.toString().replaceAll("&amp;#160;", "&#160;");
1357    }
1358
1359    /**
1360     * Returns <code>true</code> if AMSDK IdRepo plugin is enabled/present
1361     * in IdRepo Service Configuration schema
1362     */
1363    public static boolean isAMSDKEnabled() {
1364        if (amsdkChecked) {
1365            return (isAMSDKEnabled);
1366        }
1367        SSOToken adminToken = (SSOToken) AccessController
1368            .doPrivileged(AdminTokenAction.getInstance());
1369        try {
1370            if (!ServiceManager.isRealmEnabled()) {
1371                amsdkChecked = true;
1372                // If in legacy mode, then amSDK plugin would always be there.
1373                isAMSDKEnabled = true;
1374            } else {
1375                ServiceSchemaManager ssm = new ServiceSchemaManager(
1376                    IdConstants.REPO_SERVICE, adminToken);
1377                ServiceSchema idRepoSubSchema = ssm.getOrganizationSchema();
1378                Set idRepoPlugins = idRepoSubSchema.getSubSchemaNames();
1379                if (idRepoPlugins.contains("amSDK")) {
1380                    isAMSDKEnabled = true;
1381                }
1382                amsdkChecked = true;
1383            }
1384        } catch (Exception e) {
1385            debug.error("IdUtils.isAMSDKEnabled() " +
1386                "Error in checking AM.SDK being configured", e);
1387        }
1388        amsdkChecked = true;
1389        return (isAMSDKEnabled);
1390    }
1391}