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: AMStoreConnection.java,v 1.13 2009/01/28 05:34:47 ww203982 Exp $
026 *
027 * Portions Copyrighted 2011-2015 ForgeRock AS.
028 */
029
030package com.iplanet.am.sdk;
031
032import com.iplanet.am.sdk.common.IDirectoryServices;
033import com.iplanet.sso.SSOException;
034import com.iplanet.sso.SSOToken;
035import com.iplanet.sso.SSOTokenManager;
036import com.sun.identity.sm.DNMapper;
037import com.sun.identity.sm.SMSEntry;
038import com.sun.identity.sm.SMSException;
039import com.sun.identity.sm.SchemaType;
040import com.sun.identity.sm.ServiceConfig;
041import com.sun.identity.sm.ServiceManager;
042import com.sun.identity.sm.ServiceSchemaManager;
043import org.forgerock.openam.ldap.LDAPUtils;
044import org.forgerock.opendj.ldap.DN;
045
046import java.text.NumberFormat;
047import java.text.ParseException;
048import java.text.ParsePosition;
049import java.text.SimpleDateFormat;
050import java.util.ArrayList;
051import java.util.Collections;
052import java.util.Date;
053import java.util.HashSet;
054import java.util.Iterator;
055import java.util.List;
056import java.util.Map;
057import java.util.Set;
058import java.util.StringTokenizer;
059
060
061/**
062 * The <code>AMStoreConnection</code> class represents a connection to the Sun
063 * Java System Access Manager data store. It provides methods to create, remove
064 * and get different type of Sun Java System Access Manager SDK objects in the
065 * data tore. <code>AMStoreConnection</code> controls and manages access to
066 * the data store.
067 * <p>
068 * An instance of <code>AMStoreConnection</code> object should always be
069 * obtained by anyone using the AM SDK since this object is the entry point to
070 * all other AM SDK managed objects. The constructor takes the SSO token of the
071 * user. Here is some sample code on how to get a user's attributes, using AM
072 * SDK:
073 * 
074 * <PRE>
075 * 
076 * AMStoreConnection amsc = new AMStoreConnection(ssotoken); AMUser user =
077 * amsc.getUser(ssotoken.getPrincipal()); Map attributes = user.getAttributes();
078 * 
079 * </PRE>
080 * 
081 * <p>
082 * <code>AMStoreConnection</code> also has other helper methods which are very
083 * useful. Some examples below:
084 * 
085 * <PRE>
086 * 
087 * int otype = amsc.getAMObjectType(fullDN);
088 * 
089 * </PRE>
090 * 
091 * <p>
092 * <code>otype</code> returned is one of the managed <code>AMObject</code>
093 * types, like <code>AMObject.USER</code>, <code>AMObject.ROLE</code>,
094 * <code>AMObject.ORGANIZATION</code>. If the entry being checked in not of
095 * the type managed by AM SDK, then an <code>AMException</code> is thrown.
096 * 
097 * <PRE>
098 * 
099 * boolean exists = amsc.isValidEntry(fullDN);
100 * 
101 * </PRE>
102 * 
103 * <p>
104 * If there is a <code>fullDN</code> that you want to know if it exists or not
105 * in the data store, then use the above method. The typical use of this method
106 * is in the case when you know that you need to get a managed object from
107 * <code>amsc</code>, but you want to verify that it exists before you create
108 * the managed object instance:
109 * 
110 * <PRE>
111 * 
112 * if (amsc.isValidEntry(userDN)) { AMUser user = amsc.getUser(userDN); - More
113 * code here - }
114 * 
115 * </PRE>
116 * 
117 * <p>
118 * Helper method <code>getOrganizationDN()</code>: Use this method to perform
119 * a subtree scoped search for organization,based on various attribute values.
120 * 
121 * <PRE>
122 * 
123 * String orgDN = amsc.getOrganizationDN("sun.com", null);
124 * 
125 * </PRE>
126 * 
127 * <p>
128 * The above method will return the DN of a organization, which matches the
129 * search criterias of having either domain name of <code>sun.com</code>,
130 * Domain alias name of <code>sun.com</code> or it's naming attribute value is
131 * <code>sun.com</code>. More examples of how to use this method are provided
132 * in the Javadocs of the method below.
133 * 
134 * @deprecated  As of Sun Java System Access Manager 7.1.
135 * @supported.all.api
136 */
137public final class AMStoreConnection implements AMConstants {
138    // ~ Static fields/initializers
139    // ---------------------------------------------
140
141    public static String rootSuffix;
142
143    protected static String defaultOrg;
144
145    protected static Map orgMapCache = new AMHashMap();
146
147    // ~ Instance fields
148    // --------------------------------------------------------
149
150    private IDirectoryServices dsServices;
151
152    private SSOToken token;
153
154    private String locale = "en_US";
155
156    // ~ Constructors
157    // -----------------------------------------------------------
158
159    /**
160     * Gets the connection to the Sun Java System Access Manager data store if
161     * the Session is valid.
162     * 
163     * @param ssoToken
164     *            a valid SSO token object to authenticate before getting the
165     *            connection
166     * @throws SSOException
167     *             if single sign on token is invalid or expired.
168     */
169    public AMStoreConnection(SSOToken ssoToken) throws SSOException {
170        // initialize whatever you want to here.
171        SSOTokenManager.getInstance().validateToken(ssoToken);
172        this.token = ssoToken;
173        this.locale = AMCommonUtils.getUserLocale(ssoToken);
174        dsServices = AMDirectoryAccessFactory.getDirectoryServices();
175    }
176
177    // ~ Methods
178    // ----------------------------------------------------------------
179
180    /**
181     * Returns the root suffix for user management node.
182     * 
183     * @return root suffix for user management node.
184     *
185     */
186    public static String getAMSdkBaseDN() {
187        defaultOrg = rootSuffix = 
188            com.sun.identity.common.DNUtils.normalizeDN(
189                SMSEntry.getAMSdkBaseDN());
190        // Get an instance as required otherwise it causes issues on container restart.
191        if (AMCommonUtils.debug.messageEnabled()) {
192            AMCommonUtils.debug.message("AMStoreConnection:getAMSdkBaseDN():rootsuffix " +
193                rootSuffix);
194        }
195        if (AMCommonUtils.debug.messageEnabled()) {
196            AMCommonUtils.debug.message("default org: " + defaultOrg);
197            AMCommonUtils.debug.message("AMStoreConnection:getAMSdkBaseDN():default org " +
198                defaultOrg);
199        }
200        return defaultOrg;
201    }
202
203    /**
204     * Returns the filtered role naming attribute.
205     * 
206     * @return filtered role naming attribute
207     * @deprecated This method is deprecated. Use
208     *             {@link #getNamingAttribute(int) 
209     *             getNamingAttribute(int objectType)}
210     */
211    public static String getFilteredRoleNamingAttribute() {
212        return AMNamingAttrManager.getNamingAttr(AMObject.FILTERED_ROLE);
213    }
214
215    /**
216     * Returns the group container naming attribute.
217     * 
218     * @return group container naming attribute
219     * @deprecated This method is deprecated. Use
220     *             {@link #getNamingAttribute(int) 
221     *             getNamingAttribute(int objectType)}
222     */
223    public static String getGroupContainerNamingAttribute() {
224        return AMNamingAttrManager.getNamingAttr(AMObject.GROUP_CONTAINER);
225    }
226
227    /**
228     * Returns the group naming attribute.
229     * 
230     * @return group naming attribute
231     * @deprecated This method is deprecated. Use
232     *             {@link #getNamingAttribute(int) 
233     *             getNamingAttribute(int objectType)}
234     */
235    public static String getGroupNamingAttribute() {
236        return AMNamingAttrManager.getNamingAttr(AMObject.GROUP);
237    }
238
239    /**
240     * Returns the naming attribute of an object type.
241     * 
242     * @param objectType
243     *            Object type can be one of the following:
244     *            <ul>
245     *            <li> {@link AMObject#USER AMObject.USER}
246     *            <li> {@link AMObject#ROLE AMObject.ROLE}
247     *            <li> {@link AMObject#FILTERED_ROLE AMObject.FILTERED_ROLE}
248     *            <li> {@link AMObject#ORGANIZATION AMObject.ORGANIZATION}
249     *            <li> {@link AMObject#ORGANIZATIONAL_UNIT
250     *            AMObject.ORGANIZATIONAL_UNIT}
251     *            <li> {@link AMObject#GROUP AMObject.GROUP}
252     *            <li> {@link AMObject#DYNAMIC_GROUP AMObject.DYNAMIC_GROUP}
253     *            <li> {@link AMObject#ASSIGNABLE_DYNAMIC_GROUP
254     *            AMObject.ASSIGNABLE_DYNAMIC_GROUP}
255     *            <li>
256     *            {@link AMObject#PEOPLE_CONTAINER AMObject.PEOPLE_CONTAINER}
257     *            <li> {@link AMObject#GROUP_CONTAINER AMObject.GROUP_CONTAINER}
258     *            </ul>
259     * @return the naming attribute corresponding to the <code>objectType</code>
260     * @throws AMException
261     *             if an error occurred in obtaining the naming attribute
262     */
263    public static String getNamingAttribute(int objectType) throws AMException {
264        return AMNamingAttrManager.getNamingAttr(objectType);
265    }
266
267    /**
268     * Returns the organization naming attribute.
269     * 
270     * @return organization naming attribute
271     * @deprecated This method is deprecated. Use
272     *             {@link #getNamingAttribute(int) 
273     *             getNamingAttribute(int objectType)}
274     */
275    public static String getOrganizationNamingAttribute() {
276        return AMNamingAttrManager.getNamingAttr(AMObject.ORGANIZATION);
277    }
278
279    /**
280     * Returns the organizational unit naming attribute.
281     * 
282     * @return organizational unit naming attribute
283     * @deprecated This method is deprecated. Use
284     *             {@link #getNamingAttribute(int) 
285     *             getNamingAttribute(int objectType)}
286     */
287    public static String getOrganizationalUnitNamingAttribute() {
288        return AMNamingAttrManager.getNamingAttr(AMObject.ORGANIZATIONAL_UNIT);
289    }
290
291    /**
292     * Returns the people container naming attribute.
293     * 
294     * @return people container naming attribute
295     * @deprecated This method is deprecated. Use
296     *             {@link #getNamingAttribute(int) 
297     *             getNamingAttribute(int objectType)}
298     */
299    public static String getPeopleContainerNamingAttribute() {
300        return AMNamingAttrManager.getNamingAttr(AMObject.PEOPLE_CONTAINER);
301    }
302
303    /**
304     * Returns the role naming attribute.
305     * 
306     * @return role naming attribute
307     * @deprecated This method is deprecated. Use
308     *             {@link #getNamingAttribute(int)
309     *             getNamingAttribute(int objectType)}
310     */
311    public static String getRoleNamingAttribute() {
312        return AMNamingAttrManager.getNamingAttr(AMObject.ROLE);
313    }
314
315    /**
316     * Returns the user naming attribute.
317     * 
318     * @return user naming attribute
319     * @deprecated This method is deprecated. Use
320     *             {@link #getNamingAttribute(int) 
321     *             getNamingAttribute(int objectType)}
322     */
323    public static String getUserNamingAttribute() {
324        return AMNamingAttrManager.getNamingAttr(AMObject.USER);
325    }
326
327    /**
328     * Returns the type of the object given its DN.
329     * 
330     * @param dn
331     *            DN of the object whose type is to be known.
332     * @return the type of the object given its DN.
333     * @throws AMException
334     *             if the data store is unavailable or if the object type is
335     *             unknown.
336     * @throws SSOException
337     *             if single sign on token is invalid or expired.
338     */
339    public int getAMObjectType(String dn) throws AMException, SSOException {
340        return dsServices.getObjectType(token, dn);
341    }
342
343    /**
344     * Take a supported type, and returns the matching name of the supported
345     * managed type. For example, if <code> AMObject.USER</code> is passed in,
346     * it will return "user" (one of the basic supported types in AM SDK. But
347     * this method (and configuration in the service <code>DAI</code>) can be
348     * used to extend the basic supported types to include customer-specific
349     * entities, like "agents", "printers" etc.
350     * 
351     * @param type
352     *            Integer type (as returned by <code>getAMObjectType</code>)
353     * @return identifier for the above type. Returns null if type is unknown.
354     */
355    public String getAMObjectName(int type) {
356        return ((String) AMCommonUtils.supportedNames.get(Integer
357                .toString(type)));
358    }
359
360    /**
361     * Take a supported type, and returns the matching name of the supported
362     * managed type. For example, if <code> AMObject.USER</code> is passed in,
363     * it will return "user" (one of the basic supported types in AM SDK. But
364     * this method (and configuration in the service <code>DAI</code>) can be
365     * used to extend the basic supported types to include customer-specific
366     * entities, like "agents", "printers" etc.
367     * 
368     * @param type
369     *            Integer type (as returned by <code>getAMObjectType</code>)
370     * @return identifier for the above type. Returns null if type is unknown.
371     */
372    public static String getObjectName(int type) {
373        return ((String) AMCommonUtils.supportedNames.get(Integer
374                .toString(type)));
375    }
376
377    /**
378     * Returns the handle to the <code>AMAssignableDynamicGroup</code> object
379     * represented by DN. However, the validity of the handle returned by this
380     * method cannot be guaranteed, since the object is created in memory, and
381     * not instantiated from the data store. Using the
382     * <code>AMAssignableDynamicGroup</code> returned from this method may
383     * result in exceptions thrown in the later part of the application, if the
384     * DN is not valid or represents an entry that does not exist.
385     * <p>
386     * 
387     * Validity of the DN can be verified is using <code>isValidEntry()</code>
388     * method of the object returned.
389     * 
390     * @see #isValidEntry
391     * 
392     * @param assignableDynamicGroupDN
393     *            assignable dynamic group DN
394     * @return <code>AMAssignableDynamicGroup</code> object represented by DN.
395     * @throws SSOException
396     *             if single sign on token is invalid or expired.
397     */
398    public AMAssignableDynamicGroup getAssignableDynamicGroup(
399            String assignableDynamicGroupDN) throws SSOException {
400        AMAssignableDynamicGroup assignableDynamicGroup = 
401            new AMAssignableDynamicGroupImpl(this.token, 
402                    assignableDynamicGroupDN);
403
404        return assignableDynamicGroup;
405    }
406
407    /**
408     * Returns the service attribute names for a given service name and schema
409     * type.
410     * 
411     * @param serviceName
412     *            the name of the service
413     * @param schemaType
414     *            the type of service schema
415     * @return Set of service attribute names
416     * @throws AMException
417     *             if an error is encountered while retrieving information.
418     * @deprecated use <code>com.sun.identity.sm.ServiceSchemaManager.
419     * getServiceAttributeNames(com.sun.identity.sm.SchemaType)</code>
420     */
421    public Set getAttributeNames(String serviceName, AMSchema.Type schemaType)
422            throws AMException {
423        try {
424            ServiceSchemaManager ssm = new ServiceSchemaManager(serviceName,
425                    token);
426            SchemaType st = schemaType.getInternalSchemaType();
427
428            return ssm.getServiceAttributeNames(st);
429        } catch (SSOException se) {
430            AMCommonUtils.debug.message("AMStoreConnection.getAttributeNames(String, "
431                    + "AMSchema.Type)", se);
432            throw new AMException(AMSDKBundle.getString("902", locale), "902");
433        } catch (SMSException se) {
434            AMCommonUtils.debug.message("AMStoreConnection.getAttributeNames(String, " +
435                    "AMSchema.Type)", se);
436            throw new AMException(AMSDKBundle.getString("911", locale), "911");
437        }
438    }
439
440    /**
441     * Returns the handle to the <code>AMDynamicGroup</code> object
442     * represented by DN. However, the validity of the handle returned by this
443     * method cannot be guaranteed, since the object is created in memory, and
444     * not instantiated from the data store. Using the
445     * <code>AMDynamicGroup</code> returned from this method may result in
446     * exceptions thrown in the later part of the application, if the DN is not
447     * valid or represents an entry that does not exist.
448     * <p>
449     * 
450     * Validity of the DN can be verified is using <code>isValidEntry()</code>
451     * method of the object returned.
452     * 
453     * @see #isValidEntry
454     * 
455     * @param dynamicGroupDN
456     *            group DN
457     * @return <code>AMDynamicGroup</code> object represented by DN.
458     * @throws SSOException
459     *             if single sign on token is invalid or expired.
460     */
461    public AMDynamicGroup getDynamicGroup(String dynamicGroupDN)
462            throws SSOException {
463        AMDynamicGroup dynamicGroup = new AMDynamicGroupImpl(this.token,
464                dynamicGroupDN);
465
466        return dynamicGroup;
467    }
468
469    /**
470     * Returns the handle to the <code>AMFilteredRole</code> object
471     * represented by DN. However, the validity of the handle returned by this
472     * method cannot be guaranteed, since the object is created in memory, and
473     * not instantiated from the data store. Using the
474     * <code>AMFilteredRole</code> returned from this method may result in
475     * exceptions thrown in the later part of the application, if the DN is not
476     * valid or represents an entry that does not exist.
477     * <p>
478     * 
479     * Validity of the DN can be verified is using <code>isValidEntry()</code>
480     * method of the object returned.
481     * 
482     * @see #isValidEntry
483     * 
484     * @param roleDN
485     *            role DN.
486     * @return <code>AMFilteredRole</code> object represented by DN.
487     * @throws SSOException
488     *             if single sign on token is invalid or expired.
489     */
490    public AMFilteredRole getFilteredRole(String roleDN) throws SSOException {
491        AMFilteredRole role = new AMFilteredRoleImpl(this.token, roleDN);
492
493        return role;
494    }
495
496    /**
497     * Returns the handle to the <code>AMGroupContainer</code> object
498     * represented by DN. However, the validity of the handle returned by this
499     * method cannot be guaranteed, since the object is created in memory, and
500     * not instantiated from the data store. Using the
501     * <code>AMGroupContainer</code> returned from this method may result in
502     * exceptions thrown in the later part of the application, if the DN is not
503     * valid or represents an entry that does not exist.
504     * <p>
505     * 
506     * Validity of the DN can be verified is using <code>isValidEntry()</code>
507     * method of the object returned.
508     * 
509     * @see #isValidEntry
510     * 
511     * @param groupContainerDN
512     *            group container DN.
513     * @return <code>AMGroupContainer</code> object represented by DN.
514     * @throws SSOException
515     *             if single sign on token is invalid or expired.
516     */
517    public AMGroupContainer getGroupContainer(String groupContainerDN)
518            throws SSOException {
519        AMGroupContainer groupContainer = new AMGroupContainerImpl(this.token,
520                groupContainerDN);
521
522        return groupContainer;
523    }
524
525    /**
526     * Returns the I18N properties file name that contains the internationalized
527     * messages.
528     * 
529     * @param serviceName
530     *            the service name
531     * @return String String representing i18N properties file name
532     * @throws AMException
533     *             if an error is encountered while retrieving information
534     */
535    public String getI18NPropertiesFileName(String serviceName)
536            throws AMException {
537        try {
538            ServiceSchemaManager scm = new ServiceSchemaManager(serviceName,
539                    token);
540
541            return scm.getI18NFileName();
542        } catch (SSOException so) {
543            AMCommonUtils.debug.error("AMStoreConnection.getI18NPropertiesFileName(): ", so);
544            throw new AMException(AMSDKBundle.getString("902", locale), "902");
545        } catch (SMSException se) {
546            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", se);
547            throw new AMException(AMSDKBundle.getString("909", locale), "909");
548        }
549    }
550
551    /**
552     * Returns the handle to the <code>AMOrganization</code> object
553     * represented by DN. However, the validity of the handle returned by this
554     * method cannot be guaranteed, since the object is created in memory, and
555     * not instantiated from the data store. Using the
556     * <code>AMOrganization</code> returned from this method may result in
557     * exceptions thrown in the later part of the application, if the DN is not
558     * valid or represents an entry that does not exist.
559     * <p>
560     * 
561     * Validity of the DN can be verified is using <code>isValidEntry()</code>
562     * method of the object returned.
563     * 
564     * @see #isValidEntry
565     * 
566     * @param orgDN
567     *            organization DN
568     * @return <code>AMOrganization</code> object represented by DN.
569     * @throws SSOException
570     *             if single sign on token is invalid or expired.
571     */
572    public AMOrganization getOrganization(String orgDN) throws SSOException {
573        AMOrganization organization = new AMOrganizationImpl(this.token, orgDN);
574
575        return organization;
576    }
577
578    /**
579     * Returns the DN of the organization, using the <code>domainname</code>
580     * provided and the <code>searchTemplate</code> (if provided). If
581     * <code>searchTemplate</code> is null, SDK uses the default
582     * <code>searchTemplate</code> to perform the <code>orgDN</code> search.
583     * If the DC tree global flag is enabled, the DC tree is used to obtain the
584     * organization DN, otherwise an LDAP search is conducted using the
585     * <code>searchfilter</code> in the <code>searchtemplate</code>. All
586     * <code>%V</code> in the filter are replaced with <code>domainname</code>.
587     * If the search returns more than one entries, then an Exception is thrown.
588     * Otherwise the DN obtained is returned.
589     * 
590     * @param domainname
591     *            Organization identifier passed. It can be a domain name
592     *            (example: <code>sun.com</code>) or it could be a full DN or
593     *            it could be null or <code>* "/"</code>. A full DN is
594     *            verified to be an organization and returned as is. A "/" is
595     *            assumed to be a request for the root DN and the root DN is
596     *            returned. A "/" separated string is assumed to represent an
597     *            existing organization DN in the DIT. For example:
598     *            <code>/iplanet/sun</code> is converted to a DN
599     *            <code>(o=iplanet,o=sun,&lt;base DN>)</code> and the validity
600     *            of this DN is checked and returned. Any other string is
601     *            assumed to be either a domain or an associated domain or the
602     *            organization name. The search filter is created accordingly.
603     * @param orgSearchTemplate
604     *            template to use for the search.
605     * @return The full organization DN
606     * @throws AMException
607     *             If there is a problem connecting or searching the data store.
608     * @throws SSOException
609     *             If the user has an invalid SSO token.
610     */
611    public String getOrganizationDN(String domainname, String orgSearchTemplate)
612            throws AMException, SSOException {
613        if (domainname == null) {
614            return rootSuffix;
615        }
616
617        // If a DN is passed and is a valid organization DN, then
618        // return it.
619        if (LDAPUtils.isDN(domainname) && isValidEntry(domainname)
620                && (getAMObjectType(domainname) == AMObject.ORGANIZATION)) {
621            return domainname;
622        }
623
624        if (!domainname.startsWith("http://") && (domainname.indexOf("/") > -1))
625        {
626            String orgdn = orgNameToDN(domainname);
627
628            if (isValidEntry(orgdn)
629                    && (getAMObjectType(orgdn) == AMObject.ORGANIZATION)) {
630                return (orgdn);
631            } else {
632                Object[] args = { orgdn };
633                String locale = AMCommonUtils.getUserLocale(token);
634                throw new AMException(AMSDKBundle
635                        .getString("467", args, locale), "467", args);
636            }
637        }
638
639        try {
640            String orgdn;
641
642            if (AMDCTree.isRequired()) {
643                orgdn = AMDCTree.getOrganizationDN(token, domainname);
644
645                if (orgdn != null) {
646                    return orgdn;
647                }
648            }
649        } catch (AMException ae) {
650            // do nothing. will try to search the organization
651            // using search template
652            AMCommonUtils.debug.error("AMStoreConnection.getOrganizationDN-> "
653                    + "In DC tree mode, unabe to find organization "
654                    + " for domain: " + domainname);
655        }
656
657        String orgdn = (String) orgMapCache.get(domainname.toLowerCase());
658
659        if (orgdn != null) {
660            return (orgdn);
661        } else {
662            // use the searchfilter to obtain org DN
663            // replace %V with domainname.
664            String searchFilter = AMSearchFilterManager.getSearchFilter(
665                    AMObject.ORGANIZATION, null, orgSearchTemplate, false);
666
667            if ((orgSearchTemplate != null)
668                    && (searchFilter.indexOf("%V") > -1)) {
669                searchFilter = AMObjectImpl.constructFilter(AMNamingAttrManager
670                        .getNamingAttr(AMObject.ORGANIZATION), searchFilter,
671                        domainname);
672            } else {
673                StringBuilder sb = new StringBuilder();
674                sb.append("(|(&").append(searchFilter).append("(").append(
675                        AMNamingAttrManager
676                                .getNamingAttr(AMObject.ORGANIZATION)).append(
677                        "=").append(domainname).append(")").append(")(&")
678                        .append(searchFilter).append("(").append(
679                                "sunPreferredDomain=").append(domainname)
680                        .append(")").append(")(&").append(searchFilter).append(
681                                "(").append("associatedDomain=").append(
682                                domainname).append(")").append(")(&").append(
683                                searchFilter).append("(").append(
684                                "sunOrganizationAlias=").append(domainname)
685                        .append(")").append("))");
686                searchFilter = sb.toString();
687            }
688
689            if (AMCommonUtils.debug.messageEnabled()) {
690                AMCommonUtils.debug.message("AMSC:getOrgDN-> " + "using searchfilter "
691                        + searchFilter);
692            }
693
694            Set orgSet = dsServices.search(token, rootSuffix, searchFilter,
695                    SCOPE_SUB);
696
697            if ((orgSet == null) || (orgSet.size() > 1) || orgSet.isEmpty()) {
698                // throw an exception
699                Object[] args = { domainname };
700                String locale = AMCommonUtils.getUserLocale(token);
701                throw new AMException(AMSDKBundle
702                        .getString("971", args, locale), "971", args);
703            } else {
704                Iterator it = orgSet.iterator();
705                orgdn = (String) it.next();
706                addToOrgMapCache(token, orgdn);
707
708                return (orgdn);
709            }
710        }
711    }
712
713    /**
714     * Returns the handle to the <code>AMOrganizationalUnit</code> object
715     * represented by DN. However, the validity of the handle returned by this
716     * method cannot be guaranteed, since the object is created in memory, and
717     * not instantiated from the data store. Using the
718     * <code>AMOrganizationialUnit</code> returned from this method may result
719     * in exceptions thrown in the later part of the application, if the DN is
720     * not valid or represents an entry that does not exist.
721     * <p>
722     * 
723     * Validity of the DN can be verified is using <code>isValidEntry()</code>
724     * method of the object returned.
725     * 
726     * @see #isValidEntry
727     * 
728     * @param orgUnitDN
729     *            organizational unit DN
730     * @return <code>AMOrganizationalUnit</code> object represented by DN.
731     * @throws SSOException
732     *             if single sign on token is invalid or expired.
733     */
734    public AMOrganizationalUnit getOrganizationalUnit(String orgUnitDN)
735            throws SSOException {
736        AMOrganizationalUnit organizationalUnit = new AMOrganizationalUnitImpl(
737                this.token, orgUnitDN);
738
739        return organizationalUnit;
740    }
741
742    /**
743     * Returns the handle to the <code>AMPeopleContainer</code> object
744     * represented by DN. However, the validity of the handle returned by this
745     * method cannot be guaranteed, since the object is created in memory, and
746     * not instantiated from the data store. Using the
747     * <code>AMPeopleContainer</code> returned from this method may result in
748     * exceptions thrown in the later part of the application, if the DN is not
749     * valid or represents an entry that does not exist.
750     * <p>
751     * 
752     * Validity of the DN can be verified is using <code>isValidEntry()</code>
753     * method of the object returned.
754     * 
755     * @see #isValidEntry
756     * 
757     * @param peopleContainerDN
758     *            people container DN
759     * @return <code>AMPeopleContainer</code> object represented by DN.
760     * @throws SSOException
761     *             if single sign on token is invalid or expired.
762     */
763    public AMPeopleContainer getPeopleContainer(String peopleContainerDN)
764            throws SSOException {
765        AMPeopleContainer peopleContainer = new AMPeopleContainerImpl(
766                this.token, peopleContainerDN);
767
768        return peopleContainer;
769    }
770
771    /**
772     * Returns the handle to the <code>AMTemplate</code> object represented by
773     * DN. However, the validity of the handle returned by this method cannot be
774     * guaranteed, since the object is created in memory, and not instantiated
775     * from the data store. Using the <code>AMTemplate</code> returned from
776     * this method may result in exceptions thrown in the later part of the
777     * application, if the DN is not valid or represents an entry that does not
778     * exist.
779     * <p>
780     * 
781     * Validity of the DN can be verified is using <code>isValidEntry()</code>
782     * method of the object returned.
783     * 
784     * @deprecated
785     * @see #isValidEntry
786     * 
787     * @param templateDN
788     *            a policy template DN.
789     * @return <code>AMTemplate</code> object represented by DN.
790     * @throws AMException
791     *             if the DN does not represent a Policy template DN
792     * @throws SSOException
793     *             if single sign on token is invalid or expired.
794     */
795    public AMTemplate getPolicyTemplate(String templateDN) throws AMException,
796            SSOException {
797        throw new UnsupportedOperationException();
798    }
799
800    /**
801     * Returns the URL of the view bean for the service
802     * 
803     * @param serviceName
804     *            the service name
805     * @return String URL of the view bean for the service
806     * @throws AMException
807     *             if an error is encountered while retrieving information
808     */
809    public String getPropertiesViewBeanURL(String serviceName)
810            throws AMException {
811        try {
812            ServiceSchemaManager scm = new ServiceSchemaManager(serviceName,
813                    token);
814
815            return scm.getPropertiesViewBeanURL();
816        } catch (SSOException so) {
817            AMCommonUtils.debug.error("AMStoreConnection.getPropertiesViewBeanURL(): ", so);
818            throw new AMException(AMSDKBundle.getString("902", locale), "902");
819        } catch (SMSException se) {
820            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", se);
821            throw new AMException(AMSDKBundle.getString("910", locale), "910");
822        }
823    }
824
825    /**
826     * Returns the handle to the <code>AMResource</code> object represented by
827     * DN. However, the validity of the handle returned by this method cannot be
828     * guaranteed, since the object is created in memory, and not instantiated
829     * from the data store. Using the <code>AMResource</code> returned from
830     * this method may result in exceptions thrown in the later part of the
831     * application, if the DN is not valid or represents an entry that does not
832     * exist.
833     * <p>
834     * 
835     * Validity of the DN can be verified is using <code>isValidEntry()</code>
836     * method of the object returned.
837     * 
838     * @see #isValidEntry
839     * 
840     * @param resourceDN
841     *            resource DN.
842     * @return <code>AMResource</code> object represented by DN.
843     * @throws SSOException
844     *             if single sign on token is invalid or expired.
845     */
846    public AMResource getResource(String resourceDN) throws SSOException {
847        AMResource res = new AMResourceImpl(this.token, resourceDN);
848
849        return res;
850    }
851
852    /**
853     * Returns the handle to the <code>AMRole</code> object represented by DN.
854     * However, the validity of the handle returned by this method cannot be
855     * guaranteed, since the object is created in memory, and not instantiated
856     * from the data store. Using the <code>AMRole</code> returned from this
857     * method may result in exceptions thrown in the later part of the
858     * application, if the DN is not valid or represents an entry that does not
859     * exist.
860     * <p>
861     * 
862     * Validity of the DN can be verified is using <code>isValidEntry()</code>
863     * method of the object returned.
864     * 
865     * @see #isValidEntry
866     * 
867     * @param roleDN
868     *            role DN
869     * @return <code>AMRole</code> object represented by DN.
870     * @throws SSOException
871     *             if single sign on token is invalid or expired.
872     */
873    public AMRole getRole(String roleDN) throws SSOException {
874        AMRole role = new AMRoleImpl(this.token, roleDN);
875
876        return role;
877    }
878
879    /**
880     * Returns the <code>AMSchema</code> for the given service name and
881     * service type.
882     * 
883     * @param serviceName
884     *            the name of the service
885     * @param schemaType
886     *            the type of service schema that needs to be retrieved.
887     * 
888     * @return <code>AMSchema</code> corresponding to the given service name
889     *         and schema type.
890     * 
891     * @throws AMException
892     *             if an error is encountered in retrieving the
893     *             <code>AMSchema</code>.
894     * 
895     * @deprecated This method has been deprecated. Please use
896     *             <code>com.sun.identity.sm.ServiceSchemaManager.getSchema()
897     *             </code>.
898     */
899    public AMSchema getSchema(String serviceName, AMSchema.Type schemaType)
900            throws AMException {
901        throw new UnsupportedOperationException();
902    }
903
904    /**
905     * Returns the schema types available for a particular service.
906     * 
907     * @param serviceName
908     *            the name of the service whose schema types needs to be
909     *            retrieved
910     * @return Set of <code>AMSchema.Type</code> objects
911     * @throws AMException
912     *             if an error is encountered in retrieving the
913     *             <code>schemaTypes</code>.
914     * 
915     * @deprecated This method has been deprecated. Please use
916     *             <code>
917     *             com.sun.identity.sm.ServiceSchemaManager.getSchemaTypes()
918     *             </code>.
919     */
920    public Set getSchemaTypes(String serviceName) throws AMException {
921        throw new UnsupportedOperationException();
922    }
923
924    /**
925     * Returns the service hierarchy for all registered services.
926     * 
927     * @return the service hierarchy for all registered services.
928     * @throws AMException
929     *             if an error is encountered in retrieving the service
930     *             hierarchy. The return value is a Set of strings in slash
931     *             format.
932     */
933    public Set getServiceHierarchy() throws AMException {
934        try {
935            Set retSet = new HashSet();
936            ServiceManager sm = new ServiceManager(token);
937            Set serviceNames = sm.getServiceNames();
938            Iterator itr = serviceNames.iterator();
939
940            while (itr.hasNext()) {
941                String st = (String) itr.next();
942                ServiceSchemaManager scm = new ServiceSchemaManager(st, token);
943                String sh = scm.getServiceHierarchy();
944
945                if ((sh != null) && (sh.length() != 0)) {
946                    retSet.add(sh);
947                }
948            }
949
950            return retSet;
951        } catch (SSOException so) {
952            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", so);
953            throw new AMException(AMSDKBundle.getString("902", locale), "902");
954        } catch (SMSException se) {
955            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", se);
956            throw new AMException(AMSDKBundle.getString("905", locale), "905");
957        }
958    }
959
960    /**
961     * Returns the set of name of services that have been loaded to the data
962     * store.
963     * 
964     * @return set of name of services.
965     * @throws AMException
966     *             if an error is encountered in retrieving the names of the
967     *             services
968     */
969    public Set getServiceNames() throws AMException {
970        try {
971            ServiceManager sm = new ServiceManager(token);
972
973            return sm.getServiceNames();
974        } catch (SSOException so) {
975            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", so);
976            throw new AMException(AMSDKBundle.getString("902", locale), "902");
977        } catch (SMSException se) {
978            AMCommonUtils.debug.error("AMStoreConnection.getServiceNames(): ", se);
979            throw new AMException(AMSDKBundle.getString("906", locale), "906");
980        }
981    }
982
983    /**
984     * Returns the handle to the <code>AMStaticGroup</code> object represented
985     * by DN. However, the validity of the handle returned by this method cannot
986     * be guaranteed, since the object is created in memory, and not
987     * instantiated from the data store. Using the <code>AMStaticGroup</code>
988     * returned from this method may result in exceptions thrown in the later
989     * part of the application, if the DN is not valid or represents an entry
990     * that does not exist.
991     * <p>
992     * 
993     * Validity of the DN can be verified is using <code>isValidEntry()</code>
994     * method of the object returned.
995     * 
996     * @see #isValidEntry
997     * 
998     * @param groupDN
999     *            group DN
1000     * @return <code>AMStaticGroup</code> object represented by DN.
1001     * @throws SSOException
1002     *             if single sign on token is invalid or expired.
1003     */
1004    public AMStaticGroup getStaticGroup(String groupDN) throws SSOException {
1005        AMStaticGroup group = new AMStaticGroupImpl(this.token, groupDN);
1006        return group;
1007    }
1008
1009    /**
1010     * Returns the top level containers (Organizations, People Containers,
1011     * Roles, etc) for the particular user based on single sign on token as the
1012     * starting point in the tree.
1013     * 
1014     * @return set of <code>DBObjects</code> that are top level containers for
1015     *         the signed in user.
1016     * @throws AMException
1017     *             if an error occurred when retrieving the information from the
1018     *             data store.
1019     * @throws SSOException
1020     *             if single sign on token is invalid or expired.
1021     */
1022    public Set getTopLevelContainers() throws AMException, SSOException {
1023        SSOTokenManager.getInstance().validateToken(this.token);
1024        return dsServices.getTopLevelContainers(token);
1025    }
1026
1027    /**
1028     * Returns the "real" or "physical" top level organizations as the starting
1029     * point in the tree.
1030     * 
1031     * @return Set Set of DN Strings for top level Organizations
1032     * @throws AMException
1033     *             if an error occurred when retrieving the information from the
1034     *             data store.
1035     * @throws SSOException
1036     *             if single sign on token is invalid or expired.
1037     */
1038    public Set getTopLevelOrganizations() throws AMException, SSOException {
1039        SSOTokenManager.getInstance().validateToken(this.token);
1040
1041        return dsServices.search(this.token, rootSuffix, AMSearchFilterManager
1042                .getGlobalSearchFilter(AMObject.ORGANIZATION), SCOPE_ONE);
1043    }
1044
1045    /**
1046     * Returns the handle to the <code>AMUser</code> object represented by DN.
1047     * However, the validity of the handle returned by this method cannot be
1048     * guaranteed, since the object is created in memory, and not instantiated
1049     * from the data store. Using the <code>AMUser</code> returned from this
1050     * method may result in exceptions thrown in the later part of the
1051     * application, if the DN is not valid or represents an entry that does not
1052     * exist.
1053     * <p>
1054     * 
1055     * Validity of the DN can be verified is using <code>isValidEntry()</code>
1056     * method of the object returned.
1057     * 
1058     * @see #isValidEntry
1059     * 
1060     * @param userDN
1061     *            user DN
1062     * @return <code>AMUser</code> object represented by DN
1063     * @throws SSOException
1064     *             if single sign on token is invalid or expired.
1065     */
1066    public AMUser getUser(String userDN) throws SSOException {
1067        AMUser user = new AMUserImpl(this.token, userDN);
1068        return user;
1069    }
1070
1071    /**
1072     * Returns the handle to the <code>AMEntity</code> object represented by
1073     * DN. However, the validity of the handle returned by this method cannot be
1074     * guaranteed, since the object is created in memory, and not instantiated
1075     * from the data store. Using the <code>AMEntity</code> returned from this
1076     * method may result in exceptions thrown in the later part of the
1077     * application, if the DN is not valid or represents an entry that does not
1078     * exist.
1079     * <p>
1080     * 
1081     * Validity of the DN can be verified is using <code>isValidEntry()</code>
1082     * method of the object returned.
1083     * 
1084     * @see #isValidEntry
1085     * 
1086     * @param eDN
1087     *            entity DN.
1088     * @return <code>AMEntity</code> object represented by DN.
1089     * @throws SSOException
1090     *             if single sign on token is invalid or expired.
1091     */
1092    public AMEntity getEntity(String eDN) throws SSOException {
1093        AMEntity entity = null;
1094        try {
1095            entity = new AMEntityImpl(this.token, eDN, getAMObjectType(eDN));
1096        } catch (AMException ame) {
1097            // Return AMEntity without object type
1098            entity = new AMEntityImpl(this.token, eDN);
1099        }
1100        return entity;
1101    }
1102
1103    /**
1104     * Checks if the entry exists in the directory or not. First a syntax check
1105     * is done on the DN string corresponding to the entry. If the DN syntax is
1106     * valid, a directory call will be made to check for the existence of the
1107     * entry.
1108     * <p>
1109     * 
1110     * <b>NOTE:</b> This method internally invokes a call to the directory to
1111     * verify the existence of the entry. There could be a performance overhead.
1112     * Hence, please use your discretion while using this method.
1113     * 
1114     * @param dn
1115     *            DN of the entry that needs to be validated.
1116     * 
1117     * @return false if the entry does not have a valid DN syntax or if the
1118     *         entry does not exists in the Directory. True otherwise.
1119     * 
1120     * @throws SSOException
1121     *             if the single sign on token is no longer valid.
1122     */
1123    public boolean isValidEntry(String dn) throws SSOException {
1124        // First check if DN syntax is valid. Avoid making iDS call
1125        if (!LDAPUtils.isDN(dn)) { // May be a exception thrown
1126
1127            return false; // would be better here.
1128        }
1129
1130        SSOTokenManager.getInstance().validateToken(token);
1131
1132        if (AMCommonUtils.debug.messageEnabled()) {
1133            AMCommonUtils.debug.message("AMStoreConnection.isValidEntry(): DN=" + dn);
1134        }
1135
1136        return dsServices.doesEntryExists(token, dn);
1137    }
1138
1139    /**
1140     * Bootstraps the Organization tree by creating the Top Organization tree.
1141     * 
1142     * @param orgName
1143     *            name of the top organization
1144     * @param avPairs
1145     *            Attribute-Value pairs for the top organization
1146     * @return Top Organization object.
1147     * @throws AMException
1148     *             if an error occurred during the process of creation.
1149     * @throws SSOException
1150     *             if single sign on token is invalid or expired.
1151     */
1152    public AMOrganization createTopOrganization(String orgName, Map avPairs)
1153            throws AMException, SSOException {
1154        StringBuilder orgDNSB = new StringBuilder();
1155        orgDNSB
1156                .append(
1157                        AMNamingAttrManager
1158                                .getNamingAttr(AMObject.ORGANIZATION)).append(
1159                        "=").append(orgName).append(",").append(rootSuffix);
1160
1161        AMOrganizationImpl orgImpl = new AMOrganizationImpl(this.token, orgDNSB
1162                .toString());
1163        orgImpl.setAttributes(avPairs);
1164        orgImpl.create();
1165
1166        return orgImpl;
1167    }
1168
1169    /**
1170     * This method takes an organization DN and purges all objects marked for
1171     * deletion. If the organization itself is marked for deletion, then a
1172     * recursive delete of everything under the organization is called, followed
1173     * by the organization deletion. This method works in the mode where
1174     * soft-delete option in Access Manager is turned on. The Pre/Post
1175     * <code>callbacks</code> for users are executed during this method.
1176     * 
1177     * @param domainName
1178     *            domain to be purged
1179     * @param graceperiod
1180     *            time in days which should have passed since the entry was last
1181     *            modified before it can be deleted from the system.
1182     * @throws AMException
1183     *             if an error occurred when retrieving the information from the
1184     *             data store.
1185     * @throws SSOException
1186     *             if single sign on token is invalid or expired.
1187     */
1188    public void purge(String domainName, int graceperiod) throws AMException,
1189            SSOException {
1190        String orgDN;
1191        Set orgSet;
1192        boolean deleted = false;
1193        if (AMDCTree.isRequired()) {
1194            orgDN = AMDCTree.getOrganizationDN(token, domainName);
1195            orgSet = new HashSet();
1196            orgSet.add(orgDN);
1197        } else {
1198            // Use special org search filter for searching for deleted
1199            // organizations.
1200            String filter = AMCompliance
1201                    .getDeletedObjectFilter(AMObject.ORGANIZATION);
1202            filter = AMObjectImpl.constructFilter(AMNamingAttrManager
1203                    .getNamingAttr(AMObject.ORGANIZATION), filter, domainName);
1204
1205            if (AMCommonUtils.debug.messageEnabled()) {
1206                AMCommonUtils.debug.message("AMStoreConnection.purgeOrg: "
1207                        + "Using org filter= " + filter);
1208            }
1209
1210            orgSet = dsServices.search(token, rootSuffix, filter, SCOPE_SUB);
1211
1212            if ((orgSet == null) || orgSet.isEmpty()) {
1213                orgSet = getOrganizations(domainName, null);
1214                deleted = false;
1215            } else {
1216                deleted = true;
1217            }
1218        }
1219
1220        if (orgSet == null || orgSet.isEmpty()) {
1221            return;
1222        }
1223        Iterator delIter = orgSet.iterator();
1224        while (delIter.hasNext()) {
1225            orgDN = (String) delIter.next();
1226            if (AMCommonUtils.debug.messageEnabled()) {
1227                AMCommonUtils.debug.message("AMStoreConnection.purge: " + "Organization= "
1228                        + orgDN);
1229            }
1230
1231            AMOrganization org = getOrganization(orgDN);
1232
1233            // Check to see if grace period has expired.
1234            if (deleted && graceperiod < daysSinceModified(token, orgDN)) {
1235                // Delete all objects using the hardDelete method.
1236                org.purge(true, -1);
1237            } else {
1238                // Search for objects marked as deleted and
1239                // try to purge them, if graceperiod as expired.
1240                String filter = AMCompliance.getDeletedObjectFilter(-1);
1241
1242                if (AMCommonUtils.debug.messageEnabled()) {
1243                    AMCommonUtils.debug.message("AMStoreConnection.purge: "
1244                            + "Searching deleted objects. Filter: " + filter);
1245                }
1246
1247                Set deletedObjs = dsServices.search(token, orgDN, filter,
1248                        SCOPE_SUB);
1249
1250                if (deletedObjs == null) {
1251                    // No objecxts to delete
1252                    if (AMCommonUtils.debug.messageEnabled()) {
1253                        AMCommonUtils.debug.message("AMStoreConnection.purge: "
1254                                + "No objects to be deleted found for "
1255                                + orgDN);
1256                    }
1257                }
1258
1259                Iterator iter = deletedObjs.iterator();
1260                List list = new ArrayList();
1261
1262                // get number of RDNs in the entry itself
1263                int entryRDNs = DN.valueOf(orgDN).size();
1264
1265                // to count maximum level of RDNs in the search return
1266                int maxRDNCount = entryRDNs;
1267
1268                // go through all search results, add DN to the list, and
1269                // set the maximun RDN count, will be used to remove DNs
1270                while (iter.hasNext()) {
1271                    String thisDN = (String) iter.next();
1272                    DN dn = DN.valueOf(thisDN);
1273                    int count = dn.size();
1274
1275                    if (count > maxRDNCount) {
1276                        maxRDNCount = count;
1277                    }
1278
1279                    list.add(dn);
1280                }
1281
1282                int len = list.size();
1283                for (int i = maxRDNCount; i >= entryRDNs; i--) {
1284                    for (int j = 0; j < len; j++) {
1285                        // depending on object type,
1286                        DN thisdn = (DN) list.get(j);
1287
1288                        if (thisdn.size() == i) {
1289                            String thisDN = thisdn.toString();
1290                            int objType = getAMObjectType(thisDN);
1291                            AMObject thisObj;
1292
1293                            if (AMCommonUtils.debug.messageEnabled()) {
1294                                AMCommonUtils.debug.message("AMStoreConnection:purgeOrg: "
1295                                        + "deleting child " + thisDN);
1296                            }
1297                            try { // catch PreCallBackException
1298                                switch (objType) {
1299                                case AMObject.USER:
1300                                    thisObj = getUser(thisDN);
1301                                    thisObj.purge(false, graceperiod);
1302
1303                                    break;
1304
1305                                case AMObject.ASSIGNABLE_DYNAMIC_GROUP:
1306                                    thisObj = getAssignableDynamicGroup(thisDN);
1307                                    thisObj.purge(false, graceperiod);
1308
1309                                    break;
1310
1311                                case AMObject.DYNAMIC_GROUP:
1312                                    thisObj = getDynamicGroup(thisDN);
1313                                    thisObj.purge(false, graceperiod);
1314
1315                                    break;
1316
1317                                case AMObject.STATIC_GROUP:
1318                                case AMObject.GROUP:
1319                                    thisObj = getStaticGroup(thisDN);
1320                                    thisObj.purge(false, graceperiod);
1321
1322                                    break;
1323
1324                                case AMObject.RESOURCE:
1325                                    thisObj = getResource(thisDN);
1326                                    thisObj.purge(false, -1);
1327                                    break;
1328
1329                                case AMObject.ORGANIZATION:
1330                                    thisObj = getOrganization(thisDN);
1331
1332                                    if (!DN.valueOf(thisDN).equals(DN.valueOf(orgDN))) {
1333                                        thisObj.purge(true, graceperiod);
1334                                    }
1335
1336                                    break;
1337
1338                                default:
1339
1340                                    // should not show up in the searched
1341                                    // objects.
1342                                    // as none of the other objects are
1343                                    // supported
1344                                    // for being marked as soft-deleted/
1345                                    // purging.
1346                                    break;
1347                                } // switch
1348                            } catch (AMPreCallBackException amp) {
1349                                AMCommonUtils.debug.error("AMStoreConnection.purge: "
1350                                        + "Aborting delete of: "
1351                                        + thisDN
1352                                        + " due to pre-callback exception",
1353                                        amp);
1354                            }
1355                        } // if
1356                    } // for
1357                } // for
1358
1359            } // else
1360        } // delIter
1361
1362        return;
1363    }
1364
1365    /**
1366     * This method takes a user ID and a domain name, It uses default search
1367     * templates to search for the organization and uses the deleted objects
1368     * search filter for Users as defined in the Administration Service of
1369     * Access Manager. This filter is used to search for the deleted user under
1370     * the organization. If the user is marked for deletion and the grace period
1371     * is passed then the user is purged. The pre-delete call backs as listed in
1372     * the Administration service, are called before the user is deleted. If any
1373     * of the <code>callbacks</code> throw an exception the delete operation
1374     * is aborted.
1375     * 
1376     * @param uid
1377     *            user ID
1378     * @param domainName
1379     *            domain in which the user belongs.
1380     * @param graceperiod
1381     *            time in days which should have passed before this user can be
1382     *            deleted.
1383     * 
1384     * @throws AMException
1385     *             if there is an error in deleting the user, or if the user
1386     *             <code>callbacks</code> thrown an exception
1387     * @throws SSOException
1388     */
1389    public void purgeUser(String uid, String domainName, int graceperiod)
1390            throws AMException, SSOException {
1391        String orgDN = getOrganizationDN(domainName, null);
1392        String filter = AMCompliance.getDeletedObjectFilter(AMObject.USER);
1393        filter = AMObjectImpl.constructFilter(AMNamingAttrManager
1394                .getNamingAttr(AMObject.USER), filter, uid);
1395
1396        if (AMCommonUtils.debug.messageEnabled()) {
1397            AMCommonUtils.debug.message("AMStoreConnection.purgeGroup: "
1398                    + "Using deleted user filter= " + filter);
1399        }
1400
1401        Set uSet = dsServices.search(token, orgDN, filter, SCOPE_SUB);
1402
1403        if ((uSet == null) || (uSet.size() > 1) || uSet.isEmpty()) {
1404            // throw an exception
1405            Object args[] = { uid };
1406            throw new AMException(AMSDKBundle.getString("971", args, locale),
1407                    "971", args);
1408        }
1409
1410        String uDN = (String) uSet.iterator().next();
1411        AMUser user = getUser(uDN);
1412        user.purge(false, graceperiod);
1413
1414        return;
1415    }
1416
1417    /**
1418     * This method takes a resource ID and a domain name, It uses default search
1419     * templates to search for the organization and uses the deleted objects
1420     * search filter for Resources as defined in the Administration Service of
1421     * Access Manager. This filter is used to search for the deleted resource
1422     * under the organization. If the resource is marked for deletion and the
1423     * grace period is passed then the resource is purged. The pre-delete call
1424     * backs as listed in the Administration service, are called before the user
1425     * is deleted. If any of the <code>callbacks</code> throw an exception the
1426     * delete operation is aborted.
1427     * 
1428     * @param rid
1429     *            resource ID
1430     * @param domainName
1431     *            domain in which the user belongs.
1432     * @param graceperiod
1433     *            time in days which should have passed before this user can be
1434     *            deleted.
1435     * 
1436     * @throws AMException
1437     *             if there is an error in deleting the user, or if the user
1438     *             <code>callbacks</code> thrown an exception
1439     * @throws SSOException
1440     */
1441    public void purgeResource(String rid, String domainName, int graceperiod)
1442            throws AMException, SSOException {
1443        String orgDN = getOrganizationDN(domainName, null);
1444        String filter = AMCompliance.getDeletedObjectFilter(AMObject.RESOURCE);
1445        filter = AMObjectImpl.constructFilter(AMNamingAttrManager
1446                .getNamingAttr(AMObject.RESOURCE), filter, rid);
1447
1448        if (AMCommonUtils.debug.messageEnabled()) {
1449            AMCommonUtils.debug.message("AMStoreConnection.purgeGroup: "
1450                    + "Using deleted user filter= " + filter);
1451        }
1452
1453        Set uSet = dsServices.search(token, orgDN, filter, SCOPE_SUB);
1454
1455        if ((uSet == null) || (uSet.size() > 1) || uSet.isEmpty()) {
1456            // throw an exception
1457            Object args[] = { rid };
1458            throw new AMException(AMSDKBundle.getString("971", args, locale),
1459                    "971", args);
1460        }
1461
1462        String uDN = (String) uSet.iterator().next();
1463        AMResource resource = getResource(uDN);
1464        resource.purge(false, graceperiod);
1465
1466        return;
1467    }
1468
1469    /**
1470     * This method takes a group name and a domain name, It uses default search
1471     * templates to search for the organization and uses the deleted objects
1472     * search filter for Groups as defined in the Administration Service of
1473     * Access Manager. This filter is used to search for the deleted user under
1474     * the organization. If the group is marked for deletion and the grace
1475     * period is passed then the group is purged. The pre-delete call backs as
1476     * listed in the Administration service, are called before the group is
1477     * deleted. If any of the <code>callbacks</code> throw an exception the
1478     * delete operation is aborted.
1479     * 
1480     * @param gid
1481     *            group name
1482     * @param domainName
1483     *            domain in which the group belongs.
1484     * @param graceperiod
1485     *            time in days which should have passed before this user can be
1486     *            deleted. If a -1 is passed, group is deleted right away
1487     *            without check on <code>graceperiod</code>.
1488     * 
1489     * @throws AMException
1490     *             if there is an error in deleting the group, or if the
1491     *             <code>callbacks</code> thrown an exception
1492     * @throws SSOException
1493     */
1494    public void purgeGroup(String gid, String domainName, int graceperiod)
1495            throws AMException, SSOException {
1496        String orgDN = getOrganizationDN(domainName, null);
1497        String filter = AMCompliance.getDeletedObjectFilter(AMObject.GROUP);
1498        filter = AMObjectImpl.constructFilter(AMNamingAttrManager
1499                .getNamingAttr(AMObject.GROUP), filter, gid);
1500
1501        if (AMCommonUtils.debug.messageEnabled()) {
1502            AMCommonUtils.debug.message("AMStoreConnection.purgeGroup: "
1503                    + "Using deleted group filter= " + filter);
1504        }
1505
1506        Set gSet = dsServices.search(token, orgDN, filter, SCOPE_SUB);
1507
1508        if ((gSet == null) || (gSet.size() > 1) || gSet.isEmpty()) {
1509            // throw an exception
1510            Object args[] = { gid };
1511            throw new AMException(AMSDKBundle.getString("971", args, locale),
1512                    "971", args);
1513        }
1514
1515        String uDN = (String) gSet.iterator().next();
1516        AMGroup g = null;
1517        int type = getAMObjectType(uDN);
1518        switch (type) {
1519        case AMObject.GROUP:
1520        case AMObject.STATIC_GROUP:
1521            g = new AMStaticGroupImpl(token, uDN);
1522            break;
1523        case AMObject.ASSIGNABLE_DYNAMIC_GROUP:
1524            g = new AMAssignableDynamicGroupImpl(token, uDN);
1525            break;
1526        case AMObject.DYNAMIC_GROUP:
1527            g = new AMDynamicGroupImpl(token, uDN);
1528            break;
1529        default:
1530        }
1531        if (g != null) {
1532            g.purge(false, graceperiod);
1533        }
1534        return;
1535    }
1536
1537    /**
1538     * Returns a set of <code>com.iplanet.am.sdk.AMEntityType</code> objects,
1539     * which is the set of objects which are supported by the
1540     * <code>com.iplanet.am.sdk.AMEntity</code> APIs.
1541     * 
1542     * @return Set of <code>AMEntityType</code> objects.
1543     */
1544    public Set getEntityTypes() {
1545        return AMCommonUtils.getSupportedEntityTypes();
1546    }
1547
1548    protected String getBaseDN(ServiceConfig sc) {
1549        if (sc != null) {
1550            Map attrMap = sc.getAttributes();
1551            Set vals = (Set) attrMap.get("baseDN");
1552
1553            if ((vals == null) || vals.isEmpty()) {
1554                return null;
1555            } else {
1556                Iterator it = vals.iterator();
1557
1558                return ((String) it.next());
1559            }
1560        } else {
1561            return null;
1562        }
1563    }
1564
1565    protected boolean isRFC2247(ServiceConfig sc) {
1566        // ServiceConfig sc = getSearchTemplateConfig(orgTemplate);
1567        if (sc != null) {
1568            Map attrMap = sc.getAttributes();
1569            Set vals = (Set) attrMap.get("rfc2247flag");
1570
1571            if ((vals == null) || (vals.isEmpty())) {
1572                return (false);
1573            } else {
1574                Iterator it = vals.iterator();
1575
1576                return (((String) it.next()).equalsIgnoreCase("true") ? true
1577                        : false);
1578            }
1579        } else {
1580            return (false);
1581        }
1582    }
1583
1584    /**
1585     * Protected method to update the <code>orgMapCache</code>
1586     * 
1587     */
1588    protected static void addToOrgMapCache(SSOToken stoken, String dn)
1589            throws AMException, SSOException {
1590        if ((dn == null) || !LDAPUtils.isDN(dn)) {
1591            return;
1592        }
1593
1594        // String rfcDN = (new DN(dn)).toRFCString().toLowerCase();
1595        String rfcDN = dn;
1596        Set attrNames = new HashSet();
1597        attrNames.add("objectclass");
1598        attrNames.add("sunpreferreddomain");
1599        attrNames.add("associateddomain");
1600        attrNames.add("sunorganizationalias");
1601
1602        Map attributes = AMDirectoryAccessFactory.getDirectoryServices()
1603                .getAttributes(stoken, dn, attrNames, AMObject.ORGANIZATION);
1604
1605        // Add to cache
1606        String rdn = LDAPUtils.rdnValueFromDn(dn);
1607        Set prefDomain = (Set) attributes.get("sunpreferreddomain");
1608        Set associatedDomain = (Set) attributes.get("associateddomain");
1609        Set orgAlias = (Set) attributes.get("sunorganizationalias");
1610
1611        synchronized (orgMapCache) {
1612            orgMapCache.put(rdn.toLowerCase(), rfcDN);
1613
1614            if ((prefDomain != null) && (prefDomain.size() == 1)) {
1615                String preferredDomain = (String) prefDomain.iterator().next();
1616
1617                // AMHashMap so no need to lowercase
1618                orgMapCache.put(preferredDomain, rfcDN);
1619            }
1620
1621            if ((associatedDomain != null) && !associatedDomain.isEmpty()) {
1622                Iterator itr = associatedDomain.iterator();
1623
1624                while (itr.hasNext()) {
1625                    String value = (String) itr.next();
1626                    orgMapCache.put(value, rfcDN);
1627                }
1628            }
1629
1630            if ((orgAlias != null) && !orgAlias.isEmpty()) {
1631                Iterator itr = orgAlias.iterator();
1632
1633                while (itr.hasNext()) {
1634                    String value = (String) itr.next();
1635                    orgMapCache.put(value, rfcDN);
1636                }
1637            }
1638        }
1639    }
1640
1641    /**
1642     * Protected method to obtain the number of days since this DN was last
1643     * modified.
1644     */
1645    protected static int daysSinceModified(SSOToken stoken, String entryDN)
1646            throws AMException, SSOException {
1647        NumberFormat nf = NumberFormat.getInstance();
1648        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss");
1649        ParsePosition pp = new ParsePosition(0);
1650
1651        Set attrNames = new HashSet(1);
1652
1653        // Why are we adding objectclass when it is not being used?
1654        // If a specific reason, then we need to change the method call.
1655        // Same question applicable to other places where we add into orgmap
1656        // cache
1657        // attrNames.add("objectclass");
1658        attrNames.add("modifytimestamp");
1659
1660        Map attributes = AMDirectoryAccessFactory.getDirectoryServices()
1661                .getAttributes(stoken, entryDN, attrNames,
1662                        AMObject.UNDETERMINED_OBJECT_TYPE);
1663        Set values = (Set) attributes.get("modifytimestamp");
1664
1665        if ((values == null) || values.isEmpty()) {
1666            return -1;
1667        }
1668
1669        String value = (String) values.iterator().next();
1670
1671        if ((value == null) || value.length() == 0) {
1672            return -1;
1673        }
1674
1675        Number n;
1676
1677        try {
1678            n = nf.parse(value);
1679        } catch (ParseException pe) {
1680        if (AMCommonUtils.debug.warningEnabled()) {
1681            AMCommonUtils.debug.warning("AMStoreConnection.daysSinceModified: "
1682                        + "unable to parse date: " + value
1683                        + " :Returning default= -1", pe);
1684            }
1685
1686            return (-1);
1687        }
1688
1689        Date modDate = df.parse(n.toString(), pp);
1690        Date nowDate = new Date();
1691
1692        // getTime() fn returns number of milliseconds
1693        // since January 1, 1970, 00:00:00 GMT
1694        long modTimeMSecs = modDate.getTime();
1695        long nowTimeMSecs = nowDate.getTime();
1696
1697        long elapsedTimeMSecs = nowTimeMSecs - modTimeMSecs;
1698        int elapsedDays = (int) (elapsedTimeMSecs / (1000 * 60 * 60 * 24));
1699
1700        if (AMCommonUtils.debug.messageEnabled()) {
1701            AMCommonUtils.debug.message("AMStoreConnection.daysSinceModified() for dn: "
1702                    + entryDN + ", days: " + elapsedDays + " days");
1703        }
1704
1705        return (elapsedDays);
1706    }
1707
1708    /**
1709     * Protected method to update <code>orgMapCache</code>.
1710     */
1711    protected static void updateCache(String dn, int eventType) {
1712        if ((dn == null) || !LDAPUtils.isDN(dn)) {
1713            return;
1714        }
1715
1716        String rfcDN = LDAPUtils.formatToRFC(dn);
1717        switch (eventType) {
1718        case AMEvent.OBJECT_ADDED:
1719            // nothing to do
1720            return;
1721
1722        case AMEvent.OBJECT_RENAMED:
1723            synchronized (orgMapCache) {
1724                orgMapCache.clear();
1725            }
1726            return;
1727
1728        case AMEvent.OBJECT_REMOVED:
1729        case AMEvent.OBJECT_CHANGED:
1730            // Go through the entire cache and check and delete
1731            // any entries with values matching this DN
1732            synchronized (orgMapCache) {
1733                Iterator keys = orgMapCache.keySet().iterator();
1734
1735                // String removeKey = null;
1736                Set removeKeys = new HashSet();
1737
1738                while (keys.hasNext()) {
1739                    String key = (String) keys.next();
1740                    String val = (String) orgMapCache.get(key);
1741
1742                    if (val.equalsIgnoreCase(rfcDN)) {
1743                        removeKeys.add(key);
1744                    }
1745                }
1746
1747                if (removeKeys != null) {
1748                    keys = removeKeys.iterator();
1749
1750                    while (keys.hasNext()) {
1751                        String removeKey = (String) keys.next();
1752                        orgMapCache.remove(removeKey);
1753                    }
1754                }
1755                // orgMapCache.clear();
1756            }
1757        }
1758    }
1759
1760    private Set getOrganizations(String domainname, String orgSearchTemplate)
1761            throws AMException, SSOException {
1762        if (domainname == null) {
1763            return Collections.EMPTY_SET;
1764        }
1765
1766        // use the searchfilter to obtain organization DN
1767        // replace %V with domainname.
1768        String searchFilter = AMSearchFilterManager.getSearchFilter(
1769                AMObject.ORGANIZATION, null, orgSearchTemplate, false);
1770
1771        if ((orgSearchTemplate != null) && (searchFilter.indexOf("%V") > -1)) {
1772            searchFilter = AMObjectImpl.constructFilter(AMNamingAttrManager
1773                    .getNamingAttr(AMObject.ORGANIZATION), searchFilter,
1774                    domainname);
1775        } else {
1776            StringBuilder sb = new StringBuilder();
1777            sb.append("(|(&(").append(
1778                    AMNamingAttrManager.getNamingAttr(AMObject.ORGANIZATION))
1779                    .append("=").append(domainname).append(")").append(
1780                            searchFilter).append(")(&(").append(
1781                            "sunPreferredDomain=").append(domainname).append(
1782                            ")").append(searchFilter).append(")(&(").append(
1783                            "associatedDomain=").append(domainname).append(")")
1784                    .append(searchFilter).append(")(&(").append(
1785                            "sunOrganizationAlias=").append(domainname).append(
1786                            ")").append(searchFilter).append("))");
1787            searchFilter = sb.toString();
1788        }
1789
1790        if (AMCommonUtils.debug.messageEnabled()) {
1791            AMCommonUtils.debug.message("AMSC:getOrgDN-> " + "using searchfilter "
1792                    + searchFilter);
1793        }
1794
1795        Set orgSet = dsServices.search(token, rootSuffix, searchFilter,
1796                SCOPE_SUB);
1797        return orgSet;
1798    }
1799
1800    /**
1801     * Converts organization name which is "/" separated to DN.
1802     */
1803    private static String orgNameToDN(String orgName) {
1804        // Check if it is null or empty
1805        if ((orgName == null) || (orgName.length() == 0)) {
1806            return (rootSuffix);
1807        }
1808
1809        // Check if it is org name
1810        if (LDAPUtils.isDN(orgName)) {
1811            return (orgName);
1812        }
1813
1814        // Construct the DN
1815        StringBuilder buf = new StringBuilder();
1816        ArrayList arr = new ArrayList();
1817        StringTokenizer strtok = new StringTokenizer(orgName, "/");
1818
1819        while (strtok.hasMoreElements()) {
1820            arr.add(strtok.nextToken());
1821        }
1822
1823        int size = arr.size();
1824
1825        for (int i = 0; i < size; i++) {
1826            String theOrg = (String) arr.get(i);
1827            buf
1828                    .append(AMNamingAttrManager
1829                            .getNamingAttr(AMObject.ORGANIZATION));
1830            buf.append('=').append(theOrg).append(',');
1831        }
1832
1833        if (rootSuffix.length() > 0) {
1834            buf.append(rootSuffix);
1835        } else {
1836            buf.deleteCharAt(buf.length() - 1);
1837        }
1838
1839        return (buf.toString());
1840    }
1841}