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