WebSecurityConfigurerAdapter quick fix for Security 6.0.x code
This commit is contained in:
@@ -12,7 +12,6 @@ package org.springframework.ide.vscode.commons.languageserver;
|
||||
|
||||
import org.eclipse.lsp4j.WorkDoneProgressBegin;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressReport;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
|
||||
|
||||
/**
|
||||
* Eclipse progress requires sending total work units number with the begin progress message. The number is any intger.
|
||||
@@ -38,7 +37,7 @@ public class PercentageProgressTask extends AbstractProgressTask {
|
||||
|
||||
private void begin(String title) {
|
||||
WorkDoneProgressBegin progressBegin = new WorkDoneProgressBegin();
|
||||
progressBegin.setPercentage(LspClient.currentClient() == LspClient.Client.ECLIPSE ? 100 : 0);
|
||||
progressBegin.setPercentage(0);
|
||||
progressBegin.setCancellable(false);
|
||||
progressBegin.setTitle(title);
|
||||
service.progressBegin(taskId, progressBegin);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* Copyright (c) 2022, 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -30,10 +30,13 @@ final public class FixDescriptor {
|
||||
|
||||
private String label;
|
||||
|
||||
private String[] typeStubs;
|
||||
|
||||
public FixDescriptor(String recipeId, List<String> docUris, String label) {
|
||||
this.recipeId = recipeId;
|
||||
this.docUris = docUris;
|
||||
this.label = label;
|
||||
this.typeStubs = new String[0];
|
||||
}
|
||||
|
||||
public FixDescriptor withRecipeScope(RecipeScope recipeScope) {
|
||||
@@ -50,7 +53,12 @@ final public class FixDescriptor {
|
||||
this.parameters = parameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public FixDescriptor withTypeStubs(String... typeStubs) {
|
||||
this.typeStubs = typeStubs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRecipeId() {
|
||||
return recipeId;
|
||||
}
|
||||
@@ -74,5 +82,9 @@ final public class FixDescriptor {
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
public String[] getTypeStubs() {
|
||||
return typeStubs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.openrewrite.TreeVisitor;
|
||||
import org.openrewrite.internal.RecipeIntrospectionUtils;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.java.JavaParser.Builder;
|
||||
import org.openrewrite.java.JavaParsingException;
|
||||
import org.openrewrite.java.JavaVisitor;
|
||||
import org.openrewrite.java.UpdateSourcePositions;
|
||||
@@ -234,10 +235,7 @@ public class ORAstUtils {
|
||||
|
||||
public static JavaParser createJavaParser(IJavaProject project) {
|
||||
try {
|
||||
List<Path> classpath = IClasspathUtil.getAllBinaryRoots(project.getClasspath()).stream().map(f -> f.toPath()).collect(Collectors.toList());
|
||||
JavaParser jp = JavaParser.fromJavaVersion().build();
|
||||
jp.setClasspath(classpath);
|
||||
return jp;
|
||||
return createJavaParserBuilder(project).build();
|
||||
} catch (Exception e) {
|
||||
if (isExceptionFromInterrupedThread(e)) {
|
||||
log.debug("", e);
|
||||
@@ -247,34 +245,44 @@ public class ORAstUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CompilationUnit> parse(SimpleTextDocumentService documents, IJavaProject project, Consumer<SourceFile> parseCallback) {
|
||||
List<Parser.Input> inputs = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> {
|
||||
try {
|
||||
return Files.walk(folder.toPath());
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return Stream.empty();
|
||||
}).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().endsWith(".java")).map(p -> {
|
||||
TextDocument doc = documents.getLatestSnapshot(p.toUri().toASCIIString());
|
||||
if (doc == null) {
|
||||
return new Parser.Input(p, () -> {
|
||||
|
||||
public static Builder<? extends JavaParser, ?> createJavaParserBuilder(IJavaProject project) {
|
||||
List<Path> classpath = IClasspathUtil.getAllBinaryRoots(project.getClasspath()).stream().map(f -> f.toPath()).collect(Collectors.toList());
|
||||
return JavaParser.fromJavaVersion().classpath(classpath);
|
||||
}
|
||||
|
||||
public static List<Parser.Input> getParserInputs(SimpleTextDocumentService documents, IJavaProject project) {
|
||||
return IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath())
|
||||
.flatMap(folder -> {
|
||||
try {
|
||||
return Files.newInputStream(p);
|
||||
return Files.walk(folder.toPath());
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return new Parser.Input(p, () -> new ByteArrayInputStream(doc.get().getBytes()));
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
JavaParser javaParser = createJavaParser(project);
|
||||
return ORAstUtils.parseInputs(javaParser, inputs, parseCallback);
|
||||
return Stream.empty();
|
||||
})
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.getFileName().toString().endsWith(".java"))
|
||||
.map(p -> getParserInput(documents, p))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public static Parser.Input getParserInput(SimpleTextDocumentService documents, Path p) {
|
||||
TextDocument doc = documents.getLatestSnapshot(p.toUri().toASCIIString());
|
||||
if (doc == null) {
|
||||
return new Parser.Input(p, () -> {
|
||||
try {
|
||||
return Files.newInputStream(p);
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return new Parser.Input(p, () -> new ByteArrayInputStream(doc.get().getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CompilationUnit> parse(JavaParser parser, Iterable<Path> sourceFiles) {
|
||||
InMemoryExecutionContext ctx = new InMemoryExecutionContext(ORAstUtils::logExceptionWhileParsing);
|
||||
ctx.putMessage(JavaParser.SKIP_SOURCE_SET_TYPE_GENERATION, true);
|
||||
|
||||
@@ -38,7 +38,7 @@ public class RewriteConfig {
|
||||
|
||||
@ConditionalOnBean(RewriteRecipeRepository.class)
|
||||
@Bean RewriteRefactorings rewriteRefactorings(SimpleLanguageServer server, JavaProjectFinder projectFinder, RewriteRecipeRepository recipeRepo, RewriteCompilationUnitCache cuCache) {
|
||||
return new RewriteRefactorings(server.getTextDocumentService(), projectFinder, recipeRepo, cuCache);
|
||||
return new RewriteRefactorings(server, projectFinder, recipeRepo, cuCache);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(RewriteRecipeRepository.class)
|
||||
|
||||
@@ -12,10 +12,13 @@ package org.springframework.ide.vscode.boot.java.rewrite;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
@@ -24,22 +27,25 @@ import org.eclipse.lsp4j.TextDocumentEdit;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.openrewrite.Parser.Input;
|
||||
import org.openrewrite.Recipe;
|
||||
import org.openrewrite.RecipeRun;
|
||||
import org.openrewrite.Result;
|
||||
import org.openrewrite.config.DeclarativeRecipe;
|
||||
import org.openrewrite.internal.RecipeIntrospectionUtils;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.openrewrite.marker.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.PercentageProgressTask;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.CodeActionResolver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.rewrite.ORDocUtils;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
@@ -60,7 +66,7 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
|
||||
private RewriteRecipeRepository recipeRepo;
|
||||
|
||||
private SimpleTextDocumentService documents;
|
||||
private SimpleLanguageServer server;
|
||||
|
||||
private RewriteCompilationUnitCache cuCache;
|
||||
|
||||
@@ -68,8 +74,8 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
|
||||
private Gson gson;
|
||||
|
||||
public RewriteRefactorings(SimpleTextDocumentService documents, JavaProjectFinder projectFinder, RewriteRecipeRepository recipeRepo, RewriteCompilationUnitCache cuCache) {
|
||||
this.documents = documents;
|
||||
public RewriteRefactorings(SimpleLanguageServer server, JavaProjectFinder projectFinder, RewriteRecipeRepository recipeRepo, RewriteCompilationUnitCache cuCache) {
|
||||
this.server = server;
|
||||
this.projectFinder = projectFinder;
|
||||
this.recipeRepo = recipeRepo;
|
||||
this.cuCache = cuCache;
|
||||
@@ -120,7 +126,7 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
List<Result> results = reciperun.getResults();
|
||||
List<Either<TextDocumentEdit, ResourceOperation>> edits = results.stream().filter(res -> res.getAfter() != null).map(res -> {
|
||||
URI docUri = res.getAfter().getSourcePath().isAbsolute() ? res.getAfter().getSourcePath().toUri() : project.getLocationUri().resolve(res.getAfter().getSourcePath().toString());
|
||||
TextDocument doc = documents.getLatestSnapshot(docUri.toASCIIString());
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docUri.toASCIIString());
|
||||
if (doc == null) {
|
||||
doc = new TextDocument(docUri.toASCIIString(), LanguageId.JAVA, 0, res.getBefore() == null ? "" : res.getBefore().printAll());
|
||||
}
|
||||
@@ -138,12 +144,27 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(data.getDocUris().get(0)));
|
||||
if (project.isPresent()) {
|
||||
boolean projectWide = data.getRecipeScope() == RecipeScope.PROJECT;
|
||||
Recipe r = createRecipe(data);
|
||||
Recipe r = createRecipe(data);
|
||||
List<CompilationUnit> cus = Collections.emptyList();
|
||||
if (projectWide) {
|
||||
//TODO: progress here as well.
|
||||
return applyRecipe(r, project.get(), ORAstUtils.parse(documents, project.get(), null));
|
||||
JavaParser jp = ORAstUtils.createJavaParserBuilder(project.get()).dependsOn(data.getTypeStubs()).build();
|
||||
List<Input> inputs = ORAstUtils.getParserInputs(server.getTextDocumentService(), project.get());
|
||||
PercentageProgressTask progress = server.getProgressService().createPercentageProgressTask(UUID.randomUUID().toString(), inputs.size() + 1, data.getLabel());
|
||||
try {
|
||||
cus = ORAstUtils.parseInputs(jp, inputs, s -> progress.increment());
|
||||
return applyRecipe(r, project.get(), cus);
|
||||
} finally {
|
||||
progress.setCurrent(progress.getTotal());
|
||||
progress.done();
|
||||
}
|
||||
} else {
|
||||
List<CompilationUnit> cus = data.getDocUris().stream().map(docUri -> cuCache.getCU(project.get(), URI.create(docUri))).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
if (data.getTypeStubs().length == 0) {
|
||||
cus = data.getDocUris().stream().map(docUri -> cuCache.getCU(project.get(), URI.create(docUri))).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
} else {
|
||||
JavaParser jp = ORAstUtils.createJavaParserBuilder(project.get()).dependsOn(data.getTypeStubs()).build();
|
||||
List<Input> inputs = data.getDocUris().stream().map(URI::create).map(Paths::get).map(p -> ORAstUtils.getParserInput(server.getTextDocumentService(), p)).collect(Collectors.toList());
|
||||
cus = ORAstUtils.parseInputs(jp, inputs, null);
|
||||
}
|
||||
return applyRecipe(r, project.get(), cus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,33 @@ public class WebSecurityConfigurerAdapterCodeAction implements RecipeCodeActionD
|
||||
|
||||
private static final String PROBLEM_LABEL = "Class extends 'WebSecurityConfigurerAdapter' which is removed in Spring-Security 6.x";
|
||||
|
||||
protected static final String FIX_LABEL = "Refactor class into a Configuration bean not extending 'WebSecurityConfigurerAdapter'";
|
||||
private static final String FIX_LABEL = "Refactor class into a Configuration bean not extending 'WebSecurityConfigurerAdapter'";
|
||||
|
||||
private static final String STUB_WEB_SECURITY_CONFIG_ADAPTER = """
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
public abstract class WebSecurityConfigurerAdapter {
|
||||
|
||||
public void init(WebSecurity web) throws Exception {}
|
||||
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception { return null; }
|
||||
|
||||
public UserDetailsService userDetailsServiceBean() throws Exception { return null; }
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {}
|
||||
|
||||
public void configure(WebSecurity web) throws Exception {}
|
||||
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
|
||||
}
|
||||
""";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
@@ -62,48 +88,52 @@ public class WebSecurityConfigurerAdapterCodeAction implements RecipeCodeActionD
|
||||
@Override
|
||||
public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) {
|
||||
ClassDeclaration c = super.visitClassDeclaration(classDecl, p);
|
||||
if (isExtendingWebSecurityConfigurerAdapter(c)) {
|
||||
TypeTree superClass = c.getExtends();
|
||||
boolean isExtendingWebSecurityConfigurerAdapter = false;
|
||||
boolean isUnresolved = false;
|
||||
if (superClass != null) {
|
||||
if (superClass.getType() instanceof JavaType.Unknown) {
|
||||
String strType = superClass.printTrimmed(getCursor());
|
||||
isExtendingWebSecurityConfigurerAdapter = "WebSecurityConfigurerAdapter".equals(strType) || FQN_WEB_SECURITY_CONFIGURER_ADAPTER.equals(strType);
|
||||
isUnresolved = true;
|
||||
} else if (superClass.getType() instanceof JavaType.FullyQualified) {
|
||||
isExtendingWebSecurityConfigurerAdapter = FQN_WEB_SECURITY_CONFIGURER_ADAPTER.equals( ((JavaType.FullyQualified)superClass.getType()).getFullyQualifiedName());
|
||||
}
|
||||
}
|
||||
if (isExtendingWebSecurityConfigurerAdapter) {
|
||||
if (isAnnotatedWith(c.getLeadingAnnotations(), Annotations.CONFIGURATION)) {
|
||||
String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toASCIIString();
|
||||
String[] typeStubs = new String[0];
|
||||
if (isUnresolved) {
|
||||
typeStubs = new String[] { STUB_WEB_SECURITY_CONFIG_ADAPTER };
|
||||
}
|
||||
FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), ID).withLabel(PROBLEM_LABEL)
|
||||
.withFixes(
|
||||
new FixDescriptor(ID, List.of(uri),
|
||||
RecipeCodeActionDescriptor.buildLabel(FIX_LABEL, RecipeScope.FILE))
|
||||
.withRecipeScope(RecipeScope.FILE),
|
||||
.withRecipeScope(RecipeScope.FILE)
|
||||
.withTypeStubs(typeStubs),
|
||||
new FixDescriptor(ID, List.of(uri),
|
||||
RecipeCodeActionDescriptor.buildLabel(FIX_LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT));
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
.withTypeStubs(typeStubs));
|
||||
c = c.withName(c.getName().withMarkers(c.getName().getMarkers().add(marker)));
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private boolean isExtendingWebSecurityConfigurerAdapter(J.ClassDeclaration c) {
|
||||
TypeTree superClass = c.getExtends();
|
||||
if (superClass != null) {
|
||||
if (superClass.getType() instanceof JavaType.FullyQualified) {
|
||||
return FQN_WEB_SECURITY_CONFIGURER_ADAPTER.equals( ((JavaType.FullyQualified)superClass.getType()).getFullyQualifiedName());
|
||||
} else if (superClass.getType() instanceof JavaType.Unknown) {
|
||||
String strType = superClass.printTrimmed(getCursor());
|
||||
return "WebSecurityConfigurerAdapter".equals(strType) || FQN_WEB_SECURITY_CONFIGURER_ADAPTER.equals(strType);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
Version version = SpringProjectUtil.getDependencyVersion(project, "spring-security-config");
|
||||
return version != null && version.compareTo(new Version(5, 7, 0, null)) >= 0 && version.compareTo(new Version(6, 0, 0, null)) < 0;
|
||||
return version != null && version.compareTo(new Version(5, 7, 0, null)) >= 0 && version.compareTo(new Version(6, 1, 0, null)) < 0;
|
||||
}
|
||||
|
||||
private static boolean isAnnotatedWith(Collection<J.Annotation> annotations, String annotationType) {
|
||||
return annotations.stream().anyMatch(a -> TypeUtils.isOfClassType(a.getType(), annotationType));
|
||||
}
|
||||
|
||||
private static boolean isAnnotatedWith(Collection<J.Annotation> annotations, String annotationType) {
|
||||
return annotations.stream().anyMatch(a -> TypeUtils.isOfClassType(a.getType(), annotationType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user