001package com.identityworksllc.iiq.common; 002 003import sailpoint.tools.Util; 004 005import javax.naming.InvalidNameException; 006import javax.naming.ldap.LdapName; 007import java.util.List; 008 009/** 010 * Utilities for dealing with LDAP DNs and other similar concepts 011 */ 012public class LdapUtilities { 013 /** 014 * Extracts the name from an LDAP formatted group name. For example, if given CN=AD Group Name,OU=Groups,OU=Security,DC=client,DC=example,DC=com, this method would return "AD Group Name". 015 * 016 * @param groupDN The group DN 017 * @return the group name 018 * @throws InvalidNameException if this is not an LDAP name 019 */ 020 public static String ldapCleanGroupName(String groupDN) throws InvalidNameException { 021 String groupName = groupDN; 022 // Handle goofy OIM format if needed 023 if (groupName.contains("~")) { 024 groupName = groupName.substring(groupName.indexOf("~") + 1); 025 } 026 LdapName parser = new LdapName(groupName); 027 String firstElem = parser.get(parser.size() - 1); 028 if (firstElem.length() > 0 && firstElem.contains("=")) { 029 return firstElem.substring(firstElem.indexOf("=") + 1); 030 } 031 throw new IllegalArgumentException("Group name " + groupName + " does not appear to be in LDAP format"); 032 } 033 034 /** 035 * Returns true if the given list of DNs contains a matching DN by RDN. 036 * This is useful for searching a list of AD groups (e.g., user entitlements) 037 * for a given value, without having to worry about differing domain suffixes 038 * across dev, test, and prod. 039 * 040 * Equivalent to {@link #ldapContains(List, String, int)} with a depth of 1. 041 * 042 * @param container A list of candidate DNs 043 * @param seeking The DN (whole or partial) to match 044 * @return True if the list contains a matching DN, false if not 045 */ 046 public static boolean ldapContains(List<String> container, String seeking) { 047 return ldapContains(container, seeking, 1); 048 } 049 050 /** 051 * Returns true if the given list of DNs contains a value matching the given 052 * 'seeking' DN, up to the given depth. 053 * 054 * @param container A list of candidate DNs 055 * @param seeking The DN (whole or partial) to match 056 * @param depth The depth of search 057 * @return True if the list contains a matching DN, false if not 058 */ 059 public static boolean ldapContains(List<String> container, String seeking, int depth) { 060 for(String dn : Util.safeIterable(container)) { 061 if (ldapMatches(dn, seeking, depth)) { 062 return true; 063 } 064 } 065 return false; 066 } 067 068 /** 069 * Given a list of possible matching DNs (the container), finds the first 070 * one that matches the RDN of the 'seeking' string. 071 * 072 * @param container A list of candidate DNs 073 * @param seeking The DN we are seeking to match 074 * @return The DN matching the search, or null if none is found 075 */ 076 public static String ldapGetMatch(List<String> container, String seeking) { 077 return ldapGetMatch(container, seeking, 1); 078 } 079 080 /** 081 * Given a list of possible matching DNs (the container), finds the first 082 * one that matches the 'seeking' string up to the given depth. 083 * 084 * @param container A list of candidate DNs 085 * @param seeking The DN we are seeking 086 * @param depth The number of RDN components to match 087 * @return The DN matching the search, or null if none is found 088 */ 089 public static String ldapGetMatch(List<String> container, String seeking, int depth) { 090 for(String dn : Util.safeIterable(container)) { 091 if (ldapMatches(dn, seeking, depth)) { 092 return dn; 093 } 094 } 095 return null; 096 } 097 098 /** 099 * Extracts the first N RDNs from an LDAP formatted DN. For example, 100 * if given CN=AD Group Name,OU=Groups,OU=Security,DC=client,DC=example,DC=com, 101 * and a size of 1, this method would return "CN=AD Group Name". A size of 2 102 * would produce "CN=AD Group Name,OU=Groups". 103 * 104 * @param dn The object's distinguishedName 105 * @param size The number of RDN elements to return 106 * @return the first 'size' RDNs of the DN 107 * @throws InvalidNameException if this is not an LDAP name 108 */ 109 public static String ldapGetRdn(String dn, int size) throws InvalidNameException { 110 LdapName parser = new LdapName(dn); 111 return ldapGetRdn(parser, size); 112 } 113 114 /** 115 * Extracts the first N RDNs from an LDAP formatted DN. For example, 116 * if given CN=AD Group Name,OU=Groups,OU=Security,DC=client,DC=example,DC=com, 117 * and a size of 1, this method would return "CN=AD Group Name". A size of 2 118 * would produce "CN=AD Group Name,OU=Groups". 119 * 120 * @param name The already-parsed LdapName object 121 * @param size The number of RDN elements to return 122 * @return the first 'size' RDNs of the DN 123 * @throws InvalidNameException if this is not an LDAP name 124 */ 125 public static String ldapGetRdn(LdapName name, int size) throws InvalidNameException { 126 StringBuilder builder = new StringBuilder(); 127 for(int i = 1; i <= size && i <= name.size(); i++) { 128 if (builder.length() > 0) { 129 builder.append(","); 130 } 131 132 // LDAP names are sorted like a file path, with the RDN last 133 builder.append(name.get(name.size() - i)); 134 } 135 return builder.toString(); 136 } 137 138 /** 139 * Returns true if the first element of the given LDAP name matches the value provided. 140 * 141 * Equivalent to ldapMatches(name, otherName, 1). 142 * 143 * @param name The LDAP name to check 144 * @param otherName The other name to compare against 145 * @return True if the names are LDAP DNs and equal ignoring case, otherwise false 146 */ 147 public static boolean ldapMatches(String name, String otherName) { 148 return ldapMatches(name, otherName, 1); 149 } 150 151 /** 152 * Returns true if the given objects match by comparing them as LDAP DNs up 153 * to the depth specified. For example, the following two DNs will match at 154 * a depth of 1, but not a depth of 2. 155 * 156 * CN=Group Name,OU=Groups,DC=test,DC=example,DC=com 157 * cn=group name,OU=Administrators,DC=test,DC=example,DC=com 158 * 159 * This is primarily useful with AD environments where the group names will 160 * have a suffix varying by domain. 161 * 162 * @param name The first LDAP name 163 * @param otherName The second LDAP name 164 * @param depth The number of DN elements to search for a match (with 1 being the RDN only) 165 * @return True if the names are indeed LDAP DNs and equal ignoring case, false otherwise 166 */ 167 public static boolean ldapMatches(String name, String otherName, int depth) { 168 if (Util.nullSafeCaseInsensitiveEq(name, otherName)) { 169 return true; 170 } 171 try { 172 return Util.nullSafeCaseInsensitiveEq(ldapGetRdn(name, depth), ldapGetRdn(otherName, depth)); 173 } catch(Exception e) { 174 return false; 175 } 176 } 177 178 /** 179 * Private utility constructor 180 */ 181 private LdapUtilities() { 182 183 } 184}