use of com.mindbright.asn1.ASN1OctetString in project ldapsdk by pingidentity.
the class Filter method create.
/**
* Creates a new search filter from the specified portion of the provided
* string representation.
*
* @param filterString The string representation of the filter to create.
* @param startPos The position of the first character to consider as
* part of the filter.
* @param endPos The position of the last character to consider as
* part of the filter.
* @param depth The current nesting depth for this filter. It should
* be increased by one for each AND, OR, or NOT filter
* encountered, in order to prevent stack overflow
* errors from excessive recursion.
*
* @return The decoded search filter.
*
* @throws LDAPException If the provided string cannot be decoded as a valid
* LDAP search filter.
*/
@NotNull()
private static Filter create(@NotNull final String filterString, final int startPos, final int endPos, final int depth) throws LDAPException {
if (depth > 100) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_TOO_DEEP.get(filterString));
}
final byte filterType;
final Filter[] filterComps;
final Filter notComp;
final String attrName;
final ASN1OctetString assertionValue;
final ASN1OctetString subInitial;
final ASN1OctetString[] subAny;
final ASN1OctetString subFinal;
final String matchingRuleID;
final boolean dnAttributes;
if (startPos >= endPos) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_TOO_SHORT.get(filterString));
}
int l = startPos;
int r = endPos;
// it should be. If so, then strip off the outer parentheses.
if (filterString.charAt(l) == '(') {
if (filterString.charAt(r) == ')') {
l++;
r--;
} else {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_OPEN_WITHOUT_CLOSE.get(filterString, l, r));
}
} else {
// otherwise we'll raise an error.
if (l != 0) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_MISSING_PARENTHESES.get(filterString, filterString.substring(l, r + 1)));
}
}
// '!'. If we find a parenthesis, then that's an error.
switch(filterString.charAt(l)) {
case '&':
filterType = FILTER_TYPE_AND;
filterComps = parseFilterComps(filterString, l + 1, r, depth + 1);
notComp = null;
attrName = null;
assertionValue = null;
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
matchingRuleID = null;
dnAttributes = false;
break;
case '|':
filterType = FILTER_TYPE_OR;
filterComps = parseFilterComps(filterString, l + 1, r, depth + 1);
notComp = null;
attrName = null;
assertionValue = null;
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
matchingRuleID = null;
dnAttributes = false;
break;
case '!':
filterType = FILTER_TYPE_NOT;
filterComps = NO_FILTERS;
notComp = create(filterString, l + 1, r, depth + 1);
attrName = null;
assertionValue = null;
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
matchingRuleID = null;
dnAttributes = false;
break;
case '(':
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l));
case ':':
// This must be an extensible matching filter that starts with a
// dnAttributes flag and/or matching rule ID, and we should parse it
// accordingly.
filterType = FILTER_TYPE_EXTENSIBLE_MATCH;
filterComps = NO_FILTERS;
notComp = null;
attrName = null;
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
// The next element must be either the "dn:{matchingruleid}" or just
// "{matchingruleid}", and it must be followed by a colon.
final int dnMRIDStart = ++l;
while ((l <= r) && (filterString.charAt(l) != ':')) {
l++;
}
if (l > r) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos));
} else if (l == dnMRIDStart) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_EMPTY_MRID.get(filterString, startPos));
}
final String s = filterString.substring(dnMRIDStart, l++);
if (s.equalsIgnoreCase("dn")) {
dnAttributes = true;
// The colon must be followed by the matching rule ID and another
// colon.
final int mrIDStart = l;
while ((l < r) && (filterString.charAt(l) != ':')) {
l++;
}
if (l >= r) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos));
}
matchingRuleID = filterString.substring(mrIDStart, l);
if (matchingRuleID.isEmpty()) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_EMPTY_MRID.get(filterString, startPos));
}
if ((++l > r) || (filterString.charAt(l) != '=')) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_AFTER_MRID.get(filterString, startPos, filterString.charAt(l)));
}
} else {
matchingRuleID = s;
dnAttributes = false;
// The colon must be followed by an equal sign.
if ((l > r) || (filterString.charAt(l) != '=')) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_EQUAL_AFTER_MRID.get(filterString, startPos));
}
}
// Now we should be able to read the value, handling any escape
// characters as we go.
l++;
final ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1);
while (l <= r) {
final char c = filterString.charAt(l);
if (c == '\\') {
l = readEscapedHexString(filterString, ++l, valueBuffer);
} else if (c == '(') {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l));
} else if (c == ')') {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l));
} else {
valueBuffer.append(c);
l++;
}
}
assertionValue = new ASN1OctetString(valueBuffer.toByteArray());
break;
default:
// We know that it's not an AND, OR, or NOT filter, so we can eliminate
// the variables used only for them.
filterComps = NO_FILTERS;
notComp = null;
// We should now be able to read a non-empty attribute name.
final int attrStartPos = l;
int attrEndPos = -1;
byte tempFilterType = 0x00;
boolean filterTypeKnown = false;
boolean equalFound = false;
attrNameLoop: while (l <= r) {
final char c = filterString.charAt(l++);
switch(c) {
case ':':
tempFilterType = FILTER_TYPE_EXTENSIBLE_MATCH;
filterTypeKnown = true;
attrEndPos = l - 1;
break attrNameLoop;
case '>':
tempFilterType = FILTER_TYPE_GREATER_OR_EQUAL;
filterTypeKnown = true;
attrEndPos = l - 1;
if (l <= r) {
if (filterString.charAt(l++) != '=') {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_AFTER_GT.get(filterString, startPos, filterString.charAt(l - 1)));
}
} else {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_END_AFTER_GT.get(filterString, startPos));
}
break attrNameLoop;
case '<':
tempFilterType = FILTER_TYPE_LESS_OR_EQUAL;
filterTypeKnown = true;
attrEndPos = l - 1;
if (l <= r) {
if (filterString.charAt(l++) != '=') {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_AFTER_LT.get(filterString, startPos, filterString.charAt(l - 1)));
}
} else {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_END_AFTER_LT.get(filterString, startPos));
}
break attrNameLoop;
case '~':
tempFilterType = FILTER_TYPE_APPROXIMATE_MATCH;
filterTypeKnown = true;
attrEndPos = l - 1;
if (l <= r) {
if (filterString.charAt(l++) != '=') {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_AFTER_TILDE.get(filterString, startPos, filterString.charAt(l - 1)));
}
} else {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_END_AFTER_TILDE.get(filterString, startPos));
}
break attrNameLoop;
case '=':
// It could be either an equality, presence, or substring filter.
// We'll need to look at the value to determine that.
attrEndPos = l - 1;
equalFound = true;
break attrNameLoop;
}
}
if (attrEndPos <= attrStartPos) {
if (equalFound) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_EMPTY_ATTR_NAME.get(filterString, startPos));
} else {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos));
}
}
attrName = filterString.substring(attrStartPos, attrEndPos);
// variables that are specific to extensible matching filters.
if (filterTypeKnown && (tempFilterType == FILTER_TYPE_EXTENSIBLE_MATCH)) {
if (l > r) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos));
}
final char c = filterString.charAt(l++);
if (c == '=') {
matchingRuleID = null;
dnAttributes = false;
} else {
// We have either a matching rule ID or a dnAttributes flag, or
// both. Iterate through the filter until we find the equal sign,
// and then figure out what we have from that.
equalFound = false;
final int substrStartPos = l - 1;
while (l <= r) {
if (filterString.charAt(l++) == '=') {
equalFound = true;
break;
}
}
if (!equalFound) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos));
}
final String substr = filterString.substring(substrStartPos, l - 1);
final String lowerSubstr = StaticUtils.toLowerCase(substr);
if (!substr.endsWith(":")) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_CANNOT_PARSE_MRID.get(filterString, startPos));
}
if (lowerSubstr.equals("dn:")) {
matchingRuleID = null;
dnAttributes = true;
} else if (lowerSubstr.startsWith("dn:")) {
matchingRuleID = substr.substring(3, substr.length() - 1);
if (matchingRuleID.isEmpty()) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_EMPTY_MRID.get(filterString, startPos));
}
dnAttributes = true;
} else {
matchingRuleID = substr.substring(0, substr.length() - 1);
dnAttributes = false;
if (matchingRuleID.isEmpty()) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_EMPTY_MRID.get(filterString, startPos));
}
}
}
} else {
matchingRuleID = null;
dnAttributes = false;
}
// based on asterisks in the value.
if (l > r) {
assertionValue = new ASN1OctetString();
if (!filterTypeKnown) {
tempFilterType = FILTER_TYPE_EQUALITY;
}
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
} else if (l == r) {
if (filterTypeKnown) {
switch(filterString.charAt(l)) {
case '*':
case '(':
case ')':
case '\\':
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, startPos, filterString.charAt(l)));
}
assertionValue = new ASN1OctetString(filterString.substring(l, l + 1));
} else {
final char c = filterString.charAt(l);
switch(c) {
case '*':
tempFilterType = FILTER_TYPE_PRESENCE;
assertionValue = null;
break;
case '\\':
case '(':
case ')':
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, startPos, filterString.charAt(l)));
default:
tempFilterType = FILTER_TYPE_EQUALITY;
assertionValue = new ASN1OctetString(filterString.substring(l, l + 1));
break;
}
}
subInitial = null;
subAny = NO_SUB_ANY;
subFinal = null;
} else {
if (!filterTypeKnown) {
tempFilterType = FILTER_TYPE_EQUALITY;
}
final int valueStartPos = l;
ASN1OctetString tempSubInitial = null;
ASN1OctetString tempSubFinal = null;
final ArrayList<ASN1OctetString> subAnyList = new ArrayList<>(1);
ByteStringBuffer buffer = new ByteStringBuffer(r - l + 1);
while (l <= r) {
final char c = filterString.charAt(l++);
switch(c) {
case '*':
if (filterTypeKnown) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_ASTERISK.get(filterString, startPos));
} else {
if ((l - 1) == valueStartPos) {
// The first character is an asterisk, so there is no
// subInitial.
} else {
if (tempFilterType == FILTER_TYPE_SUBSTRING) {
// right next to each other, which is invalid.
if (buffer.length() == 0) {
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_DOUBLE_ASTERISK.get(filterString, startPos));
} else {
subAnyList.add(new ASN1OctetString(buffer.toByteArray()));
buffer = new ByteStringBuffer(r - l + 1);
}
} else {
// We haven't yet set the filter type, so the buffer must
// contain the subInitial portion. We also know it's not
// empty because of an earlier check.
tempSubInitial = new ASN1OctetString(buffer.toByteArray());
buffer = new ByteStringBuffer(r - l + 1);
}
}
tempFilterType = FILTER_TYPE_SUBSTRING;
}
break;
case '\\':
l = readEscapedHexString(filterString, l, buffer);
break;
case '(':
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l));
case ')':
throw new LDAPException(ResultCode.FILTER_ERROR, ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l));
default:
if (Character.isHighSurrogate(c)) {
if (l <= r) {
final char c2 = filterString.charAt(l);
if (Character.isLowSurrogate(c2)) {
l++;
final int codePoint = Character.toCodePoint(c, c2);
buffer.append(new String(new int[] { codePoint }, 0, 1));
break;
}
}
}
buffer.append(c);
break;
}
}
if ((tempFilterType == FILTER_TYPE_SUBSTRING) && (!buffer.isEmpty())) {
// The buffer must contain the subFinal portion.
tempSubFinal = new ASN1OctetString(buffer.toByteArray());
}
subInitial = tempSubInitial;
subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
subFinal = tempSubFinal;
if (tempFilterType == FILTER_TYPE_SUBSTRING) {
assertionValue = null;
} else {
assertionValue = new ASN1OctetString(buffer.toByteArray());
}
}
filterType = tempFilterType;
break;
}
if (startPos == 0) {
return new Filter(filterString, filterType, filterComps, notComp, attrName, assertionValue, subInitial, subAny, subFinal, matchingRuleID, dnAttributes);
} else {
return new Filter(filterString.substring(startPos, endPos + 1), filterType, filterComps, notComp, attrName, assertionValue, subInitial, subAny, subFinal, matchingRuleID, dnAttributes);
}
}
use of com.mindbright.asn1.ASN1OctetString in project ldapsdk by pingidentity.
the class Filter method encodeValue.
/**
* Encodes the provided value into a form suitable for use as the assertion
* value in the string representation of a search filter. Parentheses,
* asterisks, backslashes, null characters, and any non-ASCII characters will
* be escaped using a backslash before the hexadecimal representation of each
* byte in the character to escape.
*
* @param value The value to be encoded. It must not be {@code null}.
*
* @return The encoded representation of the provided string.
*/
@NotNull()
public static String encodeValue(@NotNull final String value) {
Validator.ensureNotNull(value);
final StringBuilder buffer = new StringBuilder();
encodeValue(new ASN1OctetString(value), buffer);
return buffer.toString();
}
use of com.mindbright.asn1.ASN1OctetString in project ldapsdk by pingidentity.
the class Filter method encode.
/**
* Encodes this search filter to an ASN.1 element suitable for inclusion in an
* LDAP search request protocol op.
*
* @return An ASN.1 element containing the encoded search filter.
*/
@NotNull()
public ASN1Element encode() {
switch(filterType) {
case FILTER_TYPE_AND:
case FILTER_TYPE_OR:
final ASN1Element[] filterElements = new ASN1Element[filterComps.length];
for (int i = 0; i < filterComps.length; i++) {
filterElements[i] = filterComps[i].encode();
}
return new ASN1Set(filterType, filterElements);
case FILTER_TYPE_NOT:
return new ASN1Element(filterType, notComp.encode().encode());
case FILTER_TYPE_EQUALITY:
case FILTER_TYPE_GREATER_OR_EQUAL:
case FILTER_TYPE_LESS_OR_EQUAL:
case FILTER_TYPE_APPROXIMATE_MATCH:
final ASN1OctetString[] attrValueAssertionElements = { new ASN1OctetString(attrName), assertionValue };
return new ASN1Sequence(filterType, attrValueAssertionElements);
case FILTER_TYPE_SUBSTRING:
final ArrayList<ASN1OctetString> subList = new ArrayList<>(2 + subAny.length);
if (subInitial != null) {
subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, subInitial.getValue()));
}
for (final ASN1Element subAnyElement : subAny) {
subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY, subAnyElement.getValue()));
}
if (subFinal != null) {
subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue()));
}
final ASN1Element[] subFilterElements = { new ASN1OctetString(attrName), new ASN1Sequence(subList) };
return new ASN1Sequence(filterType, subFilterElements);
case FILTER_TYPE_PRESENCE:
return new ASN1OctetString(filterType, attrName);
case FILTER_TYPE_EXTENSIBLE_MATCH:
final ArrayList<ASN1Element> emElementList = new ArrayList<>(4);
if (matchingRuleID != null) {
emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID));
}
if (attrName != null) {
emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName));
}
emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, assertionValue.getValue()));
if (dnAttributes) {
emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true));
}
return new ASN1Sequence(filterType, emElementList);
default:
throw new AssertionError(ERR_FILTER_INVALID_TYPE.get(StaticUtils.toHex(filterType)));
}
}
use of com.mindbright.asn1.ASN1OctetString in project ldapsdk by pingidentity.
the class Filter method createSubstringFilter.
/**
* Creates a new substring search filter with the provided information. At
* least one of the subInitial, subAny, and subFinal components must not be
* {@code null}.
*
* @param attributeName The attribute name for this substring filter. It
* must not be {@code null}.
* @param subInitial The subInitial component for this substring filter.
* @param subAny The set of subAny components for this substring
* filter.
* @param subFinal The subFinal component for this substring filter.
*
* @return The created substring search filter.
*/
@NotNull()
public static Filter createSubstringFilter(@NotNull final String attributeName, @Nullable final byte[] subInitial, @Nullable final byte[][] subAny, @Nullable final byte[] subFinal) {
Validator.ensureNotNull(attributeName);
Validator.ensureTrue((subInitial != null) || ((subAny != null) && (subAny.length > 0)) || (subFinal != null));
final ASN1OctetString subInitialOS;
if (subInitial == null) {
subInitialOS = null;
} else {
subInitialOS = new ASN1OctetString(subInitial);
}
final ASN1OctetString[] subAnyArray;
if (subAny == null) {
subAnyArray = NO_SUB_ANY;
} else {
subAnyArray = new ASN1OctetString[subAny.length];
for (int i = 0; i < subAny.length; i++) {
subAnyArray[i] = new ASN1OctetString(subAny[i]);
}
}
final ASN1OctetString subFinalOS;
if (subFinal == null) {
subFinalOS = null;
} else {
subFinalOS = new ASN1OctetString(subFinal);
}
return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, attributeName, null, subInitialOS, subAnyArray, subFinalOS, null, false);
}
use of com.mindbright.asn1.ASN1OctetString in project ldapsdk by pingidentity.
the class Filter method encodeValue.
/**
* Encodes the provided value into a form suitable for use as the assertion
* value in the string representation of a search filter. Parentheses,
* asterisks, backslashes, null characters, and any non-ASCII characters will
* be escaped using a backslash before the hexadecimal representation of each
* byte in the character to escape.
*
* @param value The value to be encoded. It must not be {@code null}.
*
* @return The encoded representation of the provided string.
*/
@NotNull()
public static String encodeValue(@NotNull final byte[] value) {
Validator.ensureNotNull(value);
final StringBuilder buffer = new StringBuilder();
encodeValue(new ASN1OctetString(value), buffer);
return buffer.toString();
}
Aggregations