/**
* Copyright 2009 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.safehaus.penrose.ldap;
import org.ietf.ldap.LDAPException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.TreeSet;
/**
* @author Endi S. Dewata
*/
public class LDAP {
public static Logger log = LoggerFactory.getLogger(LDAP.class);
public final static String BASE_DN = "baseDn";
public final static String NEW_BASE_DN = "newBaseDn";
public final static String SCOPE = "scope";
public final static String FILTER = "filter";
public final static String OBJECT_CLASSES = "objectClasses";
public final static String SIZE_LIMIT = "sizeLimit";
public final static String TIME_LIMIT = "timeLimit";
public final static String ATTRIBUTES = "attributes";
public final static String AUTHENTICATION = "authentication";
public final static String AUTHENTICATION_DEFAULT = "default";
public final static String AUTHENTICATION_FULL = "full";
public final static String AUTHENTICATION_DISABLED = "disabled";
public final static DN ROOT_DSE_DN = new DN("");
public final static DN SCHEMA_DN = new DN("cn=Subschema");
public final static int SUCCESS = 0;
public final static int OPERATIONS_ERROR = 1;
public final static int PROTOCOL_ERROR = 2;
public final static int TIME_LIMIT_EXCEEDED = 3;
public final static int SIZE_LIMIT_EXCEEDED = 4;
public final static int COMPARE_FALSE = 5;
public final static int COMPARE_TRUE = 6;
public final static int AUTH_METHOD_NOT_SUPPORTED = 7;
public final static int STRONG_AUTH_REQUIRED = 8;
public final static int REFERRAL = 10;
public final static int ADMIN_LIMIT_EXCEEDED = 11;
public final static int UNAVAILABLE_CRITICAL_EXTENSION = 12;
public final static int CONFIDENTIALITY_REQUIRED = 13;
public final static int SASL_BIND_IN_PROGRESS = 14;
public final static int NO_SUCH_ATTRIBUTE = 16;
public final static int UNDEFINED_ATTRIBUTE_TYPE = 17;
public final static int INAPPROPRIATE_MATCHING = 18;
public final static int CONSTRAINT_VIOLATION = 19;
public final static int ATTRIBUTE_OR_VALUE_EXISTS = 20;
public final static int INVALID_ATTRIBUTE_SYNTAX = 21;
public final static int NO_SUCH_OBJECT = 32;
public final static int ALIAS_PROBLEM = 33;
public final static int INVALID_DN_SYNTAX = 34;
public final static int IS_LEAF = 35;
public final static int ALIAS_DEREFERENCING_PROBLEM = 36;
public final static int INAPPROPRIATE_AUTHENTICATION = 48;
public final static int INVALID_CREDENTIALS = 49;
public final static int INSUFFICIENT_ACCESS_RIGHTS = 50;
public final static int BUSY = 51;
public final static int UNAVAILABLE = 52;
public final static int UNWILLING_TO_PERFORM = 53;
public final static int LOOP_DETECT = 54;
public final static int NAMING_VIOLATION = 64;
public final static int OBJECT_CLASS_VIOLATION = 65;
public final static int NOT_ALLOWED_ON_NONLEAF = 66;
public final static int NOT_ALLOWED_ON_RDN = 67;
public final static int ENTRY_ALREADY_EXISTS = 68;
public final static int OBJECT_CLASS_MODS_PROHIBITED = 69;
public final static int AFFECTS_MULTIPLE_DSAS = 71;
public final static int OTHER = 80;
public final static int SERVER_DOWN = 81;
public final static int LOCAL_ERROR = 82;
public final static int ENCODING_ERROR = 83;
public final static int DECODING_ERROR = 84;
public final static int LDAP_TIMEOUT = 85;
public final static int AUTH_UNKNOWN = 86;
public final static int FILTER_ERROR = 87;
public final static int USER_CANCELLED = 88;
public final static int NO_MEMORY = 90;
public final static int CONNECT_ERROR = 91;
public final static int LDAP_NOT_SUPPORTED = 92;
public final static int CONTROL_NOT_FOUND = 93;
public final static int NO_RESULTS_RETURNED = 94;
public final static int MORE_RESULTS_TO_RETURN = 95;
public final static int CLIENT_LOOP = 96;
public final static int REFERRAL_LIMIT_EXCEEDED = 97;
public final static int INVALID_RESPONSE = 100;
public final static int AMBIGUOUS_RESPONSE = 101;
public final static int TLS_NOT_SUPPORTED = 112;
public static SearchRequest convert(SearchControls sc) {
SearchRequest request = new SearchRequest();
request.setScope(sc.getSearchScope());
request.setSizeLimit(sc.getCountLimit());
request.setTimeLimit(sc.getTimeLimit());
request.setAttributes(sc.getReturningAttributes());
request.setTypesOnly(sc.getReturningObjFlag());
return request;
}
public static String getScope(int scope) {
switch (scope) {
case SearchRequest.SCOPE_BASE:
return "base";
case SearchRequest.SCOPE_ONE:
return "one level";
case SearchRequest.SCOPE_SUB:
return "subtree";
case SearchRequest.SCOPE_SUBORD:
return "subordinate subtree";
}
return null;
}
public static String getDereference(SearchRequest sc) {
return getDereference(sc.getDereference());
}
public static String getDereference(int deref) {
switch (deref) {
case SearchRequest.DEREF_NEVER:
return "never";
case SearchRequest.DEREF_SEARCHING:
return "searching";
case SearchRequest.DEREF_FINDING:
return "finding";
case SearchRequest.DEREF_ALWAYS:
return "always";
}
return null;
}
public static String getModificationOperation(int op) {
switch (op) {
case Modification.ADD:
return "add";
case Modification.DELETE:
return "delete";
case Modification.REPLACE:
return "replace";
}
return null;
}
public static int getModificationOperation(String op) {
if ("add".equals(op)) {
return Modification.ADD;
} else if ("delete".equals(op)) {
return Modification.DELETE;
} else if ("replace".equals(op)) {
return Modification.REPLACE;
}
return 0;
}
public static Collection<Modification> createModifications(
Attributes oldAttributes,
Attributes newAttributes
) throws Exception {
Collection<Modification> modifications = new ArrayList<Modification>();
Collection<String> oldNames = oldAttributes.getNormalizedNames();
//log.info("Old attributes: "+oldNames);
Collection<String> newNames = newAttributes.getNormalizedNames();
//log.info("New attributes: "+newNames);
Collection<String> deletes = new TreeSet<String>();
deletes.addAll(oldNames);
deletes.removeAll(newNames);
//log.info("Deleting "+deletes+" attributes.");
for (String name : deletes) {
Attribute attribute = oldAttributes.get(name);
modifications.add(new Modification(Modification.DELETE, attribute));
}
Collection<String> adds = new TreeSet<String>();
adds.addAll(newNames);
adds.removeAll(oldNames);
//log.info("Adding "+adds+" attributes.");
for (String name : adds) {
Attribute attribute = newAttributes.get(name);
if (attribute.isEmpty()) continue;
modifications.add(new Modification(Modification.ADD, attribute));
}
Collection<String> modifies = new TreeSet<String>();
modifies.addAll(oldNames);
modifies.retainAll(newNames);
//log.info("Modifying "+modifies+" attributes.");
for (String name : modifies) {
Attribute oldAttribute = oldAttributes.get(name);
Attribute newAttribute = newAttributes.get(name);
Collection<Modification> mods = createModifications(
oldAttribute,
newAttribute
);
if (mods.isEmpty()) continue;
modifications.addAll(mods);
}
return modifications;
}
public static Collection<Modification> createModifications(
Attribute oldAttribute,
Attribute newAttribute
) throws Exception {
Collection<Modification> modifications = new ArrayList<Modification>();
boolean objectClass = oldAttribute.getName().equalsIgnoreCase("objectClass");
Attribute attributeToDelete = (Attribute)oldAttribute.clone();
attributeToDelete.removeValues(newAttribute.getValues());
if (objectClass) attributeToDelete.removeValue("top");
if (!attributeToDelete.isEmpty()) {
modifications.add(new Modification(Modification.DELETE, attributeToDelete));
}
Attribute attributeToAdd = (Attribute)newAttribute.clone();
attributeToAdd.removeValues(oldAttribute.getValues());
if (objectClass) attributeToAdd.removeValue("top");
if (!attributeToAdd.isEmpty()) {
modifications.add(new Modification(Modification.ADD, attributeToAdd));
}
return modifications;
}
public static String escape(String value) throws Exception {
StringBuilder sb = new StringBuilder();
char chars[] = value.toCharArray();
boolean quote = chars[0] == ' ' || chars[chars.length-1] == ' ';
boolean space = false;
for (char c : chars) {
//if (c < 0x20 || c > 0x7E) {
if (Character.isISOControl(c) || !Character.isDefined(c)) {
sb.append('\\').append(hex(c));
continue;
}
// checking special characters
if (c == ',' || c == '=' || c == '+'
|| c == '<' || c == '>'
|| c == '#' || c == ';'
|| c == '\\' || c == '"') {
sb.append('\\');
}
if (c == '\n') {
quote = true;
}
// checking double space
if (c == ' ') {
if (space) {
quote = true;
} else {
space = true;
}
} else {
space = false;
}
sb.append(c);
}
if (quote) {
sb.insert(0, '"');
sb.append('"');
}
return sb.toString();
}
public static String unescape(String value) {
char chars[] = value.toCharArray();
if (chars[0] == '"' && chars[chars.length-1] == '"') {
return value.substring(1, chars.length-1);
}
StringBuilder sb = new StringBuilder();
for (int i=0; i<chars.length; i++) {
char c = chars[i];
if (c == '\\') {
c = chars[++i];
}
sb.append(c);
}
return sb.toString();
}
private static String hex(int b) {
String hex = Integer.toHexString(b);
if (hex.length() % 2 == 1) {
hex = "0" + hex;
}
return hex;
}
public static int getReturnCode(Throwable t) {
if (t instanceof LDAPException) return ((LDAPException)t).getResultCode();
if (t instanceof CommunicationException) return PROTOCOL_ERROR;
if (t instanceof TimeLimitExceededException) return TIME_LIMIT_EXCEEDED;
if (t instanceof SizeLimitExceededException) return SIZE_LIMIT_EXCEEDED;
if (t instanceof AuthenticationException) return INVALID_CREDENTIALS;
if (t instanceof NoPermissionException) return INSUFFICIENT_ACCESS_RIGHTS;
if (t instanceof NoSuchAttributeException) return NO_SUCH_ATTRIBUTE;
if (t instanceof InvalidAttributeIdentifierException) return UNDEFINED_ATTRIBUTE_TYPE;
if (t instanceof InvalidSearchFilterException) return INAPPROPRIATE_MATCHING;
if (t instanceof AttributeInUseException) return ATTRIBUTE_OR_VALUE_EXISTS;
if (t instanceof NameNotFoundException) return NO_SUCH_OBJECT;
if (t instanceof NameAlreadyBoundException) return ENTRY_ALREADY_EXISTS;
if (t instanceof ContextNotEmptyException) return NOT_ALLOWED_ON_NONLEAF;
return OPERATIONS_ERROR;
}
public static String getMessage(int rc) {
return LDAPException.resultCodeToString(rc);
}
public static LDAPException createException(Throwable e) {
if (e instanceof LDAPException) return (LDAPException)e;
int rc = getReturnCode(e);
return createException(rc, e.getMessage(), e);
}
public static LDAPException createException(int rc) {
return createException(rc, getMessage(rc), null);
}
public static LDAPException createException(int rc, Throwable e) {
return createException(rc, e.getMessage(), e);
}
public static LDAPException createException(int rc, String message) {
return createException(rc, message, null);
}
public static LDAPException createException(int rc, String message, Throwable exception) {
return new LDAPException(getMessage(rc), rc, message, exception);
}
}