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        if (realmEnabled) {
603            try {
604                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
605                        subOrgDN);
606                if (cEntry.isDirty()) {
607                    cEntry.refresh();
608                }
609                SMSEntry entry = cEntry.getClonedSMSEntry();
610                if (!recursive) {
611                    // Check if there are sub organization entries
612                    // and if exist
613                    // throw exception that this sub organization cannot be
614                    // deleted.
615                    if ((subRlmSet !=null) && (!subRlmSet.isEmpty())) {
616                        throw (new SMSException(SMSEntry.bundle
617                                .getString("sms-entries-exists"),
618                                "sms-entries-exists"));
619                    }
620                }
621                // Obtain the SMSEntry for the suborg and
622                // sub tree and delete it.
623                entry.delete(token);
624                cEntry.refresh(entry);
625            } catch (SSOException ssoe) {
626                SMSEntry.debug.error(
627                        "OrganizationConfigManager: deleteSubOrganization(" +
628                        "String subOrgName, boolean recursive) Unable to " +
629                        "delete sub organization ", ssoe);
630                throw (new SMSException(SMSEntry.bundle
631                        .getString(SMS_INVALID_SSO_TOKEN),
632                        SMS_INVALID_SSO_TOKEN));
633            }
634        }
635
636        // If in legacy mode or (realm mode and copy org enabled)
637        // delete the corresponding organization.
638        if ((coexistMode) || (realmEnabled && isCopyOrgEnabled())) {
639            String amsdkName = DNMapper.realmNameToAMSDKName(subOrgDN);
640            if (!SMSEntry.getRootSuffix().equalsIgnoreCase(
641                SMSEntry.getAMSdkBaseDN())) {
642                String convOrg = subOrgName;
643                if (subOrgName.startsWith("/")) {
644                    convOrg = DNMapper.convertToDN(subOrgName).toString();
645                }
646                amsdkName = convOrg + SMSEntry.COMMA + amSDKOrgDN;
647            }
648            amsdk.deleteSubOrganization(amsdkName);
649        }
650    }
651
652    /**
653     * Returns the <code>OrganizationConfigManager</code>
654     * for the given organization name.
655     * 
656     * @param subOrgName
657     *            the name of the organization.
658     * @return the configuration manager for the given organization.
659     * 
660     * @throws SMSException
661     *             if the organization name cannot be found or user doesn't have
662     *             access to that organization.
663     */
664    public OrganizationConfigManager getSubOrgConfigManager(String subOrgName)
665            throws SMSException {
666        validateConfigImpl();
667        // Normalize sub organization name
668        return (new OrganizationConfigManager(token, normalizeDN(subOrgName,
669                orgDN)));
670    }
671
672    /**
673     * Returns the organization creation attributes for
674     * the service.
675     * 
676     * @param serviceName
677     *            name of the service.
678     * @return map of organization creation attribute values for service
679     * @throws SMSException
680     *             if there is an error accessing the data store to read the
681     *             attributes of the service.
682     */
683    public Map getAttributes(String serviceName) throws SMSException {
684        validateConfigImpl();
685        if (serviceName == null) {
686            return (Collections.EMPTY_MAP);
687        }
688        Map attrValues = null;
689        // Attributes can be obtained only if DIT is migrated to AM 7.0
690        if (migratedTo70) {
691            // Lowercase the service name
692            serviceName = serviceName.toLowerCase();
693            try {
694                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
695                        orgDN);
696                if (cEntry.isDirty() || (coexistMode) ||
697                    (realmEnabled && isCopyOrgEnabled())) {
698                    // Since AMSDK org notifications will not be
699                    // obtained, the entry must be read again
700                    cEntry.refresh();
701                }
702                SMSEntry entry = cEntry.getSMSEntry();
703                Map map = SMSUtils.getAttrsFromEntry(entry);
704                if ((map != null) && (!map.isEmpty())) {
705                    Iterator itr = map.keySet().iterator();
706                    while (itr.hasNext()) {
707                        String name = (String) itr.next();
708                        if ((name.toLowerCase()).startsWith(serviceName)) {
709                            Set values = (Set) map.get(name);
710                            // Remove the serviceName and '-' and return only
711                            // the attribute name,value.
712                            String key;
713
714                            if (!serviceName.isEmpty()) {
715                                key = name.substring(serviceName.length() + 1);
716                            } else {
717                                key = name;
718                            }
719                            if (attrValues == null) {
720                                attrValues = new HashMap();
721                            }
722                            attrValues.put(key, values);
723                        }
724                    }
725                }
726            } catch (SSOException ssoe) {
727                SMSEntry.debug.error("OrganizationConfigManager: "
728                        + "getAttributes(String serviceName) Unable to "
729                        + "get Attributes", ssoe);
730                throw (new SMSException(SMSEntry.bundle
731                        .getString(SMS_INVALID_SSO_TOKEN),
732                        SMS_INVALID_SSO_TOKEN));
733            }
734        }
735
736        // If in coexistMode and serviceName is idRepoService
737        // get attributes from AMSDK organization
738        if ((coexistMode || (realmEnabled && isCopyOrgEnabled()))
739                && serviceName
740                    .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
741            Map amsdkMap = amsdk.getAttributes();
742            Map mergesdkMap = new HashMap(2);
743            if (amsdkMap != null && !amsdkMap.isEmpty()) {
744                Set mergeValues = new HashSet(2);
745                Iterator itr = amsdkMap.keySet().iterator();
746                while (itr.hasNext()) {
747                    String key = (String) itr.next();
748                    if (key.equalsIgnoreCase(SUNDNS_ALIAS) || 
749                        key.equalsIgnoreCase(SUNPREF_DOMAIN) ||
750                            key.equalsIgnoreCase(SUNORG_ALIAS)) {
751                        buildSet(key, amsdkMap, mergeValues);
752                    }
753                }
754                mergesdkMap.put(SUNORG_ALIAS, mergeValues);
755                mergesdkMap.put(SUNORG_STATUS,
756                    (Set) amsdkMap.get(SUNORG_STATUS));
757            }
758            if (attrValues == null) {
759                attrValues = mergesdkMap;
760            } else {
761                attrValues.putAll(mergesdkMap);
762            }
763        }
764        return ((attrValues == null) ? Collections.EMPTY_MAP : attrValues);
765    }
766
767    /**
768     * Builds and returns the appropriate Set for the attributes to be
769     * merged from org and realm if the system is
770     * in intrusive mode (Both org DIT and realm DIT are present).
771     * This happens when the Copy Config flag is enabled.
772     */
773    private Set buildSet(String attrName, Map attributes, Set resultSet) {
774        Set vals = (Set) attributes.get(attrName);
775        if ((vals != null) && !vals.isEmpty()) {
776            resultSet.addAll(vals);
777        }
778        return (resultSet);
779    }
780
781    /**
782     * Adds organization attributes for the service. If
783     * the attribute already exists, the values will be appended to it, provided
784     * it is a multi-valued attribute. It will throw exception if we try to add
785     * a value to an attribute which has the same value already.
786     * 
787     * @param serviceName
788     *            name of the service.
789     * @param attrName
790     *            name of the attribute.
791     * @param values
792     *            values for the attribute.
793     * @throws SMSException
794     *             if we try to add a value to an attribute which has the same
795     *             value already.
796     */
797    public void addAttributeValues(String serviceName, String attrName,
798            Set values) throws SMSException {
799        validateConfigImpl();
800        if (serviceName == null || attrName == null) {
801            return;
802        }
803
804        if (migratedTo70) {
805            // Lowercase the servicename
806            serviceName = serviceName.toLowerCase();
807            try {
808                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
809                        orgDN);
810                if (cEntry.isDirty()) {
811                    cEntry.refresh();
812                }
813                SMSEntry e = cEntry.getClonedSMSEntry();
814                ServiceSchemaManager ssm = new ServiceSchemaManager(
815                        serviceName, token);
816                ServiceSchema ss = ssm.getOrganizationCreationSchema();
817                if (ss == null) {
818                    throw (new SMSException(SMSEntry.bundle
819                            .getString("sms-SMSSchema_service_notfound"),
820                            "sms-SMSSchema_service_notfound"));
821                }
822
823                Map map = new HashMap(2);
824                Set newValues = new HashSet(values);
825                Map allAttributes = ss.getAttributeDefaults();
826                Set existingValues = (Set)allAttributes.get(attrName);
827                if ((existingValues != null) && !existingValues.isEmpty()) {
828                    newValues.addAll(existingValues);
829                }
830                map.put(attrName, newValues);
831                ss.validateAttributes(map);
832                SMSUtils.addAttribute(e, serviceName + "-" + attrName,
833                    values, ss.getSearchableAttributeNames());
834                e.save(token);
835                cEntry.refresh(e);
836            } catch (SSOException ssoe) {
837                SMSEntry.debug.error("OrganizationConfigManager: Unable "
838                        + "to add Attribute Values", ssoe);
839                throw (new SMSException(SMSEntry.bundle
840                        .getString(SMS_INVALID_SSO_TOKEN),
841                        SMS_INVALID_SSO_TOKEN));
842            }
843        }
844
845        // If in coexistMode and serviceName is idRepoService
846        // add the attributes to AMSDK organization
847        if (coexistMode
848                && serviceName
849                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
850            amsdk.addAttributeValues(attrName, values);
851        }
852    }
853
854    /**
855     * Sets/Creates organization attributes for the
856     * service. If the attributes already exists, the given attribute values
857     * will replace them.
858     * 
859     * @param serviceName
860     *            name of the service.
861     * @param attributes
862     *            attribute-values pairs.
863     * @throws SMSException
864     *             if the serviceName cannot be found.
865     */
866    public void setAttributes(String serviceName, Map attributes)
867            throws SMSException {
868        validateConfigImpl();
869        if (serviceName == null) {
870            return;
871        }
872
873        if (migratedTo70) {
874            // Lowercase the serviceName
875            serviceName = serviceName.toLowerCase();
876            try {
877                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
878                        orgDN);
879                if (cEntry.isDirty()) {
880                    cEntry.refresh();
881                }
882                SMSEntry e = cEntry.getClonedSMSEntry();
883                if ((attributes != null) && (!attributes.isEmpty())) {
884                    // Validate the attributes
885                    ServiceSchemaManager ssm = new ServiceSchemaManager(
886                            serviceName, token);
887                    ServiceSchema ss = ssm.getOrganizationCreationSchema();
888                    ss.validateAttributes(attributes);
889
890                    // Normalize the attributes with service name
891                    Map attrsMap = new HashMap();
892                    Iterator itr = attributes.keySet().iterator();
893                    while (itr.hasNext()) {
894                        String name = (String) itr.next();
895                        Set values = (Set) attributes.get(name);
896                        /*
897                         * To make the attributes qualified by service name we
898                         * prefix the attribute names with the service name.
899                         */
900                        attrsMap.put(serviceName + "-" + name, values);
901                    }
902
903                    // Look for old attrs. in the storage and add them too.
904                    Map oldAttrs = getAttributes(serviceName);
905                    Iterator it = oldAttrs.keySet().iterator();
906                    while (it.hasNext()) {
907                        String skey = (String) it.next();
908                        if (!attributes.containsKey(skey))
909                            attrsMap.put(serviceName + "-" + skey, oldAttrs
910                                    .get(skey));
911                    }
912
913                    // Set the attributes in SMSEntry
914                    SMSUtils.setAttributeValuePairs(e, attrsMap, ss
915                            .getSearchableAttributeNames());
916
917                    String dataStore = SMSEntry.getDataStore(token);
918                    // Add these OCs only for SunOne DS. Do not add the 
919                    // OCs for Active Directory.
920                    // Will get WILL_NOT_PERFORM in AD.
921                    if ((dataStore != null) && !dataStore.equals(
922                        SMSEntry.DATASTORE_ACTIVE_DIR)
923                    ) {
924                        // This is for storing organization attributes
925                        // in top/default realm node. eg.,ou=services,o=isp
926                        if (e.getDN().equalsIgnoreCase(SERVICES_NODE)) {
927                            String[] ocVals = e
928                                .getAttributeValues(SMSEntry.ATTR_OBJECTCLASS);
929                            boolean exists = false;
930                            for (int ic = 0; ocVals != null 
931                                && ic < ocVals.length; ic++) 
932                            {
933                                if (ocVals[ic].startsWith(
934                                    SMSEntry.OC_SERVICE_COMP)) {
935                                    // OC needs to be added outside the for loop
936                                    // else will throw concurrent mod exception
937                                    exists = true;
938                                    break;
939                                }
940                            }
941                            if (!exists) {
942                                e.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
943                                    SMSEntry.OC_SERVICE_COMP);
944                            }
945                        } else if (e.getDN().startsWith(
946                            SMSEntry.ORGANIZATION_RDN + SMSEntry.EQUALS)) {
947                            // This is for storing organization attributes in
948                            // organizations created via sdk through realm 
949                            // console.
950                            String[] vals = e
951                                .getAttributeValues(SMSEntry.ATTR_OBJECTCLASS);
952                            boolean rsvcExists = false;
953                            for (int n = 0; vals != null && n < vals.length; 
954                                n++) {
955                                if (vals[n].equalsIgnoreCase(
956                                    SMSEntry.OC_REALM_SERVICE)) 
957                                {
958                                    // OC needs to be added outside the for loop
959                                    // else will throw concurrent mod exception
960                                    rsvcExists = true;
961                                    break;
962                                }
963                            }
964                            if (!rsvcExists) {
965                                e.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
966                                    SMSEntry.OC_REALM_SERVICE);
967                            }
968                        }
969                    }
970
971                    // Save in backend data store and refresh the cache
972                    e.save(token);
973                    cEntry.refresh(e);
974                }
975
976            } catch (SSOException ssoe) {
977                SMSEntry.debug.error("OrganizationConfigManager: Unable "
978                        + "to set Attributes", ssoe);
979                throw (new SMSException(SMSEntry.bundle
980                        .getString(SMS_INVALID_SSO_TOKEN),
981                        SMS_INVALID_SSO_TOKEN));
982            }
983        }
984
985        // If in coexistMode and serviceName is idRepoService
986        // set the attributes to AMSDK organization
987        if ((coexistMode || (realmEnabled && isCopyOrgEnabled()))
988                && serviceName
989                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
990            amsdk.setAttributes(attributes);
991        }
992    }
993
994    /**
995     * Removes the given organization creation attribute
996     * for the service.
997     * 
998     * @param serviceName
999     *            name of service.
1000     * @param attrName
1001     *            name of attribute.
1002     * @throws SMSException
1003     *             if the organization attribute for the service to be removed
1004     *             cannot be found, or if the service name cannot be found.
1005     */
1006    public void removeAttribute(String serviceName, String attrName)
1007            throws SMSException {
1008        validateConfigImpl();
1009        if (serviceName == null || attrName == null) {
1010            return;
1011        }
1012
1013        if (migratedTo70) {
1014            try {
1015                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
1016                        orgDN);
1017                if (cEntry.isDirty()) {
1018                    cEntry.refresh();
1019                }
1020                SMSEntry e = cEntry.getClonedSMSEntry();
1021                SMSUtils.removeAttribute(e, serviceName.toLowerCase() + "-"
1022                        + attrName);
1023                e.save(token);
1024                cEntry.refresh(e);
1025            } catch (SSOException ssoe) {
1026                SMSEntry.debug.error("OrganizationConfigManager: Unable "
1027                        + "to remove Attribute", ssoe);
1028                throw (new SMSException(SMSEntry.bundle
1029                        .getString(SMS_INVALID_SSO_TOKEN),
1030                        SMS_INVALID_SSO_TOKEN));
1031            }
1032        }
1033
1034        // If in coexistMode and serviceName is idRepoService
1035        // remove the attributes to AMSDK organization
1036        if (coexistMode
1037                && serviceName
1038                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
1039            amsdk.removeAttribute(attrName);
1040        }
1041    }
1042
1043    /**
1044     * Removes the given organization creation attribute
1045     * values for the service.
1046     * 
1047     * @param serviceName
1048     *            name of service.
1049     * @param attrName
1050     *            name of attribute.
1051     * @param values
1052     *            attribute values to be removed.
1053     * @throws SMSException
1054     *             if the organization attribute for the service to be removed
1055     *             cannot be found, or if the service name cannot be found, or
1056     *             if the value cannot be removed.
1057     */
1058    public void removeAttributeValues(String serviceName, String attrName,
1059            Set values) throws SMSException {
1060        validateConfigImpl();
1061        if (serviceName == null || attrName == null) {
1062            return;
1063        }
1064        if (migratedTo70) {
1065            try {
1066                CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
1067                        orgDN);
1068                if (cEntry.isDirty()) {
1069                    cEntry.refresh();
1070                }
1071                SMSEntry e = cEntry.getClonedSMSEntry();
1072                ServiceSchemaManager ssm = new ServiceSchemaManager(
1073                        serviceName, token);
1074                ServiceSchema ss = ssm.getOrganizationCreationSchema();
1075                Map map = new HashMap(2);
1076                map.put(attrName, values);
1077                ss.validateAttributes(map);
1078                SMSUtils.removeAttributeValues(e, serviceName.toLowerCase()
1079                        + "-" + attrName, values, ss
1080                        .getSearchableAttributeNames());
1081                e.save(token);
1082                cEntry.refresh(e);
1083
1084            } catch (SSOException ssoe) {
1085                SMSEntry.debug.error("OrganizationConfigManager: Unable "
1086                        + "to remove Attribute Values", ssoe);
1087                throw (new SMSException(SMSEntry.bundle
1088                        .getString(SMS_INVALID_SSO_TOKEN),
1089                        SMS_INVALID_SSO_TOKEN));
1090            }
1091        }
1092
1093        // If in coexistMode and serviceName is idRepoService
1094        // remove the attributes to AMSDK organization
1095        if (coexistMode
1096                && serviceName
1097                        .equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE)) {
1098            amsdk.removeAttributeValues(attrName, values);
1099        }
1100    }
1101
1102    /**
1103     * Returns the service configuration object for the
1104     * given service name.
1105     * 
1106     * @param serviceName
1107     *            name of a service.
1108     * @return service configuration object for the service.
1109     * @throws SMSException
1110     *             if there is an error accessing the data store to read the
1111     *             service configuration, or if the service name cannot be
1112     *             found.
1113     */
1114    public ServiceConfig getServiceConfig(String serviceName)
1115            throws SMSException {
1116        try {
1117            ServiceConfigManager scmgr = new ServiceConfigManager(serviceName,
1118                    token);
1119            ServiceConfig scg = scmgr.getOrganizationConfig(orgName, null);
1120            return (scg);
1121        } catch (SSOException ssoe) {
1122            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1123                    + "get Service Config", ssoe);
1124            throw (new SMSException(SMSEntry.bundle
1125                    .getString(SMS_INVALID_SSO_TOKEN),
1126                    SMS_INVALID_SSO_TOKEN));
1127        }
1128    }
1129
1130    ServiceSchema getServiceSchema(String serviceName) throws SMSException {
1131        try {
1132            return new ServiceSchemaManager(serviceName, token).getOrganizationSchema();
1133        } catch (SSOException ssoe) {
1134            SMSEntry.debug.error("OrganizationConfigManager: Unable to get Service Schema", ssoe);
1135            throw new SMSException(SMSEntry.bundle.getString(SMS_INVALID_SSO_TOKEN), SMS_INVALID_SSO_TOKEN);
1136        }
1137    }
1138
1139    /**
1140     * Adds a service configuration object for the given
1141     * service name for this organization. If the service has been already added
1142     * a <code>SMSException</code> will be thrown.
1143     * 
1144     * @param serviceName
1145     *            name of the service.
1146     * @param attributes
1147     *            service configuration attributes.
1148     * @return service configuration object.
1149     * @throws SMSException
1150     *             if the service configuration has been added already.
1151     */
1152    public ServiceConfig addServiceConfig(String serviceName, Map attributes)
1153            throws SMSException {
1154        try {
1155            ServiceConfigManagerImpl scmi = ServiceConfigManagerImpl
1156                    .getInstance(token, serviceName, 
1157                    ServiceManager.getVersion(serviceName));
1158            ServiceConfigImpl sci = scmi.getOrganizationConfig(token, orgName,
1159                    null);
1160            if (sci == null || sci.isNewEntry()) {
1161                ServiceConfigManager scm = new ServiceConfigManager(
1162                        serviceName, token);
1163                return (scm.createOrganizationConfig(orgName, attributes));
1164            } else {
1165                SMSEntry.debug.error("OrganizationConfigManager: "
1166                        + "ServiceConfig already exists: " + sci.getDN());
1167                throw (new SMSException(SMSEntry.bundle
1168                        .getString("sms-service_already_exists1")));
1169            }
1170        } catch (SSOException ssoe) {
1171            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1172                    + "add Service Config", ssoe);
1173            throw (new SMSException(SMSEntry.bundle
1174                    .getString(SMS_INVALID_SSO_TOKEN),
1175                    SMS_INVALID_SSO_TOKEN));
1176        }
1177    }
1178
1179    /**
1180     * Removes the service configuration object for the
1181     * given service name for this organization.
1182     * 
1183     * @param serviceName
1184     *            name of the service.
1185     * @throws SMSException
1186     *             if the service name cannot be found, or not added to the
1187     *             organization.
1188     */
1189    public void removeServiceConfig(String serviceName) throws SMSException {
1190        try {
1191            ServiceConfigManager scm = new ServiceConfigManager(serviceName,
1192                    token);
1193            scm.deleteOrganizationConfig(orgName);
1194        } catch (SSOException ssoe) {
1195            SMSEntry.debug.error("OrganizationConfigManager: Unable to "
1196                    + "delete Service Config", ssoe);
1197            throw (new SMSException(SMSEntry.bundle
1198                    .getString(SMS_INVALID_SSO_TOKEN),
1199                    SMS_INVALID_SSO_TOKEN));
1200        }
1201    }
1202
1203    /**
1204     * Registers for changes to organization's
1205     * configuration. The object will be called when configuration for this
1206     * organization is changed.
1207     * 
1208     * @param listener
1209     *            callback object that will be invoked when organization
1210     *            configuration has changed
1211     * @return an ID of the registered listener.
1212     */
1213    public String addListener(ServiceListener listener) {
1214        return (orgConfigImpl.addListener(listener));
1215    }
1216
1217    /**
1218     * Removes the listener from the organization for the
1219     * given listener ID. The ID was issued when the listener was registered.
1220     * 
1221     * @param listenerID
1222     *            the listener ID issued when the listener was registered
1223     */
1224    public void removeListener(String listenerID) {
1225        orgConfigImpl.removeListener(listenerID);
1226    }
1227
1228    /**
1229     * Returns normalized DN for realm model
1230     */
1231    private static String normalizeDN(String subOrgName, String orgDN) {
1232        // Return orgDN if subOrgName is either null or empty
1233        if (subOrgName == null || subOrgName.length() == 0) {
1234            return (orgDN);
1235        }
1236        if (SMSEntry.debug.messageEnabled()) {
1237            SMSEntry.debug.message("OrganizationConfigManager."
1238                    + "normalizeDN()-subOrgName " + subOrgName);
1239        }
1240        String subOrgDN = null;
1241        if (LDAPUtils.isDN(subOrgName) && (!subOrgName.startsWith("///"))) {
1242            int ndx = subOrgName.lastIndexOf(DNMapper.serviceDN);
1243            if (ndx == -1) {
1244                // Check for baseDN
1245                ndx = subOrgName.lastIndexOf(SMSEntry.getRootSuffix());
1246            }
1247            if (ndx > 0) {
1248                subOrgName = subOrgName.substring(0, ndx - 1);
1249            }
1250            subOrgDN = DNMapper.normalizeDN(subOrgName) + orgDN;
1251        } else if (subOrgName.indexOf('/') != -1) {
1252            String tmp = DNMapper.convertToDN(subOrgName).toString();
1253            if (SMSEntry.debug.messageEnabled()) {
1254                SMSEntry.debug.message("OrganizationConfigManager."
1255                        + "normalizeDN()-slashConvertedString: " + tmp);
1256            }
1257            if (tmp != null && tmp.length() > 0) {
1258                if (tmp.charAt(tmp.length() - 1) == ',') {
1259                    subOrgDN = tmp + DNMapper.serviceDN;
1260                } else {
1261                    int dx = tmp.indexOf(SMSEntry.COMMA);
1262                    if (dx >= 0) {
1263                        subOrgDN = tmp + SMSEntry.COMMA + DNMapper.serviceDN;
1264                    } else {
1265                        subOrgDN = tmp + SMSEntry.COMMA + orgDN;
1266                    }
1267                }
1268            } else {
1269                subOrgDN = orgDN;
1270            }
1271        } else if (subOrgName.startsWith(SMSEntry.SUN_INTERNAL_REALM_NAME)) {
1272            subOrgDN = SMSEntry.ORG_PLACEHOLDER_RDN + subOrgName
1273                    + SMSEntry.COMMA + DNMapper.serviceDN;
1274        } else {
1275            if (coexistMode) {
1276                subOrgDN = orgNamingAttrInLegacyMode + SMSEntry.EQUALS
1277                        + subOrgName + SMSEntry.COMMA
1278                        + DNMapper.realmNameToAMSDKName(orgDN);
1279            } else {
1280                subOrgDN = SMSEntry.ORG_PLACEHOLDER_RDN + subOrgName
1281                        + SMSEntry.COMMA + orgDN;
1282            }
1283        }
1284        if (SMSEntry.debug.messageEnabled()) {
1285            SMSEntry.debug.message("OrganizationConfigManager::"
1286                    + "normalizeDN() suborgdn " + subOrgDN);
1287        }
1288        return (subOrgDN);
1289    }
1290
1291    /**
1292     * Returns all service names configured for AM
1293     */
1294    static Set getServiceNames(SSOToken token) throws SMSException,
1295            SSOException {
1296        // Get the service names from ServiceManager
1297        CachedSubEntries cse = CachedSubEntries.getInstance(token,
1298                DNMapper.serviceDN);
1299        return (cse.getSubEntries(token));
1300    }
1301
1302    /**
1303     * Returns a set of service names that can be assigned
1304     * to a realm. This set excludes name of services that are already assigned
1305     * to the realm and services that are required for the existence of a realm.
1306     * 
1307     * @return a set of service names that can be assigned to a realm.
1308     * @throws SMSException
1309     *             if there is an error accessing the data store to read the
1310     *             service configuration
1311     */
1312    public Set getAssignableServices() throws SMSException {
1313        // Get all service names, and remove the assigned services
1314        // Set containing service names that has organization schema
1315        Set orgSchemaServiceNames = new HashSet();
1316        try {
1317            for (Iterator names = getServiceNames(token).iterator(); names
1318                    .hasNext();) {
1319                String serviceName = (String) names.next();
1320                ServiceSchemaManagerImpl ssmi = ServiceSchemaManagerImpl
1321                        .getInstance(token, serviceName, 
1322                        ServiceManager.getVersion(serviceName));
1323                if (ssmi.getSchema(SchemaType.ORGANIZATION) != null) {
1324                    // Need to check if the user has permission
1325                    // to add/assign the service
1326                    StringBuilder d = new StringBuilder(100);
1327                    // Need to construct
1328                    // "ou=default,ou=organizationconfig,ou=1.0,ou="
1329                    d.append(SMSEntry.PLACEHOLDER_RDN).append(SMSEntry.EQUALS)
1330                            .append(SMSUtils.DEFAULT).append(SMSEntry.COMMA)
1331                            .append(CreateServiceConfig.ORG_CONFIG_NODE)
1332                            .append(SMSEntry.PLACEHOLDER_RDN).append(
1333                                    SMSEntry.EQUALS).append("1.0").append(
1334                                    SMSEntry.COMMA).append(
1335                                    SMSEntry.PLACEHOLDER_RDN).append(
1336                                    SMSEntry.EQUALS);
1337                    // Append service name, and org name
1338                    d.append(serviceName);
1339                    if (!orgDN.equalsIgnoreCase(DNMapper.serviceDN)) {
1340                        d.append(SMSEntry.COMMA).append(SMSEntry.SERVICES_RDN);
1341                    }
1342                    d.append(SMSEntry.COMMA).append(orgDN);
1343                    try {
1344                        // The function will throw exception if
1345                        // user does not have permissions
1346                        SMSEntry.getDelegationPermission(token, d.toString(),
1347                                SMSEntry.modifyActionSet);
1348                        orgSchemaServiceNames.add(serviceName);
1349                    } catch (SMSException smse) {
1350                        if (smse.getExceptionCode() != 
1351                            SMSException.STATUS_NO_PERMISSION) 
1352                        {
1353                            throw (smse);
1354                        }
1355                    }
1356                }
1357            }
1358            // Need to remove mandatory services
1359            // %%% TODO. Need to have SMS Service with this information
1360            // orgSchemaServiceNames.removeAll(getMandatoryServices());
1361        } catch (SSOException ssoe) {
1362            SMSEntry.debug.error("OrganizationConfigManager."
1363                    + "getAssignableServices(): SSOException", ssoe);
1364            throw (new SMSException(SMSEntry.bundle
1365                    .getString(SMS_INVALID_SSO_TOKEN),
1366                    SMS_INVALID_SSO_TOKEN));
1367        }
1368        // Remove assigned services
1369        HashSet answer = new HashSet(orgSchemaServiceNames);
1370        answer.removeAll(getAssignedServices());
1371        return (answer);
1372    }
1373
1374    /**
1375     * Returns a set of service names that are assigned to
1376     * a realm.
1377     * 
1378     * @return a set of service names that are assigned to a realm.
1379     * @throws SMSException
1380     *             if there is an error accessing the data store to read the
1381     *             service configuration
1382     */
1383    public Set<String> getAssignedServices() throws SMSException {
1384        return (getAssignedServices(true));
1385    }
1386
1387    /**
1388     * Returns a set of service names that are assigned to a realm.
1389     * 
1390     * @param includeMandatory
1391     *            <code>true</code> to include mandatory service names.
1392     * @return a set of service names that are assigned to a realm.
1393     * @throws SMSException
1394     *             if there is an error accessing the data store to read the
1395     *             service configuration
1396     */
1397    public Set<String> getAssignedServices(boolean includeMandatory)
1398            throws SMSException {
1399        validateConfigImpl();
1400        Set<String> assignedServices = Collections.EMPTY_SET;
1401        if (coexistMode) {
1402            // Get assigned services from OrgConfigViaAMSDK
1403            assignedServices = amsdk.getAssignedServices();
1404        } else {
1405            // Get assigned service names from OrganizationConfigManagerImpl
1406            assignedServices = orgConfigImpl.getAssignedServices(token);
1407        }
1408        if (!includeMandatory) {
1409            // Get services assigned by default
1410            Set ds = ServiceManager.requiredServices();
1411            assignedServices.removeAll(ds);
1412        }
1413        return (assignedServices);
1414    }
1415
1416    /**
1417     * Assigns the given service to the orgnization with
1418     * the respective attributes. If the service has been already added a <code>
1419     * SMSException</code>
1420     * will be thrown.
1421     * 
1422     * @param serviceName
1423     *            name of the service
1424     * @param attributes
1425     *            service configuration attributes
1426     * @throws SMSException
1427     *             if the service configuration has been added already.
1428     */
1429    public void assignService(String serviceName, Map attributes)
1430            throws SMSException {
1431        addServiceConfig(serviceName, attributes);
1432    }
1433
1434    /**
1435     * Returns attributes configured for the service.
1436     * 
1437     * @param serviceName
1438     *            name of the service
1439     * @return a map of attributes for the service
1440     * @throws SMSException
1441     *             if there is an error accessing the data store to read the
1442     *             service configuration, or if the service name cannot be
1443     *             found.
1444     */
1445    public Map getServiceAttributes(String serviceName) throws SMSException {
1446        ServiceConfig scg = getServiceConfig(serviceName);
1447        if (scg == null) {
1448            Object args[] = { serviceName };
1449            SMSEntry.debug.error(
1450                    "OrganizationConfigManager.getServiceAttributes() Unable " +
1451                    "to get service attributes. ");
1452            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
1453                    "sms-no-organization-schema",
1454                    args));
1455
1456        }
1457        return (scg.getAttributes());
1458    }
1459
1460    /**
1461     * Unassigns the service from the organization.
1462     * 
1463     * @param serviceName
1464     *            name of the service
1465     * @throws SMSException
1466     *             if the service name cannot be found or assigned, or if the
1467     *             service is a mandatory service.
1468     */
1469    public void unassignService(String serviceName) throws SMSException {
1470        // if (coexistMode) {
1471        // amsdk.unassignService(serviceName);
1472        // } else {
1473        removeServiceConfig(serviceName);
1474        // }
1475    }
1476
1477    /**
1478     * Sets the attributes related to provided service.
1479     * The assumption is that the service is already assigned to the
1480     * organization. The attributes for the service are validated against the
1481     * service schema.
1482     * 
1483     * @param serviceName
1484     *            name of the service
1485     * @param attributes
1486     *            attributes of the service
1487     * @throws SMSException
1488     *             if the service name cannot be found or not assigned to the
1489     *             organization.
1490     */
1491    public void modifyService(String serviceName, Map attributes)
1492            throws SMSException {
1493        try {
1494            getServiceConfig(serviceName).setAttributes(attributes);
1495        } catch (SSOException ssoe) {
1496            SMSEntry.debug.error("OrganizationConfigManager.modifyService "
1497                    + "SSOException in modify service ", ssoe);
1498            throw (new SMSException(SMSEntry.bundle
1499                    .getString(SMS_INVALID_SSO_TOKEN),
1500                    SMS_INVALID_SSO_TOKEN));
1501        }
1502    }
1503
1504    public String getNamingAttrForOrg() {
1505        return OrgConfigViaAMSDK.getNamingAttrForOrg();
1506    }
1507
1508    /**
1509     * Returns the <code>OrganizationConfigManager</code>
1510     * of the parent for the given organization name.
1511     * 
1512     * @return the configuration manager of the parent for the given
1513     *         organization.
1514     * @throws SMSException
1515     *             if user doesn't have access to that organization.
1516     */
1517    public OrganizationConfigManager getParentOrgConfigManager()
1518            throws SMSException {
1519        OrganizationConfigManager ocm = null;
1520        String parentDN = null;
1521        if (LDAPUtils.isDN(orgDN)) {
1522            if (orgDN.equalsIgnoreCase(DNMapper.serviceDN)) {
1523                return (this);
1524            }
1525            parentDN = DN.valueOf(orgDN).parent().toString();
1526            if (SMSEntry.debug.messageEnabled()) {
1527                SMSEntry.debug.message("OrganizationConfigManager."
1528                        + "getParentOrgConfigManager() parentDN : " + parentDN);
1529            }
1530            if (parentDN != null && parentDN.length() > 0) {
1531                ocm = new OrganizationConfigManager(token, parentDN);
1532            }
1533        }
1534        return ocm;
1535    }
1536
1537    /**
1538     * Loads default services to a newly created realm
1539     */
1540    public static void loadDefaultServices(SSOToken token,
1541            OrganizationConfigManager ocm) throws SMSException {
1542        // Check if DIT has been migrated to 7.0
1543        if (!migratedTo70) {
1544            return;
1545        }
1546        Set defaultServices = ServiceManager.servicesAssignedByDefault();
1547        // Load the default services automatically
1548        OrganizationConfigManager parentOrg = ocm.getParentOrgConfigManager();
1549        if (defaultServices == null) {
1550            // There are no services to be loaded
1551            return;
1552        }
1553
1554        Set assignedServices = new CaseInsensitiveHashSet(
1555            parentOrg.getAssignedServices());
1556        if (SMSEntry.debug.messageEnabled()) {
1557            SMSEntry.debug.message("OrganizationConfigManager"
1558                    + "::loadDefaultServices " + "assignedServices : "
1559                    + assignedServices);
1560        }
1561        boolean doAuthServiceLater = false;
1562        boolean doAuthHttpBasicLater = false;
1563        String serviceName = null;
1564
1565        // Copy service configuration
1566        Iterator items = defaultServices.iterator();
1567        while (items.hasNext() || doAuthHttpBasicLater || doAuthServiceLater) {
1568            if (items.hasNext()) {
1569                serviceName = (String) items.next();
1570                if (serviceName.equals(ISAuthConstants.AUTH_SERVICE_NAME)) {
1571                    doAuthServiceLater = true;
1572                    continue;
1573                } else if (serviceName.equals(
1574                    ISAuthConstants.AUTH_HTTP_BASIC_SERVICE_NAME)) {
1575
1576                    doAuthHttpBasicLater = true;
1577                    continue;
1578                }
1579            } else if (doAuthHttpBasicLater) {
1580                serviceName = ISAuthConstants.AUTH_HTTP_BASIC_SERVICE_NAME;
1581                doAuthHttpBasicLater = false;
1582            } else if (doAuthServiceLater) {
1583                serviceName = ISAuthConstants.AUTH_SERVICE_NAME;
1584                doAuthServiceLater = false;
1585            }
1586            if (SMSEntry.debug.messageEnabled()) {
1587                SMSEntry.debug.message("OrganizationConfigManager" +
1588                    "::loadDefaultServices:ServiceName " + serviceName);
1589            }
1590            try {
1591                ServiceConfig sc = parentOrg.getServiceConfig(serviceName);
1592                ServiceSchema ss = parentOrg.getServiceSchema(serviceName);
1593                Map attrs = null;
1594                if (sc != null && assignedServices.contains(serviceName)) {
1595                    attrs = sc.getAttributesWithoutDefaults();
1596                    if (SMSEntry.debug.messageEnabled()) {
1597                        SMSEntry.debug
1598                                .message("OrganizationConfigManager"
1599                                        + "::loadDefaultServices "
1600                                        + "Copying service from parent: "
1601                                        + serviceName);
1602                    }
1603                    ServiceConfig scn = ocm
1604                            .addServiceConfig(serviceName, attrs);
1605                    // Copy sub-configurations, if any
1606                    copySubConfig(sc, scn, ss);
1607                }
1608            } catch (SSOException ssoe) {
1609                if (SMSEntry.debug.messageEnabled()) {
1610                    SMSEntry.debug.message(
1611                            "OrganizationConfigManager.loadDefaultServices " +
1612                            "SSOException in loading default services ",
1613                                    ssoe);
1614                }
1615                throw (new SMSException(SMSEntry.bundle
1616                        .getString(SMS_INVALID_SSO_TOKEN),
1617                        SMS_INVALID_SSO_TOKEN));
1618            }
1619        }
1620    }
1621
1622
1623    /**
1624     * Registers default services to newly created suborganizations.
1625     */
1626    private void registerSvcsForOrg(String subOrgName, String subOrgDN)
1627    {
1628        try {
1629            Set defaultServices =
1630                ServiceManager.servicesAssignedByDefault();
1631            if (SMSEntry.debug.messageEnabled()) {
1632                SMSEntry.debug.message("OrganizationConfigManager::"+
1633                    "registerSvcsForOrg. "+
1634                    "defaultServices : " + defaultServices);
1635            }
1636
1637            // Register the default services to the newly created orgs,so
1638            // they will be marked with the OC sunRegisteredServiceName.
1639            if (defaultServices != null) {
1640                Set assignedServices = amsdk.getAssignedServices();
1641                if (SMSEntry.debug.messageEnabled()) {
1642                    SMSEntry.debug.message("OrganizationConfigManager::" +
1643                        "registerSvcsForOrg:assignedServices: " +
1644                            assignedServices);
1645                }
1646                Iterator items = defaultServices.iterator();
1647                String serviceName = null;
1648                if (SMSEntry.getRootSuffix().equalsIgnoreCase(
1649                    SMSEntry.getAMSdkBaseDN())) {
1650                    amsdk = new OrgConfigViaAMSDK(token,
1651                      orgNamingAttrInLegacyMode + SMSEntry.EQUALS +
1652                        subOrgName + SMSEntry.COMMA +
1653                        DNMapper.realmNameToAMSDKName(orgDN), subOrgDN);
1654                } else {
1655                    amsdk = new OrgConfigViaAMSDK(token,
1656                      orgNamingAttrInLegacyMode + SMSEntry.EQUALS +
1657                        subOrgName + SMSEntry.COMMA + amSDKOrgDN, subOrgDN);
1658                }
1659                while (items.hasNext()) {
1660                    serviceName = (String) items.next();
1661                    if (assignedServices.contains(serviceName)) {
1662                        if (SMSEntry.debug.messageEnabled()) {
1663                            SMSEntry.debug.message(
1664                                "OrganizationConfigManager::"+
1665                                "registerSvcsForOrg:ServiceName : " +
1666                                serviceName);
1667                        }
1668                        amsdk.assignService(serviceName);
1669                    }
1670                }
1671            }
1672        } catch (SMSException smse) {
1673            // Unable to load default services
1674            if (SMSEntry.debug.warningEnabled()) {
1675                SMSEntry.debug.warning("OrganizationConfigManager::" +
1676                    "registerSvcsForOrg. " +
1677                    "SMSException in registering services: ", smse);
1678            }
1679        }
1680    }
1681
1682    /**
1683     * Copies service configurations recursively from source to destination
1684     */
1685    static void copySubConfig(ServiceConfig from, ServiceConfig to, ServiceSchema serviceSchema)
1686            throws SMSException, SSOException {
1687        Set subConfigNames = from.getSubConfigNames();
1688        for (Iterator items = subConfigNames.iterator(); items.hasNext();) {
1689            String subConfigName = (String) items.next();
1690            ServiceConfig scf = from.getSubConfig(subConfigName);
1691            ServiceSchema subSchema = serviceSchema.getSubSchema(scf.getSchemaID());
1692            if (subSchema.isRealmCloneable()) {
1693                to.addSubConfig(subConfigName, scf.getSchemaID(), scf.getPriority(), scf.getAttributesWithoutDefaults());
1694                ServiceConfig sct = to.getSubConfig(subConfigName);
1695                copySubConfig(scf, sct, subSchema);
1696            }
1697        }
1698    }
1699
1700
1701    /**
1702     * Determines whether an organization ought to be created for each
1703     * realm in realm only mode of installation based on the boolean flag
1704     * in amSDK plugin.
1705     * This requirement is for portal customers.
1706     */
1707    protected boolean isCopyOrgEnabled() {
1708        if (copyOrgInitialized) {
1709            return (copyOrgEnabled);
1710        }
1711        if (SMSEntry.debug.messageEnabled()) {
1712            SMSEntry.debug.message("OrganizationConfigManager: "+
1713                "in isCopyOrgEnabled() ");
1714        }
1715        // Check if AMSDK is configured for the realm
1716        try {
1717            ServiceConfig s = getServiceConfig(ServiceManager.REALM_SERVICE);
1718            if (s != null) {
1719                Iterator items = s.getSubConfigNames().iterator();
1720                while (items.hasNext()) {
1721                    String name = items.next().toString();
1722                    ServiceConfig subConfig = s.getSubConfig(name);
1723                    if (subConfig == null) {
1724                        SMSEntry.debug.error("OrganizationConfigManager.is" +
1725                            "CopyOrgEnabled. SubConfig is NULL: " +
1726                            "SC Name: " + name + " For org: " + orgDN);
1727                        return (false);
1728                    }
1729                    if (subConfig.getSchemaID().equalsIgnoreCase(
1730                        IdConstants.AMSDK_PLUGIN_NAME)) {
1731                        Map configMap = subConfig.getAttributes();
1732                        if ((configMap != null) && !configMap.isEmpty()) {
1733                            // Get the amsdkOrgName from the amSDKRepo to build
1734                            // OrgConfigViaSDK instance.
1735                            Set orgs = (Set) configMap.get("amSDKOrgName");
1736                            if (orgs != null && !orgs.isEmpty()) {
1737                                amSDKOrgDN = (String) orgs.iterator().next();
1738                                Set cfgs = (Set) configMap.get(CONF_ENABLED);
1739                                if ( (cfgs != null) && (!cfgs.isEmpty()) &&
1740                                    (cfgs.contains("true")) &&
1741                                        (amSDKOrgDN !=null) ) {
1742                                    amsdk = new OrgConfigViaAMSDK(token,
1743                                        amSDKOrgDN, orgDN);
1744                                    if (orgNamingAttrInLegacyMode == null) {
1745                                        orgNamingAttrInLegacyMode =
1746                                        getNamingAttrForOrg();
1747                                    }
1748                                    copyOrgEnabled = true;
1749                                }
1750                                break;
1751                            }
1752                        }
1753                    }
1754                }
1755            }
1756        } catch (SSOException sse) {
1757            // Use default values i.e., false
1758            if (SMSEntry.debug.messageEnabled()) {
1759                SMSEntry.debug.message("OrganizationConfigManager:" +
1760                    "isCopyOrgEnabled() Unable to get service: " +
1761                    ServiceManager.REALM_SERVICE, sse);
1762            }
1763        } catch (SMSException e) {
1764            // Use default values i.e., false
1765            if (SMSEntry.debug.messageEnabled()) {
1766                SMSEntry.debug.message("OrganizationConfigManager:" +
1767                    "isCopyOrgEnabled() Unable to get service: " +
1768                    ServiceManager.REALM_SERVICE, e);
1769            }
1770        }
1771        copyOrgInitialized = true;
1772        if (SMSEntry.debug.messageEnabled()) {
1773            SMSEntry.debug.message("OrganizationConfigManager: "+
1774                "copyOrgEnabled == " + copyOrgEnabled);
1775        }
1776        return (copyOrgEnabled);
1777    }
1778
1779    static void initializeFlags() {
1780        realmEnabled = ServiceManager.isRealmEnabled();
1781        coexistMode = ServiceManager.isCoexistenceMode();
1782        migratedTo70 = ServiceManager.isConfigMigratedTo70();
1783    }
1784    
1785    void validateConfigImpl() throws SMSException {
1786        // Instantiate the OrgConfigImpl and cache it
1787        if ((orgConfigImpl == null) || !orgConfigImpl.isValid()) {
1788            try {
1789                orgConfigImpl = OrganizationConfigManagerImpl.getInstance(
1790                    token, orgName);
1791            } catch (SSOException ssoe) {
1792                throw (new SMSException(ssoe, SMS_INVALID_SSO_TOKEN));
1793            }
1794        }
1795    }
1796
1797    class OrganizationConfigManagerListener implements ServiceListener {
1798        public void schemaChanged(String serviceName, String version) {
1799            // Call ServiceManager to notify
1800            ServiceManager.schemaChanged();
1801            // If naming service has changed, reload the AM Servers
1802            if (serviceName.equalsIgnoreCase(ServiceManager.PLATFORM_SERVICE)) {
1803                ServiceManager.accessManagerServers = null;
1804            }
1805        }
1806
1807        public void globalConfigChanged(String serviceName, String version,
1808                String groupName, String serviceComponent, int type) {
1809            if (serviceName.equalsIgnoreCase(ServiceManager.REALM_SERVICE)) {
1810                try {
1811                    ServiceManager.checkFlags(token);
1812                } catch (SSOException ssoe) {
1813                    SMSEntry.debug.error("OrganizationConfigManager: "
1814                            + "globalConfigChanged ", ssoe);
1815                } catch (SMSException smse) {
1816                    SMSEntry.debug.error("OrganizationConfigManager: "
1817                            + "globalConfigChanged ", smse);
1818                }
1819                realmEnabled = ServiceManager.isRealmEnabled();
1820                coexistMode = ServiceManager.isCoexistenceMode();
1821                migratedTo70 = ServiceManager.isConfigMigratedTo70();
1822            }
1823        }
1824
1825        public void organizationConfigChanged(String serviceName,
1826                String version, String orgName, String groupName,
1827                String serviceComponent, int type) {
1828            // Reset the cached configuration in OrgConfigViaAMSDK
1829            if (serviceName.equalsIgnoreCase(OrgConfigViaAMSDK.IDREPO_SERVICE))
1830            {
1831                OrgConfigViaAMSDK.attributeMappings = new HashMap();
1832                OrgConfigViaAMSDK.reverseAttributeMappings = new HashMap();
1833            }
1834        }
1835    }
1836
1837    // ******* Static Variables ************
1838    // To determine if notification object has been registered for config
1839    // changes
1840    private static boolean registeredForConfigNotifications;
1841
1842    // Realm & Co-existence modes
1843    private static boolean realmEnabled;
1844
1845    private static boolean coexistMode;
1846
1847    private static boolean migratedTo70;
1848}