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: StaticGroup.java,v 1.4 2009/01/28 05:34:51 ww203982 Exp $
026 *
027 */
028
029package com.iplanet.ums;
030
031import java.security.Principal;
032import java.util.Enumeration;
033
034import com.sun.identity.shared.ldap.LDAPAttribute;
035import com.sun.identity.shared.ldap.LDAPDN;
036
037import com.iplanet.services.ldap.Attr;
038import com.iplanet.services.ldap.AttrSet;
039import com.iplanet.services.ldap.ModSet;
040import com.iplanet.services.util.I18n;
041
042/**
043 * Represents a static group entry.
044 * @supported.api
045 */
046public class StaticGroup extends PersistentObject implements
047        IAssignableMembership {
048
049    /**
050     * Level indicator for no nesting of group membership. Use this level
051     * indicator for getting direct membership in a group.
052     * 
053     * @supported.api
054     */
055    public static final int LEVEL_DIRECT = 0;
056
057    /**
058     * Level indicator for expanding nested membership to the fullest. Use this
059     * level indicator in getting all direct and indirect members through nested
060     * group behavior.
061     * 
062     * @supported.api
063     */
064    public static final int LEVEL_ALL = -1;
065
066    /**
067     * Internal maximum level used if no default is found in configuration.
068     */
069    static final int DEFAULT_MAX = 5;
070
071    private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
072
073    /**
074     * Default constructor
075     */
076    protected StaticGroup() {
077    }
078
079    /**
080     * Constructs a group object from an ID by reading from persistent storage.
081     * 
082     * @param session
083     *            Authenticated session
084     * @param guid
085     *            Globally unique identifier for the group entry
086     * @exception UMSException
087     *                on failure to instantiate from persistent storage
088     * @deprecated
089     */
090    StaticGroup(Principal principal, Guid guid) throws UMSException {
091        super(principal, guid);
092        verifyClass();
093    }
094
095    /**
096     * Constructs a group object in memory using the default registered template
097     * for StaticGroup. This is an in-memory representation of a new StaticGroup
098     * object; the save method must be called to save the new object to
099     * persistent storage.
100     * 
101     * @param attrSet
102     *            Attribute/value set
103     * @exception UMSException
104     *                on failure to instantiate from persistent storage
105     */
106    StaticGroup(AttrSet attrSet) throws UMSException {
107        this(TemplateManager.getTemplateManager().getCreationTemplate(_class,
108                null), attrSet);
109    }
110
111    /**
112     * Constructs a StaticGroup object in memory with
113     * a given template. This one simply creates a Group object in memory; the
114     * save method must be called to save the new object to persistent storage.
115     * 
116     * @param template
117     *            Template for creating a group
118     * @param attrSet
119     *            Attribute/value set
120     * @exception UMSException
121     *                on failure to instantiate from persistent storage
122     * @supported.api
123     */
124    public StaticGroup(CreationTemplate template, AttrSet attrSet)
125            throws UMSException {
126        super(template, attrSet);
127    }
128
129    /**
130     * Adds a member to the group. The change is saved to
131     * persistent storage.
132     * 
133     * @param guid
134     *            Globally unique identifier for the member to be added
135     * @exception UMSException
136     *                on failure to save to persistent storage
137     * @supported.api
138     */
139    public void addMember(Guid guid) throws UMSException {
140
141        String id = LDAPDN.normalize(guid.getDn());
142
143        PersistentObject entry = null;
144
145        try {
146            // entry = getUMSSession().getObject(guid);
147            entry = UMSObject.getObject(getPrincipal(), guid);
148        } catch (UMSException ignore) {
149        }
150
151        if (entry != null && entry instanceof StaticGroup) {
152            StaticGroup g = (StaticGroup) entry;
153            if (id.equalsIgnoreCase(getDN())
154                    || g.hasMember(getGuid(), LEVEL_ALL)) {
155                throw new UMSException(i18n
156                        .getString(IUMSConstants.NO_RECURSION_ALLOW));
157            }
158        }
159
160        modify(new Attr(MEMBER_ATTR_NAME, id), ModSet.ADD);
161        save();
162    }
163
164    /**
165     * Adds a member to the group. The change is saved to
166     * persistent storage.
167     * 
168     * @param member
169     *            Object to be added as member
170     * @exception UMSException
171     *                on failure to save to persistent storage
172     * @supported.api
173     */
174    public void addMember(PersistentObject member) throws UMSException {
175        addMember(member.getGuid());
176    }
177
178    /**
179     * Adds a list of members to the group. The change is
180     * saved to persistent storage.
181     * 
182     * @param guids
183     *            Array of member guids to be added as members to the group
184     * @exception UMSException
185     *                on failure to save to persistent storage
186     * @supported.api
187     */
188    public void addMembers(Guid[] guids) throws UMSException {
189        if (guids == null) {
190            String msg = i18n.getString(IUMSConstants.BAD_GUID);
191            throw new IllegalArgumentException(msg);
192        }
193
194        for (int i = 0; i < guids.length; i++) {
195            addMember(guids[i]);
196        }
197    }
198
199    /**
200     * Gets the members of the group.
201     * 
202     * @return SearchResults for members of the group
203     * @exception Not
204     *                thrown by this class
205     * @supported.api
206     */
207    public SearchResults getMemberIDs() throws UMSException {
208        return getMembers(LEVEL_DIRECT);
209    }
210
211    static int getMaxNestingLevel() {
212        // PKB: Get it from the dsConfig manager
213        // TO FIX
214        return DEFAULT_MAX;
215    }
216
217    /**
218     * Get members of the group.
219     * 
220     * @param level
221     *            Nesting level
222     * @return SearchResults for members of the group
223     * @exception Not
224     *                thrown by this class
225     * @supported.api
226     * 
227     */
228    public SearchResults getMembers(int level) throws UMSException {
229        Attr attr = getAttribute(MEMBER_ATTR_NAME);
230        if (attr == null) {
231            return null;
232        }
233
234        if (level == LEVEL_ALL) {
235            level = getMaxNestingLevel();
236        }
237
238        if (level == LEVEL_DIRECT) {
239            return new SearchResults(getAttribute(MEMBER_ATTR_NAME));
240        }
241
242        Attr nestedMembers = new Attr(MEMBER_ATTR_NAME);
243        LDAPAttribute la = attr.toLDAPAttribute();
244        Enumeration en = la.getStringValues();
245
246        while (en.hasMoreElements()) {
247            String memberdn = (String) en.nextElement();
248            PersistentObject entry = null;
249
250            try {
251                // entry = getUMSSession().getObject(new Guid(memberdn));
252                entry = UMSObject.getObject(getPrincipal(), new Guid(memberdn));
253            } catch (UMSException ignore) {
254            }
255
256            if (entry != null && entry instanceof StaticGroup) {
257                SearchResults r = ((StaticGroup) entry).getMembers(level - 1);
258
259                while (r.hasMoreElements()) {
260                    PersistentObject member = null;
261
262                    try {
263                        member = r.next();
264                        nestedMembers.addValue(member.getDN());
265                    } catch (UMSException ignore) {
266                    }
267                }
268            } else {
269                nestedMembers.addValue(memberdn);
270            }
271
272            entry = null;
273        }
274
275        return new SearchResults(nestedMembers);
276    }
277
278    /**
279     * Gets the member count.
280     * 
281     * @return Number of members of the group
282     * @exception Not
283     *                thrown by this class
284     * @supported.api
285     */
286    public int getMemberCount() throws UMSException {
287        return getMemberCount(LEVEL_DIRECT);
288    }
289
290    /**
291     * Gets the member count.
292     * 
293     * @param level
294     *            Nesting level
295     * @return Number of members of the group
296     * @exception Not
297     *                thrown by this class
298     * @supported.api
299     */
300    public int getMemberCount(int level) throws UMSException {
301
302        if (level == LEVEL_ALL) {
303            level = getMaxNestingLevel();
304        }
305
306        if (level == LEVEL_DIRECT) {
307            Attr attr = getAttribute(MEMBER_ATTR_NAME);
308            return (attr != null) ? attr.size() : 0;
309        }
310
311        SearchResults allMembers = getMembers(level);
312
313        if (allMembers == null)
314            return 0;
315        int count = 0;
316        while (allMembers.hasMoreElements()) {
317            allMembers.next();
318            count++;
319        }
320        return count;
321    }
322
323    /**
324     * Gets a member given an index (zero-based).
325     * 
326     * @param index
327     *            Zero-based index into the group container
328     * @return The unique identifier for a member
329     * @exception Not
330     *                thrown by this class
331     * @supported.api
332     */
333    public Guid getMemberIDAt(int index) throws UMSException {
334        Attr attr = getAttribute(MEMBER_ATTR_NAME);
335        String value = attr.getStringValues()[index];
336        return (value != null) ? new Guid(value) : null;
337    }
338
339    /**
340     * Gets a member given an index (zero-based).
341     * 
342     * @param index
343     *            Zero-based index into the group container
344     * @param level
345     *            Nesting level
346     * @return The unique identifier for a member
347     * @exception Not
348     *                thrown by this class
349     * @supported.api
350     */
351    public Guid getMemberIDAt(int index, int level) throws UMSException {
352        SearchResults allMembers = getMembers(level);
353        if (allMembers == null) {
354            return null;
355        }
356
357        int i = 0;
358        while (allMembers.hasMoreElements()) {
359            PersistentObject entry = allMembers.next();
360            if (i++ == index) {
361                return new Guid(entry.getDN());
362            }
363        }
364        return null;
365    }
366
367    /**
368     * Removes a member from the group. The change is saved to persistent
369     * storage.
370     * 
371     * @param guid
372     *            Unique identifier for the member to be removed
373     * @exception UMSException
374     *                on failure to save to persistent storage
375     * @supported.api
376     */
377    public void removeMember(Guid guid) throws UMSException {
378        String dn = guid.getDn();
379        super.modify(new Attr(MEMBER_ATTR_NAME, LDAPDN.normalize(dn)),
380                ModSet.DELETE);
381        save();
382    }
383
384    /**
385     * Removes a member from the group. The change is saved to persistent
386     * storage.
387     * 
388     * @param member
389     *            Object to be removed
390     * @exception UMSException
391     *                on failure to save to persistent storage
392     * @supported.api
393     */
394    public void removeMember(PersistentObject member) throws UMSException {
395        removeMember(member.getGuid());
396    }
397
398    /**
399     * Removes all members of the group.
400     * 
401     * @exception UMSException
402     *                on failure to save to persistent storage
403     * @supported.api
404     */
405    public void removeAllMembers() throws UMSException {
406
407        if (getMemberCount() == 0) {
408            return;
409        }
410
411        ModSet modSet = new ModSet();
412
413        // TODO: this should probably be REPLACE instead of DELETE, so it
414        // works even if there are no members
415        modSet.add(ModSet.DELETE, new LDAPAttribute(MEMBER_ATTR_NAME));
416
417        modify(modSet);
418        save();
419    }
420
421    /**
422     * Checks if a given identifier is a member of the group.
423     * 
424     * @param guid
425     *            Identity of member to be checked for membership
426     * @return <code>true if it is a member
427     * @exception   Not thrown by this class
428     * @supported.api
429     */
430    public boolean hasMember(Guid guid) throws UMSException {
431        return isMemberAtLevel(guid.getDn(), LEVEL_DIRECT);
432    }
433
434    private boolean isMemberAtLevel(String normalizedID, int level)
435            throws UMSException {
436
437        if (level == LEVEL_ALL) {
438            level = getMaxNestingLevel();
439        }
440
441        SearchResults members = getMembers(level);
442
443        while (members.hasMoreElements()) {
444            PersistentObject entry = members.next();
445            String entryDN = entry.getDN();
446            if (Guid.equals(normalizedID, entryDN)) {
447                return true;
448            }
449        }
450
451        return false;
452    }
453
454    /**
455     * Checks if a given identifier is a member of the group.
456     * 
457     * @param guid
458     *            Identity of member to be checked for membership
459     * @param level
460     *            Nesting level
461     * @return <code>true</code> if it is a member
462     * @exception Not
463     *                thrown by this class
464     * @supported.api
465     */
466    public boolean hasMember(Guid guid, int level) throws UMSException {
467
468        if (level == LEVEL_ALL) {
469            level = getMaxNestingLevel();
470        }
471
472        String id = guid.getDn();
473
474        for (int i = LEVEL_DIRECT; i <= level; i++) {
475            if (isMemberAtLevel(id, i)) {
476                return true;
477            }
478        }
479        return false;
480    }
481
482    private static final String MEMBER_ATTR_NAME = "uniquemember";
483
484    private static final Class _class = new StaticGroup().getClass();
485}