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