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