/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://IdentityConnectors.dev.java.net/legal/license.txt * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.ldap.search; import static org.identityconnectors.ldap.LdapEntry.isDNAttribute; import static org.identityconnectors.ldap.LdapUtil.escapeAttrValue; import static org.identityconnectors.ldap.ADLdapUtil.guidStringtoByteString; import java.util.List; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator; import org.identityconnectors.framework.common.objects.filter.AttributeFilter; import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter; import org.identityconnectors.framework.common.objects.filter.ContainsFilter; import org.identityconnectors.framework.common.objects.filter.EndsWithFilter; import org.identityconnectors.framework.common.objects.filter.EqualsFilter; import org.identityconnectors.framework.common.objects.filter.GreaterThanFilter; import org.identityconnectors.framework.common.objects.filter.GreaterThanOrEqualFilter; import org.identityconnectors.framework.common.objects.filter.LessThanFilter; import org.identityconnectors.framework.common.objects.filter.LessThanOrEqualFilter; import org.identityconnectors.framework.common.objects.filter.SingleValueAttributeFilter; import org.identityconnectors.framework.common.objects.filter.StartsWithFilter; import org.identityconnectors.ldap.LdapConstants; import org.identityconnectors.ldap.schema.LdapSchemaMapping; public class LdapFilterTranslator extends AbstractFilterTranslator<LdapFilter> { // Notes: // // - The connector EqualsFilter matches an attribute and // its values exactly, so we try to do the same. // // - The note in the openconnectors LDAP connector claiming that // "(!(a > X)) is only the same as (a <= X) if every object has a value of a" // seems incorrect. For an object not having an a attribute, (a <= X) will // be Undefined (cf. RFC 4511, section 4.5.1.7). But (a > X) would be Undefined // too, and so would be (!(a > X)). private final LdapSchemaMapping mapping; private final ObjectClass objectClass; public LdapFilterTranslator(LdapSchemaMapping mapping, ObjectClass objectClass) { this.mapping = mapping; this.objectClass = objectClass; } @Override public LdapFilter createAndExpression(LdapFilter leftExpression, LdapFilter rightExpression) { return leftExpression.and(rightExpression); } @Override public LdapFilter createOrExpression(LdapFilter leftExpression, LdapFilter rightExpression) { return leftExpression.or(rightExpression); } @Override public LdapFilter createContainsExpression(ContainsFilter filter, boolean not) { String attrName = mapping.getLdapAttribute(objectClass, filter.getAttribute()); if (attrName == null) { return null; } if (isDNAttribute(attrName)) { return LdapFilter.forEntryDN(filter.getValue()); } StringBuilder builder = createBuilder(not); builder.append(attrName); builder.append('='); builder.append('*'); if (escapeAttrValue(filter.getValue(), builder)) { builder.append('*'); } return finishBuilder(builder); } @Override public LdapFilter createEndsWithExpression(EndsWithFilter filter, boolean not) { String attrName = mapping.getLdapAttribute(objectClass, filter.getAttribute()); if (attrName == null) { return null; } if (isDNAttribute(attrName)) { return LdapFilter.forEntryDN(filter.getValue()); } StringBuilder builder = createBuilder(not); builder.append(attrName); builder.append('='); builder.append('*'); escapeAttrValue(filter.getValue(), builder); return finishBuilder(builder); } @Override public LdapFilter createEqualsExpression(EqualsFilter filter, boolean not) { // XXX is there a way in LDAP to test that the values of an attribute // exactly match a given list of values? return createContainsAllValuesFilter(filter, not); } @Override public LdapFilter createGreaterThanExpression(GreaterThanFilter filter, boolean not) { return createSingleValueFilter("<=", filter, !not); } @Override public LdapFilter createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, boolean not) { return createSingleValueFilter(">=", filter, not); } @Override public LdapFilter createLessThanExpression(LessThanFilter filter, boolean not) { return createSingleValueFilter(">=", filter, !not); } @Override public LdapFilter createLessThanOrEqualExpression(LessThanOrEqualFilter filter, boolean not) { return createSingleValueFilter("<=", filter, not); } @Override public LdapFilter createStartsWithExpression(StartsWithFilter filter, boolean not) { String attrName = mapping.getLdapAttribute(objectClass, filter.getAttribute()); if (attrName == null) { return null; } if (isDNAttribute(attrName)) { return LdapFilter.forEntryDN(filter.getValue()); } StringBuilder builder = createBuilder(not); builder.append(attrName); builder.append('='); escapeAttrValue(filter.getValue(), builder); builder.append('*'); return finishBuilder(builder); } @Override public LdapFilter createContainsAllValuesExpression(ContainsAllValuesFilter filter, boolean not) { return createContainsAllValuesFilter(filter, not); } private LdapFilter createSingleValueFilter(String type, SingleValueAttributeFilter filter, boolean not) { String attrName = mapping.getLdapAttribute(objectClass, filter.getAttribute()); if (attrName == null) { return null; } if (isDNAttribute(attrName)) { return LdapFilter.forEntryDN(filter.getValue().toString()); } StringBuilder builder = createBuilder(not); Object value = filter.getValue(); addSimpleFilter(attrName, type, value, builder); return finishBuilder(builder); } private void addSimpleFilter(String ldapAttr, String type, Object value, StringBuilder toBuilder) { toBuilder.append(ldapAttr); toBuilder.append(type); if (!escapeAttrValue(value, toBuilder)) { toBuilder.append('*'); } } private LdapFilter createContainsAllValuesFilter(AttributeFilter filter, boolean not) { String attrName = mapping.getLdapAttribute(objectClass, filter.getAttribute()); if (attrName == null) { return null; } List<Object> values = filter.getAttribute().getValue(); if (values == null) { return null; } StringBuilder builder; switch (values.size()) { case 0: return null; case 1: Object single = values.get(0); if (single == null) { return null; } if (isDNAttribute(attrName)) { return LdapFilter.forEntryDN(single.toString()); } if (LdapConstants.MS_GUID_ATTR.equalsIgnoreCase(attrName)){ return LdapFilter.forNativeFilter("("+LdapConstants.MS_GUID_ATTR+"="+guidStringtoByteString((String)values.get(0))+")"); } builder = createBuilder(not); addSimpleFilter(attrName, "=", values.get(0), builder); return finishBuilder(builder); default: if (isDNAttribute(attrName)) { return null; // Because the DN is single-valued. } builder = createBuilder(not); boolean hasValue = false; builder.append('&'); for (Object value : values) { if (value != null) { hasValue = true; builder.append('('); addSimpleFilter(attrName, "=", value, builder); builder.append(')'); } } if (!hasValue) { return null; } return finishBuilder(builder); } } private StringBuilder createBuilder(boolean not) { return new StringBuilder(not ? "(!(" : "("); } private LdapFilter finishBuilder(StringBuilder builder) { boolean not = builder.charAt(0) == '(' && builder.charAt(1) == '!'; builder.append(not ? "))" : ")"); return LdapFilter.forNativeFilter(builder.toString()); } }