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