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