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