001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.controls; 037 038 039 040import java.util.Collections; 041import java.util.EnumSet; 042import java.util.HashMap; 043import java.util.HashSet; 044import java.util.List; 045import java.util.Map; 046import java.util.Set; 047import java.util.StringTokenizer; 048import java.util.logging.Level; 049 050import com.unboundid.ldap.sdk.Attribute; 051import com.unboundid.ldap.sdk.Entry; 052import com.unboundid.ldap.sdk.ReadOnlyEntry; 053import com.unboundid.util.Debug; 054import com.unboundid.util.DebugType; 055import com.unboundid.util.NotMutable; 056import com.unboundid.util.NotNull; 057import com.unboundid.util.Nullable; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061import com.unboundid.util.Validator; 062 063 064 065/** 066 * This class provides a mechanism for extracting the effective rights 067 * information from an entry returned for a search request that included the 068 * get effective rights request control. In particular, it provides the ability 069 * to parse the values of the aclRights attributes in order to determine what 070 * rights the specified user may have when interacting with the entry. 071 * <BR> 072 * <BLOCKQUOTE> 073 * <B>NOTE:</B> This class, and other classes within the 074 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 075 * supported for use against Ping Identity, UnboundID, and 076 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 077 * for proprietary functionality or for external specifications that are not 078 * considered stable or mature enough to be guaranteed to work in an 079 * interoperable way with other types of LDAP servers. 080 * </BLOCKQUOTE> 081 * <BR> 082 * See the {@link GetEffectiveRightsRequestControl} for an example that 083 * demonstrates the use of the get effective rights request control and this 084 * entry. 085 */ 086@NotMutable() 087@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 088public final class EffectiveRightsEntry 089 extends ReadOnlyEntry 090{ 091 /** 092 * The name of the attribute that includes the rights information. 093 */ 094 @NotNull private static final String ATTR_ACL_RIGHTS = "aclRights"; 095 096 097 098 /** 099 * The serial version UID for this serializable class. 100 */ 101 private static final long serialVersionUID = -3203127456449579174L; 102 103 104 105 // The set of entry-level rights parsed from the entry. 106 @Nullable private final Set<EntryRight> entryRights; 107 108 // The set of attribute-level rights parsed from the entry, mapped from the 109 // name of the attribute to the set of the corresponding attribute rights. 110 @Nullable private final Map<String,Set<AttributeRight>> attributeRights; 111 112 113 114 /** 115 * Creates a new get effective rights entry from the provided entry. 116 * 117 * @param entry The entry to use to create this get effective rights entry. 118 * It must not be {@code null}. 119 */ 120 public EffectiveRightsEntry(@NotNull final Entry entry) 121 { 122 super(entry); 123 124 final HashSet<String> options = StaticUtils.hashSetOf("entryLevel"); 125 List<Attribute> attrList = 126 getAttributesWithOptions(ATTR_ACL_RIGHTS, options); 127 if ((attrList == null) || attrList.isEmpty()) 128 { 129 if (Debug.debugEnabled(DebugType.LDAP)) 130 { 131 Debug.debug(Level.WARNING, DebugType.LDAP, 132 "No entry-level aclRights information contained in entry " + 133 entry.getDN()); 134 } 135 136 entryRights = null; 137 } 138 else 139 { 140 entryRights = Collections.unmodifiableSet(parseEntryRights(attrList)); 141 } 142 143 options.clear(); 144 options.add("attributeLevel"); 145 attrList = getAttributesWithOptions(ATTR_ACL_RIGHTS, options); 146 if ((attrList == null) || attrList.isEmpty()) 147 { 148 if (Debug.debugEnabled(DebugType.LDAP)) 149 { 150 Debug.debug(Level.WARNING, DebugType.LDAP, 151 "No attribute-level aclRights information contained in entry " + 152 entry.getDN()); 153 } 154 155 attributeRights = null; 156 } 157 else 158 { 159 final HashMap<String,Set<AttributeRight>> attrRightsMap = 160 new HashMap<>(StaticUtils.computeMapCapacity(attrList.size())); 161 for (final Attribute a : attrList) 162 { 163 final Set<String> attrOptions = a.getOptions(); 164 String attrName = null; 165 for (final String s : attrOptions) 166 { 167 if (! s.equalsIgnoreCase("attributeLevel")) 168 { 169 attrName = s; 170 } 171 } 172 173 if (attrName == null) 174 { 175 if (Debug.debugEnabled(DebugType.LDAP)) 176 { 177 Debug.debug(Level.WARNING, DebugType.LDAP, 178 "Unable to determine the target attribute name from " + 179 a.getName()); 180 } 181 } 182 else 183 { 184 final String lowerName = StaticUtils.toLowerCase(attrName); 185 final Set<AttributeRight> rights = parseAttributeRights(a); 186 attrRightsMap.put(lowerName, rights); 187 } 188 } 189 190 attributeRights = Collections.unmodifiableMap(attrRightsMap); 191 } 192 } 193 194 195 196 /** 197 * Parses the entry rights information from the entry. 198 * 199 * @param attrList The list of attributes to be parsed. 200 * 201 * @return The set of entry rights parsed from the entry. 202 */ 203 @NotNull() 204 private static Set<EntryRight> parseEntryRights( 205 @NotNull final List<Attribute> attrList) 206 { 207 final EnumSet<EntryRight> entryRightsSet = EnumSet.noneOf(EntryRight.class); 208 for (final Attribute a : attrList) 209 { 210 for (final String value : a.getValues()) 211 { 212 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 213 while (tokenizer.hasMoreTokens()) 214 { 215 final String token = tokenizer.nextToken(); 216 if (token.endsWith(":1")) 217 { 218 final String rightName = token.substring(0, token.length()-2); 219 final EntryRight r = EntryRight.forName(rightName); 220 if (r == null) 221 { 222 if (Debug.debugEnabled(DebugType.LDAP)) 223 { 224 Debug.debug(Level.WARNING, DebugType.LDAP, 225 "Unrecognized entry right " + rightName); 226 } 227 } 228 else 229 { 230 entryRightsSet.add(r); 231 } 232 } 233 } 234 } 235 } 236 237 return entryRightsSet; 238 } 239 240 241 242 /** 243 * Parses the attribute rights information from the provided attribute. 244 * 245 * @param a The attribute to be parsed. 246 * 247 * @return The set of attribute rights parsed from the provided attribute. 248 */ 249 @NotNull() 250 private static Set<AttributeRight> parseAttributeRights( 251 @NotNull final Attribute a) 252 { 253 final EnumSet<AttributeRight> rightsSet = 254 EnumSet.noneOf(AttributeRight.class); 255 256 for (final String value : a.getValues()) 257 { 258 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 259 while (tokenizer.hasMoreTokens()) 260 { 261 final String token = tokenizer.nextToken(); 262 if (token.endsWith(":1")) 263 { 264 final String rightName = token.substring(0, token.length()-2); 265 final AttributeRight r = AttributeRight.forName(rightName); 266 if (r == null) 267 { 268 if (Debug.debugEnabled(DebugType.LDAP)) 269 { 270 Debug.debug(Level.WARNING, DebugType.LDAP, 271 "Unrecognized attribute right " + rightName); 272 } 273 } 274 else 275 { 276 rightsSet.add(r); 277 } 278 } 279 } 280 } 281 282 return rightsSet; 283 } 284 285 286 287 /** 288 * Indicates whether any access control rights information was contained in 289 * the entry. 290 * 291 * @return {@code true} if access control rights information was contained in 292 * the entry, or {@code false} if not. 293 */ 294 public boolean rightsInformationAvailable() 295 { 296 return ((entryRights != null) || (attributeRights != null)); 297 } 298 299 300 301 /** 302 * Retrieves the set of entry-level rights parsed from the entry. 303 * 304 * @return The set of entry-level rights parsed from the entry, or 305 * {@code null} if the entry did not have any entry-level rights 306 * information. 307 */ 308 @Nullable() 309 public Set<EntryRight> getEntryRights() 310 { 311 return entryRights; 312 } 313 314 315 316 /** 317 * Indicates whether the specified entry right is granted for this entry. 318 * 319 * @param entryRight The entry right for which to make the determination. 320 * It must not be {@code null}. 321 * 322 * @return {@code true} if the entry included entry-level rights information 323 * and the specified entry right is granted, or {@code false} if not. 324 */ 325 public boolean hasEntryRight(@NotNull final EntryRight entryRight) 326 { 327 Validator.ensureNotNull(entryRight); 328 329 return ((entryRights != null) && entryRights.contains(entryRight)); 330 } 331 332 333 334 /** 335 * Retrieves the set of attribute-level rights parsed from the entry, mapped 336 * from attribute name (in all lowercase characters) to the set of 337 * attribute-level rights for that attribute. 338 * 339 * @return The set of attribute-level rights parsed from the entry, or 340 * {@code null} if the entry did not have any attribute-level rights 341 * information. 342 */ 343 @Nullable() 344 public Map<String,Set<AttributeRight>> getAttributeRights() 345 { 346 return attributeRights; 347 } 348 349 350 351 /** 352 * Retrieves the set of attribute-level rights parsed from the entry for the 353 * specified attribute. 354 * 355 * @param attributeName The name of the attribute for which to retrieve the 356 * attribute-level rights. It must not be 357 * {@code null}. 358 * 359 * @return The set of attribute-level rights for the specified attribute, or 360 * {@code null} if the entry did not include any attribute-level 361 * rights information for the specified attribute. 362 */ 363 @Nullable() 364 public Set<AttributeRight> getAttributeRights( 365 @NotNull final String attributeName) 366 { 367 Validator.ensureNotNull(attributeName); 368 369 if (attributeRights == null) 370 { 371 return null; 372 } 373 374 return attributeRights.get(StaticUtils.toLowerCase(attributeName)); 375 } 376 377 378 379 /** 380 * Indicates whether the specified attribute right is granted for the 381 * specified attribute in this entry. 382 * 383 * @param attributeRight The attribute right for which to make the 384 * determination. It must not be {@code null}. 385 * @param attributeName The name of the attribute for which to make the 386 * determination. It must not be {@code null}. 387 * 388 * @return {@code true} if the entry included attribute-level rights 389 * information for the specified attribute and the indicated right is 390 * granted, or {@code false} if not. 391 */ 392 public boolean hasAttributeRight(@NotNull final AttributeRight attributeRight, 393 @NotNull final String attributeName) 394 { 395 Validator.ensureNotNull(attributeName, attributeRight); 396 397 final Set<AttributeRight> attrRights = getAttributeRights(attributeName); 398 return ((attrRights != null) && attrRights.contains(attributeRight)); 399 } 400}