001/* 002 * Copyright 2008-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2020 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) 2015-2020 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.StaticUtils; 057import com.unboundid.util.ThreadSafety; 058import com.unboundid.util.ThreadSafetyLevel; 059import com.unboundid.util.Validator; 060 061 062 063/** 064 * This class provides a mechanism for extracting the effective rights 065 * information from an entry returned for a search request that included the 066 * get effective rights request control. In particular, it provides the ability 067 * to parse the values of the aclRights attributes in order to determine what 068 * rights the specified user may have when interacting with the entry. 069 * <BR> 070 * <BLOCKQUOTE> 071 * <B>NOTE:</B> This class, and other classes within the 072 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 073 * supported for use against Ping Identity, UnboundID, and 074 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 075 * for proprietary functionality or for external specifications that are not 076 * considered stable or mature enough to be guaranteed to work in an 077 * interoperable way with other types of LDAP servers. 078 * </BLOCKQUOTE> 079 * <BR> 080 * See the {@link GetEffectiveRightsRequestControl} for an example that 081 * demonstrates the use of the get effective rights request control and this 082 * entry. 083 */ 084@NotMutable() 085@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 086public final class EffectiveRightsEntry 087 extends ReadOnlyEntry 088{ 089 /** 090 * The name of the attribute that includes the rights information. 091 */ 092 private static final String ATTR_ACL_RIGHTS = "aclRights"; 093 094 095 096 /** 097 * The serial version UID for this serializable class. 098 */ 099 private static final long serialVersionUID = -3203127456449579174L; 100 101 102 103 // The set of entry-level rights parsed from the entry. 104 private final Set<EntryRight> entryRights; 105 106 // The set of attribute-level rights parsed from the entry, mapped from the 107 // name of the attribute to the set of the corresponding attribute rights. 108 private final Map<String,Set<AttributeRight>> attributeRights; 109 110 111 112 /** 113 * Creates a new get effective rights entry from the provided entry. 114 * 115 * @param entry The entry to use to create this get effective rights entry. 116 * It must not be {@code null}. 117 */ 118 public EffectiveRightsEntry(final Entry entry) 119 { 120 super(entry); 121 122 final HashSet<String> options = StaticUtils.hashSetOf("entryLevel"); 123 List<Attribute> attrList = 124 getAttributesWithOptions(ATTR_ACL_RIGHTS, options); 125 if ((attrList == null) || attrList.isEmpty()) 126 { 127 if (Debug.debugEnabled(DebugType.LDAP)) 128 { 129 Debug.debug(Level.WARNING, DebugType.LDAP, 130 "No entry-level aclRights information contained in entry " + 131 entry.getDN()); 132 } 133 134 entryRights = null; 135 } 136 else 137 { 138 entryRights = Collections.unmodifiableSet(parseEntryRights(attrList)); 139 } 140 141 options.clear(); 142 options.add("attributeLevel"); 143 attrList = getAttributesWithOptions(ATTR_ACL_RIGHTS, options); 144 if ((attrList == null) || attrList.isEmpty()) 145 { 146 if (Debug.debugEnabled(DebugType.LDAP)) 147 { 148 Debug.debug(Level.WARNING, DebugType.LDAP, 149 "No attribute-level aclRights information contained in entry " + 150 entry.getDN()); 151 } 152 153 attributeRights = null; 154 } 155 else 156 { 157 final HashMap<String,Set<AttributeRight>> attrRightsMap = 158 new HashMap<>(StaticUtils.computeMapCapacity(attrList.size())); 159 for (final Attribute a : attrList) 160 { 161 final Set<String> attrOptions = a.getOptions(); 162 String attrName = null; 163 for (final String s : attrOptions) 164 { 165 if (! s.equalsIgnoreCase("attributeLevel")) 166 { 167 attrName = s; 168 } 169 } 170 171 if (attrName == null) 172 { 173 if (Debug.debugEnabled(DebugType.LDAP)) 174 { 175 Debug.debug(Level.WARNING, DebugType.LDAP, 176 "Unable to determine the target attribute name from " + 177 a.getName()); 178 } 179 } 180 else 181 { 182 final String lowerName = StaticUtils.toLowerCase(attrName); 183 final Set<AttributeRight> rights = parseAttributeRights(a); 184 attrRightsMap.put(lowerName, rights); 185 } 186 } 187 188 attributeRights = Collections.unmodifiableMap(attrRightsMap); 189 } 190 } 191 192 193 194 /** 195 * Parses the entry rights information from the entry. 196 * 197 * @param attrList The list of attributes to be parsed. 198 * 199 * @return The set of entry rights parsed from the entry. 200 */ 201 private static Set<EntryRight> parseEntryRights( 202 final List<Attribute> attrList) 203 { 204 final EnumSet<EntryRight> entryRightsSet = EnumSet.noneOf(EntryRight.class); 205 for (final Attribute a : attrList) 206 { 207 for (final String value : a.getValues()) 208 { 209 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 210 while (tokenizer.hasMoreTokens()) 211 { 212 final String token = tokenizer.nextToken(); 213 if (token.endsWith(":1")) 214 { 215 final String rightName = token.substring(0, token.length()-2); 216 final EntryRight r = EntryRight.forName(rightName); 217 if (r == null) 218 { 219 if (Debug.debugEnabled(DebugType.LDAP)) 220 { 221 Debug.debug(Level.WARNING, DebugType.LDAP, 222 "Unrecognized entry right " + rightName); 223 } 224 } 225 else 226 { 227 entryRightsSet.add(r); 228 } 229 } 230 } 231 } 232 } 233 234 return entryRightsSet; 235 } 236 237 238 239 /** 240 * Parses the attribute rights information from the provided attribute. 241 * 242 * @param a The attribute to be parsed. 243 * 244 * @return The set of attribute rights parsed from the provided attribute. 245 */ 246 private static Set<AttributeRight> parseAttributeRights(final Attribute a) 247 { 248 final EnumSet<AttributeRight> rightsSet = 249 EnumSet.noneOf(AttributeRight.class); 250 251 for (final String value : a.getValues()) 252 { 253 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 254 while (tokenizer.hasMoreTokens()) 255 { 256 final String token = tokenizer.nextToken(); 257 if (token.endsWith(":1")) 258 { 259 final String rightName = token.substring(0, token.length()-2); 260 final AttributeRight r = AttributeRight.forName(rightName); 261 if (r == null) 262 { 263 if (Debug.debugEnabled(DebugType.LDAP)) 264 { 265 Debug.debug(Level.WARNING, DebugType.LDAP, 266 "Unrecognized attribute right " + rightName); 267 } 268 } 269 else 270 { 271 rightsSet.add(r); 272 } 273 } 274 } 275 } 276 277 return rightsSet; 278 } 279 280 281 282 /** 283 * Indicates whether any access control rights information was contained in 284 * the entry. 285 * 286 * @return {@code true} if access control rights information was contained in 287 * the entry, or {@code false} if not. 288 */ 289 public boolean rightsInformationAvailable() 290 { 291 return ((entryRights != null) || (attributeRights != null)); 292 } 293 294 295 296 /** 297 * Retrieves the set of entry-level rights parsed from the entry. 298 * 299 * @return The set of entry-level rights parsed from the entry, or 300 * {@code null} if the entry did not have any entry-level rights 301 * information. 302 */ 303 public Set<EntryRight> getEntryRights() 304 { 305 return entryRights; 306 } 307 308 309 310 /** 311 * Indicates whether the specified entry right is granted for this entry. 312 * 313 * @param entryRight The entry right for which to make the determination. 314 * It must not be {@code null}. 315 * 316 * @return {@code true} if the entry included entry-level rights information 317 * and the specified entry right is granted, or {@code false} if not. 318 */ 319 public boolean hasEntryRight(final EntryRight entryRight) 320 { 321 Validator.ensureNotNull(entryRight); 322 323 return ((entryRights != null) && entryRights.contains(entryRight)); 324 } 325 326 327 328 /** 329 * Retrieves the set of attribute-level rights parsed from the entry, mapped 330 * from attribute name (in all lowercase characters) to the set of 331 * attribute-level rights for that attribute. 332 * 333 * @return The set of attribute-level rights parsed from the entry, or 334 * {@code null} if the entry did not have any attribute-level rights 335 * information. 336 */ 337 public Map<String,Set<AttributeRight>> getAttributeRights() 338 { 339 return attributeRights; 340 } 341 342 343 344 /** 345 * Retrieves the set of attribute-level rights parsed from the entry for the 346 * specified attribute. 347 * 348 * @param attributeName The name of the attribute for which to retrieve the 349 * attribute-level rights. It must not be 350 * {@code null}. 351 * 352 * @return The set of attribute-level rights for the specified attribute, or 353 * {@code null} if the entry did not include any attribute-level 354 * rights information for the specified attribute. 355 */ 356 public Set<AttributeRight> getAttributeRights(final String attributeName) 357 { 358 Validator.ensureNotNull(attributeName); 359 360 if (attributeRights == null) 361 { 362 return null; 363 } 364 365 return attributeRights.get(StaticUtils.toLowerCase(attributeName)); 366 } 367 368 369 370 /** 371 * Indicates whether the specified attribute right is granted for the 372 * specified attribute in this entry. 373 * 374 * @param attributeRight The attribute right for which to make the 375 * determination. It must not be {@code null}. 376 * @param attributeName The name of the attribute for which to make the 377 * determination. It must not be {@code null}. 378 * 379 * @return {@code true} if the entry included attribute-level rights 380 * information for the specified attribute and the indicated right is 381 * granted, or {@code false} if not. 382 */ 383 public boolean hasAttributeRight(final AttributeRight attributeRight, 384 final String attributeName) 385 { 386 Validator.ensureNotNull(attributeName, attributeRight); 387 388 final Set<AttributeRight> attrRights = getAttributeRights(attributeName); 389 return ((attrRights != null) && attrRights.contains(attributeRight)); 390 } 391}