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