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: OrganizationConfigManager.java,v 1.31 2010/01/20 17:01:36 veiming Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted 2011-2013 ForgeRock Inc
031 */
032package com.sun.identity.sm;
033
034import com.iplanet.am.util.SystemProperties;
035import java.util.Collections;
036import java.util.HashMap;
037import java.util.HashSet;
038import java.util.Iterator;
039import java.util.Map;
040import java.util.regex.Pattern;
041import java.util.Set;
042import java.util.StringTokenizer;
043
044import com.sun.identity.shared.ldap.util.DN;
045
046import com.iplanet.sso.SSOException;
047import com.iplanet.sso.SSOToken;
048import com.iplanet.ums.IUMSConstants;
049import com.sun.identity.authentication.util.ISAuthConstants;
050import com.sun.identity.common.CaseInsensitiveHashSet;
051import com.sun.identity.idm.IdConstants;
052import com.sun.identity.idm.IdRepoException;
053import com.sun.identity.idm.plugins.internal.AgentsRepo;
054import com.sun.identity.shared.Constants;
055
056/**
057 * The class <code>OrganizationConfigManager</code> provides interfaces to
058 * manage an organization's configuration data. It provides interfaces to create
059 * and delete organizations, service attributes for organizations and service
060 * configuration parameters.
061 * <p>
062 * The organization configuration can be managed in a hierarchical manner, and a
063 * forward slash "/" will be used to separate the name hierarchy. Hence the root
064 * of the organization hierarchy will be represented by a single forward slash
065 * "/", and sub-organizations will be separated by "/". For example "/a/b/c"
066 * would represent a "c" sub-organization within "b" which would be a
067 * sub-organization of "a".
068 *
069 * @supported.all.api
070 */
071public class OrganizationConfigManager {
072    // Instance variables
073    private SSOToken token;
074
075    private String orgName;
076
077    private String orgDN;
078
079    private OrgConfigViaAMSDK amsdk;
080
081    private OrganizationConfigManagerImpl orgConfigImpl;
082
083    static String orgNamingAttrInLegacyMode;
084
085    static Pattern baseDNpattern = Pattern.compile(SMSEntry.getRootSuffix());
086
087    protected static final String SERVICES_NODE = SMSEntry.SERVICES_RDN
088            + SMSEntry.COMMA + SMSEntry.getRootSuffix();
089
090    // set the special characters which are not in realm names.
091    static String specialCharsString = "*|(|)|!|/|=";
092
093    private static String SEPERATOR = "|";
094
095    private String CONF_ENABLED =
096        "sun-idrepo-amSDK-config-copyconfig-enabled";
097
098    private boolean copyOrgInitialized;
099
100    private boolean copyOrgEnabled;
101
102    private String amSDKOrgDN;
103
104    // sunOrganizationAlias in org DIT.
105    public static final String SUNORG_ALIAS = "sunOrganizationAliases";
106
107    // associatedDomain in org DIT.
108    private String SUNDNS_ALIAS = "sunDNSAliases";
109
110    // sunPreferredDomain in org DIT.
111    private String SUNPREF_DOMAIN = "sunPreferredDomain";
112
113    // inetDomainStatus in org DIT.
114    private String SUNORG_STATUS = "sunOrganizationStatus";
115
116    static {
117        initializeFlags();
118    }
119
120    /**
121     * Constructor to obtain an instance of
122     * <code>OrganizationConfigManager
123     * </code> for an organization by providing
124     * an authenticated identity of the user. The organization name would be "/"
125     * seperated to represent organization hierarchy.
126     * 
127     * @param token
128     *            single sign on token of authenticated user identity.
129     * @param orgName
130     *            name of the organization. The value of <code>null
131     * </code> or
132     *            "/" would represent the root organization.
133     * 
134     * @throws SMSException
135     *             if an error has occurred while getting the instance of
136     *             <code>OrganizationConfigManager
137     *                      </code>.
138     */
139    public OrganizationConfigManager(SSOToken token, String orgName)
140            throws SMSException {
141        // Copy instance variables
142        this.token = token;
143        this.orgName = orgName;
144
145        // Instantiate and validate
146        validateConfigImpl();
147        orgDN = orgConfigImpl.getOrgDN();
148        try {
149            if (migratedTo70 && !registeredForConfigNotifications) {
150                ServiceConfigManager scmr = new ServiceConfigManager(
151                        ServiceManager.REALM_SERVICE, token);
152                scmr.addListener(new OrganizationConfigManagerListener());
153                registeredForConfigNotifications = true;
154            }
155        } catch (SMSException s) {
156            String installTime = SystemProperties.get(
157                Constants.SYS_PROPERTY_INSTALL_TIME, "false");
158            if (!installTime.equals("true")) {
159                SMSEntry.debug.warning("OrganizationConfigManager: "
160                    + "constructor. Unable to "
161                    + "construct ServiceConfigManager for idRepoService ", s);
162            }
163            throw s;
164        } catch (SSOException ssoe) {
165            SMSEntry.debug.error("OrganizationConfigManager:Constructor", ssoe);
166            throw (new SMSException(SMSEntry.bundle
167                    .getString("sms-INVALID_SSO_TOKEN"),
168                    "sms-INVALID_SSO_TOKEN"));
169        }
170
171        if (coexistMode) {
172            amsdk = new OrgConfigViaAMSDK(token, DNMapper
173                    .realmNameToAMSDKName(orgDN), orgDN);
174            if (orgNamingAttrInLegacyMode == null) {
175                orgNamingAttrInLegacyMode = getNamingAttrForOrg();
176            }
177        }
178    }
179
180    /**
181     * Returns the fully qualified name of the
182     * organization from the root
183     * 
184     * @return the name of the organization
185     */
186    public String getOrganizationName() {
187        return (orgName);
188    }
189
190    /**
191     * Returns the services configured for the organization.
192     * 
193     * @return service names configured for the organization.
194     * @throws SMSException
195     *             if there is an error accessing the data store to read the
196     *             configured services.
197     * 
198     * @deprecated This method has been deprecated, use <code>
199     * getAssignedServices()</code>
200     *             instead.
201     */
202    public Set getConfiguredServices() throws SMSException {
203        return (getAssignedServices());
204    }
205
206    /**
207     * Returns a set of service schemas to be used for
208     * creation of an organization. The service schemas contain a list of
209     * attributes and their schema, and will be provided as
210     * <code>ServiceSchema</code>.
211     * 
212     * @return Set of <code>ServiceSchema</code> to be used for creation of an
213     *         organization.
214     * @throws SMSException
215     *             if there is an error accessing the data store to read the
216     *             service schemas.
217     */
218    public Set getServiceSchemas() throws SMSException {
219        // Loop through the services and determine the
220        // organization creation schemas
221        Set serviceSchemaSet = null;
222        try {
223            Set serviceNames = getServiceNames(token);
224            serviceSchemaSet = new HashSet(serviceNames.size() * 2);
225            for (Iterator names = serviceNames.iterator(); names.hasNext();) {
226                ServiceSchemaManager ssm = new ServiceSchemaManager(
227                    (String) names.next(), token);
228                ServiceSchema ss = ssm.getOrganizationCreationSchema();
229                if (ss != null) {
230                    serviceSchemaSet.add(ss);
231                }
232            }
233        } catch (SSOException ssoe) {
234            SMSEntry.debug.error("OrganizationConfigManager:getServiceSchemas"
235                    + " unable to get service schema", ssoe);
236            throw (new SMSException(SMSEntry.bundle
237                    .getString("sms-INVALID_SSO_TOKEN"), ssoe,
238                    "sms-INVALID_SSO_TOKEN"));
239        }
240        return (serviceSchemaSet);
241    }
242
243    /**
244     * Creates a sub-organization under the current
245     * organization and sets the specified attributes. The sub-organization
246     * created can be only one level below the current organization. For
247     * multiple levels this method must be called recursively with the
248     * corresponding <code>OrganizationConfigManager
249     * </code>. The organization
250     * name must not have forward slash ("/"). For eg., the actual organization
251     * name 'iplanet' cannot be 'iplan/et' because we are using '/' as the
252     * seperator here. The attributes for the organization can be <code>
253     * null</code>;
254     * else would contain service name as the key and another <code>Map</code>
255     * as the value that would contain the key-values pair for the services.
256     * 
257     * @param subOrgName
258     *            the name of the sub-organization.
259     * @param attributes
260     *            Map of attributes for the organization per service. The
261     *            parameter Map attributes contains another Map as its value,
262     *            which then has attribute names and values. The way it is
263     *            arranged is: Map::attributes --> Key: String::ServiceName
264     *            Value: Map::svcAttributes Map::svcAttributes --> Key:
265     *            String::AttributeName Value: Set::AttributeValues
266     * 
267     * @return organization config manager of the newly created
268     *         sub-organization.
269     * @throws SMSException
270     *             if creation of sub-organization failed, or if creation of
271     *             sub-organization is attempted when configuration is not
272     *             migrated to realms.
273     */
274    public OrganizationConfigManager createSubOrganization(String subOrgName,
275            Map attributes) throws SMSException {
276        validateConfigImpl();
277        /*
278         * Since the "Map attributes" can contain more than one service name,
279         * creation of the sub organization is be achieved in 2 steps. i) create
280         * the sub-organization without the attributes ii) for the service names
281         * in the Map call setAttributes(...)
282         */
283        boolean orgExists = false;
284        String subOrgDN = normalizeDN(subOrgName, orgDN);
285        try {
286            // Check if realm exists, this throws SMSException
287            // if realm does not exist
288            // This is to avoid duplicate creation of realms.
289            new OrganizationConfigManager(token, subOrgDN);
290            SMSEntry.debug.error("OrganizationConfigManager::"
291                    + "createSubOrganization() " + "Realm Already Exists.. "
292                    + subOrgDN);
293            orgExists = true;
294        } catch (SMSException smse) {
295            // Realm does not exist, create it
296            if (SMSEntry.debug.messageEnabled()) {
297                SMSEntry.debug.message("OrganizationConfigManager::"
298                        + "createSubOrganization() "
299                        + "New Realm, creating realm: " + subOrgName + "-"
300                        + smse);
301            }
302        }
303        Object args[] = { subOrgName };
304        if (orgExists) {
305            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
306                "sms-organization_already_exists1",
307                    args));
308        }
309        StringTokenizer st =
310            new StringTokenizer(specialCharsString, SEPERATOR);
311        while (st.hasMoreTokens()) {
312            String obj = (String) st.nextToken();
313            if (subOrgName.indexOf(obj) > -1) {
314                SMSEntry.debug.error("OrganizationConfigManager::"+
315                    "createSubOrganization() : Invalid realm name: "+
316                        subOrgName);
317                SMSEntry.debug.error("OrganizationConfigManager::"+
318                    "createSubOrganization() : Detected invalid chars: "+obj);
319                Object args1[] = {subOrgName};
320                throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
321                    SMSEntry.bundle.getString("sms-invalid-org-name"),args1));
322            }
323        }
324
325        // If in legacy mode or (realm mode and copy org enabled)
326        // Create the AMSDK organization first
327        if ((coexistMode) || (realmEnabled && isCopyOrgEnabled())) {
328            amsdk.createSubOrganization(subOrgName);
329        }
330        if ((realmEnabled || subOrgDN.toLowerCase().startsWith(
331                SMSEntry.SUN_INTERNAL_REALM_PREFIX))
332                && getSubOrganizationNames(subOrgName, false).isEmpty()) {
333            CreateServiceConfig.createOrganization(token, subOrgDN);
334        }
335        // Update the attributes
336        // If in coexistMode and serviceName is idRepoService
337        // the following call sets the attributes to AMSDK organization also.
338        OrganizationConfigManager ocm = getSubOrgConfigManager(subOrgName);
339        if ((attributes != null) && (!attributes.isEmpty())) {
340            for (Iterator svcNames = attributes.keySet().iterator(); svcNames
341                    .hasNext();) {
342                String serviceName = (String) svcNames.next();
343                Map svcAttributes = (Map) attributes.get(serviceName);
344                if ((svcAttributes != null) && (!svcAttributes.isEmpty())) {
345                    ocm.setAttributes(serviceName, svcAttributes);
346                }
347            }
348        }
349
350        if (realmEnabled) {
351            AgentsRepo agentsRepo = new AgentsRepo();
352            HashMap config = new HashMap(1);
353            HashSet realmName = new HashSet(1);
354            realmName.add(subOrgName);
355            config.put("agentsRepoRealmName", realmName);
356            try {
357                agentsRepo.initialize(config);
358                agentsRepo.createAgentGroupConfig(token);
359            } catch (IdRepoException ide) {
360                SMSEntry.debug.error("OrganizationConfigManager::"+
361                        "createSubOrganization:", ide);
362            }
363        }
364                        
365        // If in realm mode and not in legacy mode, default services needs
366        // to be added.
367        if (realmEnabled && !coexistMode) {
368            loadDefaultServices(token, ocm);
369        }
370
371        // If in realm mode and copy org enabled, default services needs
372        // to be registered for the newly created org/suborg and the
373        // amSDKOrgName/OpenSSO Organization is updated with the
374        // new suborg dn.
375        if (realmEnabled && isCopyOrgEnabled()) {
376            registerSvcsForOrg(subOrgName, subOrgDN);
377            OrganizationConfigManager subOrg =
378                getSubOrgConfigManager(subOrgName);
379            ServiceConfig s =
380                subOrg.getServiceConfig(ServiceManager.REALM_SERVICE);
381            if (s != null) {
382                try {
383                    Iterator items = s.getSubConfigNames().iterator();
384                    while (items.hasNext()) {
385                        ServiceConfig subConfig =
386                            s.getSubConfig((String) items.next());
387                        if (subConfig.getSchemaID().equalsIgnoreCase(
388                            IdConstants.AMSDK_PLUGIN_NAME)) {
389                            Map amsdkConfig = new HashMap();
390                            Set vals = new HashSet();
391                            vals.add(orgNamingAttrInLegacyMode +
392                                SMSEntry.EQUALS +
393                                subOrgName + SMSEntry.COMMA + amSDKOrgDN);
394                            amsdkConfig.put("amSDKOrgName", vals);
395                            subConfig.setAttributes(amsdkConfig);
396                        }
397                        break;
398                    }
399                } catch (SSOException ssoe) {
400                    SMSEntry.debug.error("OrganizationConfigManager::"+
401                        "createSubOrganization:", ssoe);
402                    throw (new SMSException(SMSEntry.bundle.getString(
403                        "sms-INVALID_SSO_TOKEN"), "sms-INVALID_SSO_TOKEN"));
404                }
405            }
406        }
407
408        // Return the newly created organization config manager
409        return (ocm);
410    }
411
412    /**
413     * Returns the names of all sub-organizations.
414     * 
415     * @return set of names of all sub-organizations.
416     * @throws SMSException
417     *             if there is an error accessing the data store to read the
418     *             sub-organization names.
419     */
420
421    public Set getSubOrganizationNames() throws SMSException {
422        try {
423            return (getSubOrganizationNames("*", false));
424        } catch (SMSException s) {
425            SMSEntry.debug.error("OrganizationConfigManager: "
426                    + "getSubOrganizationNames() Unable to "
427                    + "get sub organization names ", s);
428            throw s;
429        }
430    }
431
432    /**
433     * Returns the names of all peer-organizations.
434     * 
435     * @return set of names of all peer-organizations.
436     * @throws SMSException
437     *             if there is an error accessing the data store to read the
438     *             peer-organization names.
439     */
440
441    public Set getPeerOrganizationNames() throws SMSException {
442        Set getPeerSet = Collections.EMPTY_SET;
443        if (realmEnabled) {
444            try {
445                OrganizationConfigManager ocmParent = 
446                    getParentOrgConfigManager();
447                getPeerSet = ocmParent.getSubOrganizationNames();
448            } catch (SMSException s) {
449                if (SMSEntry.debug.warningEnabled()) {
450                    SMSEntry.debug.warning("OrganizationConfigManager: "
451                            + "getPeerOrganizationNames() Unable to "
452                            + "get Peer organization names ", s);
453                }
454                throw s;
455            }
456        }
457        return (getPeerSet);
458    }
459
460    /**
461     * Returns names of sub-organizations matching the
462     * given pattern. If the parameter <code>recursive</code> is set to
463     * <code>true</code>, search will be performed for the entire sub-tree.
464     * The pattern can contain "*" as the wildcard to represent zero or more
465     * characters.
466     * 
467     * @param pattern
468     *            pattern that will be used for searching, where "*" will be the
469     *            wildcard.
470     * @param recursive
471     *            if set to <code>true</code> the entire sub-tree will be
472     *            searched for the organization names.
473     * @return names of sub-organizations matching the pattern.
474     * @throws SMSException
475     *             if there is an error accessing the data store to read the
476     *             sub-organization names.
477     */
478    public Set getSubOrganizationNames(String pattern, boolean recursive)
479            throws SMSException {
480        validateConfigImpl();
481        try {
482            if (realmEnabled) {
483                return (orgConfigImpl.getSubOrganizationNames(token, pattern,
484                        recursive));
485            } else {
486                // Must be in coexistence mode
487                return (amsdk.getSubOrganizationNames(pattern, recursive));
488            }
489        } catch (SMSException s) {
490            SMSEntry.debug.error("OrganizationConfigManager: "
491                    + "getSubOrganizationNames(String pattern, "
492                    + "boolean recursive) Unable to get sub organization "
493                    + "names for filter: " + pattern, s);
494            throw s;
495        }
496    }
497
498    /**
499     * Deletes the given sub-organization. If the
500     * parameter <code>recursive</code> is set to <code>true</code>, then
501     * the suborganization and the sub-tree will be deleted.
502     * 
503     * If the parameter <code>recursive</code> is set to <code>false</code>
504     * then the sub-organization shall be deleted provided it is the leaf node.
505     * If there are entries beneath the sub-organization and if the parameter
506     * <code>recursive</code> is set to <code>false</code>, then an
507     * exception is thrown that this sub-organization cannot be deleted.
508     * 
509     * @param subOrgName
510     *            sub-organization name to be deleted.
511     * @param recursive
512     *            if set to <code>true</code> the entire sub-tree will be
513     *            deleted.
514     * @throws SMSException
515     *             if the sub-organization name cannot be found, or if there are
516     *             entries beneath the sub-organization and if the parameter
517     *             <code>recursive</code> is set to <code>false</code>.
518     */
519    public void deleteSubOrganization(String subOrgName, boolean recursive)
520            throws SMSException {
521        validateConfigImpl();
522        // Should not delete the root realm, should throw exception if
523        // attempted.
524        String subOrgDN = normalizeDN(subOrgName, orgDN);
525        if (subOrgDN.equals(SMSEntry.SLASH_STR) ||
526            subOrgDN.equalsIgnoreCase(SMSEntry.getRootSuffix()) ||
527            subOrgDN.equalsIgnoreCase(SERVICES_NODE)) {
528            
529            Object parms[] = { orgName };
530            SMSEntry.debug.error(
531                    "OrganizationConfigManager: deleteSubOrganization(" +
532                    "Root realm "+orgName + " cannot be deleted. ");
533            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
534                "sms-cannot_delete_rootsuffix",parms));
535                    
536        }
537        // Delete the sub-organization
538        OrganizationConfigManager subRlmConfigMgr =
539            getSubOrgConfigManager(subOrgName);
540        //set the filter "*" to be passed for the search.
541        Set subRlmSet =
542            subRlmConfigMgr.getSubOrganizationNames("*", true);
543
544        if (realmEnabled) {
545            try {
546                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
547                        subOrgDN);
548                if (cEntry.isDirty()) {
549                    cEntry.refresh();
550                }
551                SMSEntry entry = cEntry.getClonedSMSEntry();
552                if (!recursive) {
553                    // Check if there are sub organization entries
554                    // and if exist
555                    // throw exception that this sub organization cannot be
556                    // deleted.
557                    if ((subRlmSet !=null) && (!subRlmSet.isEmpty())) {
558                        throw (new SMSException(SMSEntry.bundle
559                                .getString("sms-entries-exists"),
560                                "sms-entries-exists"));
561                    }
562                }
563                // Obtain the SMSEntry for the suborg and
564                // sub tree and delete it.
565                entry.delete(token);
566                cEntry.refresh(entry);
567            } catch (SSOException ssoe) {
568                SMSEntry.debug.error(
569                        "OrganizationConfigManager: deleteSubOrganization(" +
570                        "String subOrgName, boolean recursive) Unable to " +
571                        "delete sub organization ", ssoe);
572                throw (new SMSException(SMSEntry.bundle
573                        .getString("sms-INVALID_SSO_TOKEN"),
574                        "sms-INVALID_SSO_TOKEN"));
575            }
576        }
577
578        // If in legacy mode or (realm mode and copy org enabled)
579        // delete the corresponding organization.
580        if ((coexistMode) || (realmEnabled && isCopyOrgEnabled())) {
581            String amsdkName = DNMapper.realmNameToAMSDKName(subOrgDN);
582            if (!SMSEntry.getRootSuffix().equalsIgnoreCase(
583                SMSEntry.getAMSdkBaseDN())) {
584                String convOrg = subOrgName;
585                if (subOrgName.startsWith("/")) {
586                    convOrg = DNMapper.convertToDN(subOrgName).toString();
587                }
588                amsdkName = convOrg + SMSEntry.COMMA + amSDKOrgDN;
589            }
590            amsdk.deleteSubOrganization(amsdkName);
591        }
592    }
593
594    /**
595     * Returns the <code>OrganizationConfigManager</code>
596     * for the given organization name.
597     * 
598     * @param subOrgName
599     *            the name of the organization.
600     * @return the configuration manager for the given organization.
601     * 
602     * @throws SMSException
603     *             if the organization name cannot be found or user doesn't have
604     *             access to that organization.
605     */
606    public OrganizationConfigManager getSubOrgConfigManager(String subOrgName)
607            throws SMSException {
608        validateConfigImpl();
609        // Normalize sub organization name
610        return (new OrganizationConfigManager(token, normalizeDN(subOrgName,
611                orgDN)));
612    }
613
614    /**
615     * Returns the organization creation attributes for
616     * the service.
617     * 
618     * @param serviceName
619     *            name of the service.
620     * @return map of organization creation attribute values for service
621     * @throws SMSException
622     *             if there is an error accessing the data store to read the
623     *             attributes of the service.
624     */
625    public Map getAttributes(String serviceName) throws SMSException {
626        validateConfigImpl();
627        if (serviceName == null) {
628            return (Collections.EMPTY_MAP);
629        }
630        Map attrValues = null;
631        // Attributes can be obtained only if DIT is migrated to AM 7.0
632        if (migratedTo70) {
633            // Lowercase the service name
634            serviceName = serviceName.toLowerCase();
635            try {
636                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
637                        orgDN);
638                if (cEntry.isDirty() || (coexistMode) ||
639                    (realmEnabled && isCopyOrgEnabled())) {
640                    // Since AMSDK org notifications will not be
641                    // obtained, the entry must be read again
642                    cEntry.refresh();
643                }
644                SMSEntry entry = cEntry.getSMSEntry();
645                Map map = SMSUtils.getAttrsFromEntry(entry);
646                if ((map != null) && (!map.isEmpty())) {
647                    Iterator itr = map.keySet().iterator();
648                    while (itr.hasNext()) {
649                        String name = (String) itr.next();
650                        if ((name.toLowerCase()).startsWith(serviceName)) {
651                            Set values = (Set) map.get(name);
652                            // Remove the serviceName and '-' and return only
653                            // the attribute name,value.
654                            String key = name
655                                    .substring(serviceName.length() + 1);
656                            if (attrValues == null) {
657                                attrValues = new HashMap();
658                            }
659                            attrValues.put(key, values);
660                        }
661                    }
662                }
663            } catch (SSOException ssoe) {
664                SMSEntry.debug.error("OrganizationConfigManager: "
665                        + "getAttributes(String serviceName) Unable to "
666                        + "get Attributes", ssoe);
667                throw (new SMSException(SMSEntry.bundle
668                        .getString("sms-INVALID_SSO_TOKEN"),
669                        "sms-INVALID_SSO_TOKEN"));
670            }
671        }
672
673        // If in coexistMode and serviceName is idRepoService
674        // get attributes from AMSDK organization
675        if ((coexistMode || (realmEnabled && isCopyOrgEnabled()))
676                && serviceName
677                    .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
678            Map amsdkMap = amsdk.getAttributes();
679            Map mergesdkMap = new HashMap(2);
680            if (amsdkMap != null && !amsdkMap.isEmpty()) {
681                Set mergeValues = new HashSet(2);
682                Iterator itr = amsdkMap.keySet().iterator();
683                while (itr.hasNext()) {
684                    String key = (String) itr.next();
685                    if (key.equalsIgnoreCase(SUNDNS_ALIAS) || 
686                        key.equalsIgnoreCase(SUNPREF_DOMAIN) ||
687                            key.equalsIgnoreCase(SUNORG_ALIAS)) {
688                        buildSet(key, amsdkMap, mergeValues);
689                    }
690                }
691                mergesdkMap.put(SUNORG_ALIAS, mergeValues);
692                mergesdkMap.put(SUNORG_STATUS,
693                    (Set) amsdkMap.get(SUNORG_STATUS));
694            }
695            if (attrValues == null) {
696                attrValues = mergesdkMap;
697            } else {
698                attrValues.putAll(mergesdkMap);
699            }
700        }
701        return ((attrValues == null) ? Collections.EMPTY_MAP : attrValues);
702    }
703
704    /**
705     * Builds and returns the appropriate Set for the attributes to be
706     * merged from org and realm if the system is
707     * in intrusive mode (Both org DIT and realm DIT are present).
708     * This happens when the Copy Config flag is enabled.
709     */
710    private Set buildSet(String attrName, Map attributes, Set resultSet) {
711        Set vals = (Set) attributes.get(attrName);
712        if ((vals != null) && !vals.isEmpty()) {
713            resultSet.addAll(vals);
714        }
715        return (resultSet);
716    }
717
718    /**
719     * Adds organization attributes for the service. If
720     * the attribute already exists, the values will be appended to it, provided
721     * it is a multi-valued attribute. It will throw exception if we try to add
722     * a value to an attribute which has the same value already.
723     * 
724     * @param serviceName
725     *            name of the service.
726     * @param attrName
727     *            name of the attribute.
728     * @param values
729     *            values for the attribute.
730     * @throws SMSException
731     *             if we try to add a value to an attribute which has the same
732     *             value already.
733     */
734    public void addAttributeValues(String serviceName, String attrName,
735            Set values) throws SMSException {
736        validateConfigImpl();
737        if (serviceName == null || attrName == null) {
738            return;
739        }
740
741        if (migratedTo70) {
742            // Lowercase the servicename
743            serviceName = serviceName.toLowerCase();
744            try {
745                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
746                        orgDN);
747                if (cEntry.isDirty()) {
748                    cEntry.refresh();
749                }
750                SMSEntry e = cEntry.getClonedSMSEntry();
751                ServiceSchemaManager ssm = new ServiceSchemaManager(
752                        serviceName, token);
753                ServiceSchema ss = ssm.getOrganizationCreationSchema();
754                if (ss == null) {
755                    throw (new SMSException(SMSEntry.bundle
756                            .getString("sms-SMSSchema_service_notfound"),
757                            "sms-SMSSchema_service_notfound"));
758                }
759
760                Map map = new HashMap(2);
761                Set newValues = new HashSet(values);
762                Map allAttributes = ss.getAttributeDefaults();
763                Set existingValues = (Set)allAttributes.get(attrName);
764                if ((existingValues != null) && !existingValues.isEmpty()) {
765                    newValues.addAll(existingValues);
766                }
767                map.put(attrName, newValues);
768                ss.validateAttributes(map);
769                SMSUtils.addAttribute(e, serviceName + "-" + attrName,
770                    values, ss.getSearchableAttributeNames());
771                e.save(token);
772                cEntry.refresh(e);
773            } catch (SSOException ssoe) {
774                SMSEntry.debug.error("OrganizationConfigManager: Unable "
775                        + "to add Attribute Values", ssoe);
776                throw (new SMSException(SMSEntry.bundle
777                        .getString("sms-INVALID_SSO_TOKEN"),
778                        "sms-INVALID_SSO_TOKEN"));
779            }
780        }
781
782        // If in coexistMode and serviceName is idRepoService
783        // add the attributes to AMSDK organization
784        if (coexistMode
785                && serviceName
786                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
787            amsdk.addAttributeValues(attrName, values);
788        }
789    }
790
791    /**
792     * Sets/Creates organization attributes for the
793     * service. If the attributes already exists, the given attribute values
794     * will replace them.
795     * 
796     * @param serviceName
797     *            name of the service.
798     * @param attributes
799     *            attribute-values pairs.
800     * @throws SMSException
801     *             if the serviceName cannot be found.
802     */
803    public void setAttributes(String serviceName, Map attributes)
804            throws SMSException {
805        validateConfigImpl();
806        if (serviceName == null) {
807            return;
808        }
809
810        if (migratedTo70) {
811            // Lowercase the serviceName
812            serviceName = serviceName.toLowerCase();
813            try {
814                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
815                        orgDN);
816                if (cEntry.isDirty()) {
817                    cEntry.refresh();
818                }
819                SMSEntry e = cEntry.getClonedSMSEntry();
820                if ((attributes != null) && (!attributes.isEmpty())) {
821                    // Validate the attributes
822                    ServiceSchemaManager ssm = new ServiceSchemaManager(
823                            serviceName, token);
824                    ServiceSchema ss = ssm.getOrganizationCreationSchema();
825                    ss.validateAttributes(attributes);
826
827                    // Normalize the attributes with service name
828                    Map attrsMap = new HashMap();
829                    Iterator itr = attributes.keySet().iterator();
830                    while (itr.hasNext()) {
831                        String name = (String) itr.next();
832                        Set values = (Set) attributes.get(name);
833                        /*
834                         * To make the attributes qualified by service name we
835                         * prefix the attribute names with the service name.
836                         */
837                        attrsMap.put(serviceName + "-" + name, values);
838                    }
839
840                    // Look for old attrs. in the storage and add them too.
841                    Map oldAttrs = getAttributes(serviceName);
842                    Iterator it = oldAttrs.keySet().iterator();
843                    while (it.hasNext()) {
844                        String skey = (String) it.next();
845                        if (!attributes.containsKey(skey))
846                            attrsMap.put(serviceName + "-" + skey, oldAttrs
847                                    .get(skey));
848                    }
849
850                    // Set the attributes in SMSEntry
851                    SMSUtils.setAttributeValuePairs(e, attrsMap, ss
852                            .getSearchableAttributeNames());
853
854                    String dataStore = SMSEntry.getDataStore(token);
855                    // Add these OCs only for SunOne DS. Do not add the 
856                    // OCs for Active Directory.
857                    // Will get WILL_NOT_PERFORM in AD.
858                    if ((dataStore != null) && !dataStore.equals(
859                        SMSEntry.DATASTORE_ACTIVE_DIR)
860                    ) {
861                        // This is for storing organization attributes
862                        // in top/default realm node. eg.,ou=services,o=isp
863                        if (e.getDN().equalsIgnoreCase(SERVICES_NODE)) {
864                            String[] ocVals = e
865                                .getAttributeValues(SMSEntry.ATTR_OBJECTCLASS);
866                            boolean exists = false;
867                            for (int ic = 0; ocVals != null 
868                                && ic < ocVals.length; ic++) 
869                            {
870                                if (ocVals[ic].startsWith(
871                                    SMSEntry.OC_SERVICE_COMP)) {
872                                    // OC needs to be added outside the for loop
873                                    // else will throw concurrent mod exception
874                                    exists = true;
875                                    break;
876                                }
877                            }
878                            if (!exists) {
879                                e.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
880                                    SMSEntry.OC_SERVICE_COMP);
881                            }
882                        } else if (e.getDN().startsWith(
883                            SMSEntry.ORGANIZATION_RDN + SMSEntry.EQUALS)) {
884                            // This is for storing organization attributes in
885                            // organizations created via sdk through realm 
886                            // console.
887                            String[] vals = e
888                                .getAttributeValues(SMSEntry.ATTR_OBJECTCLASS);
889                            boolean rsvcExists = false;
890                            for (int n = 0; vals != null && n < vals.length; 
891                                n++) {
892                                if (vals[n].equalsIgnoreCase(
893                                    SMSEntry.OC_REALM_SERVICE)) 
894                                {
895                                    // OC needs to be added outside the for loop
896                                    // else will throw concurrent mod exception
897                                    rsvcExists = true;
898                                    break;
899                                }
900                            }
901                            if (!rsvcExists) {
902                                e.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
903                                    SMSEntry.OC_REALM_SERVICE);
904                            }
905                        }
906                    }
907
908                    // Save in backend data store and refresh the cache
909                    e.save(token);
910                    cEntry.refresh(e);
911                }
912
913            } catch (SSOException ssoe) {
914                SMSEntry.debug.error("OrganizationConfigManager: Unable "
915                        + "to set Attributes", ssoe);
916                throw (new SMSException(SMSEntry.bundle
917                        .getString("sms-INVALID_SSO_TOKEN"),
918                        "sms-INVALID_SSO_TOKEN"));
919            }
920        }
921
922        // If in coexistMode and serviceName is idRepoService
923        // set the attributes to AMSDK organization
924        if ((coexistMode || (realmEnabled && isCopyOrgEnabled()))
925                && serviceName
926                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
927            amsdk.setAttributes(attributes);
928        }
929    }
930
931    /**
932     * Removes the given organization creation attribute
933     * for the service.
934     * 
935     * @param serviceName
936     *            name of service.
937     * @param attrName
938     *            name of attribute.
939     * @throws SMSException
940     *             if the organization attribute for the service to be removed
941     *             cannot be found, or if the service name cannot be found.
942     */
943    public void removeAttribute(String serviceName, String attrName)
944            throws SMSException {
945        validateConfigImpl();
946        if (serviceName == null || attrName == null) {
947            return;
948        }
949
950        if (migratedTo70) {
951            try {
952                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
953                        orgDN);
954                if (cEntry.isDirty()) {
955                    cEntry.refresh();
956                }
957                SMSEntry e = cEntry.getClonedSMSEntry();
958                SMSUtils.removeAttribute(e, serviceName.toLowerCase() + "-"
959                        + attrName);
960                e.save(token);
961                cEntry.refresh(e);
962            } catch (SSOException ssoe) {
963                SMSEntry.debug.error("OrganizationConfigManager: Unable "
964                        + "to remove Attribute", ssoe);
965                throw (new SMSException(SMSEntry.bundle
966                        .getString("sms-INVALID_SSO_TOKEN"),
967                        "sms-INVALID_SSO_TOKEN"));
968            }
969        }
970
971        // If in coexistMode and serviceName is idRepoService
972        // remove the attributes to AMSDK organization
973        if (coexistMode
974                && serviceName
975                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
976            amsdk.removeAttribute(attrName);
977        }
978    }
979
980    /**
981     * Removes the given organization creation attribute
982     * values for the service.
983     * 
984     * @param serviceName
985     *            name of service.
986     * @param attrName
987     *            name of attribute.
988     * @param values
989     *            attribute values to be removed.
990     * @throws SMSException
991     *             if the organization attribute for the service to be removed
992     *             cannot be found, or if the service name cannot be found, or
993     *             if the value cannot be removed.
994     */
995    public void removeAttributeValues(String serviceName, String attrName,
996            Set values) throws SMSException {
997        validateConfigImpl();
998        if (serviceName == null || attrName == null) {
999            return;
1000        }
1001        if (migratedTo70) {
1002            try {
1003                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
1004                        orgDN);
1005                if (cEntry.isDirty()) {
1006                    cEntry.refresh();
1007                }
1008                SMSEntry e = cEntry.getClonedSMSEntry();
1009                ServiceSchemaManager ssm = new ServiceSchemaManager(
1010                        serviceName, token);
1011                ServiceSchema ss = ssm.getOrganizationCreationSchema();
1012                Map map = new HashMap(2);
1013                map.put(attrName, values);
1014                ss.validateAttributes(map);
1015                SMSUtils.removeAttributeValues(e, serviceName.toLowerCase()
1016                        + "-" + attrName, values, ss
1017                        .getSearchableAttributeNames());
1018                e.save(token);
1019                cEntry.refresh(e);
1020
1021            } catch (SSOException ssoe) {
1022                SMSEntry.debug.error("OrganizationConfigManager: Unable "
1023                        + "to remove Attribute Values", ssoe);
1024                throw (new SMSException(SMSEntry.bundle
1025                        .getString("sms-INVALID_SSO_TOKEN"),
1026                        "sms-INVALID_SSO_TOKEN"));
1027            }
1028        }
1029
1030        // If in coexistMode and serviceName is idRepoService
1031        // remove the attributes to AMSDK organization
1032        if (coexistMode
1033                && serviceName
1034                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
1035            amsdk.removeAttributeValues(attrName, values);
1036        }
1037    }
1038
1039    /**
1040     * Returns the service configuration object for the
1041     * given service name.
1042     * 
1043     * @param serviceName
1044     *            name of a service.
1045     * @return service configuration object for the service.
1046     * @throws SMSException
1047     *             if there is an error accessing the data store to read the
1048     *             service configuration, or if the service name cannot be
1049     *             found.
1050     */
1051    public ServiceConfig getServiceConfig(String serviceName)
1052            throws SMSException {
1053        try {
1054            ServiceConfigManager scmgr = new ServiceConfigManager(serviceName,
1055                    token);
1056            ServiceConfig scg = scmgr.getOrganizationConfig(orgName, null);
1057            return (scg);
1058        } catch (SSOException ssoe) {
1059            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1060                    + "get Service Config", ssoe);
1061            throw (new SMSException(SMSEntry.bundle
1062                    .getString("sms-INVALID_SSO_TOKEN"),
1063                    "sms-INVALID_SSO_TOKEN"));
1064        }
1065    }
1066
1067    /**
1068     * Adds a service configuration object for the given
1069     * service name for this organization. If the service has been already added
1070     * a <code>SMSException</code> will be thrown.
1071     * 
1072     * @param serviceName
1073     *            name of the service.
1074     * @param attributes
1075     *            service configuration attributes.
1076     * @return service configuration object.
1077     * @throws SMSException
1078     *             if the service configuration has been added already.
1079     */
1080    public ServiceConfig addServiceConfig(String serviceName, Map attributes)
1081            throws SMSException {
1082        try {
1083            ServiceConfigManagerImpl scmi = ServiceConfigManagerImpl
1084                    .getInstance(token, serviceName, 
1085                    ServiceManager.getVersion(serviceName));
1086            ServiceConfigImpl sci = scmi.getOrganizationConfig(token, orgName,
1087                    null);
1088            if (sci == null || sci.isNewEntry()) {
1089                ServiceConfigManager scm = new ServiceConfigManager(
1090                        serviceName, token);
1091                return (scm.createOrganizationConfig(orgName, attributes));
1092            } else {
1093                SMSEntry.debug.error("OrganizationConfigManager: "
1094                        + "ServiceConfig already exists: " + sci.getDN());
1095                throw (new SMSException(SMSEntry.bundle
1096                        .getString("sms-service_already_exists1")));
1097            }
1098        } catch (SSOException ssoe) {
1099            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1100                    + "add Service Config", ssoe);
1101            throw (new SMSException(SMSEntry.bundle
1102                    .getString("sms-INVALID_SSO_TOKEN"),
1103                    "sms-INVALID_SSO_TOKEN"));
1104        }
1105    }
1106
1107    /**
1108     * Removes the service configuration object for the
1109     * given service name for this organization.
1110     * 
1111     * @param serviceName
1112     *            name of the service.
1113     * @throws SMSException
1114     *             if the service name cannot be found, or not added to the
1115     *             organization.
1116     */
1117    public void removeServiceConfig(String serviceName) throws SMSException {
1118        try {
1119            ServiceConfigManager scm = new ServiceConfigManager(serviceName,
1120                    token);
1121            scm.deleteOrganizationConfig(orgName);
1122        } catch (SSOException ssoe) {
1123            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1124                    + "delete Service Config", ssoe);
1125            throw (new SMSException(SMSEntry.bundle
1126                    .getString("sms-INVALID_SSO_TOKEN"),
1127                    "sms-INVALID_SSO_TOKEN"));
1128        }
1129    }
1130
1131    /**
1132     * Registers for changes to organization's
1133     * configuration. The object will be called when configuration for this
1134     * organization is changed.
1135     * 
1136     * @param listener
1137     *            callback object that will be invoked when organization
1138     *            configuration has changed
1139     * @return an ID of the registered listener.
1140     */
1141    public String addListener(ServiceListener listener) {
1142        return (orgConfigImpl.addListener(listener));
1143    }
1144
1145    /**
1146     * Removes the listener from the organization for the
1147     * given listener ID. The ID was issued when the listener was registered.
1148     * 
1149     * @param listenerID
1150     *            the listener ID issued when the listener was registered
1151     */
1152    public void removeListener(String listenerID) {
1153        orgConfigImpl.removeListener(listenerID);
1154    }
1155
1156    /**
1157     * Returns normalized DN for realm model
1158     */
1159    private static String normalizeDN(String subOrgName, String orgDN) {
1160        // Return orgDN if subOrgName is either null or empty
1161        if (subOrgName == null || subOrgName.length() == 0) {
1162            return (orgDN);
1163        }
1164        if (SMSEntry.debug.messageEnabled()) {
1165            SMSEntry.debug.message("OrganizationConfigManager."
1166                    + "normalizeDN()-subOrgName " + subOrgName);
1167        }
1168        String subOrgDN = null;
1169        if (DN.isDN(subOrgName) && (!subOrgName.startsWith("///"))) {
1170            int ndx = subOrgName.lastIndexOf(DNMapper.serviceDN);
1171            if (ndx == -1) {
1172                // Check for baseDN
1173                ndx = subOrgName.lastIndexOf(SMSEntry.getRootSuffix());
1174            }
1175            if (ndx > 0) {
1176                subOrgName = subOrgName.substring(0, ndx - 1);
1177            }
1178            subOrgDN = DNMapper.normalizeDN(subOrgName) + orgDN;
1179        } else if (subOrgName.indexOf('/') != -1) {
1180            String tmp = DNMapper.convertToDN(subOrgName).toString();
1181            if (SMSEntry.debug.messageEnabled()) {
1182                SMSEntry.debug.message("OrganizationConfigManager."
1183                        + "normalizeDN()-slashConvertedString: " + tmp);
1184            }
1185            if (tmp != null && tmp.length() > 0) {
1186                if (tmp.charAt(tmp.length() - 1) == ',') {
1187                    subOrgDN = tmp + DNMapper.serviceDN;
1188                } else {
1189                    int dx = tmp.indexOf(SMSEntry.COMMA);
1190                    if (dx >= 0) {
1191                        subOrgDN = tmp + SMSEntry.COMMA + DNMapper.serviceDN;
1192                    } else {
1193                        subOrgDN = tmp + SMSEntry.COMMA + orgDN;
1194                    }
1195                }
1196            } else {
1197                subOrgDN = orgDN;
1198            }
1199        } else if (subOrgName.startsWith(SMSEntry.SUN_INTERNAL_REALM_NAME)) {
1200            subOrgDN = SMSEntry.ORG_PLACEHOLDER_RDN + subOrgName
1201                    + SMSEntry.COMMA + DNMapper.serviceDN;
1202        } else {
1203            if (coexistMode) {
1204                subOrgDN = orgNamingAttrInLegacyMode + SMSEntry.EQUALS
1205                        + subOrgName + SMSEntry.COMMA
1206                        + DNMapper.realmNameToAMSDKName(orgDN);
1207            } else {
1208                subOrgDN = SMSEntry.ORG_PLACEHOLDER_RDN + subOrgName
1209                        + SMSEntry.COMMA + orgDN;
1210            }
1211        }
1212        if (SMSEntry.debug.messageEnabled()) {
1213            SMSEntry.debug.message("OrganizationConfigManager::"
1214                    + "normalizeDN() suborgdn " + subOrgDN);
1215        }
1216        return (subOrgDN);
1217    }
1218
1219    /**
1220     * Returns all service names configured for AM
1221     */
1222    static Set getServiceNames(SSOToken token) throws SMSException,
1223            SSOException {
1224        // Get the service names from ServiceManager
1225        CachedSubEntries cse = CachedSubEntries.getInstance(token,
1226                DNMapper.serviceDN);
1227        return (cse.getSubEntries(token));
1228    }
1229
1230    /**
1231     * Returns a set of service names that can be assigned
1232     * to a realm. This set excludes name of services that are already assigned
1233     * to the realm and services that are required for the existence of a realm.
1234     * 
1235     * @return a set of service names that can be assigned to a realm.
1236     * @throws SMSException
1237     *             if there is an error accessing the data store to read the
1238     *             service configuration
1239     */
1240    public Set getAssignableServices() throws SMSException {
1241        // Get all service names, and remove the assigned services
1242        // Set containing service names that has organization schema
1243        Set orgSchemaServiceNames = new HashSet();
1244        try {
1245            for (Iterator names = getServiceNames(token).iterator(); names
1246                    .hasNext();) {
1247                String serviceName = (String) names.next();
1248                ServiceSchemaManagerImpl ssmi = ServiceSchemaManagerImpl
1249                        .getInstance(token, serviceName, 
1250                        ServiceManager.getVersion(serviceName));
1251                if (ssmi.getSchema(SchemaType.ORGANIZATION) != null) {
1252                    // Need to check if the user has permission
1253                    // to add/assign the service
1254                    StringBuilder d = new StringBuilder(100);
1255                    // Need to construct
1256                    // "ou=default,ou=organizationconfig,ou=1.0,ou="
1257                    d.append(SMSEntry.PLACEHOLDER_RDN).append(SMSEntry.EQUALS)
1258                            .append(SMSUtils.DEFAULT).append(SMSEntry.COMMA)
1259                            .append(CreateServiceConfig.ORG_CONFIG_NODE)
1260                            .append(SMSEntry.PLACEHOLDER_RDN).append(
1261                                    SMSEntry.EQUALS).append("1.0").append(
1262                                    SMSEntry.COMMA).append(
1263                                    SMSEntry.PLACEHOLDER_RDN).append(
1264                                    SMSEntry.EQUALS);
1265                    // Append service name, and org name
1266                    d.append(serviceName);
1267                    if (!orgDN.equalsIgnoreCase(DNMapper.serviceDN)) {
1268                        d.append(SMSEntry.COMMA).append(SMSEntry.SERVICES_NODE);
1269                    }
1270                    d.append(SMSEntry.COMMA).append(orgDN);
1271                    try {
1272                        // The function will throw exception if
1273                        // user does not have permissions
1274                        SMSEntry.getDelegationPermission(token, d.toString(),
1275                                SMSEntry.modifyActionSet);
1276                        orgSchemaServiceNames.add(serviceName);
1277                    } catch (SMSException smse) {
1278                        if (smse.getExceptionCode() != 
1279                            SMSException.STATUS_NO_PERMISSION) 
1280                        {
1281                            throw (smse);
1282                        }
1283                    }
1284                }
1285            }
1286            // Need to remove mandatory services
1287            // %%% TODO. Need to have SMS Service with this information
1288            // orgSchemaServiceNames.removeAll(getMandatoryServices());
1289        } catch (SSOException ssoe) {
1290            SMSEntry.debug.error("OrganizationConfigManager."
1291                    + "getAssignableServices(): SSOException", ssoe);
1292            throw (new SMSException(SMSEntry.bundle
1293                    .getString("sms-INVALID_SSO_TOKEN"),
1294                    "sms-INVALID_SSO_TOKEN"));
1295        }
1296        // Remove assigned services
1297        HashSet answer = new HashSet(orgSchemaServiceNames);
1298        answer.removeAll(getAssignedServices());
1299        return (answer);
1300    }
1301
1302    /**
1303     * Returns a set of service names that are assigned to
1304     * a realm.
1305     * 
1306     * @return a set of service names that are assigned to a realm.
1307     * @throws SMSException
1308     *             if there is an error accessing the data store to read the
1309     *             service configuration
1310     */
1311    public Set getAssignedServices() throws SMSException {
1312        return (getAssignedServices(true));
1313    }
1314
1315    /**
1316     * Returns a set of service names that are assigned to a realm.
1317     * 
1318     * @param includeMandatory
1319     *            <code>true</code> to include mandatory service names.
1320     * @return a set of service names that are assigned to a realm.
1321     * @throws SMSException
1322     *             if there is an error accessing the data store to read the
1323     *             service configuration
1324     */
1325    public Set getAssignedServices(boolean includeMandatory)
1326            throws SMSException {
1327        validateConfigImpl();
1328        Set assignedServices = Collections.EMPTY_SET;
1329        if (coexistMode) {
1330            // Get assigned services from OrgConfigViaAMSDK
1331            assignedServices = amsdk.getAssignedServices();
1332        } else {
1333            // Get assigned service names from OrganizationConfigManagerImpl
1334            assignedServices = orgConfigImpl.getAssignedServices(token);
1335        }
1336        if (!includeMandatory) {
1337            // Get services assigned by default
1338            Set ds = ServiceManager.requiredServices();
1339            assignedServices.removeAll(ds);
1340        }
1341        return (assignedServices);
1342    }
1343
1344    /**
1345     * Assigns the given service to the orgnization with
1346     * the respective attributes. If the service has been already added a <code>
1347     * SMSException</code>
1348     * will be thrown.
1349     * 
1350     * @param serviceName
1351     *            name of the service
1352     * @param attributes
1353     *            service configuration attributes
1354     * @throws SMSException
1355     *             if the service configuration has been added already.
1356     */
1357    public void assignService(String serviceName, Map attributes)
1358            throws SMSException {
1359        addServiceConfig(serviceName, attributes);
1360    }
1361
1362    /**
1363     * Returns attributes configured for the service.
1364     * 
1365     * @param serviceName
1366     *            name of the service
1367     * @return a map of attributes for the service
1368     * @throws SMSException
1369     *             if there is an error accessing the data store to read the
1370     *             service configuration, or if the service name cannot be
1371     *             found.
1372     */
1373    public Map getServiceAttributes(String serviceName) throws SMSException {
1374        ServiceConfig scg = getServiceConfig(serviceName);
1375        if (scg == null) {
1376            Object args[] = { serviceName };
1377            SMSEntry.debug.error(
1378                    "OrganizationConfigManager.getServiceAttributes() Unable " +
1379                    "to get service attributes. ");
1380            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
1381                    "sms-no-organization-schema",
1382                    args));
1383
1384        }
1385        return (scg.getAttributes());
1386    }
1387
1388    /**
1389     * Unassigns the service from the organization.
1390     * 
1391     * @param serviceName
1392     *            name of the service
1393     * @throws SMSException
1394     *             if the service name cannot be found or assigned, or if the
1395     *             service is a mandatory service.
1396     */
1397    public void unassignService(String serviceName) throws SMSException {
1398        // if (coexistMode) {
1399        // amsdk.unassignService(serviceName);
1400        // } else {
1401        removeServiceConfig(serviceName);
1402        // }
1403    }
1404
1405    /**
1406     * Sets the attributes related to provided service.
1407     * The assumption is that the service is already assigned to the
1408     * organization. The attributes for the service are validated against the
1409     * service schema.
1410     * 
1411     * @param serviceName
1412     *            name of the service
1413     * @param attributes
1414     *            attributes of the service
1415     * @throws SMSException
1416     *             if the service name cannot be found or not assigned to the
1417     *             organization.
1418     */
1419    public void modifyService(String serviceName, Map attributes)
1420            throws SMSException {
1421        try {
1422            getServiceConfig(serviceName).setAttributes(attributes);
1423        } catch (SSOException ssoe) {
1424            SMSEntry.debug.error("OrganizationConfigManager.modifyService "
1425                    + "SSOException in modify service ", ssoe);
1426            throw (new SMSException(SMSEntry.bundle
1427                    .getString("sms-INVALID_SSO_TOKEN"),
1428                    "sms-INVALID_SSO_TOKEN"));
1429        }
1430    }
1431
1432    public String getNamingAttrForOrg() {
1433        return OrgConfigViaAMSDK.getNamingAttrForOrg();
1434    }
1435
1436    /**
1437     * Returns the <code>OrganizationConfigManager</code>
1438     * of the parent for the given organization name.
1439     * 
1440     * @return the configuration manager of the parent for the given
1441     *         organization.
1442     * @throws SMSException
1443     *             if user doesn't have access to that organization.
1444     */
1445    public OrganizationConfigManager getParentOrgConfigManager()
1446            throws SMSException {
1447        OrganizationConfigManager ocm = null;
1448        String parentDN = null;
1449        if (DN.isDN(orgDN)) {
1450            if (orgDN.equalsIgnoreCase(DNMapper.serviceDN)) {
1451                return (this);
1452            }
1453            parentDN = (new DN(orgDN)).getParent().toString();
1454            if (SMSEntry.debug.messageEnabled()) {
1455                SMSEntry.debug.message("OrganizationConfigManager."
1456                        + "getParentOrgConfigManager() parentDN : " + parentDN);
1457            }
1458            if (parentDN != null && parentDN.length() > 0) {
1459                ocm = new OrganizationConfigManager(token, parentDN);
1460            }
1461        }
1462        return ocm;
1463    }
1464
1465    /**
1466     * Loads default services to a newly created realm
1467     */
1468    public static void loadDefaultServices(SSOToken token,
1469            OrganizationConfigManager ocm) throws SMSException {
1470        // Check if DIT has been migrated to 7.0
1471        if (!migratedTo70) {
1472            return;
1473        }
1474        Set defaultServices = ServiceManager.servicesAssignedByDefault();
1475        // Load the default services automatically
1476        OrganizationConfigManager parentOrg = ocm.getParentOrgConfigManager();
1477        if (defaultServices == null) {
1478            // There are no services to be loaded
1479            return;
1480        }
1481
1482        Set assignedServices = new CaseInsensitiveHashSet(
1483            parentOrg.getAssignedServices());
1484        if (SMSEntry.debug.messageEnabled()) {
1485            SMSEntry.debug.message("OrganizationConfigManager"
1486                    + "::loadDefaultServices " + "assignedServices : "
1487                    + assignedServices);
1488        }
1489        boolean doAuthServiceLater = false;
1490        boolean doAuthHttpBasicLater = false;
1491        String serviceName = null;
1492
1493        // Copy service configuration
1494        Iterator items = defaultServices.iterator();
1495        while (items.hasNext() || doAuthHttpBasicLater || doAuthServiceLater) {
1496            if (items.hasNext()) {
1497                serviceName = (String) items.next();
1498                if (serviceName.equals(ISAuthConstants.AUTH_SERVICE_NAME)) {
1499                    doAuthServiceLater = true;
1500                    continue;
1501                } else if (serviceName.equals(
1502                    ISAuthConstants.AUTH_HTTP_BASIC_SERVICE_NAME)) {
1503
1504                    doAuthHttpBasicLater = true;
1505                    continue;
1506                }
1507            } else if (doAuthHttpBasicLater) {
1508                serviceName = ISAuthConstants.AUTH_HTTP_BASIC_SERVICE_NAME;
1509                doAuthHttpBasicLater = false;
1510            } else if (doAuthServiceLater) {
1511                serviceName = ISAuthConstants.AUTH_SERVICE_NAME;
1512                doAuthServiceLater = false;
1513            }
1514            if (SMSEntry.debug.messageEnabled()) {
1515                SMSEntry.debug.message("OrganizationConfigManager" +
1516                    "::loadDefaultServices:ServiceName " + serviceName);
1517            }
1518            try {
1519                ServiceConfig sc = parentOrg.getServiceConfig(serviceName);
1520                Map attrs = null;
1521                if (sc != null && assignedServices.contains(serviceName)) {
1522                    attrs = sc.getAttributesWithoutDefaults();
1523                    if (SMSEntry.debug.messageEnabled()) {
1524                        SMSEntry.debug
1525                                .message("OrganizationConfigManager"
1526                                        + "::loadDefaultServices "
1527                                        + "Copying service from parent: "
1528                                        + serviceName);
1529                    }
1530                    ServiceConfig scn = ocm
1531                            .addServiceConfig(serviceName, attrs);
1532                    // Copy sub-configurations, if any
1533                    copySubConfig(sc, scn);
1534                }
1535            } catch (SSOException ssoe) {
1536                if (SMSEntry.debug.messageEnabled()) {
1537                    SMSEntry.debug.message(
1538                            "OrganizationConfigManager.loadDefaultServices " +
1539                            "SSOException in loading default services ",
1540                                    ssoe);
1541                }
1542                throw (new SMSException(SMSEntry.bundle
1543                        .getString("sms-INVALID_SSO_TOKEN"),
1544                        "sms-INVALID_SSO_TOKEN"));
1545            }
1546        }
1547    }
1548
1549
1550    /**
1551     * Registers default services to newly created suborganizations.
1552     */
1553    private void registerSvcsForOrg(String subOrgName, String subOrgDN)
1554    {
1555        try {
1556            Set defaultServices =
1557                ServiceManager.servicesAssignedByDefault();
1558            if (SMSEntry.debug.messageEnabled()) {
1559                SMSEntry.debug.message("OrganizationConfigManager::"+
1560                    "registerSvcsForOrg. "+
1561                    "defaultServices : " + defaultServices);
1562            }
1563
1564            // Register the default services to the newly created orgs,so
1565            // they will be marked with the OC sunRegisteredServiceName.
1566            if (defaultServices != null) {
1567                Set assignedServices = amsdk.getAssignedServices();
1568                if (SMSEntry.debug.messageEnabled()) {
1569                    SMSEntry.debug.message("OrganizationConfigManager::" +
1570                        "registerSvcsForOrg:assignedServices: " +
1571                            assignedServices);
1572                }
1573                Iterator items = defaultServices.iterator();
1574                String serviceName = null;
1575                if (SMSEntry.getRootSuffix().equalsIgnoreCase(
1576                    SMSEntry.getAMSdkBaseDN())) {
1577                    amsdk = new OrgConfigViaAMSDK(token,
1578                      orgNamingAttrInLegacyMode + SMSEntry.EQUALS +
1579                        subOrgName + SMSEntry.COMMA +
1580                        DNMapper.realmNameToAMSDKName(orgDN), subOrgDN);
1581                } else {
1582                    amsdk = new OrgConfigViaAMSDK(token,
1583                      orgNamingAttrInLegacyMode + SMSEntry.EQUALS +
1584                        subOrgName + SMSEntry.COMMA + amSDKOrgDN, subOrgDN);
1585                }
1586                while (items.hasNext()) {
1587                    serviceName = (String) items.next();
1588                    if (assignedServices.contains(serviceName)) {
1589                        if (SMSEntry.debug.messageEnabled()) {
1590                            SMSEntry.debug.message(
1591                                "OrganizationConfigManager::"+
1592                                "registerSvcsForOrg:ServiceName : " +
1593                                serviceName);
1594                        }
1595                        amsdk.assignService(serviceName);
1596                    }
1597                }
1598            }
1599        } catch (SMSException smse) {
1600            // Unable to load default services
1601            if (SMSEntry.debug.warningEnabled()) {
1602                SMSEntry.debug.warning("OrganizationConfigManager::" +
1603                    "registerSvcsForOrg. " +
1604                    "SMSException in registering services: ", smse);
1605            }
1606        }
1607    }
1608
1609    /**
1610     * Copies service configurations recursively from source to destination
1611     */
1612    static void copySubConfig(ServiceConfig from, ServiceConfig to)
1613            throws SMSException, SSOException {
1614        Set subConfigNames = from.getSubConfigNames();
1615        for (Iterator items = subConfigNames.iterator(); items.hasNext();) {
1616            String subConfigName = (String) items.next();
1617            ServiceConfig scf = from.getSubConfig(subConfigName);
1618            to.addSubConfig(subConfigName, scf.getSchemaID(),
1619                    scf.getPriority(), scf.getAttributesWithoutDefaults());
1620            ServiceConfig sct = to.getSubConfig(subConfigName);
1621            copySubConfig(scf, sct);
1622        }
1623    }
1624
1625
1626    /**
1627     * Determines whether an organization ought to be created for each
1628     * realm in realm only mode of installation based on the boolean flag
1629     * in amSDK plugin.
1630     * This requirement is for portal customers.
1631     */
1632    protected boolean isCopyOrgEnabled() {
1633        if (copyOrgInitialized) {
1634            return (copyOrgEnabled);
1635        }
1636        if (SMSEntry.debug.messageEnabled()) {
1637            SMSEntry.debug.message("OrganizationConfigManager: "+
1638                "in isCopyOrgEnabled() ");
1639        }
1640        // Check if AMSDK is configured for the realm
1641        try {
1642            ServiceConfig s = getServiceConfig(ServiceManager.REALM_SERVICE);
1643            if (s != null) {
1644                Iterator items = s.getSubConfigNames().iterator();
1645                while (items.hasNext()) {
1646                    String name = items.next().toString();
1647                    ServiceConfig subConfig = s.getSubConfig(name);
1648                    if (subConfig == null) {
1649                        SMSEntry.debug.error("OrganizationConfigManager.is" +
1650                            "CopyOrgEnabled. SubConfig is NULL: " +
1651                            "SC Name: " + name + " For org: " + orgDN);
1652                        return (false);
1653                    }
1654                    if (subConfig.getSchemaID().equalsIgnoreCase(
1655                        IdConstants.AMSDK_PLUGIN_NAME)) {
1656                        Map configMap = subConfig.getAttributes();
1657                        if ((configMap != null) && !configMap.isEmpty()) {
1658                            // Get the amsdkOrgName from the amSDKRepo to build
1659                            // OrgConfigViaSDK instance.
1660                            Set orgs = (Set) configMap.get("amSDKOrgName");
1661                            if (orgs != null && !orgs.isEmpty()) {
1662                                amSDKOrgDN = (String) orgs.iterator().next();
1663                                Set cfgs = (Set) configMap.get(CONF_ENABLED);
1664                                if ( (cfgs != null) && (!cfgs.isEmpty()) &&
1665                                    (cfgs.contains("true")) &&
1666                                        (amSDKOrgDN !=null) ) {
1667                                    amsdk = new OrgConfigViaAMSDK(token,
1668                                        amSDKOrgDN, orgDN);
1669                                    if (orgNamingAttrInLegacyMode == null) {
1670                                        orgNamingAttrInLegacyMode =
1671                                        getNamingAttrForOrg();
1672                                    }
1673                                    copyOrgEnabled = true;
1674                                }
1675                                break;
1676                            }
1677                        }
1678                    }
1679                }
1680            }
1681        } catch (SSOException sse) {
1682            // Use default values i.e., false
1683            if (SMSEntry.debug.messageEnabled()) {
1684                SMSEntry.debug.message("OrganizationConfigManager:" +
1685                    "isCopyOrgEnabled() Unable to get service: " +
1686                    ServiceManager.REALM_SERVICE, sse);
1687            }
1688        } catch (SMSException e) {
1689            // Use default values i.e., false
1690            if (SMSEntry.debug.messageEnabled()) {
1691                SMSEntry.debug.message("OrganizationConfigManager:" +
1692                    "isCopyOrgEnabled() Unable to get service: " +
1693                    ServiceManager.REALM_SERVICE, e);
1694            }
1695        }
1696        copyOrgInitialized = true;
1697        if (SMSEntry.debug.messageEnabled()) {
1698            SMSEntry.debug.message("OrganizationConfigManager: "+
1699                "copyOrgEnabled == " + copyOrgEnabled);
1700        }
1701        return (copyOrgEnabled);
1702    }
1703
1704    static void initializeFlags() {
1705        realmEnabled = ServiceManager.isRealmEnabled();
1706        coexistMode = ServiceManager.isCoexistenceMode();
1707        migratedTo70 = ServiceManager.isConfigMigratedTo70();
1708    }
1709    
1710    void validateConfigImpl() throws SMSException {
1711        // Instantiate the OrgConfigImpl and cache it
1712        if ((orgConfigImpl == null) || !orgConfigImpl.isValid()) {
1713            try {
1714                orgConfigImpl = OrganizationConfigManagerImpl.getInstance(
1715                    token, orgName);
1716            } catch (SSOException ssoe) {
1717                throw (new SMSException(ssoe, "sms-INVALID_SSO_TOKEN"));
1718            }
1719        }
1720    }
1721
1722    class OrganizationConfigManagerListener implements ServiceListener {
1723        public void schemaChanged(String serviceName, String version) {
1724            // Call ServiceManager to notify
1725            ServiceManager.schemaChanged();
1726            // If naming service has changed, reload the AM Servers
1727            if (serviceName.equalsIgnoreCase(ServiceManager.PLATFORM_SERVICE)) {
1728                ServiceManager.accessManagerServers = null;
1729            }
1730        }
1731
1732        public void globalConfigChanged(String serviceName, String version,
1733                String groupName, String serviceComponent, int type) {
1734            if (serviceName.equalsIgnoreCase(ServiceManager.REALM_SERVICE)) {
1735                try {
1736                    ServiceManager.checkFlags(token);
1737                } catch (SSOException ssoe) {
1738                    SMSEntry.debug.error("OrganizationConfigManager: "
1739                            + "globalConfigChanged ", ssoe);
1740                } catch (SMSException smse) {
1741                    SMSEntry.debug.error("OrganizationConfigManager: "
1742                            + "globalConfigChanged ", smse);
1743                }
1744                realmEnabled = ServiceManager.isRealmEnabled();
1745                coexistMode = ServiceManager.isCoexistenceMode();
1746                migratedTo70 = ServiceManager.isConfigMigratedTo70();
1747            }
1748        }
1749
1750        public void organizationConfigChanged(String serviceName,
1751                String version, String orgName, String groupName,
1752                String serviceComponent, int type) {
1753            // Reset the cached configuration in OrgConfigViaAMSDK
1754            if (serviceName.equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE))
1755            {
1756                OrgConfigViaAMSDK.attributeMappings = new HashMap();
1757                OrgConfigViaAMSDK.reverseAttributeMappings = new HashMap();
1758            }
1759        }
1760    }
1761
1762    // ******* Static Variables ************
1763    // To determine if notification object has been registered for config
1764    // changes
1765    private static boolean registeredForConfigNotifications;
1766
1767    // Realm & Co-existence modes
1768    private static boolean realmEnabled;
1769
1770    private static boolean coexistMode;
1771
1772    private static boolean migratedTo70;
1773}