001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import java.util.Collections; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Set; 023import java.util.concurrent.atomic.AtomicReference; 024 025import org.forgerock.i18n.LocalizableMessage; 026import org.forgerock.i18n.LocalizedIllegalArgumentException; 027import org.forgerock.i18n.slf4j.LocalizedLogger; 028import org.forgerock.opendj.config.server.ConfigException; 029import org.forgerock.opendj.ldap.ByteString; 030import org.forgerock.opendj.ldap.DN; 031import org.forgerock.opendj.ldap.ResultCode; 032import org.forgerock.opendj.ldap.SearchScope; 033import org.forgerock.opendj.ldap.schema.AttributeType; 034import org.forgerock.opendj.server.config.server.VirtualStaticGroupImplementationCfg; 035import org.opends.server.api.Group; 036import org.opends.server.core.DirectoryServer; 037import org.opends.server.core.ServerContext; 038import org.opends.server.types.Attribute; 039import org.opends.server.types.DirectoryException; 040import org.opends.server.types.Entry; 041import org.opends.server.types.InitializationException; 042import org.opends.server.types.MemberList; 043import org.opends.server.types.Modification; 044import org.opends.server.types.SearchFilter; 045 046import static org.forgerock.util.Reject.*; 047import static org.opends.messages.ExtensionMessages.*; 048import static org.opends.server.config.ConfigConstants.*; 049import static org.opends.server.util.ServerConstants.*; 050 051/** 052 * This class provides a virtual static group implementation, in which 053 * membership is based on membership of another group. 054 */ 055public class VirtualStaticGroup 056 extends Group<VirtualStaticGroupImplementationCfg> 057{ 058 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 059 060 /** The DN of the entry that holds the definition for this group. */ 061 private DN groupEntryDN; 062 063 /** The DN of the target group that will provide membership information. */ 064 private DN targetGroupDN; 065 066 /** 067 * Creates a new, uninitialized virtual static group instance. This is 068 * intended for internal use only. 069 */ 070 public VirtualStaticGroup() 071 { 072 super(); 073 074 // No initialization is required here. 075 } 076 077 /** 078 * Creates a new virtual static group instance with the provided information. 079 * 080 * @param groupEntryDN The DN of the entry that holds the definition for 081 * this group. It must not be {@code null}. 082 * @param targetGroupDN The DN of the target group that will provide 083 * membership information. It must not be 084 * {@code null}. 085 */ 086 public VirtualStaticGroup(DN groupEntryDN, DN targetGroupDN) 087 { 088 super(); 089 090 ifNull(groupEntryDN, targetGroupDN); 091 092 this.groupEntryDN = groupEntryDN; 093 this.targetGroupDN = targetGroupDN; 094 } 095 096 @Override 097 public void initializeGroupImplementation( 098 VirtualStaticGroupImplementationCfg configuration) 099 throws ConfigException, InitializationException 100 { 101 // No additional initialization is required. 102 } 103 104 @Override 105 public VirtualStaticGroup newInstance(ServerContext serverContext, Entry groupEntry) 106 throws DirectoryException 107 { 108 ifNull(groupEntry); 109 110 // Get the target group DN attribute from the entry, if there is one. 111 DN targetDN = null; 112 AttributeType targetType = DirectoryServer.getSchema().getAttributeType(ATTR_TARGET_GROUP_DN); 113 for (Attribute a : groupEntry.getAttribute(targetType)) 114 { 115 for (ByteString v : a) 116 { 117 if (targetDN != null) 118 { 119 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS.get(groupEntry.getName()); 120 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 121 } 122 123 try 124 { 125 targetDN = DN.valueOf(v); 126 } 127 catch (LocalizedIllegalArgumentException e) 128 { 129 logger.traceException(e); 130 131 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET. 132 get(v, groupEntry.getName(), e.getMessageObject()); 133 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, e); 134 } 135 } 136 } 137 138 if (targetDN == null) 139 { 140 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET.get(groupEntry.getName()); 141 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 142 } 143 144 return new VirtualStaticGroup(groupEntry.getName(), targetDN); 145 } 146 147 @Override 148 public SearchFilter getGroupDefinitionFilter() 149 throws DirectoryException 150 { 151 // FIXME -- This needs to exclude enhanced groups once we have support for 152 // them. 153 return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" + 154 OC_VIRTUAL_STATIC_GROUP + ")"); 155 } 156 157 @Override 158 public boolean isGroupDefinition(Entry entry) 159 { 160 ifNull(entry); 161 162 // FIXME -- This needs to exclude enhanced groups once we have support for them. 163 return entry.hasObjectClass(DirectoryServer.getSchema().getObjectClass(OC_VIRTUAL_STATIC_GROUP)); 164 } 165 166 @Override 167 public DN getGroupDN() 168 { 169 return groupEntryDN; 170 } 171 172 @Override 173 public void setGroupDN(DN groupDN) 174 { 175 groupEntryDN = groupDN; 176 } 177 178 /** 179 * Retrieves the DN of the target group for this virtual static group. 180 * 181 * @return The DN of the target group for this virtual static group. 182 */ 183 public DN getTargetGroupDN() 184 { 185 return targetGroupDN; 186 } 187 188 @Override 189 public boolean supportsNestedGroups() 190 { 191 // Virtual static groups don't support nesting. 192 return false; 193 } 194 195 @Override 196 public List<DN> getNestedGroupDNs() 197 { 198 // Virtual static groups don't support nesting. 199 return Collections.<DN>emptyList(); 200 } 201 202 @Override 203 public void addNestedGroup(DN nestedGroupDN) 204 throws UnsupportedOperationException, DirectoryException 205 { 206 // Virtual static groups don't support nesting. 207 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get(); 208 throw new UnsupportedOperationException(message.toString()); 209 } 210 211 @Override 212 public void removeNestedGroup(DN nestedGroupDN) 213 throws UnsupportedOperationException, DirectoryException 214 { 215 // Virtual static groups don't support nesting. 216 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get(); 217 throw new UnsupportedOperationException(message.toString()); 218 } 219 220 @Override 221 public boolean isMember(DN userDN, AtomicReference<Set<DN>> examinedGroups) 222 throws DirectoryException 223 { 224 Set<DN> groups = getExaminedGroups(examinedGroups); 225 if (! groups.add(getGroupDN())) 226 { 227 return false; 228 } 229 230 Group targetGroup = 231 DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN); 232 if (targetGroup == null) 233 { 234 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN); 235 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 236 } 237 else if (targetGroup instanceof VirtualStaticGroup) 238 { 239 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN); 240 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 241 } 242 else 243 { 244 return targetGroup.isMember(userDN); 245 } 246 } 247 248 @Override 249 public boolean isMember(Entry userEntry, AtomicReference<Set<DN>> examinedGroups) 250 throws DirectoryException 251 { 252 Set<DN> groups = getExaminedGroups(examinedGroups); 253 if (! groups.add(getGroupDN())) 254 { 255 return false; 256 } 257 258 Group targetGroup = 259 DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN); 260 if (targetGroup == null) 261 { 262 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN); 263 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 264 } 265 else if (targetGroup instanceof VirtualStaticGroup) 266 { 267 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN); 268 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 269 } 270 else 271 { 272 return targetGroup.isMember(userEntry); 273 } 274 } 275 276 private Set<DN> getExaminedGroups(AtomicReference<Set<DN>> examinedGroups) 277 { 278 Set<DN> groups = examinedGroups.get(); 279 if (groups == null) 280 { 281 groups = new HashSet<DN>(); 282 examinedGroups.set(groups); 283 } 284 return groups; 285 } 286 287 @Override 288 public MemberList getMembers() 289 throws DirectoryException 290 { 291 Group targetGroup = 292 DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN); 293 if (targetGroup == null) 294 { 295 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN); 296 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 297 } 298 else if (targetGroup instanceof VirtualStaticGroup) 299 { 300 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN); 301 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 302 } 303 else 304 { 305 return targetGroup.getMembers(); 306 } 307 } 308 309 @Override 310 public MemberList getMembers(DN baseDN, SearchScope scope, 311 SearchFilter filter) 312 throws DirectoryException 313 { 314 Group targetGroup = 315 DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN); 316 if (targetGroup == null) 317 { 318 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN); 319 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 320 } 321 else if (targetGroup instanceof VirtualStaticGroup) 322 { 323 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN); 324 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 325 } 326 else 327 { 328 return targetGroup.getMembers(baseDN, scope, filter); 329 } 330 } 331 332 @Override 333 public boolean mayAlterMemberList() 334 { 335 return false; 336 } 337 338 @Override 339 public void updateMembers(List<Modification> modifications) 340 throws UnsupportedOperationException, DirectoryException 341 { 342 // Virtual static groups don't support altering the member list. 343 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(groupEntryDN); 344 throw new UnsupportedOperationException(message.toString()); 345 } 346 347 @Override 348 public void addMember(Entry userEntry) 349 throws UnsupportedOperationException, DirectoryException 350 { 351 // Virtual static groups don't support altering the member list. 352 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(groupEntryDN); 353 throw new UnsupportedOperationException(message.toString()); 354 } 355 356 @Override 357 public void removeMember(DN userDN) 358 throws UnsupportedOperationException, DirectoryException 359 { 360 // Virtual static groups don't support altering the member list. 361 LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(groupEntryDN); 362 throw new UnsupportedOperationException(message.toString()); 363 } 364 365 @Override 366 public void toString(StringBuilder buffer) 367 { 368 buffer.append("VirtualStaticGroup(dn="); 369 buffer.append(groupEntryDN); 370 buffer.append(",targetGroupDN="); 371 buffer.append(targetGroupDN); 372 buffer.append(")"); 373 } 374}