diff --git a/plugin-modernizer-core/pom.xml b/plugin-modernizer-core/pom.xml index a2135268f..52277a884 100644 --- a/plugin-modernizer-core/pom.xml +++ b/plugin-modernizer-core/pom.xml @@ -282,6 +282,11 @@ ssh-slaves 3.1021.va_cc11b_de26a_e + + org.jenkins-ci.main + jenkins-core + 2.497 + diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurity.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurity.java new file mode 100644 index 000000000..aee7d929c --- /dev/null +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurity.java @@ -0,0 +1,153 @@ +package io.jenkins.tools.pluginmodernizer.core.recipes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.ChangeMethodName; +import org.openrewrite.java.ChangePackage; +import org.openrewrite.java.ChangeType; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JLeftPadded; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MigrateAcegiSecurityToSpringSecurity extends Recipe { + /** + * Logger + */ + private static final Logger LOG = LoggerFactory.getLogger(MigrateAcegiSecurityToSpringSecurity.class); + + @Override + public String getDisplayName() { + return "Migrate Acegi Security to Spring Security"; + } + + @Override + public String getDescription() { + return "Migrate acegi security to spring security."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor<>() { + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { + // ChangePackage will take care for the most of the migration so don't need to add separate migrations + // For those import statements that ChangePackage will not account correctly, add separate logic + cu = (J.CompilationUnit) + new ChangePackage("org.acegisecurity", "org.springframework.security.core", false) + .getVisitor() + .visitNonNull(cu, ctx); + + cu = (J.CompilationUnit) new ChangeType( + "org.springframework.security.core.GrantedAuthorityImpl", + "org.springframework.security.core.authority.SimpleGrantedAuthority", + null) + .getVisitor() + .visitNonNull(cu, ctx); + + // Authentication classes + cu = (J.CompilationUnit) new ChangeType( + "org.acegisecurity.providers.AbstractAuthenticationToken", + "org.springframework.security.authentication.AbstractAuthenticationToken", + null) + .getVisitor() + .visitNonNull(cu, ctx); + + List originalImports = cu.getImports(); + + cu = (J.CompilationUnit) new ChangeType( + "org.springframework.security.core.AuthenticationManager", + " org.springframework.security.authentication.AuthenticationManager", + null) + .getVisitor() + .visitNonNull(cu, ctx); + if (!cu.getImports().equals(originalImports)) { + cu = addImportIfNotExists( + cu, "AuthenticationManager", "org.springframework.security.authentication"); + } + originalImports = cu.getImports(); + cu = (J.CompilationUnit) new ChangeType( + "org.springframework.security.core.BadCredentialsException", + "org.springframework.security.authentication.BadCredentialsException", + null) + .getVisitor() + .visitNonNull(cu, ctx); + + if (!cu.getImports().equals(originalImports)) { + cu = addImportIfNotExists( + cu, "BadCredentialsException", "org.springframework.security.authentication"); + } + + return super.visitCompilationUnit(cu, ctx); + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + method = (J.MethodInvocation) new ChangeMethodName( + "jenkins.model.Jenkins getAuthentication()", "getAuthentication2", null, null) + .getVisitor() + .visitNonNull(method, ctx); + // Migrate fireAuthenticated to fireAuthenticated2 + if (method.getSimpleName().equals("fireAuthenticated")) { + method = method.withName(method.getName().withSimpleName("fireAuthenticated2")); + } + return super.visitMethodInvocation(method, ctx); + } + + private J.CompilationUnit addImportIfNotExists(J.CompilationUnit cu, String className, String packageName) { + boolean importExists = cu.getImports().stream().anyMatch(anImport -> (packageName + "." + className) + .equals(anImport.getQualid().toString())); + if (!importExists) { + J.Identifier identifier = new J.Identifier( + UUID.randomUUID(), + Space.EMPTY, + Markers.EMPTY, + Collections.emptyList(), + className, + null, + null); + + J.Identifier packageIdentifier = new J.Identifier( + UUID.randomUUID(), + Space.SINGLE_SPACE, + Markers.EMPTY, + Collections.emptyList(), + packageName, + null, + null); + + J.FieldAccess fieldAccess = new J.FieldAccess( + UUID.randomUUID(), + Space.EMPTY, + Markers.EMPTY, + packageIdentifier, + JLeftPadded.build(identifier), + null); + + J.Import newImport = new J.Import( + UUID.randomUUID(), + Space.format("\n"), // Ensure the import appears on a new line + Markers.EMPTY, + JLeftPadded.build(false), // static: false + fieldAccess, + null); + + List modifiedImports = new ArrayList<>(cu.getImports()); + modifiedImports.add(newImport); + + return cu.withImports(modifiedImports); + } + + return cu; + } + }; + } +} diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateStaplerAndJavaxToJakarta.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateStaplerAndJavaxToJakarta.java index b992e7a76..01dd259ca 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateStaplerAndJavaxToJakarta.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateStaplerAndJavaxToJakarta.java @@ -65,7 +65,7 @@ public TreeVisitor getVisitor(Set acc) { @Override public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { if (acc.isEmpty()) { - cu = (J.CompilationUnit) new ChangePackage("javax.servlet", "jakarta.servlet", false) + cu = (J.CompilationUnit) new ChangePackage("javax.servlet", "jakarta.servlet", true) .getVisitor() .visitNonNull(cu, ctx); } diff --git a/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml b/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml index 5ee2d58e7..d29dc747f 100644 --- a/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml +++ b/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml @@ -153,6 +153,7 @@ recipeList: - io.jenkins.tools.pluginmodernizer.core.recipes.UpgradeJenkinsTestHarnessVersion: jenkinsVersion: 2.479.1 - io.jenkins.tools.pluginmodernizer.core.recipes.MigrateStaplerAndJavaxToJakarta + - io.jenkins.tools.pluginmodernizer.core.recipes.MigrateAcegiSecurityToSpringSecurity - io.jenkins.tools.pluginmodernizer.RemoveDevelopersTag - io.jenkins.tools.pluginmodernizer.RemoveDependencyVersionOverride - io.jenkins.tools.pluginmodernizer.RemoveExtraMavenProperties diff --git a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/DeclarativeRecipesTest.java b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/DeclarativeRecipesTest.java index c51d4d6a9..949aff2cf 100644 --- a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/DeclarativeRecipesTest.java +++ b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/DeclarativeRecipesTest.java @@ -1852,11 +1852,25 @@ void upgradeNextMajorParentVersionTest() { import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; + import org.acegisecurity.Authentication; + import org.acegisecurity.GrantedAuthority; + import org.acegisecurity.GrantedAuthorityImpl; + import org.acegisecurity.providers.AbstractAuthenticationToken; + import org.acegisecurity.context.SecurityContextHolder; + import org.acegisecurity.AuthenticationException; + import org.acegisecurity.AuthenticationManager; + import org.acegisecurity.BadCredentialsException; + import org.acegisecurity.userdetails.UserDetails; + import org.acegisecurity.userdetails.UserDetailsService; + import org.acegisecurity.userdetails.UsernameNotFoundException; + import jenkins.model.Jenkins; + import jenkins.security.SecurityListener; public class Foo { public void foo() { StaplerRequest req = Stapler.getCurrentRequest(); StaplerResponse response = Stapler.getCurrentResponse(); + Authentication auth = Jenkins.getAuthentication(); } } """, @@ -1865,11 +1879,24 @@ public void foo() { import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest2; import org.kohsuke.stapler.StaplerResponse2; + import org.springframework.security.core.Authentication; + import org.springframework.security.core.GrantedAuthority; + import org.springframework.security.authentication.AbstractAuthenticationToken; + import org.springframework.security.core.context.SecurityContextHolder; + import org.springframework.security.core.AuthenticationException; + import org.springframework.security.core.userdetails.UserDetails; + import org.springframework.security.core.userdetails.UserDetailsService; + import org.springframework.security.core.userdetails.UsernameNotFoundException; + import jenkins.model.Jenkins; + import jenkins.security.SecurityListener; + import org.springframework.security.authentication.AuthenticationManager; + import org.springframework.security.authentication.BadCredentialsException; public class Foo { public void foo() { StaplerRequest2 req = Stapler.getCurrentRequest2(); StaplerResponse2 response = Stapler.getCurrentResponse2(); + Authentication auth = Jenkins.getAuthentication2(); } } """))); diff --git a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurityTest.java b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurityTest.java new file mode 100644 index 000000000..a55040d99 --- /dev/null +++ b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurityTest.java @@ -0,0 +1,152 @@ +package io.jenkins.tools.pluginmodernizer.core.recipes; + +import static io.jenkins.tools.pluginmodernizer.core.recipes.DeclarativeRecipesTest.collectRewriteTestDependencies; +import static org.openrewrite.java.Assertions.java; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RewriteTest; + +/** + * Test for {@link MigrateAcegiSecurityToSpringSecurity}. + */ +@Execution(ExecutionMode.CONCURRENT) +public class MigrateAcegiSecurityToSpringSecurityTest implements RewriteTest { + + @Test + void migrateAcegiToSpringSecurity() { + rewriteRun( + spec -> { + var parser = JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true); + collectRewriteTestDependencies().forEach(parser::addClasspathEntry); + spec.recipe(new MigrateAcegiSecurityToSpringSecurity()).parser(parser); + }, + java( + """ + import org.acegisecurity.Authentication; + import org.acegisecurity.GrantedAuthority; + import org.acegisecurity.GrantedAuthorityImpl; + import org.acegisecurity.providers.AbstractAuthenticationToken; + import org.acegisecurity.context.SecurityContextHolder; + import org.acegisecurity.AuthenticationException; + import org.acegisecurity.AuthenticationManager; + import org.acegisecurity.BadCredentialsException; + import org.acegisecurity.userdetails.UserDetails; + import org.acegisecurity.userdetails.UserDetailsService; + import org.acegisecurity.userdetails.UsernameNotFoundException; + import jenkins.model.Jenkins; + import jenkins.security.SecurityListener; + + public class Foo implements UserDetails { + @Override + public GrantedAuthority[] getAuthorities() { + return new GrantedAuthority[] { + new GrantedAuthorityImpl("ROLE_USER") + }; + } + + @Override + public String getPassword() { + return "password123"; + } + + @Override + public String getUsername() { + return "testUser"; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + public UserDetails loadUserByUsername(String username) { + return new Foo(); + } + public void foo() { + Authentication auth = Jenkins.getAuthentication(); + Foo userDetails = new Foo(); + SecurityListener.fireAuthenticated(userDetails); + } + } + """, + """ + import org.springframework.security.core.Authentication; + import org.springframework.security.core.GrantedAuthority; + import org.springframework.security.core.authority.SimpleGrantedAuthority; + import org.springframework.security.authentication.AbstractAuthenticationToken; + import org.springframework.security.core.context.SecurityContextHolder; + import org.springframework.security.core.AuthenticationException; + import org.springframework.security.core.userdetails.UserDetails; + import org.springframework.security.core.userdetails.UserDetailsService; + import org.springframework.security.core.userdetails.UsernameNotFoundException; + import jenkins.model.Jenkins; + import jenkins.security.SecurityListener; + import org.springframework.security.authentication.AuthenticationManager; + import org.springframework.security.authentication.BadCredentialsException; + + public class Foo implements UserDetails { + @Override + public GrantedAuthority[] getAuthorities() { + return new GrantedAuthority[] { + new SimpleGrantedAuthority("ROLE_USER") + }; + } + + @Override + public String getPassword() { + return "password123"; + } + + @Override + public String getUsername() { + return "testUser"; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + public UserDetails loadUserByUsername(String username) { + return new Foo(); + } + public void foo() { + Authentication auth = Jenkins.getAuthentication2(); + Foo userDetails = new Foo(); + SecurityListener.fireAuthenticated2(userDetails); + } + } + """)); + } +}