Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APIProxyKey path matching issue fix #1181

Merged
merged 12 commits into from
Jul 10, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void setSpecs(List<OpenAPISpec> specs) {

@Override
public void init() throws Exception {
key = new APIProxyKey(getIp(),getHost(),getPort(), test); // Must come before super. init()
key = new APIProxyKey(key, test, !specs.isEmpty());
super.init();
initOpenAPI();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@

import java.util.*;

import static java.util.Arrays.asList;

public class APIProxyKey extends ServiceProxyKey {

private static final Logger log = LoggerFactory.getLogger(APIProxyKey.class.getName());
Expand All @@ -35,30 +33,37 @@ public class APIProxyKey extends ServiceProxyKey {

private Expression testExpr;

public APIProxyKey(String ip, String host, int port, String test) {
super(host, "*", null, port, ip);
public APIProxyKey(RuleKey key, String test, boolean openAPI) {
this(key.getIp(), key.getHost(), key.getPort(), key.getPath(), key.getMethod(), test, openAPI);
}

public APIProxyKey(String ip, String host, int port, String path, String method, String test, boolean openAPI) {
super(host, method, path, port, ip);
init(test, openAPI);
setUsePathPattern(true);
}

protected void init(String test, boolean openAPI) {
if (test != null)
testExpr = new SpelExpressionParser().parseExpression(test);

if (!openAPI)
return;

// Add basePaths of OpenAPIPublisherInterceptor to accept them also
basePaths.add(OpenAPIPublisherInterceptor.PATH); // new path
basePaths.add(OpenAPIPublisherInterceptor.PATH_UI); // "
basePaths.add("/api-doc"); // old to stay compatible
basePaths.add("/api-doc/ui"); // "
}

@Override
public boolean isMethodWildcard() {
return true;
}

@Override
public boolean complexMatch(Exchange exc) {
if (!testCondition(exc))
return false;

if (!additionalBasePaths(basePaths))
if (basePaths.isEmpty())
return true;

var uri = exc.getRequest().getUri();
Expand All @@ -73,22 +78,13 @@ public boolean complexMatch(Exchange exc) {
return false;
}

static boolean additionalBasePaths(ArrayList<String> basePaths) {
return !basePaths.equals(asList("/api-docs", "/api-docs/ui", "/api-doc", "/api-doc/ui"));
}

private boolean testCondition(Exchange exc) {
if (testExpr == null)
return true;
Boolean result = testExpr.getValue(new ExchangeEvaluationContext(exc, exc.getRequest()), Boolean.class);
return result != null && result;
}

@Override
public String getPath() {
return "*";
}

void addBasePaths(ArrayList<String> paths) {
basePaths.addAll(paths);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,19 @@
limitations under the License. */
package com.predic8.membrane.core.openapi.serviceproxy;

import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.interceptor.AbstractInterceptor;
import com.predic8.membrane.core.interceptor.Outcome;
import com.predic8.membrane.core.rules.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.predic8.membrane.core.openapi.serviceproxy.OpenAPIPublisher.PATH;
import static java.lang.String.valueOf;
import com.predic8.membrane.annot.*;
import com.predic8.membrane.core.exchange.*;
import com.predic8.membrane.core.interceptor.*;
import com.predic8.membrane.core.rules.*;
import org.slf4j.*;

import java.util.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;

import static com.predic8.membrane.core.openapi.serviceproxy.OpenAPIPublisher.*;
import static java.lang.String.*;

@MCElement(name = "apiDocs")
public class ApiDocsInterceptor extends AbstractInterceptor {
Expand Down Expand Up @@ -103,12 +96,12 @@ private static Stream<Map.Entry<String, OpenAPIRecord>> getRecordEntryStream(Ope
}

private boolean hasOpenAPIInterceptor(Rule rule) {
return rule.getInterceptors().stream().anyMatch(ic -> ic instanceof OpenAPIInterceptor);
return rule.getInterceptors().stream().anyMatch(OpenAPIInterceptor.class::isInstance);
}

static Optional<OpenAPIInterceptor> getOpenAPIInterceptor(Rule rule) {
return rule.getInterceptors().stream()
.filter(ic -> ic instanceof OpenAPIInterceptor)
.filter(OpenAPIInterceptor.class::isInstance)
.map(ic -> (OpenAPIInterceptor) ic) // Previous line checks type, so cast should be fine
.findFirst();
}
Expand Down Expand Up @@ -151,6 +144,8 @@ public String getLongDescription() {

public static <T, U> void setIfNull(T rewrite, Function<T, U> getter, BiConsumer<T, U> setter, U defaultValue) {
if(getter.apply(rewrite) != null) return;
if(defaultValue == null)
return;
if(!defaultValue.equals("*")) {
setter.accept(rewrite, defaultValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,123 +21,129 @@

public abstract class AbstractRuleKey implements RuleKey {

private static Logger log = LoggerFactory.getLogger(AbstractRuleKey.class.getName());
private static Logger log = LoggerFactory.getLogger(AbstractRuleKey.class.getName());

/**
* -1 is used as a wildcard. It is used by HttpServletHandler, since its port
* is determined by the webserver and not by the proxies.xml
*/
protected int port;
/**
* -1 is used as a wildcard. It is used by HttpServletHandler, since its port
* is determined by the webserver and not by the proxies.xml
*/
protected int port;

private String path;
private String path;

protected volatile Pattern pathPattern;
protected volatile Pattern pathPattern;

protected boolean pathRegExp = true;
protected boolean pathRegExp = true;

protected boolean usePathPattern;
protected boolean usePathPattern;

protected String ip;
protected String ip;

public AbstractRuleKey(int port, String ip) {
this.port = port;
this.ip = ip;
}
public AbstractRuleKey(int port, String ip) {
this.port = port;
this.ip = ip;
}

public String getHost() {
return "";
}
public String getHost() {
return "";
}

public String getMethod() {
return "";
}
public String getMethod() {
return "";
}

public int getPort() {
return port;
}
public int getPort() {
return port;
}

public boolean isHostWildcard() {
public boolean isHostWildcard() {

return false;
}
return false;
}

public boolean isMethodWildcard() {
return false;
}
public boolean isMethodWildcard() {
return false;
}

public void setPort(int port) {
this.port = port;
}
public void setPort(int port) {
this.port = port;
}

public boolean isPathRegExp() {
return pathRegExp;
}
public boolean isPathRegExp() {
return pathRegExp;
}

public void setPathRegExp(boolean pathRegExp) {
this.pathRegExp = pathRegExp;
}
public void setPathRegExp(boolean pathRegExp) {
this.pathRegExp = pathRegExp;
}

public boolean isUsePathPattern() {
return usePathPattern;
}
public boolean isUsePathPattern() {
return usePathPattern;
}

public void setUsePathPattern(boolean usePathPattern) {
this.usePathPattern = usePathPattern;
pathPattern = null;
}
public void setUsePathPattern(boolean usePathPattern) {
this.usePathPattern = usePathPattern;
pathPattern = null;
}

public void setPath(String path) {
this.path = path;
pathPattern = null;
}
public void setPath(String path) {
this.path = path;
pathPattern = null;
}

public String getPath() {
return path;
}
public String getPath() {
return path;
}

public boolean matchesPath(String path) {
if (isPathRegExp())
return matchesPathPattern(path);
return path.startsWith(getPath());
}
public boolean matchesPath(String path) {
if (isPathRegExp())
return matchesPathPattern(path);
return path.startsWith(getPath());
}

private boolean matchesPathPattern(String path) {
log.debug("matches path: " + path + " with path pattern: "
+ getPathPattern());
return getPathPattern().matcher(path).matches();
}
private boolean matchesPathPattern(String path) {
log.debug("matches path: " + path + " with path pattern: "
+ getPathPattern());
return getPathPattern().matcher(path).matches();
}

private Pattern getPathPattern() {
Pattern p = pathPattern;
if (p == null) {
synchronized (this) {
if (pathPattern == null)
p = pathPattern = Pattern.compile(path);
}
}
private Pattern getPathPattern() {
if (pathPattern != null)
return pathPattern;

return p;
}
synchronized (this) {
// Check again this time in synchronized block
if (pathPattern != null)
return pathPattern;
pathPattern = Pattern.compile(computePathPattern(path));
}

public String getIp() {
return ip;
}
return pathPattern;
}

public void setIp(String ip) {
this.ip = ip;
}
private String computePathPattern(String path) {
return path != null ? path : ".*";
}

@Override
public boolean matchesHostHeader(String hostHeader) {
return false;
}

@Override
public boolean matchesVersion(String version) {
return !"STOMP".equals(version);
}

public boolean complexMatch(Exchange exc) {
return true;
}
public String getIp() {
return ip;
}

public void setIp(String ip) {
this.ip = ip;
}

@Override
public boolean matchesHostHeader(String hostHeader) {
return false;
}

@Override
public boolean matchesVersion(String version) {
return !"STOMP".equals(version);
}

public boolean complexMatch(Exchange exc) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,10 @@

package com.predic8.membrane.core.rules;

import com.predic8.membrane.core.transport.ssl.StaticSSLContext;
import com.predic8.membrane.core.transport.ssl.GeneratingSSLContext;
import org.apache.commons.lang3.StringUtils;

import com.predic8.membrane.annot.MCAttribute;
import com.predic8.membrane.annot.MCChildElement;
import com.predic8.membrane.core.config.security.SSLParser;
import com.predic8.membrane.core.transport.ssl.SSLContext;
import com.predic8.membrane.core.transport.ssl.SSLProvider;
import org.apache.commons.lang3.StringUtils;

public abstract class SSLableProxy extends AbstractProxy {
private SSLContext sslOutboundContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private static APIProxy getApiProxy() throws Exception {
spec.setRewrite(rw);
spec.setValidateRequests(OpenAPISpec.YesNoOpenAPIOption.YES);
proxy.setSpecs(singletonList(spec));
proxy.setKey(new APIProxyKey(null, "*", 2000, null));
proxy.setKey(new APIProxyKey(null, "*", 2000, null, "*", null, false));
proxy.getInterceptors().add(new LogInterceptor());
proxy.init(r);
return proxy;
Expand Down
Loading
Loading