From 7c5dee41f62f3a8c939184478fe64f3ce9e4f6d2 Mon Sep 17 00:00:00 2001 From: aboyko Date: Fri, 24 Feb 2023 16:42:42 -0500 Subject: [PATCH] Marker and quick fix for WebSecurityConfigurerAdapter --- ...ConfigurationAnnotationIfBeansPresent.java | 142 ------------------ .../boot/java/Boot2JavaProblemType.java | 4 +- .../rewrite/BootCodeActionRepository.java | 4 +- ...ConfigurationIfBeansPresentCodeAction.java | 4 +- ...ebSecurityConfigurerAdapterCodeAction.java | 99 ++++++++++++ .../src/main/resources/problem-types.json | 12 ++ .../vscode-spring-boot/package.json | 24 +++ 7 files changed, 143 insertions(+), 146 deletions(-) delete mode 100644 headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddConfigurationAnnotationIfBeansPresent.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/WebSecurityConfigurerAdapterCodeAction.java diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddConfigurationAnnotationIfBeansPresent.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddConfigurationAnnotationIfBeansPresent.java deleted file mode 100644 index e746df33c..000000000 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddConfigurationAnnotationIfBeansPresent.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VMware, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.commons.rewrite.java; - -import java.util.Comparator; - -import org.openrewrite.Cursor; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.java.AnnotationMatcher; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaParser; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.Statement; -import org.openrewrite.java.tree.TypeUtils; - -public class AddConfigurationAnnotationIfBeansPresent extends Recipe { - - private static final String CONFIGURATION_PACKAGE = "org.springframework.context.annotation"; - - private static final String CONFIGURATION_SIMPLE_NAME = "Configuration"; - - private static final String FQN_CONFIGURATION = CONFIGURATION_PACKAGE + "." + CONFIGURATION_SIMPLE_NAME; - - private static final String FQN_BEAN = "org.springframework.context.annotation.Bean"; - - @Override - public String getDisplayName() { - return "Add missing '@Configuration' annotation"; - } - - @Override - public String getDescription() { - return "Class having `@Bean' annotation over any methods but missing '@Configuration' annotation over the declaring class would have '@Configuration' annotation added."; - } - - @Override - protected TreeVisitor getApplicableTest() { - return new UsesType<>(FQN_BEAN); - } - - @Override - protected TreeVisitor getVisitor() { - return new JavaIsoVisitor() { - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext p) { - J.ClassDeclaration c = super.visitClassDeclaration(classDecl, p); - if (isApplicableClass(c, getCursor())) { - c = addConfigurationAnnotation(c); - } - return c; - } - - private J.ClassDeclaration addConfigurationAnnotation(J.ClassDeclaration c) { - maybeAddImport(FQN_CONFIGURATION); - JavaTemplate template = JavaTemplate.builder(() -> getCursor(), "@" + CONFIGURATION_SIMPLE_NAME) - .imports( - FQN_CONFIGURATION) - .javaParser(() -> JavaParser.fromJavaVersion().dependsOn("package " + CONFIGURATION_PACKAGE - + "; public @interface " + CONFIGURATION_SIMPLE_NAME + " {}").build()) - .build(); - return c.withTemplate(template, - c.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); - } - - }; - } - - public static boolean isApplicableClass(J.ClassDeclaration classDecl, Cursor cursor) { - if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Class) { - boolean isStatic = false; - for (J.Modifier m : classDecl.getModifiers()) { - if (m.getType() == J.Modifier.Type.Abstract) { - return false; - } else if (m.getType() == J.Modifier.Type.Static) { - isStatic = true; - } - } - - if (!isStatic) { - // no static keyword? check if it is top level class in the CU - J.CompilationUnit cu = cursor.dropParentUntil(J.CompilationUnit.class::isInstance).getValue(); - if (!cu.getClasses().contains(classDecl)) { - return false; - } - } - - // check if '@Configuration' is already over the class - AnnotationMatcher matcher = new AnnotationMatcher("@" + FQN_CONFIGURATION, true); - - for (J.Annotation a : classDecl.getLeadingAnnotations()) { - JavaType.FullyQualified aType = TypeUtils.asFullyQualified(a.getType()); - if (aType != null && matcher.matchesAnnotationOrMetaAnnotation(aType)) { - // Found '@Configuration' annotation - return false; - } - } - // No '@Configuration' present. Check if any methods have '@Bean' annotation - for (Statement s : classDecl.getBody().getStatements()) { - if (s instanceof J.MethodDeclaration) { - if (isBeanMethod((J.MethodDeclaration) s)) { - return true; - } - } - } - - } - return false; - } - - private static boolean isBeanMethod(J.MethodDeclaration methodDecl) { - for (J.Modifier m : methodDecl.getModifiers()) { - if (m.getType() == J.Modifier.Type.Abstract) { - return false; - } else if (m.getType() == J.Modifier.Type.Static) { - return false; - } - } - AnnotationMatcher beanAnnotationMatcher = new AnnotationMatcher("@" + FQN_BEAN, true); - for (J.Annotation a : methodDecl.getLeadingAnnotations()) { - JavaType.FullyQualified aType = TypeUtils.asFullyQualified(a.getType()); - if (aType != null && beanAnnotationMatcher.matchesAnnotationOrMetaAnnotation(aType)) { - return true; - } - } - return false; - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java index 2a50d5d17..bf0edf008 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java @@ -38,7 +38,9 @@ public enum Boot2JavaProblemType implements ProblemType { MISSING_CONFIGURATION_ANNOTATION(WARNING, "Class likely missing '@Configuration' annotation, i.e. has Bean methods but no '@Configuration' annotation", "Missing '@Configuration'"), - HTTP_SECIRITY_AUTHORIZE_HTTP_REQUESTS(WARNING, "'HttpSecurity.authroizeRequests(...)' API and related classes are to be deprecated use new `authorizeHttpRequests(...) and related classes", "Usage of old 'HttpSecurity.authroizeRequests(...)' API"); + HTTP_SECIRITY_AUTHORIZE_HTTP_REQUESTS(WARNING, "'HttpSecurity.authroizeRequests(...)' API and related classes are to be deprecated use new `authorizeHttpRequests(...) and related classes", "Usage of old 'HttpSecurity.authroizeRequests(...)' API"), + + WEB_SECURITY_CONFIGURER_ADAPTER(WARNING, "'WebSecurityConfigurerAdapter' is removed in Spring-Security 6.x. Refactor classes extending the 'WebSecurityConfigurerAdapter' into 'Configuration' beans and methods into 'Bean' definitions ", "Replace usage of 'WebSecurityConfigurerAdapter' as this class to be removed in Security 6.x"); private final ProblemSeverity defaultSeverity; private String description; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java index 838af83a1..3a64f61e4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java @@ -26,6 +26,7 @@ import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NotRegisteredB import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.ServerHttpSecurityLambdaDslCodeAction; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.UnnecessarySpringExtensionProblem; +import org.springframework.ide.vscode.boot.java.rewrite.reconcile.WebSecurityConfigurerAdapterCodeAction; import org.springframework.ide.vscode.commons.rewrite.config.CodeActionRepository; import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; @@ -47,7 +48,8 @@ public class BootCodeActionRepository extends CodeActionRepository { new HttpSecurityLamdaDslCodeAction(), new ServerHttpSecurityLambdaDslCodeAction(), new AddConfigurationIfBeansPresentCodeAction(), - new AuthorizeHttpRequestsCodeAction() + new AuthorizeHttpRequestsCodeAction(), + new WebSecurityConfigurerAdapterCodeAction() ); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/AddConfigurationIfBeansPresentCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/AddConfigurationIfBeansPresentCodeAction.java index d6e244fa5..5254e0816 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/AddConfigurationIfBeansPresentCodeAction.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/AddConfigurationIfBeansPresentCodeAction.java @@ -16,6 +16,7 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.spring.boot2.AddConfigurationAnnotationIfBeansPresent; import org.openrewrite.java.tree.J.ClassDeclaration; import org.openrewrite.java.tree.J.MethodDeclaration; import org.openrewrite.java.tree.J.VariableDeclarations; @@ -27,14 +28,13 @@ import org.springframework.ide.vscode.commons.java.Version; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; -import org.springframework.ide.vscode.commons.rewrite.java.AddConfigurationAnnotationIfBeansPresent; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.JavaMarkerVisitor; public class AddConfigurationIfBeansPresentCodeAction implements RecipeCodeActionDescriptor { - private static final String ID = "org.springframework.ide.vscode.commons.rewrite.java.AddConfigurationAnnotationIfBeansPresent"; + private static final String ID = "org.openrewrite.java.spring.boot2.AddConfigurationAnnotationIfBeansPresent"; private static final String PROBLEM_LABEL = "'@Configuration' is missing on a class defining Spring Beans"; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/WebSecurityConfigurerAdapterCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/WebSecurityConfigurerAdapterCodeAction.java new file mode 100644 index 000000000..690956ff0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/WebSecurityConfigurerAdapterCodeAction.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.rewrite.reconcile; + +import java.util.List; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; +import org.openrewrite.Tree; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.ClassDeclaration; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeTree; +import org.springframework.context.ApplicationContext; +import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.java.Version; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; +import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; +import org.springframework.ide.vscode.commons.rewrite.java.JavaMarkerVisitor; + +public class WebSecurityConfigurerAdapterCodeAction implements RecipeCodeActionDescriptor { + + private static final String ID = "org.openrewrite.java.spring.boot2.WebSecurityConfigurerAdapter"; + + private static final String FQN_WEB_SECURITY_CONFIGURER_ADAPTER = "org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter"; + + 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'"; + + @Override + public String getId() { + return ID; + } + + @Override + public ProblemType getProblemType() { + return Boot2JavaProblemType.WEB_SECURITY_CONFIGURER_ADAPTER; + } + + @Override + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { + return new JavaMarkerVisitor<>() { + + @Override + public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { + ClassDeclaration c = super.visitClassDeclaration(classDecl, p); + if (isExtendingWebSecurityConfigurerAdapter(c)) { + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toASCIIString(); + 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), + new FixDescriptor(ID, List.of(uri), + RecipeCodeActionDescriptor.buildLabel(FIX_LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT)); + 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; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json index 2b4d99cb9..7c51d5a2c 100644 --- a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -61,6 +61,18 @@ "label": "Missing '@Configuration'", "description": "Class likely missing '@Configuration' annotation, i.e. has Bean methods but no '@Configuration' annotation", "defaultSeverity": "WARNING" + }, + { + "code": "HTTP_SECIRITY_AUTHORIZE_HTTP_REQUESTS", + "label": "Usage of old 'HttpSecurity.authroizeRequests(...)' API", + "description": "'HttpSecurity.authroizeRequests(...)' API and related classes are to be deprecated use new `authorizeHttpRequests(...) and related classes", + "defaultSeverity": "WARNING" + }, + { + "code": "WEB_SECURITY_CONFIGURER_ADAPTER", + "label": "Replace usage of 'WebSecurityConfigurerAdapter' as this class to be removed in Security 6.x", + "description": "'WebSecurityConfigurerAdapter' is removed in Spring-Security 6.x. Refactor classes extending the 'WebSecurityConfigurerAdapter' into 'Configuration' beans and methods into 'Bean' definitions ", + "defaultSeverity": "WARNING" } ] }, diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index da5c458ee..477333434 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -426,6 +426,30 @@ "HINT", "ERROR" ] + }, + "spring-boot.ls.problem.boot2.HTTP_SECIRITY_AUTHORIZE_HTTP_REQUESTS": { + "type": "string", + "default": "WARNING", + "description": "'HttpSecurity.authroizeRequests(...)' API and related classes are to be deprecated use new `authorizeHttpRequests(...) and related classes", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] + }, + "spring-boot.ls.problem.boot2.WEB_SECURITY_CONFIGURER_ADAPTER": { + "type": "string", + "default": "WARNING", + "description": "'WebSecurityConfigurerAdapter' is removed in Spring-Security 6.x. Refactor classes extending the 'WebSecurityConfigurerAdapter' into 'Configuration' beans and methods into 'Bean' definitions ", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] } } },