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