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