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: AssignableDynamicGroup.java,v 1.6 2009/01/28 05:34:50 ww203982 Exp $ 026 * 027 */ 028/** 029 * Portions Copyrighted 2013 ForgeRock, Inc. 030 */ 031package com.iplanet.ums; 032 033import com.iplanet.services.ldap.Attr; 034import com.iplanet.services.ldap.AttrSet; 035import com.iplanet.services.ldap.ModSet; 036import com.iplanet.services.util.I18n; 037import com.sun.identity.shared.debug.Debug; 038import com.sun.identity.shared.ldap.LDAPUrl; 039import com.sun.identity.shared.ldap.LDAPv2; 040import com.sun.identity.shared.ldap.util.DN; 041 042/** 043 * Represents a dynamic group entry that uses memberOf as its filter. It checks 044 * whether the user is the member of the specified group 045 * 046 * @supported.api 047 */ 048public class AssignableDynamicGroup extends DynamicGroup implements 049 IAssignableMembership { 050 051 private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG); 052 053 private static Debug debug; 054 static { 055 debug = Debug.getInstance(IUMSConstants.UMS_DEBUG); 056 } 057 058 /** 059 * Default constructor 060 * 061 * @supported.api 062 */ 063 public AssignableDynamicGroup() { 064 } 065 066 /** 067 * Constructs an in memory AssignableDynamicGroup object. Default registered 068 * template will be used. This is an in memory Group object and one needs to 069 * call <code>save</code> method to save this newly created object to 070 * persistent storage. 071 * 072 * @param attrSet Attribute/value set. 073 * @exception UMSException if fail to instantiate from persistent storage. 074 */ 075 AssignableDynamicGroup(AttrSet attrSet) throws UMSException { 076 this(TemplateManager.getTemplateManager().getCreationTemplate(_class, 077 null), attrSet); 078 } 079 080 /** 081 * Constructs an in memory <code>AssignableDynamicGroup</code> object with 082 * a given template. This is an in memory Group object and one needs to 083 * call save method to <code>save</code> this newly created object to 084 * persistent storage. 085 * 086 * @param template Template for creating a group. 087 * @param attrSet Attribute/value set. 088 * @exception UMSException if fail to instantiate from persistent storage. 089 * 090 * @supported.api 091 */ 092 public AssignableDynamicGroup(CreationTemplate template, AttrSet attrSet) 093 throws UMSException { 094 super(template, attrSet); 095 } 096 097 /** 098 * Constructs an in memory <code>AssignableDynamicGroup</code> object using 099 * default registered for <code>AssignableDynamicGroup</code>. This is an 100 * in memory Group object and one needs to call <code>save</code> method to 101 * save this newly created object to persistent storage. 102 * 103 * @param attrSet Attribute/value set, which should not contain 104 * <code>memberUrl</code>; any values of <code>memberUrl</code> will 105 * be overwritten by the explicit search criteria arguments. 106 * @param base Search base for evaluating members of the group. 107 * @param scope Search scope for evaluating members of the group the value 108 * has to be <code>LDAPv2.SCOPE_ONE</code> or 109 * <code>LDAPv2.SCOPE_SUB</code>. 110 * @exception UMSException if fail to instantiate from persistent storage. 111 */ 112 AssignableDynamicGroup(AttrSet attrSet, Guid baseGuid, int scope) 113 throws UMSException { 114 this(TemplateManager.getTemplateManager().getCreationTemplate(_class, 115 null), attrSet, baseGuid, scope); 116 } 117 118 /** 119 * Constructs an <code>AssignableDynamicGroup</code> object with a given 120 * template. This is an in memory Group object and one needs to call 121 * <code>save</code> method to save this newly created object to 122 * persistent storage. 123 * 124 * @param template Template for creating a group. 125 * @param attrSet Attribute-value set which should not contain member URL; 126 * any values of member URL will be overwritten by the explicit 127 * search criteria arguments. 128 * @param baseGuid Search base for evaluating members of the group 129 * @param scope Search scope for evaluating members of the group has to be 130 * <code>LDAPv2.SCOPE_ONE</code> or <code>LDAPv2.SCOPE_SUB</code>. 131 * @exception UMSException if fail to instantiate from persistent storage 132 * 133 * @supported.api 134 */ 135 public AssignableDynamicGroup(CreationTemplate template, AttrSet attrSet, 136 Guid baseGuid, int scope) throws UMSException { 137 super(template, attrSet); 138 // No host, port, or attributes in the URL 139 // setUrl( new LDAPUrl( null, 0, base, (String[])null, scope, "" ) ); 140 setUrl(baseGuid, null, scope); 141 } 142 143 /** 144 * Sets the search filter used to evaluate this dynamic group. For an 145 * <code>AssignableDynamicGroup</code>, the filter is always 146 * <code>"memberof=THIS_DN"</code>, so this method should not generally be 147 * called outside the package. 148 * 149 * @param filter Search filter for evaluating members of the group the 150 * scope in the filter has to be <code>LDAPv2.SCOPE_ONE</code> or 151 * <code>LDAPv2.SCOPE_SUB</code>. 152 * 153 * @supported.api 154 */ 155 public void setSearchFilter(String filter) { 156 LDAPUrl url = getUrl(); 157 int scope = url.getScope(); 158 if (scope != LDAPv2.SCOPE_ONE && scope != LDAPv2.SCOPE_SUB) { 159 String msg = i18n.getString(IUMSConstants.ILLEGAL_ADGROUP_SCOPE); 160 throw new IllegalArgumentException(msg); 161 } 162 Guid baseGuid = new Guid(url.getDN()); 163 setUrl(baseGuid, filter, scope); 164 } 165 166 /** 167 * Sets the GUID of the entity; used within the package. 168 * 169 * @param guid GUID <code>REVIEW</code>: This method overloads the 170 * <code>PersistentObject.setGuid()</code> method. Hence the 171 * signature has to match, and we can't throw the 172 * <code>UMSException</code> that could be thrown from 173 * <code>"setSearchFilter"</code>. Is it enough to log such an 174 * error ??? 175 */ 176 protected void setGuid(Guid guid) { 177 super.setGuid(guid); 178 // setSearchFilter( "(" + "memberof=" + getDN() + ")" ); 179 try { 180 setSearchFilter("memberof=" + getDN()); 181 } catch (Exception e) { 182 // TODO - Log Exception 183 if (debug.messageEnabled()) { 184 debug.message("AssignableDynamicGroup.setGuid() : " 185 + "Exception : " + e.getMessage()); 186 } 187 } 188 } 189 190 /** 191 * Adds a member to the group. The change is saved to persistent storage. 192 * 193 * @param userGuid Globally unique identifier for the member to be added. 194 * @exception UMSException if fail to save to persistent storage or if the 195 * user is not within the scope of the group. 196 * 197 * @supported.api 198 */ 199 public void addMember(Guid userGuid) throws UMSException { 200 // UMSSession session = getUMSSession(); 201 if (getPrincipal() == null) { 202 throw new IllegalArgumentException(i18n 203 .getString(IUMSConstants.NULL_PRINCIPAL)); 204 } 205 206 addMember(UMSObject.getObject(getPrincipal(), userGuid)); 207 } 208 209 /** 210 * Adds a member to the group. The change is saved to persistent storage. 211 * 212 * @param member Object to be added as member. 213 * @exception UMSException if fail to save to persistent storage or if the 214 * user is not within the scope of the group. 215 * 216 * @supported.api 217 */ 218 public void addMember(PersistentObject member) throws UMSException { 219 // check whether the userGuid is within the scope of memberUrl 220 DN userDN = new DN(member.getGuid().getDn()); 221 LDAPUrl memberUrl = getUrl(); 222 DN memberDN = new DN(memberUrl.getDN()); 223 224 if (!userDN.isDescendantOf(memberDN) || userDN.equals(memberDN)) { 225 String args[] = new String[2]; 226 args[0] = userDN.toString(); 227 args[1] = memberUrl.toString(); 228 String msg = i18n.getString(IUMSConstants.USER_NOT_IN_GROUP_SCOPE, 229 args); 230 throw new UMSException(msg); 231 } else if (((userDN.countRDNs() - memberDN.countRDNs()) > 1) 232 && (memberUrl.getScope() == LDAPv2.SCOPE_ONE)) { 233 String args[] = new String[2]; 234 args[0] = userDN.toString(); 235 args[1] = memberUrl.toString(); 236 String msg = i18n.getString(IUMSConstants.USER_NOT_IN_GROUP_SCOPE, 237 args); 238 throw new UMSException(msg); 239 } 240 member.modify(new Attr(MEMBER_ATTR_NAME, this.getDN()), ModSet.ADD); 241 member.save(); 242 } 243 244 /** 245 * Adds a list of members to the group. The change is saved to persistent 246 * storage. 247 * 248 * @param guids Array of member GUIDs to be added as members to the group. 249 * @exception UMSException if fail to save to persistent storage. 250 * 251 * @supported.api 252 */ 253 public void addMembers(Guid[] guids) throws UMSException { 254 if (guids == null) { 255 throw new IllegalArgumentException(i18n 256 .getString(IUMSConstants.NULL_GUIDS)); 257 } 258 for (int i = 0; i < guids.length; i++) { 259 addMember(guids[i]); 260 } 261 } 262 263 /** 264 * Removes a member from the group. The change is saved to persistent 265 * storage. 266 * 267 * @param guid Unique identifier for the member to be removed. 268 * @exception UMSException if fail to save to persistent storage. 269 * 270 * @supported.api 271 */ 272 public void removeMember(Guid guid) throws UMSException { 273 PersistentObject member = UMSObject.getObject(getPrincipal(), guid); 274 removeMember(member); 275 } 276 277 /** 278 * Removes a member from the group. The change is saved to persistent 279 * storage. 280 * 281 * @param member Object to be removed. 282 * @exception UMSException if fail to save to persistent storage. 283 * 284 * @supported.api 285 */ 286 public void removeMember(PersistentObject member) throws UMSException { 287 member.modify(new Attr(MEMBER_ATTR_NAME, this.getDN()), ModSet.DELETE); 288 member.save(); 289 } 290 291 /** 292 * Removes all members of the group. 293 * 294 * @exception UMSException if fail to save to persistent storage. 295 * 296 * @supported.api 297 */ 298 public void removeAllMembers() throws UMSException { 299 300 String filter = getSearchFilter(); 301 if (filter == null) { 302 return; 303 } 304 String[] attributesToGet = { "dn" }; 305 SearchResults searchResults = getMemberIDs(attributesToGet); 306 while (searchResults.hasMoreElements()) { 307 PersistentObject member = searchResults.next(); 308 member.setPrincipal(getPrincipal()); 309 removeMember(member); 310 } 311 } 312 313 /** 314 * Returns <code>true</code> if a given identifier is a member of the 315 * group. 316 * 317 * @param guid Identity of member to be checked for membership. 318 * @return <code>true</code> if it is a member. 319 * @exception UMSException if fail to read object for guid. 320 * 321 * @supported.api 322 */ 323 public boolean hasMember(Guid guid) throws UMSException { 324 if (getPrincipal() == null) { 325 throw new IllegalArgumentException(i18n 326 .getString(IUMSConstants.NULL_PRINCIPAL)); 327 } 328 PersistentObject object = UMSObject.getObject(getPrincipal(), guid); 329 Attr attr = object.getAttribute(MEMBER_ATTR_NAME); 330 if (attr == null) { 331 if (debug.messageEnabled()) { 332 debug.message("AssignableDynamicGroup.hasMember: no " 333 + "attribute " + MEMBER_ATTR_NAME + " in " 334 + guid.getDn()); 335 } 336 return false; 337 } 338 339 // need to normalize DN to escape spaces and such 340 // for accurate checking of membership 341 // TODO: This ties guids to DNS. The methods to normalize and compare 342 // should be managed separately. 343 // TODO: The members should have been normalized before adding to 344 // the group (i.e. when creating or modifying it), so it should not 345 // be necessary to have normalizing code spread out in the classes 346 // and methods. 347 String normalized = getGuid().getDn(); 348 String[] members = attr.getStringValues(); 349 for (int i = 0; i < members.length; i++) { 350 String target = members[i]; 351 if (debug.messageEnabled()) { 352 debug.message("AssignableDynamicGroup.hasMember: comparing " 353 + normalized + " to " + target); 354 } 355 if (Guid.equals(normalized, target)) { 356 return true; 357 } 358 } 359 360 return false; 361 } 362 363 /** 364 * Saves the modification(s) to the object to persistent storage. 365 * 366 * @return UMSException on failure to save to persistent storage. 367 */ 368 /* 369 * public void save () throws UMSException { String filter = 370 * getSearchFilter(); if ( (filter == null) || (filter.length() < 1) ) { 371 * setSearchFilter( "memberof=" + getDN() ); } super.save(); } 372 */ 373 374 private static final String MEMBER_ATTR_NAME = "memberof"; 375 376 private static final Class _class = new AssignableDynamicGroup().getClass(); 377}