Skip to content

Commit

Permalink
Polishing.
Browse files Browse the repository at this point in the history
Refine Javadoc. Move LdapEncoder lookup into LdapParameters. Eagerly instantiate LdapEncoder. Refine method naming.
  • Loading branch information
mp911de committed Nov 18, 2024
1 parent 2c2593e commit 13f1095
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,36 @@
import org.springframework.core.annotation.AliasFor;

/**
* Allows passing of custom {@link LdapEncoder}.
* Annotation which indicates that a method parameter should be encoded using a specific {@link LdapEncoder} for a
* repository query method invocation.
* <p>
* If no {@link LdapEncoder} is configured, method parameters are encoded using
* {@link org.springframework.ldap.support.LdapEncoder#filterEncode(String)}. The default encoder considers chars such
* as {@code *} (asterisk) to be encoded which might interfere with the intent of running a Like query. Since Spring
* Data LDAP doesn't parse queries it is up to you to decide which encoder to use.
* <p>
* {@link LdapEncoder} implementations must declare a no-args constructor so they can be instantiated during repository
* initialization.
*
* @author Marcin Grzejszczak
* @since 3.5.0
* @author Mark Paluch
* @since 3.5
*/
@Target(ElementType.PARAMETER)
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LdapEncode {

/**
* {@link LdapEncoder} to instantiate to encode query parameters.
* {@link LdapEncoder} to encode query parameters.
*
* @return {@link LdapEncoder} class
*/
@AliasFor("encoder")
Class<? extends LdapEncoder> value();

/**
* {@link LdapEncoder} to instantiate to encode query parameters.
* {@link LdapEncoder} to encode query parameters.
*
* @return {@link LdapEncoder} class
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,25 @@
package org.springframework.data.ldap.repository;

/**
* Allows plugging in custom encoding for {@link LdapEncode}.
* Strategy interface to escape values for use in LDAP filters.
* <p>
* Accepts an LDAP filter value to be encoded (escaped) for String-based LDAP query usage as LDAP queries do not feature
* an out-of-band parameter binding mechanism.
* <p>
* Make sure that your implementation escapes special characters in the value adequately to prevent injection attacks.
*
* @author Marcin Grzejszczak
* @since 3.5.0
* @author Mark Paluch
* @since 3.5
*/
public interface LdapEncoder {

/**
* Escape a value for use in a filter.
* @param value the value to escape.
* @return a properly escaped representation of the supplied value.
* Encode a value for use in a filter.
*
* @param value the value to encode.
* @return a properly encoded representation of the supplied value.
*/
String filterEncode(String value);
String encode(String value);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,22 @@
import java.lang.reflect.Method;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.data.geo.Distance;
import org.springframework.data.ldap.repository.LdapEncode;
import org.springframework.data.ldap.repository.LdapEncoder;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersSource;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;

/**
* Custom extension of {@link Parameters} discovering additional
*
* @author Marcin Grzejszczak
* @since 3.5.0
* @since 3.5
*/
public class LdapParameters extends Parameters<LdapParameters, LdapParameters.LdapParameter> {

Expand Down Expand Up @@ -65,9 +69,10 @@ protected LdapParameters createFrom(List<LdapParameter> parameters) {
*
* @author Marcin Grzejszczak
*/
static class LdapParameter extends Parameter {
protected static class LdapParameter extends Parameter {

final MethodParameter parameter;
private final @Nullable LdapEncoder ldapEncoder;
private final MethodParameter parameter;

/**
* Creates a new {@link LdapParameter}.
Expand All @@ -76,8 +81,29 @@ static class LdapParameter extends Parameter {
* @param domainType must not be {@literal null}.
*/
LdapParameter(MethodParameter parameter, TypeInformation<?> domainType) {

super(parameter, domainType);
this.parameter = parameter;

LdapEncode encode = parameter.getParameterAnnotation(LdapEncode.class);

if (encode != null) {
this.ldapEncoder = BeanUtils.instantiateClass(encode.value());
} else {
this.ldapEncoder = null;
}
}

public boolean hasLdapEncoder() {
return ldapEncoder != null;
}

public LdapEncoder getLdapEncoder() {

if (ldapEncoder == null) {
throw new IllegalStateException("No LdapEncoder found for parameter " + parameter);
}
return ldapEncoder;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 the original author or authors.
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,8 @@
*/
package org.springframework.data.ldap.repository.query;

import static org.springframework.data.ldap.repository.query.StringBasedQuery.BindingContext.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -25,10 +27,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.beans.BeanUtils;
import org.springframework.data.expression.ValueExpression;
import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.ldap.repository.LdapEncode;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
Expand All @@ -39,12 +39,11 @@
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import static org.springframework.data.ldap.repository.query.StringBasedQuery.BindingContext.ParameterBinding;

/**
* String-based Query abstracting a query with parameter bindings.
*
* @author Marcin Grzejszczak
* @author Mark Paluch
* @since 3.4
*/
class StringBasedQuery {
Expand Down Expand Up @@ -358,15 +357,19 @@ private Object getParameterValueForBinding(ParameterBinding binding) {
if (binding.isExpression()) {
return evaluator.apply(binding.getRequiredExpression());
}
Object value = binding.isNamed()
? parameterAccessor.getBindableValue(getParameterIndex(parameters, binding.getRequiredParameterName()))
: parameterAccessor.getBindableValue(binding.getParameterIndex());
int index = binding.isNamed() ? getParameterIndex(parameters, binding.getRequiredParameterName())
: binding.getParameterIndex();
Object value = parameterAccessor.getBindableValue(index);

if (value == null) {
return null;
}

return binding.getEncodedValue(parameters, value);
String toString = value.toString();
LdapParameters.LdapParameter parameter = parameters.getBindableParameter(index);

return parameter.hasLdapEncoder() ? parameter.getLdapEncoder().encode(toString)
: LdapEncoder.filterEncode(toString);
}

private int getParameterIndex(Parameters<?, ?> parameters, String parameterName) {
Expand Down Expand Up @@ -413,38 +416,6 @@ static ParameterBinding named(String name) {
return new ParameterBinding(-1, null, name);
}

Object getEncodedValue(LdapParameters ldapParameters, Object value) {
org.springframework.data.ldap.repository.LdapEncoder encoder = encoderForParameter(ldapParameters);
if (encoder == null) {
return LdapEncoder.filterEncode(value.toString());
}
return encoder.filterEncode(value.toString());
}


@Nullable
org.springframework.data.ldap.repository.LdapEncoder encoderForParameter(LdapParameters ldapParameters) {
for (LdapParameters.LdapParameter parameter : ldapParameters) {
if (isByName(parameter) || isByIndex(parameter)) {
LdapEncode ldapEncode = parameter.parameter.getParameterAnnotation(LdapEncode.class);
if (ldapEncode == null) {
return null;
}
Class<? extends org.springframework.data.ldap.repository.LdapEncoder> encoder = ldapEncode.value();
return BeanUtils.instantiateClass(encoder);
}
}
return null;
}

private boolean isByIndex(LdapParameters.LdapParameter parameter) {
return parameterIndex != -1 && parameter.getIndex() == parameterIndex;
}

private boolean isByName(LdapParameters.LdapParameter parameter) {
return parameterName != null && parameterName.equals(parameter.getName().orElse(null));
}

boolean isNamed() {
return (parameterName != null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
*/
package org.springframework.data.ldap.repository.query;

import static org.assertj.core.api.Assertions.*;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.ldap.repository.LdapEncoder;
import org.springframework.data.ldap.repository.LdapEncode;
import org.springframework.data.ldap.repository.LdapEncoder;
import org.springframework.data.ldap.repository.LdapRepository;
import org.springframework.data.ldap.repository.Query;
import org.springframework.data.mapping.model.EntityInstantiators;
Expand All @@ -32,8 +34,6 @@
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQuery;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Unit tests for {@link AnnotatedLdapRepositoryQuery}
*
Expand Down Expand Up @@ -122,7 +122,7 @@ interface QueryRepository extends LdapRepository<SchemaEntry> {
static class MyEncoder implements LdapEncoder {

@Override
public String filterEncode(String value) {
public String encode(String value) {
return value + "bar";
}
}
Expand Down

0 comments on commit 13f1095

Please sign in to comment.