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: IdUtils.java,v 1.34 2009/11/20 23:52:54 ww203982 Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.sun.identity.idm;
033
034import com.iplanet.am.sdk.AMDirectoryAccessFactory;
035import com.iplanet.am.sdk.AMException;
036import com.iplanet.am.sdk.AMObject;
037import com.iplanet.am.sdk.AMOrganization;
038import com.iplanet.am.sdk.AMStoreConnection;
039import com.iplanet.am.sdk.AMConstants;
040import com.iplanet.am.sdk.common.IDirectoryServices;
041import com.iplanet.am.util.SystemProperties;
042import com.iplanet.sso.SSOException;
043import com.iplanet.sso.SSOToken;
044import com.sun.identity.common.CaseInsensitiveHashMap;
045import com.sun.identity.common.DNUtils;
046import com.sun.identity.security.AdminTokenAction;
047import com.sun.identity.shared.Constants;
048import com.sun.identity.shared.debug.Debug;
049import com.sun.identity.sm.DNMapper;
050import com.sun.identity.sm.SMSEntry;
051import com.sun.identity.sm.OrganizationConfigManager;
052import com.sun.identity.sm.OrgConfigViaAMSDK;
053
054import com.sun.identity.sm.SMSException;
055import com.sun.identity.sm.ServiceConfig;
056import com.sun.identity.sm.ServiceConfigManager;
057import com.sun.identity.sm.ServiceManager;
058
059import java.security.AccessController;
060import java.util.Collections;
061import java.util.HashSet;
062import java.util.Iterator;
063import java.util.Map;
064import java.util.Set;
065import java.util.StringTokenizer;
066
067import com.sun.identity.shared.ldap.LDAPDN;
068import com.sun.identity.shared.ldap.util.DN;
069
070
071/**
072 * The class defines some static utilities used by other components like policy
073 * and auth
074 *
075 * @supported.api
076 */
077public final class IdUtils {
078    private static Debug debug = AMIdentityRepository.debug;
079
080    private static Map mapSupportedTypes = new CaseInsensitiveHashMap(10);
081
082    public static Set supportedTypes = new HashSet();
083
084    private static Map mapTypesToServiceNames = new CaseInsensitiveHashMap();
085
086    protected static Map typesCanBeMemberOf = new CaseInsensitiveHashMap();
087
088    protected static Map typesCanHaveMembers = new CaseInsensitiveHashMap();
089
090    protected static Map typesCanAddMembers = new CaseInsensitiveHashMap();
091
092    // Static map to cache "orgIdentifier" and organization DN
093    private static Map orgIdentifierToOrgName = Collections.synchronizedMap(
094        new CaseInsensitiveHashMap());
095    private static Map orgStatusCache = Collections.synchronizedMap(
096        new CaseInsensitiveHashMap());
097
098    // ServiceConfigManager for sunidentityrepository service
099    private static String notificationId;
100
101    private static ServiceConfigManager serviceConfigManager;
102    
103    // User naming attribute for AMSDK
104    private static String USER_NAMING_ATTR;
105    
106    // Organization naming attribute for AMSDK
107    private static String ORG_NAMING_ATTR;
108    
109    // SMS Root Suffix
110    private static String ROOT_SUFFIX;
111    
112    // DN pointing to the services node
113    private static String SERVICES_SUFFIX;
114    
115    // Special Users
116    private static Set specialUsers = new HashSet();
117    
118    static {
119        initialize();
120    }
121
122    protected static void initialize() {
123        if (ServiceManager.isConfigMigratedTo70()) {
124            // IdRepo service schema exists. Read the supported
125            // entities from there
126            try {
127                SSOToken adminToken = (SSOToken) AccessController
128                        .doPrivileged(AdminTokenAction.getInstance());
129                serviceConfigManager = new ServiceConfigManager(adminToken,
130                        IdConstants.REPO_SERVICE, "1.0");
131                ServiceConfig ss = serviceConfigManager.getGlobalConfig(null);
132                Set typeSchemaNames = ss.getSubConfigNames("*",
133                        IdConstants.SUPPORTED_TYPES);
134                if (typeSchemaNames == null || typeSchemaNames.isEmpty()) {
135                    loadDefaultTypes();
136                } else {
137                    Iterator it = typeSchemaNames.iterator();
138                    while (it.hasNext()) {
139                        String typeSchema = (String) it.next();
140                        IdType idType = new IdType(typeSchema);
141                        supportedTypes.add(idType);
142                        mapSupportedTypes.put(idType.getName(), idType);
143                        ServiceConfig tsc = ss.getSubConfig(typeSchema);
144                        Map attributes = tsc.getAttributes();
145                        Set serviceNameSet = (Set) attributes
146                                .get(IdConstants.SERVICE_NAME);
147                        Set canBeMembersOf = (Set) attributes
148                                .get(IdConstants.ATTR_MEMBER_OF);
149                        Set canHaveMembers = (Set) attributes
150                                .get(IdConstants.ATTR_HAVE_MEMBERS);
151                        Set canAddMembers = (Set) attributes
152                                .get(IdConstants.ATTR_ADD_MEMBERS);
153                        if (serviceNameSet != null && 
154                                !serviceNameSet.isEmpty()) {
155                            mapTypesToServiceNames.put(typeSchema,
156                                    (String) serviceNameSet.iterator().next());
157                        }
158                        if (canBeMembersOf != null && 
159                                !canBeMembersOf.isEmpty()) {
160                            Set memberOfSet = getMemberSet(canBeMembersOf);
161                            typesCanBeMemberOf.put(typeSchema, memberOfSet);
162                        }
163                        if (canHaveMembers != null && 
164                                !canHaveMembers.isEmpty()) {
165                            Set memberSet = getMemberSet(canHaveMembers);
166                            typesCanHaveMembers.put(typeSchema, memberSet);
167                        }
168                        if (canAddMembers != null && 
169                                !canAddMembers.isEmpty()) 
170                        {
171                            Set memberSet = getMemberSet(canAddMembers);
172                            typesCanAddMembers.put(typeSchema, memberSet);
173                        }
174                    }
175                }
176            } catch (SMSException e) {
177                String installTime = SystemProperties.get(
178                    Constants.SYS_PROPERTY_INSTALL_TIME, "false");
179                if (!installTime.equals("true")) {
180                    debug.error(
181                        "IdUtils.initialize: Loading default types.", e);
182                }
183                loadDefaultTypes();
184            } catch  (SSOException ssoe) {
185                debug.error("dUtils.initialize: Loading default types", ssoe);
186                loadDefaultTypes();
187            }
188        } else {
189            loadDefaultTypes();
190        }
191
192        // Register for SMS notifications to root realm
193        if (notificationId == null) {
194            try {
195                SSOToken adminToken = (SSOToken) AccessController
196                        .doPrivileged(AdminTokenAction.getInstance());
197                if (serviceConfigManager == null) {
198                    serviceConfigManager = new ServiceConfigManager(adminToken,
199                        IdConstants.REPO_SERVICE, "1.0");
200                }
201                notificationId = serviceConfigManager.addListener(
202                    new IdUtilsListener());
203            } catch (SMSException e) {
204                String installTime = SystemProperties.get(
205                    Constants.SYS_PROPERTY_INSTALL_TIME, "false");
206                if (!installTime.equals("true")) {
207                    debug.error(
208                        "IdUtils.initialize: Register notification", e);
209                }
210            } catch (SSOException ssoe) {
211                String installTime = SystemProperties.get(
212                    Constants.SYS_PROPERTY_INSTALL_TIME, "false");
213                if (!installTime.equals("true")) {
214                    debug.error(
215                        "IdUtils.initialize: Register notification", ssoe);
216                }
217            }
218        }
219    }
220
221    /**
222     * @supported.api 
223     * Returns a handle of the Identity object based on
224     * the SSO Token passed in (<code>AMIdentity</code> object of the user
225     * who is authenticated).
226     * 
227     * @param token    Single sign on token of user.
228     * @return Identity object.
229     * @throws IdRepoException if there are repository related error conditions.
230     * @throws SSOException if user's single sign on token is invalid.
231     */
232    public static AMIdentity getIdentity(SSOToken token)
233        throws IdRepoException, SSOException {
234        String principal = token.getProperty(Constants.UNIVERSAL_IDENTIFIER);
235        if (principal == null) {
236            // This could happen during co-existence with AM 6.x
237            // and SSOToken created by AM 6.x server. In this case
238            // the principal name would be the DN
239            principal = token.getPrincipal().getName();
240        }
241        return (getIdentity(token, principal));
242    }
243
244    /**
245     * @supported.api
246     * 
247     * Returns a string which uniquely represents this identity object.
248     * 
249     * @param id
250     *            <code>AMIdentity</code> object whose string represenation is
251     *            needed.
252     * @return universal identifier of <code>id</code>.
253     */
254    public static String getUniversalId(AMIdentity id) {
255        return id.getUniversalId();
256    }
257
258    /**
259     * @supported.api 
260     * 
261     * Returns an <code>AMIdentity</code> object, if provided with a string 
262     * identifier for the object.
263     * 
264     * @param token SSOToken of the administrator
265     * @param univId String represenation of the identity.
266     * @return Identity object
267     * @throws IdRepoException if the identifier provided is wrong.
268     */
269    public static AMIdentity getIdentity(SSOToken token, String univId)
270            throws IdRepoException {
271        return (getIdentity(token, univId, null));
272    }
273
274    /**
275     * Returns an <code>AMIdentity</code> object, given the
276     * DN of an authenticated identity, realm name and identity type.
277     * This interface is mainly for authentication component to get
278     * back the identity of the user.
279     * 
280     * @param token SSOToken of the administrator
281     * @param amsdkdn DN of the authenticated user
282     * @param realm  realm name where the user was authenticated
283     * @return Identity object or <code>null</code> 
284     * @throws IdRepoException if the underly components throws
285     * exception while obtaining the identity object
286     */
287    public static AMIdentity getIdentity(SSOToken token, String amsdkdn,
288        String realm) throws IdRepoException {
289        DN amsdkdnObject = null;
290        if (amsdkdn == null || !(amsdkdnObject = new DN(amsdkdn)).isDN()) {
291            Object[] args = { amsdkdn };
292            throw (new IdRepoException(IdRepoBundle.BUNDLE_NAME,
293                "215", args));
294        }
295
296        // Try constructing the identity object
297        if (amsdkdn.toLowerCase().startsWith("id=")) {
298            try {
299                return (new AMIdentity(amsdkdnObject, token));
300            } catch (IdRepoException ide) {
301                // this could be a AMSDK DN. Follow the AMSDK rules
302                if (debug.messageEnabled()) {
303                    debug.message("IdUtils:getIdentity(token, " +
304                        amsdkdn + ") got exception: " + ide.getMessage() +
305                        "\n\tContinuing with AMSDK DN check");
306                }
307            }
308        }
309
310        // Check for Special Users
311        initializeSpecialUsers();
312        if (specialUsers.contains(DNUtils.normalizeDN(amsdkdnObject))) {
313            return (new AMIdentity(amsdkdnObject, token, LDAPDN.explodeDN(
314                amsdkdnObject, true)[0], IdType.USER, ROOT_SUFFIX));
315        }
316
317        // Since "amsdkdn" is not a UUID, check if realm has AMSDK configured
318        // This change is to avoid the issue of IdUtils always checking the 
319        // users in AMSDK as IdUtils does not check if AMSDK is configured in 
320        // any of the realms. 
321        try {
322            if (!ServiceManager.isAMSDKEnabled() || ((realm != null) && 
323                !OrgConfigViaAMSDK.isAMSDKConfigured(realm)) ||
324                    (!ServiceManager.isAMSDKConfigured())) { 
325                // Not configured for AMSDK, return
326                return (null);
327            }
328        } catch (SMSException smse) {
329            // Ignore the exception and continue
330        } 
331
332        // Initialize root realm suffix, org and user naming attributes
333        initializeForGetIdentity();
334
335        // Determine if the amsdkdn is valid. Obtain name & type
336        String name = null;
337        IdType type = null;
338        try {
339            // Since we would using AMSDK, get AMDirectoryManager preload
340            // all the attributes and check if it exists
341            IDirectoryServices dsServices = 
342                AMDirectoryAccessFactory.getDirectoryServices();
343            // Preload/cache all the attributes assuming it is a user
344            // Mainly for performance reasons, since getObjectType would
345            // force multiple another directory lookup
346            try {
347                if (amsdkdn.startsWith(USER_NAMING_ATTR)) {
348                    dsServices.getAttributes(token, amsdkdn, AMObject.USER);
349                }
350            } catch (Exception e) {
351                // Ignore the exception and continue since this for cache
352            }
353
354            // Getting object type would use the cached attributes
355            int sdkType = dsServices.getObjectType(token, amsdkdn);
356
357            // Convert the sdkType to IdRepo type
358            type = getType(AMStoreConnection.getObjectName(sdkType));
359            name = AMConstants.CONTAINER_DEFAULT_TEMPLATE_ROLE;
360            if (!type.equals(IdType.REALM)) {
361                name = LDAPDN.explodeDN(amsdkdnObject, true)[0];
362            }
363        } catch (AMException ame) {
364            // Debug the message and return null
365            if (debug.messageEnabled()) {
366                debug.message("IdUtils.getIdentity: Unable to resolve " +
367                    "AMSDK DN: " + amsdkdn, ame);
368            }
369            return (null);
370        } catch (SSOException ssoe) {
371            // Debug the message and return null
372            if (debug.messageEnabled()) {
373                debug.message("IdUtils.getIdentity: Unable to resolve " +
374                    "AMSDK DN. Got SSOException", ssoe);
375            }
376            return (null);
377        }
378
379        // Need to determine realm for amsdkdn
380        String srealm = ROOT_SUFFIX;
381        if (!amsdkdn.equals(ROOT_SUFFIX) &&
382            !amsdkdn.equals(SERVICES_SUFFIX)) {
383            // Need to get the object type and walk up the tree
384            int index = amsdkdn.indexOf(ORG_NAMING_ATTR);
385            if (index == 0) {
386                srealm = OrgConfigViaAMSDK.getRealmForAMSDK(amsdkdn, realm);
387            } else if (index > 0) {
388                srealm = OrgConfigViaAMSDK.getRealmForAMSDK(
389                    amsdkdn.substring(index), realm);
390            }
391            if (debug.messageEnabled()) {
392                debug.message("IdUtils.getIdentity:: amsdkdn=" +
393                    amsdkdn + " maps to realm=" + srealm);
394            }
395        } else if (amsdkdn.equals(SERVICES_SUFFIX)) {
396            // Since amsdkdn points to services node,
397            // it should be reset to root suffix
398            amsdkdn = ROOT_SUFFIX;
399        }
400
401        return (new AMIdentity(amsdkdnObject, token, name, type, srealm));
402    }
403
404    /**
405     * Returns the name of service which defines the profile information for
406     * this type. Returns null, if nothing is defined.
407     * 
408     * @param type IdType whose service name is needed.
409     * @return Name of the service.
410     */
411    public static String getServiceName(IdType type) {
412        return (String) mapTypesToServiceNames.get(type.getName());
413    }
414
415    /**
416     * Returns corresponding <code>IdType</code> object given a type.
417     * 
418     * @param type of object to return.
419     * @return Idtype of type.
420     * @throws IdRepoException if there are no corresponding types.
421     */
422    public static IdType getType(String type) throws IdRepoException {
423        if (type.equalsIgnoreCase("managedrole")) {
424            type = "role";
425        } else if (type.equalsIgnoreCase("organization")
426                || type.equalsIgnoreCase("organizationalunit")) {
427            type = "realm";
428        }
429
430        IdType returnType = (IdType) mapSupportedTypes.get(type);
431        if (returnType == null) {
432            Object args[] = { type };
433            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "217", args);
434        }
435        return returnType;
436    }
437
438    /**
439     * Returns the matching DN from the AM SDK for this entry. This utility is
440     * required by auth.
441     * 
442     * @param id  <code>AMIdentity</code> object.
443     * @return <code>DN</code> of the object, as represented in the datastore.
444     */
445    public static String getDN(AMIdentity id) {
446        if (id.getDN() != null) {
447            return id.getDN();
448        } else {
449            return id.getUniversalId();
450        }
451    }
452
453    /**
454     * Returns an organization which maps to the identifier used by application
455     * 
456     * @param orgIdentifier  Organization identifier
457     * @return Organization mapping to that identifier.
458     */
459    public static String getOrganization(SSOToken token, String orgIdentifier)
460            throws IdRepoException, SSOException {
461        // Check in cache first
462        String id = null;
463        if ((id = (String) orgIdentifierToOrgName.get(orgIdentifier)) != null) {
464            return (id);
465        }
466
467        // Compute the organization name
468        if (debug.messageEnabled()) {
469            debug.message("IdUtils:getOrganization Input orgname: "
470                    + orgIdentifier);
471        }
472        if (orgIdentifier == null || orgIdentifier.length() == 0
473                || orgIdentifier.equals("/")) {
474            // Return base DN
475            id = DNMapper.orgNameToDN("/");
476        } else if (orgIdentifier.startsWith("/")) {
477            // If orgIdentifier is in "/" format covert to DN and return
478            id = DNMapper.orgNameToDN(orgIdentifier);
479        } else if (DN.isDN(orgIdentifier)) {
480            id = orgIdentifier;
481            try {
482                // Search for realms with orgIdentifier name
483                OrganizationConfigManager ocm = 
484                    new OrganizationConfigManager(token, orgIdentifier);
485            } catch (SMSException smse) {
486                // debug message here.
487                if (debug.messageEnabled()) {
488                    debug.message("IdUtils.getOrganization Exception in "
489                            + "getting org name from SMS", smse);
490                }
491                Object[] args = { orgIdentifier };
492                throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "401", 
493                        args);                  
494            }
495        } else if (ServiceManager.isCoexistenceMode()) {
496            // Return the org DN as determined by AMStoreConnection
497            if (debug.messageEnabled()) {
498                debug.message("IdUtils.getOrganization: getting from AMSDK");
499            }
500            try {
501                AMStoreConnection amsc = new AMStoreConnection(token);
502                id = amsc.getOrganizationDN(orgIdentifier, null);
503            } catch (AMException ame) {
504                if (debug.messageEnabled()) {
505                    debug.message("IdUtils.getOrganization Exception in "
506                            + "getting org name from AMSDK", ame);
507                }
508                throw convertAMException(ame);
509            }
510        } else {
511            // Get the realm name from SMS
512            if (debug.messageEnabled()) {
513                debug.message("IdUtils.getOrganization: getting from " +
514                    "SMS realms");
515            }
516            try {
517                boolean foundOrg = false;
518                ServiceManager sm = new ServiceManager(token);
519                // First search for realms with orgIdentifier name
520                OrganizationConfigManager ocm = sm
521                        .getOrganizationConfigManager("/");
522                Set subOrgNames = ocm.getSubOrganizationNames(orgIdentifier,
523                        true);
524                if (subOrgNames != null && !subOrgNames.isEmpty()) {
525                    if (subOrgNames.size() == 1) {
526                        id = DNMapper.orgNameToDN((String) subOrgNames
527                                .iterator().next());
528                        foundOrg = true;
529                    } else {
530                        for (Iterator items = subOrgNames.iterator();
531                            items.hasNext();) {
532                            // check for orgIdentifier
533                            String subRealmName = (String) items.next();
534                            StringTokenizer st = new StringTokenizer(
535                                    subRealmName, "/");
536                            // Need to handle the scenario where multiple
537                            // sub-realm with the same name should not be
538                            // allowed
539                            while (st.hasMoreTokens()) {
540                                if (st.nextToken().equalsIgnoreCase(
541                                        orgIdentifier)) {
542                                    if (!foundOrg) {
543                                        id = DNMapper.orgNameToDN(subRealmName);
544                                        foundOrg = true;
545                                    } else {
546                                        Object[] args = {orgIdentifier};
547                                        throw new IdRepoException(IdRepoBundle
548                                            .BUNDLE_NAME, "404", args);
549                                    }
550                                }
551                            }
552                        }
553                    }
554                }
555
556                // Check if organization name has been determined
557                if (debug.messageEnabled()) {
558                    debug.message("IdUtils.getOrganization: getting from " +
559                        "SMS realms aliases");
560                }
561                // perform organization alias search
562                Set vals = new HashSet();
563                vals.add(orgIdentifier);
564                Set orgAliases = sm.searchOrganizationNames(
565                    IdConstants.REPO_SERVICE,
566                    IdConstants.ORGANIZATION_ALIAS_ATTR, vals);
567                if (!foundOrg &&
568                    ((orgAliases == null) || orgAliases.isEmpty())) {
569                    if (debug.warningEnabled()) {
570                        debug.warning("IdUtils.getOrganization Unable" +
571                            " to find Org name for: " + orgIdentifier);
572                    }
573                    Object[] args = {orgIdentifier};
574                    throw new IdRepoException(IdRepoBundle.BUNDLE_NAME,
575                        "401", args);
576                } else if ((orgAliases != null)  && (orgAliases.size() > 0) &&
577                    (foundOrg || orgAliases.size() > 1)) {
578                    // Multiple realms should not have the same alias
579                    if (debug.warningEnabled()) {
580                        debug.warning("IdUtils.getOrganization Multiple " +
581                            " matching Orgs found for: " + orgIdentifier);
582                    }
583                    Object[] args = {orgIdentifier};
584                    throw new IdRepoException(IdRepoBundle.BUNDLE_NAME,
585                        "404", args);
586                }
587                if (!foundOrg) {
588                    String tmpS = (String) orgAliases.iterator().next();
589                    id = DNMapper.orgNameToDN(tmpS);
590                }
591            } catch (SMSException smse) {
592                // debug message here.
593                if (debug.messageEnabled()) {
594                    debug.message("IdUtils.getOrganization Exception in "
595                            + "getting org name from SMS", smse);
596                }
597                Object[] args = { orgIdentifier };
598                throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "401", 
599                        args);
600            }
601        }
602
603        if (debug.messageEnabled()) {
604            debug.message("IdUtils:getOrganization Search for OrgIdentifier:" +
605                orgIdentifier + " returning realm DN: " + id);
606        }
607
608        // Add to cache and return id
609        orgIdentifierToOrgName.put(orgIdentifier, id);
610        return id;
611    }
612
613    /**
614     * Clears the cache containing orgIdentifiers to organization names
615     */
616    protected static void clearOrganizationNamesCache() {
617        orgIdentifierToOrgName.clear();
618        orgStatusCache.clear();
619        if (debug.messageEnabled()) {
620            debug.message("IdUtils.clearOrganizationNamesCache called");
621        }
622    }
623
624    /**
625     * Returs true or false, depending on if this organization is enabled or
626     * not. The organization string passed to this method should be an
627     * identifier returned from the method
628     * <code> IdUtils.getOrganization </code>. In the default mode, where
629     * relams are enabled but backward comaptibility is required, this checks
630     * for organization status in the AM enabled Sun DS. Otherwise, it checks
631     * for organization status from the realms tree.
632     * 
633     * @param token token SSOToken a valid SSOToken.
634     * @param org name of the organization of interest.
635     * @return <code>true</code> if org is active; 
636     *    otherwise <code>false</code>
637     * @throws IdRepoException if there are repository related error conditions.
638     * @throws SSOException If user's single sign on token is invalid.
639     */
640    public static boolean isOrganizationActive(SSOToken token, String org)
641            throws IdRepoException, SSOException {
642        // Check the cache
643        if (orgStatusCache.containsKey(org)) {
644            return (((Boolean) orgStatusCache.get(org)).booleanValue());
645        }
646        boolean isActive = true;
647        // Need to initialize ServiceManager by creating the constructor
648        if (!ServiceManager.isCoexistenceMode()) {
649            // Pick it up from the realms tree.
650            try {
651                OrganizationConfigManager ocm = new OrganizationConfigManager(
652                        token, org);
653                if (ocm == null) {
654                    Object[] args = { org };
655                    throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "401",
656                            args);
657                }
658                Map attributes = ocm.getAttributes(IdConstants.REPO_SERVICE);
659                Set vals = (Set) attributes
660                        .get(IdConstants.ORGANIZATION_STATUS_ATTR);
661                if (vals == null || vals.isEmpty()) {
662                    isActive = true;
663                } else {
664                    String stringActive = (String) vals.iterator().next();
665                    isActive = stringActive.equalsIgnoreCase("Active");
666                }
667            } catch (SMSException smse) {
668                Object args[] = { org };
669                throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "401", 
670                        args);
671            }
672        } else if (ServiceManager.isAMSDKEnabled()) {
673            // Return the org DN as determined by AMStoreConnection.
674            try {
675                AMStoreConnection amsc = new AMStoreConnection(token);
676                AMOrganization orgObj = amsc.getOrganization(org);
677                isActive = orgObj.isActivated();
678            } catch (AMException ame) {
679                throw convertAMException(ame);
680            }
681        }
682        // Add to cache
683        orgStatusCache.put(org, isActive);
684        return isActive;
685    }
686
687    private static void initializeForGetIdentity() {
688        // Initialize root realm, if not already initalized
689        if (ROOT_SUFFIX == null) {
690            ROOT_SUFFIX = SMSEntry.getRootSuffix();
691            StringBuilder sb = new StringBuilder(100);
692            sb.append(SMSEntry.SERVICES_RDN)
693                .append(SMSEntry.COMMA).append(ROOT_SUFFIX);
694            SERVICES_SUFFIX = DNUtils.normalizeDN(sb.toString());
695        }
696
697        // Initialize organization and user naming attributes
698        if ((ORG_NAMING_ATTR == null) || (USER_NAMING_ATTR == null)) {
699            try {
700                ORG_NAMING_ATTR = AMStoreConnection.getNamingAttribute(
701                    AMObject.ORGANIZATION).toLowerCase() + "=";
702                USER_NAMING_ATTR = AMStoreConnection.getNamingAttribute(
703                    AMObject.USER).toLowerCase() + "=";
704            } catch (AMException ame) {
705                if (debug.warningEnabled()) {
706                    debug.warning("IdUtils: unable to get naming " +
707                        "attribute for org/user. Using \"o\"/\"uid\"");
708                }
709                ORG_NAMING_ATTR = "o=";
710                USER_NAMING_ATTR = "uid=";
711            }
712        }
713    }
714
715    private static void initializeSpecialUsers() {
716        // Populate special users
717        if (specialUsers.isEmpty()) {
718            String susers = SystemProperties.get(
719                Constants.AUTHENTICATION_SPECIAL_USERS, "");
720            StringTokenizer st = new StringTokenizer(
721                susers, "|");
722            while (st.hasMoreTokens()) {
723                specialUsers.add(DNUtils.normalizeDN(st.nextToken()));
724            }
725            susers = SystemProperties.get(
726                "com.sun.identity.authentication.super.user", "");
727            specialUsers.add(DNUtils.normalizeDN(susers));
728        }
729    }
730    
731    /**
732     * Returns an IdRepoException based on an <code>AMException</code>
733     * 
734     * @param ame
735     * @return IdRepoException based on ame.
736     */
737    public static IdRepoException convertAMException(AMException ame) {
738        Object[] args = ame.getMessageArgs();
739        String eCode = ame.getErrorCode();
740        IdRepoException ide = null;
741        if (args == null) {
742            ide = new IdRepoException("amProfile", eCode, null);
743        } else { 
744            ide = new IdRepoException("amProfile", ame.getErrorCode(), args);
745        }
746        ide.setLDAPErrorCode(ame.getLDAPErrorCode());
747        return ide;
748    }
749
750    private static void loadDefaultTypes() {
751        supportedTypes.add(IdType.REALM);
752        supportedTypes.add(IdType.AGENT);
753        supportedTypes.add(IdType.USER);
754        supportedTypes.add(IdType.ROLE);
755        supportedTypes.add(IdType.GROUP);
756        supportedTypes.add(IdType.FILTEREDROLE);
757        mapSupportedTypes.put(IdType.REALM.getName(), IdType.REALM);        
758        mapSupportedTypes.put(IdType.USER.getName(), IdType.USER);
759        mapSupportedTypes.put(IdType.ROLE.getName(), IdType.ROLE);
760        mapSupportedTypes.put(IdType.FILTEREDROLE.getName(),
761                IdType.FILTEREDROLE);
762        mapSupportedTypes.put(IdType.AGENT.getName(), IdType.AGENT);
763        mapSupportedTypes.put(IdType.GROUP.getName(), IdType.GROUP);
764        Set memberSet = new HashSet();
765        memberSet.add(IdType.ROLE);
766        memberSet.add(IdType.GROUP);
767        memberSet.add(IdType.FILTEREDROLE);
768        typesCanBeMemberOf.put(IdType.USER.getName(), memberSet);
769        Set memberShipSet = new HashSet();
770        memberShipSet.add(IdType.USER);
771        typesCanHaveMembers.put(IdType.ROLE.getName(), memberShipSet);
772        typesCanHaveMembers.put(IdType.GROUP.getName(), memberShipSet);
773        typesCanHaveMembers.put(IdType.FILTEREDROLE.getName(), memberShipSet);
774        typesCanAddMembers.put(IdType.GROUP.getName(), memberShipSet);
775        typesCanAddMembers.put(IdType.ROLE.getName(), memberShipSet);
776    }
777
778    private static Set getMemberSet(Set members) {
779        Set memberSet = new HashSet(members.size() * 2);
780        for (Iterator iter = members.iterator(); iter.hasNext();) {
781            String currType = (String) iter.next();
782            memberSet.add(new IdType(currType));
783        }
784        return memberSet;
785    }
786
787    /**
788     * Returns the user name extracted from the uuid
789     * if the orgName supplied in the parameter is 
790     * not same realm name in uuid then <code>IdRepoException</code>
791     * is thrown
792     * 
793     * @param uuid uuid of the user
794     * @param orgName the org user is trying to login to
795     * @return user name
796     * @throws IdRepoException 
797     */
798    public static String getIdentityName(String uuid, String orgName)
799        throws IdRepoException {
800        String username = uuid;
801        // Check uuid
802        if ((uuid != null) && uuid.toLowerCase().startsWith("id=")) {
803            // Could be universal id, get the identity object
804            AMIdentity id = new AMIdentity(null, uuid);
805            username = id.getName();
806            // Check the realm names
807            String realm = DNUtils.normalizeDN(id.getRealm());
808            if (!DNUtils.normalizeDN(orgName).equals(realm)) {
809                Object[] args = {uuid, orgName};
810                throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "403", args);
811            }
812        }
813        return (username);
814    } 
815    
816    // SMS service listener to reinitialize if IdRepo service changes
817    static class IdUtilsListener implements com.sun.identity.sm.ServiceListener 
818    {
819        public void schemaChanged(String serviceName, String version) {
820            initialize();
821        }
822
823        public void globalConfigChanged(String serviceName, String version,
824            String groupName, String serviceComponent, int type) {
825            initialize();
826            clearOrganizationNamesCache();
827        }
828
829        public void organizationConfigChanged(String serviceName,
830            String version, String orgName, String groupName,
831            String serviceComponent, int type) {
832            clearOrganizationNamesCache();
833        }
834    }
835}