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: AMIdentity.java,v 1.37 2009/11/20 23:52:54 ww203982 Exp $
026 *
027 * Portions Copyrighted 2011-2015 ForgeRock AS.
028 */
029package com.sun.identity.idm;
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;
037
038import com.iplanet.am.sdk.AMCommonUtils;
039import com.iplanet.am.sdk.AMCrypt;
040import com.iplanet.am.sdk.AMHashMap;
041import com.iplanet.sso.SSOException;
042import com.iplanet.sso.SSOToken;
043import com.sun.identity.common.CaseInsensitiveHashMap;
044import com.sun.identity.common.CaseInsensitiveHashSet;
045import com.sun.identity.idm.common.IdRepoUtils;
046import com.sun.identity.shared.Constants;
047import com.sun.identity.shared.debug.Debug;
048import com.sun.identity.sm.DNMapper;
049import com.sun.identity.sm.SMSException;
050import com.sun.identity.sm.SchemaType;
051import com.sun.identity.sm.ServiceManager;
052import com.sun.identity.sm.ServiceNotFoundException;
053import com.sun.identity.sm.ServiceSchema;
054import com.sun.identity.sm.ServiceSchemaManager;
055import org.forgerock.i18n.LocalizedIllegalArgumentException;
056import org.forgerock.openam.ldap.LDAPUtils;
057import org.forgerock.opendj.ldap.DN;
058import org.forgerock.opendj.ldap.RDN;
059
060/**
061 * This class represents an Identity which needs to be managed by Access
062 * Manager. This identity could exist in multiple repositories, which are
063 * configured for a given realm or organization. When any operation is performed
064 * from this class, it executes all plugins that are configured for performing
065 * that operation. For eg: getAttributes. The application gets access to
066 * constructing <code> AMIdentity </code> objects by using
067 * <code> AMIdentityRepository
068 * </code> interfaces. For example:
069 * <p>
070 *
071 * <PRE>
072 *
073 * AMIdentityRepository idrepo = new AMIdentityRepository(token, org);
074 * AMIdentity id = idrepo.getRealmIdentity();
075 *
076 * </PRE>
077 *
078 * The <code>id</code> returned above is the AMIdentity object of the user's
079 * single sign-on token passed above. The results obtained from search performed
080 * using AMIdentityRepository also return AMIdentity objects. The type of an
081 * object can be determined by doing the following:
082 * <p>
083 *
084 * <PRE>
085 *
086 * IdType type = identity.getType();
087 *
088 * </PRE>
089 *
090 * The name of an object can be determined by:
091 * <p>
092 *
093 * <PRE>
094 *
095 * String name = identity.getName();
096 *
097 * </PRE>
098 *
099 * @supported.api
100 */
101
102public class AMIdentity {
103
104    private String univIdWithoutDN;
105
106    private final SSOToken token;
107
108    private final String name;
109
110    private final IdType type;
111
112    private final String orgName;
113
114    private Set fullyQualifiedNames;
115
116    private final AMHashMap modMap = new AMHashMap(false);
117
118    private final AMHashMap binaryModMap = new AMHashMap(true);
119
120    protected String univDN = null;
121
122    /**
123     * @supported.api
124     *
125     * Constructor for the <code>AMIdentity</code> object.
126     *
127     * @param ssotoken
128     *            Single sign on token of the user
129     * @throws SSOException
130     *             if user's single sign on token is invalid.
131     * @throws IdRepoException
132     *            if the single sign on token does not have a
133     *            a valid universal identifier
134     */
135    public AMIdentity(SSOToken ssotoken) throws SSOException, IdRepoException {
136        this(ssotoken, ssotoken.getProperty(Constants.UNIVERSAL_IDENTIFIER));
137    }
138
139    /**
140     * @supported.api
141     *
142     * Constructor for the <code>AMIdentity</code> object.
143     *
144     * @param ssotoken
145     *            Single sign on token to construct the identity
146     *            object. Access permission to Identity object
147     *            would be based on this user
148     * @param universalId
149     *            Universal Identifier of the identity.
150     *
151     * @throws IdRepoException
152     *            if the universal identifier is invalid
153     *
154     */
155    public AMIdentity(SSOToken ssotoken, String universalId)
156        throws IdRepoException {
157        this(universalId == null ? null : LDAPUtils.newDN(universalId), ssotoken);
158    }
159
160    public AMIdentity(DN universalId, SSOToken ssotoken) throws IdRepoException {
161        this.token = ssotoken;
162        // Validate Universal ID
163        if (universalId == null || universalId.size() < 3 || !"id".equalsIgnoreCase(LDAPUtils.rdnTypeFromDn(universalId))) {
164            // Not a valid UUID since it should have the
165            // name, type and realm components
166            Object args[] = { universalId };
167            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.ILLEGAL_UNIVERSAL_IDENTIFIER, args);
168        }
169
170        // Valid UUID, construct rest of the parameters
171        univIdWithoutDN = universalId.toString();
172
173        // Check for AMSDK DN
174        int index;
175        if ((index = univIdWithoutDN.toLowerCase().indexOf(",amsdkdn=")) != -1) {
176            // obtain DN and univIdWithoutDN
177            univDN = univIdWithoutDN.substring(index + 9);
178            univIdWithoutDN = univIdWithoutDN.substring(0, index);
179            universalId = DN.valueOf(univIdWithoutDN);
180        }
181        name = LDAPUtils.rdnValue(universalId.rdn());
182        type = new IdType(LDAPUtils.rdnValue(universalId.parent().rdn()));
183        orgName = universalId.parent().parent().toString();
184    }
185
186    /**
187     * Constructor for the <code>AMIdentity</code> object.
188     *
189     * @param token
190     *            Single sign on token to construct the identity
191     *            object. Access permission to Identity object
192     *            would be based on this user
193     * @param name
194     *            the name associated with this identity.
195     * @param type
196     *            the <code>IdType</code> of this identity.
197     * @param orgName
198     *            the organizaton name this identity belongs to.
199     * @param amsdkdn
200     *            the amsdk name assoicated with this identity if any.
201     */
202    public AMIdentity(SSOToken token, String name, IdType type, String orgName, String amsdkdn) {
203        this(amsdkdn == null ? null : DN.valueOf(amsdkdn), token, name, type, orgName);
204    }
205
206    public AMIdentity(DN amsdkdn, SSOToken token, String name, IdType type, String orgName) {
207        this.name = name;
208        this.type = type;
209        this.orgName = DNMapper.orgNameToDN(orgName);
210        this.token = token;
211        if (amsdkdn != null && amsdkdn.size() > 0) {
212            this.univDN = amsdkdn.toString();
213        }
214
215        if (LDAPUtils.isDN(name)) {
216            name = LDAPUtils.rdnValueFromDn(name);
217        }
218
219        try {
220            univIdWithoutDN = LDAPUtils.newDN(this.orgName)
221                    .child(new RDN("ou", type.getName()))
222                    .child(new RDN("id", name))
223                    .toString();
224        } catch (LocalizedIllegalArgumentException e) {
225            throw new IllegalArgumentException("Cannot parse orgName: " + orgName, e);
226        }
227    }
228
229    // General APIs
230    /**
231     *
232     * Returns the name of the identity.
233     *
234     * @return Name of the identity
235     * @supported.api
236     */
237    public String getName() {
238        String sname = name;
239        if (type.equals(IdType.REALM)) {
240            // Since '0'th location currently has ContainerDefaultTemplate
241            // the 2nd location would have the realm name
242            sname = LDAPUtils.rdnValue(DN.valueOf(univIdWithoutDN).parent().parent().rdn());
243        }
244        return sname;
245    }
246
247    /**
248     * Returns the Type of the Identity.
249     *
250     * @return <code>IdType</code> representing the type of this object.
251     * @supported.api
252     */
253    public IdType getType() {
254        return type;
255    }
256
257    /**
258     * Returns the realm for this identity.
259     *
260     * @return String representing realm name.
261     * @supported.api
262     */
263    public String getRealm() {
264        return orgName;
265    }
266
267    /**
268     * If there is a status attribute configured, then verifies if the identity
269     * is active and returns true. This method is only valid for AMIdentity
270     * objects of type User and Agent.
271     *
272     * @return true if the identity is active or if it is not configured for a
273     *         status attribute, false otherwise.
274     * @throws IdRepoException
275     *             If there are repository related error conditions.
276     * @throws SSOException
277     *             If user's single sign on token is invalid.
278     * @supported.api
279     */
280    public boolean isActive() throws IdRepoException, SSOException {
281        IdServices idServices = IdServicesFactory.getDataStoreServices();
282        return idServices.isActive(token, type, name, orgName, univDN);
283    }
284
285    /**
286     * If there is a status attribute configured, then set its status to
287     * true or activated state if the parameter active is true.
288     * This method is only valid for AMIdentity objects of type User and Agent.
289     *
290     * @param active The state value to assign to status attribute. The actual
291     * value assigned to the status attribute will depend on what is configured
292     * for that particular plugin.  If active is true, the status will be
293     * assigned the value corresponding to activated.
294     * @throws IdRepoException If there are repository related error conditions.
295     * @throws SSOException If user's single sign on token is invalid.
296     * @supported.api
297     */
298    public void setActiveStatus(boolean active)
299        throws IdRepoException, SSOException {
300        IdServices idServices =
301            IdServicesFactory.getDataStoreServices();
302        idServices.setActiveStatus(token, type, name, orgName, univDN, active);
303    }
304
305    /**
306     * Returns all attributes and values of this identity. This method is only
307     * valid for AMIdentity objects of type User, Agent, Group, and Role.
308     *
309     * @return Map of attribute-values
310     * @throws IdRepoException
311     *             If there are repository related error conditions.
312     * @throws SSOException
313     *             If user's single sign on token is invalid.
314     * @supported.api
315     */
316    public Map getAttributes() throws IdRepoException, SSOException {
317
318        IdServices idServices = IdServicesFactory.getDataStoreServices();
319        Map attrs = idServices
320                .getAttributes(token, type, name, orgName, univDN);
321        if (debug.messageEnabled()) {
322            debug.message("AMIdentity.getAttributes all: attrs=" +
323                IdRepoUtils.getAttrMapWithoutPasswordAttrs(attrs, null));
324        }
325        return attrs;
326    }
327
328    /**
329     * Returns requested attributes and values of this object.
330     *
331     * This method is only valid for AMIdentity object of type User, Agent,
332     * Group, and Role.
333     *
334     * @param attrNames
335     *            Set of attribute names to be read
336     * @return Map of attribute-values.
337     * @throws IdRepoException
338     *             If there are repository related error conditions.
339     * @throws SSOException
340     *             If user's single sign on token is invalid.
341     * @supported.api
342     */
343    public Map getAttributes(Set attrNames) throws IdRepoException,
344            SSOException {
345
346        IdServices idServices = IdServicesFactory.getDataStoreServices();
347        Map attrs = idServices.getAttributes(token, type, name, attrNames,
348                orgName, univDN, true);
349        CaseInsensitiveHashMap caseAttrs = new CaseInsensitiveHashMap(attrs);
350        CaseInsensitiveHashMap resultMap = new CaseInsensitiveHashMap();
351        Iterator it = attrNames.iterator();
352        while (it.hasNext()) {
353            String attrName = (String) it.next();
354            if (caseAttrs.containsKey(attrName)) {
355                resultMap.put(attrName, caseAttrs.get(attrName));
356            }
357        }
358
359        if (debug.messageEnabled()) {
360            debug.message("AMIdentity.getAttributes 6: attrNames=" + attrNames
361                    + ";  resultMap=" + resultMap + "; attrs=" + attrs);
362        }
363        return resultMap;
364    }
365
366    /**
367     * Returns requested attributes and values of this object.
368     *
369     * This method is only valid for AMIdentity objects of type User, Agent,
370     * Group, and Role.
371     *
372     * @param attrNames
373     *            Set of attribute names to be read
374     * @return Map of attribute-values.
375     * @throws IdRepoException
376     *             If there are repository related error conditions.
377     * @throws SSOException
378     *             If user's single sign on token is invalid.
379     * @supported.api
380     */
381    public Map getBinaryAttributes(Set attrNames) throws IdRepoException,
382            SSOException {
383
384        IdServices idServices = IdServicesFactory.getDataStoreServices();
385        return idServices.getAttributes(token, type, name, attrNames, orgName,
386                univDN, false);
387    }
388
389    /**
390     * Returns the values of the requested attribute. Returns an empty set, if
391     * the attribute is not set in the object.
392     *
393     * This method is only valid for AMIdentity objects of type User, Agent,
394     * Group, and Role.
395     *
396     * @param attrName
397     *            Name of attribute
398     * @return Set of attribute values.
399     * @throws IdRepoException
400     *             if there are repository related error conditions.
401     * @throws SSOException
402     *             If user's single sign on token is invalid.
403     * @supported.api
404     */
405    public Set getAttribute(String attrName) throws IdRepoException,
406            SSOException {
407
408        Set attrNames = new HashSet();
409        attrNames.add(attrName);
410        IdServices idServices = IdServicesFactory.getDataStoreServices();
411        Map valMap = idServices.getAttributes(token, type, name, attrNames,
412                orgName, univDN, true);
413        return ((Set) valMap.get(attrName));
414    }
415
416    /**
417     * Sets the values of attributes. This method should be followed by the
418     * method "store" to commit the changes to the Repository.
419     * This method is only valid for <code>AMIdentity</code> objects of
420     * type User and Agent.
421     *
422     * @param attrMap is a map of attribute name
423     *        <code>(String)</code>
424     *        to a <code>Set</code> of attribute values <code>(String)</code>.
425     *        It is arranged as:
426     *        Map::attrMap -->
427     *        Key: String::AttributeName
428     *        Value: Set::AttributeValues (Set of String)
429     * @throws IdRepoException
430     *             If there are repository related error conditions.
431     * @throws SSOException
432     *             If user's single sign on token is invalid.
433     * @supported.api
434     */
435    public void setAttributes(Map attrMap) throws IdRepoException, SSOException
436    {
437        modMap.copy(attrMap);
438    }
439
440    /**
441     * Changes password for the identity.
442     *
443     * @param oldPassword old password
444     * @param newPassword new password
445     * @throws IdRepoException If there are repository related error conditions.
446     * @throws SSOException If user's single sign on token is invalid.
447     * @supported.api
448     */
449    public void changePassword(String oldPassword, String newPassword)
450        throws IdRepoException, SSOException {
451
452        IdServices idServices = IdServicesFactory.getDataStoreServices();
453        idServices.changePassword(token, type, name, oldPassword,
454            newPassword, orgName, getDN());
455    }
456
457    /**
458     * Set the values of binary attributes. This method should be followed by
459     * the method "store" to commit the changes to the Repository
460     *
461     * This method is only valid for AMIdentity objects of type User and Agent.
462     *
463     * @param attrMap
464     *            Map of attribute-values to be set in the repository or
465     *            repositories (if multiple plugins are configured for "edit").
466     * @throws IdRepoException
467     *             If there are repository related error conditions.
468     * @throws SSOException
469     *             If user's single sign on token is invalid.
470     * @supported.api
471     */
472    public void setBinaryAttributes(Map attrMap) throws IdRepoException,
473            SSOException {
474        binaryModMap.copy(attrMap);
475    }
476
477    /**
478     * Removes the attributes from the identity entry. This method should be
479     * followed by a "store" to commit the changes to the Repository.
480     *
481     * This method is only valid for AMIdentity objects of type User and Agent.
482     *
483     * @param attrNames
484     *            Set of attribute names to be removed
485     * @throws IdRepoException
486     *             If there are repository related error conditions.
487     * @throws SSOException
488     *             If the user's single sign on token is invalid
489     * @supported.api
490     */
491    public void removeAttributes(Set attrNames) throws IdRepoException,
492            SSOException {
493        if (attrNames == null || attrNames.isEmpty()) {
494            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.ILLEGAL_ARGUMENTS, null);
495        }
496
497        boolean agentflg = getType().equals(IdType.AGENTONLY);
498        if (agentflg) {
499            IdServices idServices = IdServicesFactory.getDataStoreServices();
500            idServices.removeAttributes(token, type, name, attrNames,
501                orgName, null);
502            Iterator it = attrNames.iterator();
503            while (it.hasNext()) {
504                String attr = (String) it.next();
505                modMap.remove(attr);
506            }
507        } else {
508            Iterator it = attrNames.iterator();
509            while (it.hasNext()) {
510                String attr = (String) it.next();
511                modMap.put(attr, Collections.EMPTY_SET);
512            }
513        }
514    }
515
516    /**
517     * Stores the attributes of the object.
518     *
519     * This method is only valid for AMIdentity objects of type User and Agent.
520     *
521     * @throws IdRepoException
522     *             If there are repository related error conditions.
523     * @throws SSOException
524     *             If user's single sign on token is invalid.
525     * @supported.api
526     */
527    public void store() throws IdRepoException, SSOException {
528        IdServices idServices = IdServicesFactory.getDataStoreServices();
529        if (modMap != null && !modMap.isEmpty()) {
530            idServices.setAttributes(token, type, name, modMap, false, orgName,
531                    univDN, true);
532            modMap.clear();
533        }
534        if (binaryModMap != null && !binaryModMap.isEmpty()) {
535            idServices.setAttributes(token, type, name, binaryModMap, false,
536                    orgName, univDN, false);
537            binaryModMap.clear();
538        }
539    }
540
541    // SERVICE RELATED APIS
542
543    /**
544     * Returns the set of services already assigned to this identity.
545     *
546     * This method is only valid for AMIdentity object of type User.
547     *
548     * @return Set of serviceNames
549     * @throws IdRepoException
550     *             If there are repository related error conditions.
551     * @throws SSOException
552     *             If user's single sign on token is invalid.
553     * @supported.api
554     */
555    public Set getAssignedServices() throws IdRepoException, SSOException {
556        // Get all service names for the type from SMS
557        ServiceManager sm;
558        try {
559            sm = new ServiceManager(token);
560        } catch (SMSException smse) {
561            debug.error("Error while creating Service manager:", smse);
562            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode
563                    .SERVICE_MANAGER_INITIALIZATION_FAILED, null);
564        }
565        Map sMap = sm.getServiceNamesAndOCs(type.getName());
566
567        // Get the list of assigned services
568        IdServices idServices = IdServicesFactory.getDataStoreServices();
569        Set assigned = Collections.EMPTY_SET;
570        try {
571            assigned = idServices.getAssignedServices(token, type, name, sMap,
572                    orgName, univDN);
573        } catch (IdRepoException ide) {
574            // Check if this is permission denied exception
575            if (!ide.getErrorCode().equals(IdRepoErrorCode.ACCESS_DENIED)) {
576                throw (ide);
577            }
578        }
579        return (assigned);
580    }
581
582    /**
583     * Returns all services which can be assigned to this entity.
584     *
585     * This method is only valid for AMIdentity object of type User.
586     *
587     * @return Set of service names
588     * @throws IdRepoException
589     *             if there are repository related error conditions.
590     * @throws SSOException
591     *             If user's single sign on token is invalid.
592     * @supported.api
593     */
594    public Set getAssignableServices() throws IdRepoException, SSOException {
595        // Get all service names for the type from SMS
596        ServiceManager sm;
597        try {
598            sm = new ServiceManager(token);
599        } catch (SMSException smse) {
600            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode
601                    .SERVICE_MANAGER_INITIALIZATION_FAILED, null);
602        }
603        Map sMap = sm.getServiceNamesAndOCs(type.getName());
604
605        // Get the list of assigned services
606        IdServices idServices = IdServicesFactory.getDataStoreServices();
607        Set assigned = Collections.EMPTY_SET;
608        try {
609            assigned = idServices.getAssignedServices(token, type, name, sMap,
610                    orgName, univDN);
611        } catch (IdRepoException ide) {
612            // Check if this is permission denied exception
613            if (!ide.getErrorCode().equals(IdRepoErrorCode.ACCESS_DENIED)) {
614                throw (ide);
615            } else {
616                // Return the empty set
617                return (assigned);
618            }
619        }
620
621        // Return the difference
622        Set keys = sMap.keySet();
623        keys.removeAll(assigned);
624        return (keys);
625
626    }
627
628    /**
629     * Assigns the service and service related attributes to the identity.
630     *
631     * This method is only valid for AMIdentity object of type User.
632     *
633     * @param serviceName
634     *            Name of service to be assigned.
635     * @param attributes
636     *            Map of attribute-values
637     * @throws IdRepoException
638     *             If there are repository related error conditions.
639     * @throws SSOException
640     *             If user's single sign on token is invalid.
641     * @supported.api
642     */
643    public void assignService(String serviceName, Map attributes)
644            throws IdRepoException, SSOException {
645
646        IdServices idServices = IdServicesFactory.getDataStoreServices();
647        Set OCs = getServiceOCs(token, serviceName);
648        SchemaType stype;
649        Map tMap = new HashMap();
650        tMap.put(serviceName, OCs);
651        Set assignedServices = idServices.getAssignedServices(token, type,
652                name, tMap, orgName, univDN);
653
654        if (assignedServices.contains(serviceName)) {
655            Object args[] = { serviceName, type.getName() };
656            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_ALREADY_ASSIGNED, args);
657        }
658
659        // Validate the service attributes
660        try {
661            ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName,
662                    token);
663            ServiceSchema ss = ssm.getSchema(type.getName());
664
665            if (ss != null) {
666                // Check if attrMap has cos priority attribute
667                // If present, remove it for validating the attributes
668                Set cosPriority = (attributes != null) ?
669                    (Set)attributes.remove(COS_PRIORITY) : null;
670                attributes = ss.validateAndInheritDefaults(attributes, orgName,
671                        true);
672                if (cosPriority != null) {
673                    attributes.put(COS_PRIORITY, cosPriority);
674                }
675                attributes = AMCommonUtils.removeEmptyValues(attributes);
676                stype = ss.getServiceType();
677            } else {
678                ss = ssm.getSchema(SchemaType.DYNAMIC);
679                if (ss == null) {
680                    Object args[] = { serviceName };
681                    throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.UNABLE_GET_SERVICE_SCHEMA,
682                            args);
683                }
684                if (attributes == null) {
685                    try {
686                        attributes = getServiceConfig(token, serviceName,
687                                SchemaType.DYNAMIC);
688                    } catch (SMSException smsex) {
689                        Object args[] = { serviceName, type.getName() };
690                        throw new IdRepoException(IdRepoBundle.BUNDLE_NAME,
691                                "451", args);
692                    }
693                } else {
694                    attributes = ss.validateAndInheritDefaults(attributes,
695                            orgName, true);
696                }
697                attributes = AMCommonUtils.removeEmptyValues(attributes);
698                stype = SchemaType.DYNAMIC;
699            }
700
701            // TODO: Remove this dependency of AMCrypt
702            attributes = AMCrypt.encryptPasswords(attributes, ss);
703        } catch (SMSException smse) {
704            // debug.error here
705            Object[] args = { serviceName };
706            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_NOT_ASSIGNED, args);
707        }
708
709        attributes.put("objectclass", OCs);
710        // The protocol for params is to pass the
711        // name of the service, and attribute Map containing the
712        // OCs to be set and validated attribute map
713        idServices.assignService(token, type, name, serviceName, stype,
714                attributes, orgName, univDN);
715    }
716
717    /**
718     * Removes a service from the identity.
719     *
720     * This method is only valid for AMIdentity object of type User.
721     *
722     * @param serviceName
723     *            Name of service to be removed.
724     * @throws IdRepoException
725     *             If there are repository related error conditions.
726     * @throws SSOException
727     *             If user's single sign on token is invalid.
728     * @supported.api
729     */
730    public void unassignService(String serviceName) throws IdRepoException,
731            SSOException {
732        IdServices idServices = IdServicesFactory.getDataStoreServices();
733        Set OCs = getServiceOCs(token, serviceName);
734
735        Map tMap = new HashMap();
736        tMap.put(serviceName, OCs);
737        Set assignedServices = idServices.getAssignedServices(token, type,
738                name, tMap, orgName, univDN);
739
740        if (!assignedServices.contains(serviceName)) {
741            Object args[] = { serviceName };
742            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_NOT_ASSIGNED, args);
743        }
744
745        Map attrMap = new HashMap();
746        Set objectclasses = getAttribute("objectclass");
747        if (objectclasses != null && !objectclasses.isEmpty()) {
748            Set removeOCs = AMCommonUtils.updateAndGetRemovableOCs(
749                    objectclasses, OCs);
750
751            try {
752                // Get attribute names for USER type only, so plugin knows
753                // what attributes to remove.
754                Set attrNames = new HashSet();
755                ServiceSchemaManager ssm = new ServiceSchemaManager(
756                        serviceName, token);
757                ServiceSchema uss = ssm.getSchema(type.getName());
758
759                if (uss != null) {
760                    attrNames = uss.getAttributeSchemaNames();
761                }
762
763                Iterator it = attrNames.iterator();
764                while (it.hasNext()) {
765                    String a = (String) it.next();
766                    attrMap.put(a, Collections.EMPTY_SET);
767                }
768            } catch (SMSException smse) {
769                /*
770                 * debug.error( "AMIdentity.unassignService: Caught SM
771                 * exception", smse); do nothing
772                 */
773            }
774
775            attrMap.put("objectclass", removeOCs);
776            // The protocol is to pass service Name and Map of objectclasses
777            // to be removed from entry.
778        }
779
780        idServices.unassignService(token, type, name, serviceName, attrMap,
781                orgName, univDN);
782    }
783
784    /**
785     * Returns attributes related to a service, if the service is assigned to
786     * the identity.
787     *
788     * This method is only valid for AMIdentity object of type User.
789     *
790     * @param serviceName
791     *            Name of the service.
792     * @return Map of attribute-values.
793     * @throws IdRepoException
794     *             if there are repository related error conditions.
795     * @throws SSOException
796     *             If user's single sign on token is invalid.
797     * @supported.api
798     */
799    public Map getServiceAttributes(String serviceName)
800        throws IdRepoException, SSOException {
801        Set attrNames = getServiceAttributesName(serviceName);
802
803        IdServices idServices =
804            IdServicesFactory.getDataStoreServices();
805        if (debug.messageEnabled()) {
806            debug.message("AMIdentity.getServiceAttributes: attrNames="
807                + attrNames + ";  orgName=" + orgName + ";  univDN=" + univDN);
808       }
809        return idServices.getServiceAttributes(token, type, name, serviceName,
810            attrNames, orgName, univDN);
811    }
812
813
814    /**
815     * Returns attributes related to a service, if the service is assigned
816     * to the identity.
817     *
818     * This method is only valid for AMIdentity object of type User.
819     *
820     * @param serviceName Name of the service.
821     * @return Map of attribute-values in array of byte.
822     * @throws IdRepoException if there are repository related error conditions.
823     * @throws SSOException If user's single sign on token is invalid.
824     * iPlanet-PUBLIC-METHOD
825     */
826    public Map getBinaryServiceAttributes(String serviceName)
827        throws IdRepoException, SSOException {
828        Set attrNames = getServiceAttributesName(serviceName);
829
830        IdServices idServices =
831            IdServicesFactory.getDataStoreServices();
832        if (debug.messageEnabled()) {
833            debug.message("AMIdentity.getBinaryServiceAttributes: attrNames="
834                + attrNames + ";  orgName=" + orgName + ";  univDN=" + univDN);
835        }
836        return idServices.getBinaryServiceAttributes(token, type, name,
837            serviceName, attrNames, orgName, univDN);
838    }
839
840
841    /**
842     * Returns attributes related to a service, if the service is assigned
843     * to the identity.
844     *
845     * This method is only valid for AMIdentity object of type User.
846     *
847     * @param serviceName Name of the service.
848     * @return Map of attribute-values.
849     * @throws IdRepoException if there are repository related error conditions.
850     * @throws SSOException If user's single sign on token is invalid.
851     * @supported.api
852     */
853    public Map getServiceAttributesAscending(String serviceName)
854        throws IdRepoException, SSOException {
855        Set attrNames = getServiceAttributesName(serviceName);
856
857        IdServices idServices =
858            IdServicesFactory.getDataStoreServices();
859        if (debug.messageEnabled()) {
860            debug.message("AMIdentity.getServiceAttributesAscending: "
861                + "attrNames=" + attrNames + ";  orgName=" + orgName
862                + ";  univDN=" + univDN);
863        }
864        return idServices.getServiceAttributesAscending(token, type, name,
865            serviceName, attrNames, orgName, univDN);
866    }
867
868
869    /**
870     * Set attributes related to a specific service. The assumption is that the
871     * service is already assigned to the identity. The attributes for the
872     * service are validated against the service schema.
873     *
874     * This method is only valid for AMIdentity object of type User.
875     *
876     * @param serviceName
877     *            Name of the service.
878     * @param attrMap
879     *            Map of attribute-values.
880     * @throws IdRepoException
881     *             If there are repository related error conditions.
882     * @throws SSOException
883     *             If user's single sign on token is invalid.
884     * @supported.api
885     */
886    public void modifyService(String serviceName, Map attrMap)
887            throws IdRepoException, SSOException {
888        IdServices idServices = IdServicesFactory.getDataStoreServices();
889        Set OCs = getServiceOCs(token, serviceName);
890        SchemaType stype;
891        Map tMap = new HashMap();
892        tMap.put(serviceName, OCs);
893        Set assignedServices = idServices.getAssignedServices(token, type,
894                name, tMap, orgName, univDN);
895        if (!assignedServices.contains(serviceName)) {
896            Object args[] = { serviceName };
897            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_NOT_ASSIGNED, args);
898        }
899
900        // Check if attrMap has cos priority attribute
901        // If present, remove it for validating the attributes
902        boolean hasCosPriority = (new CaseInsensitiveHashSet(
903            attrMap.keySet()).contains(COS_PRIORITY));
904        Object values = null;
905        if (hasCosPriority) {
906             attrMap = new CaseInsensitiveHashMap(attrMap);
907             values = attrMap.remove(COS_PRIORITY);
908        }
909
910        // Validate the attributes
911        try {
912            ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName,
913                    token);
914            ServiceSchema ss = ssm.getSchema(type.getName());
915            if (ss != null) {
916                attrMap = ss.validateAndInheritDefaults(attrMap, false);
917                stype = ss.getServiceType();
918            } else if ((ss = ssm.getSchema(SchemaType.DYNAMIC)) != null) {
919                 attrMap = ss.validateAndInheritDefaults(attrMap, false);
920                 stype = SchemaType.DYNAMIC;
921            } else {
922                 Object args[] = { serviceName };
923                 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME,
924                         IdRepoErrorCode.UNABLE_GET_SERVICE_SCHEMA, args);
925            }
926        } catch (SMSException smse) {
927            // debug.error
928            Object args[] = { serviceName };
929            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.DATA_INVALID_FOR_SERVICE, args);
930        }
931
932        // Add COS priority if present
933        if (hasCosPriority) {
934            attrMap.put(COS_PRIORITY, values);
935        }
936
937        // modify service attrs
938        if (debug.messageEnabled()) {
939            debug.message("AMIdentity.modifyService befre idService " +
940                "serviceName=" + serviceName + ";  attrMap=" + attrMap);
941        }
942        idServices.modifyService(token, type, name, serviceName, stype,
943            attrMap, orgName, univDN);
944    }
945
946
947    /**
948
949     * Removes attributes value related to a specific service by
950     * setting it to empty.
951     * The assumption is that the service is already assigned to
952     * the identity. The attributes for the service are validated
953     * against the service schema.
954     *
955     * This method is only valid for <AMIdentity> object of type User.
956     *
957     * @param serviceName Name of the service.
958     * @param attrNames Set of attributes name.
959     * @throws IdRepoException If there are repository related error conditions.
960     * @throws SSOException If user's single sign on token is invalid.
961     * @supported.api
962     */
963    public void removeServiceAttributes(String serviceName, Set attrNames)
964        throws IdRepoException, SSOException {
965        Map attrMap = new HashMap(attrNames.size() *2);
966        Iterator it = attrNames.iterator();
967        while (it.hasNext()) {
968            String attrName = (String) it.next();
969            attrMap.put(attrName, Collections.EMPTY_SET);
970        }
971        modifyService(serviceName, attrMap);
972    }
973
974
975    // MEMBERSHIP RELATED APIS
976    /**
977     * Verifies if this identity is a member of the identity being passed.
978     *
979     * This method is only valid for AMIdentity objects of type Role, Group and
980     * User.
981     *
982     * @param identity
983     *            <code>AMIdentity</code> to check membership with
984     * @return true if this Identity is a member of the given Identity
985     * @throws IdRepoException
986     *             if there are repository related error conditions.
987     * @throws SSOException
988     *             if user's single sign on token is invalid.
989     * @supported.api
990     */
991    public boolean isMember(AMIdentity identity) throws IdRepoException,
992            SSOException {
993        boolean ismember = false;
994        IdRepoException idException = null;
995        IdServices idServices = IdServicesFactory.getDataStoreServices();
996        try {
997            //This method should always retrieve all the membership information a user could possibly have (either
998            //through the user when memberOf attribute is defined, or through the group using uniquemember attribute),
999            //hence there is no need to try to look up the group and query its members to see if this given identity
1000            //is in that list.
1001            //Generally speaking, this should be the case for every IdRepo implementation -> when we ask for the user
1002            //memberships, we should always get all of them for the sake of consistency.
1003            Set members = idServices.getMemberships(token, getType(),
1004                    getName(), identity.getType(), orgName, getDN());
1005            if (members != null && members.contains(identity)) {
1006                ismember = true;
1007            } else if (members != null) {
1008                // Check for fully qualified names or
1009                // if AM SDK DNs for these identities match
1010                String dn = identity.getDN();
1011                Iterator it = members.iterator();
1012                while (it.hasNext()) {
1013                    AMIdentity id = (AMIdentity) it.next();
1014                    if (identity.equals(id)) {
1015                        ismember = true;
1016                        break;
1017                    } else if (dn != null) {
1018                        String mdn = id.getDN();
1019                        if ((mdn != null) && mdn.equalsIgnoreCase(dn)) {
1020                            ismember = true;
1021                            break;
1022                        }
1023                    }
1024                }
1025            }
1026
1027            // If membership is still false, check only the UUID
1028            // without the amsdkdn
1029            if (!ismember && members != null && !members.isEmpty()) {
1030                // Get UUID without amsdkdn for "membership" identity
1031                String identityDN = identity.getUniversalId();
1032                String amsdkdn = identity.getDN();
1033                if ((amsdkdn != null) &&
1034                    (identityDN.toLowerCase().indexOf(",amsdkdn=") != -1)) {
1035                    identityDN = identityDN.substring(0, identityDN
1036                            .indexOf(amsdkdn) - 9);
1037                }
1038                // Get UUID without amsdkdn for users memberships
1039                Iterator it = members.iterator();
1040                while (it.hasNext()) {
1041                    AMIdentity id = (AMIdentity) it.next();
1042                    String idDN = id.getUniversalId();
1043                    String mdn = id.getDN();
1044                    if (mdn != null) {
1045                        int endIdx = idDN.indexOf(mdn) - 9;
1046                        if (endIdx >= 0) {
1047                            idDN = idDN.substring(0, endIdx);
1048                        }
1049                    }
1050                    if (idDN.equalsIgnoreCase(identityDN)) {
1051                        ismember = true;
1052                        break;
1053                    }
1054                }
1055            }
1056
1057        } catch (IdRepoException ide) {
1058            // Save the exception to be used later
1059            idException = ide;
1060        }
1061
1062        if (idException != null) {
1063            throw (idException);
1064        }
1065        return ismember;
1066    }
1067
1068    /**
1069     * @supported.api
1070     *
1071     * If membership is supported then add the new identity as a member.
1072     *
1073     * @param identity
1074     *            AMIdentity to be added
1075     * @throws IdRepoException
1076     *             if there are repository related error conditions.
1077     * @throws SSOException
1078     *             if user's single sign on token is invalid. non-public methods
1079     */
1080    public void addMember(AMIdentity identity) throws IdRepoException,
1081            SSOException {
1082        IdServices idServices = IdServicesFactory.getDataStoreServices();
1083        Set members = new HashSet();
1084        members.add(identity.getName());
1085        idServices.modifyMemberShip(token, type, name, members, identity
1086                .getType(), IdRepo.ADDMEMBER, orgName);
1087    }
1088
1089    /**
1090     * @supported.api
1091     *
1092     * Removes the identity from this identity's membership.
1093     *
1094     * @param identity
1095     *            AMIdentity to be removed from membership.
1096     * @throws IdRepoException
1097     *             if there are repository related error conditions.
1098     * @throws SSOException
1099     *             if user's single sign on token is invalid. non-public methods
1100     */
1101    public void removeMember(AMIdentity identity) throws IdRepoException,
1102            SSOException {
1103        IdServices idServices = IdServicesFactory.getDataStoreServices();
1104        Set members = new HashSet();
1105        members.add(identity.getName());
1106        idServices.modifyMemberShip(token, type, name, members, identity
1107                .getType(), IdRepo.REMOVEMEMBER, orgName);
1108    }
1109
1110    /**
1111     * @supported.api
1112     *
1113     * Removes the identities from this identity's membership.
1114     *
1115     * @param identityObjects
1116     *            Set of AMIdentity objects
1117     * @throws IdRepoException
1118     *             if there are repository related error conditions.
1119     * @throws SSOException
1120     *             if user's single sign on token is invalid. non-public methods
1121     */
1122    public void removeMembers(Set identityObjects) throws IdRepoException,
1123            SSOException {
1124        IdServices idServices = IdServicesFactory.getDataStoreServices();
1125        Set members = new HashSet();
1126        Iterator it = identityObjects.iterator();
1127
1128        while (it.hasNext()) {
1129            AMIdentity identity = (AMIdentity) it.next();
1130            members.add(identity.getName());
1131            idServices.modifyMemberShip(token, type, name, members, identity
1132                    .getType(), IdRepo.REMOVEMEMBER, orgName);
1133            members = new HashSet();
1134        }
1135    }
1136
1137    /**
1138     * Return all members of a given identity type of this identity as a Set of
1139     * AMIdentity objects.
1140     *
1141     * This method is only valid for AMIdentity objects of type Group and User.
1142     *
1143     * @param mtype
1144     *            Type of identity objects
1145     * @return Set of AMIdentity objects that are members of this object.
1146     * @throws IdRepoException
1147     *             if there are repository related error conditions.
1148     * @throws SSOException
1149     *             if user's single sign on token is invalid.
1150     * @supported.api
1151     */
1152    public Set getMembers(IdType mtype) throws IdRepoException, SSOException {
1153        IdServices idServices = IdServicesFactory.getDataStoreServices();
1154        return idServices
1155                .getMembers(token, type, name, orgName, mtype, getDN());
1156    }
1157
1158    /**
1159     * Returns the set of identities that this identity belongs to.
1160     *
1161     * This method is only valid for AMIdentity objects of type User and Role.
1162     *
1163     * @param mtype
1164     *            Type of member identity.
1165     * @return Set of AMIdentity objects of the given type that this identity
1166     *         belongs to.
1167     * @throws IdRepoException
1168     *             if there are repository related error conditions.
1169     * @throws SSOException
1170     *             if user's single sign on token is invalid.
1171     * @supported.api
1172     */
1173    public Set getMemberships(IdType mtype) throws IdRepoException,
1174            SSOException {
1175        IdServices idServices = IdServicesFactory.getDataStoreServices();
1176        return idServices.getMemberships(token, type, name, mtype, orgName,
1177                getDN());
1178    }
1179
1180    /**
1181     * This method determines if the identity exists and returns true or false.
1182     *
1183     * This method is only valid for AMIdentity objects of type User and Agent.
1184     *
1185     * @return true if the identity exists or false otherwise.
1186     * @throws IdRepoException
1187     *             If there are repository related error conditions.
1188     * @throws SSOException
1189     *             If user's single sign on token is invalid.
1190     * @supported.api
1191     */
1192    public boolean isExists() throws IdRepoException, SSOException {
1193        IdServices idServices = IdServicesFactory.getDataStoreServices();
1194        return idServices.isExists(token, type, name, orgName);
1195    }
1196
1197    /**
1198     * Returns <code>true</code> if the given object is equal to this object.
1199     *
1200     * @param o Object for comparison.
1201     * @return <code>true</code> if the given object is equal to this object.
1202     * @supported.api
1203     */
1204    @Override
1205    public boolean equals(Object o) {
1206        boolean isEqual = false;
1207        if (o instanceof AMIdentity) {
1208            AMIdentity compareTo = (AMIdentity) o;
1209            if (univIdWithoutDN.equalsIgnoreCase(
1210                compareTo.univIdWithoutDN)) {
1211                isEqual = true;
1212            } else if (univDN != null) {
1213                // check if the amsdkdn match
1214                String dn = compareTo.getDN();
1215                if (dn != null && dn.equalsIgnoreCase(univDN)) {
1216                    isEqual = true;
1217                }
1218            }
1219
1220            if (!isEqual && !type.equals(IdType.REALM) &&
1221                type.equals(compareTo.getType())) {
1222                // Check fully qualified names
1223                Set sfqn = getFullyQualifiedNames();
1224                Set cfqn = compareTo.getFullyQualifiedNames();
1225                if ((sfqn != null) && (cfqn != null) &&
1226                    !sfqn.isEmpty() && !cfqn.isEmpty()) {
1227                    for (Iterator items = sfqn.iterator();
1228                        items.hasNext();) {
1229                        String next = (String)items.next();
1230                        if (next != null && cfqn.contains(next)) {
1231                            isEqual = true;
1232                            break;
1233                        }
1234                    }
1235                }
1236            }
1237        }
1238        return (isEqual);
1239    }
1240
1241    /**
1242     * Non-javadoc, non-public methods
1243     */
1244    @Override
1245    public int hashCode() {
1246        return (univIdWithoutDN.toLowerCase().hashCode());
1247    }
1248
1249    /**
1250     * Nonjavadoc, non-public methods
1251     *
1252     */
1253    public void setDN(String dn) {
1254        univDN = dn;
1255    }
1256
1257    /**
1258     * Returns universal distinguished name of this object.
1259     *
1260     * @return universal distinguished name of this object.
1261     */
1262    public String getDN() {
1263        return univDN;
1264    }
1265
1266    /**
1267     * Returns the universal identifier of this object.
1268     *
1269     * @return String representing the universal identifier of this object.
1270     * @supported.api
1271     */
1272    public String getUniversalId() {
1273        return univIdWithoutDN;
1274    }
1275
1276    /**
1277     * Returns String representation of the <code>AMIdentity</code>
1278     * object. It returns universal identifier, orgname, type, etc.
1279     *
1280     * @return String representation of the <code>ServiceConfig</code> object.
1281     */
1282    @Override
1283    public String toString() {
1284        StringBuilder sb = new StringBuilder(100);
1285        sb.append("AMIdentity object: ").append(univIdWithoutDN);
1286        if (univDN != null) {
1287            sb.append("AMSDKDN=").append(univDN);
1288        }
1289        return (sb.toString());
1290    }
1291
1292    // Returns a set of fully qulified names, as returned by DataStores
1293    protected Set getFullyQualifiedNames() {
1294        if (fullyQualifiedNames == null) {
1295            try {
1296                IdServices idServices =
1297                    IdServicesFactory.getDataStoreServices();
1298                fullyQualifiedNames = idServices.getFullyQualifiedNames(
1299                    token, type, name, orgName);
1300            } catch (IdRepoException ire) {
1301                if (debug.messageEnabled()) {
1302                    debug.message("AMIdentity:getFullyQualifiedNames: " +
1303                        "got exception: ", ire);
1304                }
1305            } catch (SSOException ssoe) {
1306                if (debug.messageEnabled()) {
1307                    debug.message("AMIdentity:getFullyQualifiedNames: " +
1308                        "got exception: ", ssoe);
1309                }
1310            }
1311        }
1312        return (fullyQualifiedNames);
1313    }
1314
1315    private Set getServiceOCs(SSOToken token, String serviceName)
1316            throws SSOException {
1317        Set result = new HashSet();
1318        try {
1319            if (serviceHasSubSchema(token, serviceName, SchemaType.GLOBAL)) {
1320                Map attrs = getServiceConfig(token, serviceName,
1321                        SchemaType.GLOBAL);
1322                Set vals = (Set) attrs.get("serviceObjectClasses");
1323
1324                if (vals != null) {
1325                    result.addAll(vals);
1326                }
1327            }
1328        } catch (SMSException smsex) {
1329        }
1330
1331        return result;
1332    }
1333
1334    /**
1335     * Get service default config from SMS
1336     *
1337     * @param token
1338     *            SSOToken a valid SSOToken
1339     * @param serviceName
1340     *            the service name
1341     * @param type
1342     *            service schema type (Dynamic, Policy etc)
1343     * @return returns a Map of Default Configuration values for the specified
1344     *         service.
1345     */
1346    private Map getServiceConfig(SSOToken token, String serviceName,
1347            SchemaType type) throws SMSException, SSOException {
1348        Map attrMap = null; // Map of attribute/value pairs
1349        if (type != SchemaType.POLICY) {
1350            ServiceSchemaManager scm = new ServiceSchemaManager(serviceName,
1351                    token);
1352            ServiceSchema gsc = scm.getSchema(type);
1353            attrMap = gsc.getAttributeDefaults();
1354        }
1355        return attrMap;
1356    }
1357
1358    /**
1359     * Returns true if the service has the subSchema. False otherwise.
1360     *
1361     * @param token
1362     *            SSOToken a valid SSOToken
1363     * @param serviceName
1364     *            the service name
1365     * @param schemaType
1366     *            service schema type (Dynamic, Policy etc)
1367     * @return true if the service has the subSchema.
1368     */
1369    private boolean serviceHasSubSchema(SSOToken token, String serviceName,
1370            SchemaType schemaType) throws SMSException, SSOException {
1371        boolean schemaTypeFlg = false;
1372        try {
1373            ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName,
1374                    token);
1375            Set types = ssm.getSchemaTypes();
1376            if (debug.messageEnabled()) {
1377                debug.message("AMServiceUtils.serviceHasSubSchema() "
1378                        + "SchemaTypes types for " + serviceName + " are: "
1379                        + types);
1380            }
1381            schemaTypeFlg = types.contains(schemaType);
1382        } catch (ServiceNotFoundException ex) {
1383            if (debug.warningEnabled()) {
1384                debug.warning("AMServiceUtils.serviceHasSubSchema() "
1385                        + "Service does not exist : " + serviceName);
1386            }
1387        }
1388        return (schemaTypeFlg);
1389    }
1390
1391    private Set getServiceAttributesName(String serviceName)
1392        throws IdRepoException, SSOException {
1393        Set attrNames = Collections.EMPTY_SET;
1394
1395        try {
1396            // Get attribute names for USER type only, so plugin knows
1397            // what attributes to remove.
1398            attrNames = new HashSet();
1399            ServiceSchemaManager ssm = new ServiceSchemaManager(
1400                serviceName, token);
1401            ServiceSchema uss = ssm.getSchema(type.getName());
1402
1403            if (uss != null) {
1404                attrNames = uss.getAttributeSchemaNames();
1405            }
1406            // If the identity type is not of role, filteredrole or
1407            // realm, need to add dynamic attributes also
1408            if (!(type.equals(IdType.ROLE) || type.equals(IdType.REALM) ||
1409                type.equals(IdType.FILTEREDROLE))) {
1410                uss = ssm.getDynamicSchema();
1411                if (uss != null) {
1412                    if (attrNames == Collections.EMPTY_SET) {
1413                        attrNames = uss.getAttributeSchemaNames();
1414                    } else {
1415                        attrNames.addAll(uss.getAttributeSchemaNames());
1416                    }
1417                }
1418            } else {
1419                // Add COS priority attribute
1420                attrNames.add(COS_PRIORITY);
1421            }
1422        } catch (SMSException smse) {
1423            if (debug.messageEnabled()) {
1424                debug.message(
1425                    "AMIdentity.getServiceAttributes: Caught SM exception",
1426                    smse);
1427            }
1428            // just returned whatever we find or empty set
1429            // if services is not found.
1430        }
1431
1432        return attrNames;
1433    }
1434
1435    private static Debug debug = Debug.getInstance("amIdm");
1436
1437    public static String COS_PRIORITY = "cospriority";
1438}