Skip to content

Commit

Permalink
UseNewRequestMatchers should also replace with 5.7 on classpath (#398)
Browse files Browse the repository at this point in the history
* UseNewRequestMatchers should also replace with 5.7 on classpath

* Also migrate ignoringAntMatchers to ignoringRequestMatchers

* Ignore type validation issues on combined test

* Use getCoordinates().replaceMethod()

* Update name to remove need for typeValidationOptions

* Remove unused imports
  • Loading branch information
timtebeek authored Jul 28, 2023
1 parent 1d54bb8 commit 20e4f75
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,23 @@
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

import java.util.List;
import java.util.Optional;

import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;

@Value
@EqualsAndHashCode(callSuper = false)
public class UseNewRequestMatchers extends Recipe {

private static final MethodMatcher ANT_MATCHERS = new MethodMatcher("org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry antMatchers(..)");
private static final MethodMatcher MVC_MATCHERS = new MethodMatcher("org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry mvcMatchers(..)", true);
private static final MethodMatcher REGEX_MATCHERS = new MethodMatcher("org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry regexMatchers(..)");
private static final String CLAZZ = "org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry";
private static final MethodMatcher ANT_MATCHERS = new MethodMatcher(CLAZZ + " antMatchers(..)");
private static final MethodMatcher MVC_MATCHERS = new MethodMatcher(CLAZZ + " mvcMatchers(..)", true);
private static final MethodMatcher REGEX_MATCHERS = new MethodMatcher(CLAZZ + " regexMatchers(..)");
private static final MethodMatcher CSRF_MATCHERS = new MethodMatcher("org.springframework.security.config.annotation.web.configurers.CsrfConfigurer ignoringAntMatchers(..)");


@Override
Expand All @@ -57,46 +56,27 @@ public String getDescription() {
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.or(
new UsesMethod<>(ANT_MATCHERS),
new UsesMethod<>(MVC_MATCHERS),
new UsesMethod<>(REGEX_MATCHERS)), new JavaIsoVisitor<ExecutionContext>() {

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
if (ANT_MATCHERS.matches(mi) || MVC_MATCHERS.matches(mi) || REGEX_MATCHERS.matches(mi)) {
mi = maybeChangeMethodInvocation(mi);
}
return mi;
}
});

}

private J.MethodInvocation maybeChangeMethodInvocation(J.MethodInvocation mi) {
return findRequestMatchersMethodWithMatchingParameterTypes(mi)
.map(requestMatchersMethod -> mi
.withMethodType(requestMatchersMethod)
.withName(mi.getName().withSimpleName("requestMatchers")))
.orElse(mi);
}

private Optional<JavaType.Method> findRequestMatchersMethodWithMatchingParameterTypes(J.MethodInvocation mi) {
JavaType.Method methodType = mi.getMethodType();
if (methodType == null) {
return Optional.empty();
}
List<JavaType> parameterTypes = methodType.getParameterTypes();
List<JavaType.Method> methods;
boolean isOverride = TypeUtils.isOverride(mi.getMethodType());
if (isOverride) {
methods = requireNonNull(methodType.getDeclaringType().getSupertype(), "superType").getMethods();
} else {
methods = methodType.getDeclaringType().getMethods();
}
return methods.stream()
.filter(m -> m.getName().equals("requestMatchers"))
.filter(m -> m.getParameterTypes().equals(parameterTypes))
.findFirst();
new UsesMethod<>(ANT_MATCHERS),
new UsesMethod<>(MVC_MATCHERS),
new UsesMethod<>(REGEX_MATCHERS)),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
boolean isCsrfMatcher = CSRF_MATCHERS.matches(mi);
if ((ANT_MATCHERS.matches(mi) || MVC_MATCHERS.matches(mi) || REGEX_MATCHERS.matches(mi) || isCsrfMatcher)
&& mi.getSelect() != null) {
String parametersTemplate = mi.getArguments().stream().map(arg -> "#{any()}").collect(joining(", "));
String replacementMethodName = isCsrfMatcher ? "ignoringRequestMatchers" : "requestMatchers";
JavaTemplate template = JavaTemplate.builder(String.format(replacementMethodName + "(%s)", parametersTemplate))
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "spring-security-config-5.8"))
.build();
J.MethodInvocation apply = template.apply(getCursor(), mi.getCoordinates().replaceMethod(), mi.getArguments().toArray());
return apply.withSelect(mi.getSelect())
.withName(mi.getName().withSimpleName(replacementMethodName));
}
return mi;
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
package org.openrewrite.spring.security5;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.DocumentExample;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaVisitor;
Expand All @@ -39,7 +39,7 @@ public void defaults(RecipeSpec spec) {
spec.recipe(new UseNewSecurityMatchers())
.parser(JavaParser.fromJavaVersion()
.logCompilationWarningsAndErrors(true)
.classpathFromResources(new InMemoryExecutionContext(),"spring-context-5.3.+", "spring-beans-5.3.+", "spring-web-5.3.+", "spring-security-web-5.8.+", "spring-security-config-5.8.+"));
.classpathFromResources(new InMemoryExecutionContext(), "spring-context-5.3.+", "spring-beans-5.3.+", "spring-web-5.3.+", "spring-security-web-5.8.+", "spring-security-config-5.8.+"));
}

@DocumentExample
Expand Down Expand Up @@ -99,18 +99,68 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) {
);
}

@Test
void chainedCalls() {
rewriteRun(
//language=java
java("""
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.antMatcher("/**").authorizeRequests()
.antMatchers(HttpMethod.POST, "/verify").access("hasRole('ROLE_USER')")
.anyRequest().authenticated();
return http.build();
}
}
""","""
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.securityMatcher("/**").authorizeRequests()
.antMatchers(HttpMethod.POST, "/verify").access("hasRole('ROLE_USER')")
.anyRequest().authenticated();
return http.build();
}
}
""")
);
}

@Test
void togetherWithRequestMatchers() {
//language=java
rewriteRun(
spec -> spec.recipe(toRecipe(() -> new JavaVisitor<>() {
@Override
public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) {
tree = new UseNewRequestMatchers().getVisitor().visit(tree, ctx);
tree = new UseNewSecurityMatchers().getVisitor().visit(tree, ctx);
return (J) tree;
}
})),
@Override
public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) {
tree = new UseNewRequestMatchers().getVisitor().visit(tree, ctx);
tree = new UseNewSecurityMatchers().getVisitor().visit(tree, ctx);
return (J) tree;
}
})),
java(
"""
package com.example;
Expand Down
Loading

0 comments on commit 20e4f75

Please sign in to comment.