Skip to content

Commit bb76958

Browse files
authored
Merge pull request #836 from CodexRaunak/Update-Recipe/Migrate-Acegi-Security-to-Spring-Security
Update recipe/migrate acegi security to spring security
2 parents ce39591 + 48b5562 commit bb76958

3 files changed

Lines changed: 425 additions & 18 deletions

File tree

plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/recipes/MigrateAcegiSecurityToSpringSecurity.java

Lines changed: 219 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44
import java.util.Collections;
55
import java.util.List;
66
import java.util.UUID;
7+
import java.util.stream.Collectors;
78
import org.openrewrite.ExecutionContext;
89
import org.openrewrite.Recipe;
910
import org.openrewrite.TreeVisitor;
1011
import org.openrewrite.java.ChangeMethodName;
1112
import org.openrewrite.java.ChangePackage;
1213
import org.openrewrite.java.ChangeType;
1314
import org.openrewrite.java.JavaIsoVisitor;
15+
import org.openrewrite.java.MethodMatcher;
16+
import org.openrewrite.java.tree.Expression;
1417
import org.openrewrite.java.tree.J;
18+
import org.openrewrite.java.tree.JContainer;
1519
import org.openrewrite.java.tree.JLeftPadded;
20+
import org.openrewrite.java.tree.JRightPadded;
21+
import org.openrewrite.java.tree.JavaType;
1622
import org.openrewrite.java.tree.Space;
1723
import org.openrewrite.marker.Markers;
1824
import org.slf4j.Logger;
@@ -42,26 +48,40 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon
4248
// ChangePackage will take care for the most of the migration so don't need to add separate migrations
4349
// For those import statements that ChangePackage will not account correctly, add separate logic
4450
cu = (J.CompilationUnit)
45-
new ChangePackage("org.acegisecurity", "org.springframework.security.core", false)
51+
new ChangePackage("org.acegisecurity", "org.springframework.security.core", true)
4652
.getVisitor()
4753
.visitNonNull(cu, ctx);
4854

55+
List<J.Import> originalImports = cu.getImports();
56+
4957
cu = (J.CompilationUnit) new ChangeType(
5058
"org.springframework.security.core.GrantedAuthorityImpl",
5159
"org.springframework.security.core.authority.SimpleGrantedAuthority",
5260
null)
5361
.getVisitor()
5462
.visitNonNull(cu, ctx);
5563

64+
if (!cu.getImports().equals(originalImports)) {
65+
cu = addImportIfNotExists(
66+
cu, "SimpleGrantedAuthority", "org.springframework.security.core.authority");
67+
}
68+
originalImports = cu.getImports();
69+
5670
// Authentication classes
5771
cu = (J.CompilationUnit) new ChangeType(
58-
"org.acegisecurity.providers.AbstractAuthenticationToken",
72+
"org.springframework.security.core.providers.AbstractAuthenticationToken",
5973
"org.springframework.security.authentication.AbstractAuthenticationToken",
6074
null)
6175
.getVisitor()
6276
.visitNonNull(cu, ctx);
77+
if (!cu.getImports().equals(originalImports)) {
78+
cu = addImportIfNotExists(
79+
cu, "AbstractAuthenticationToken", "org.springframework.security.authentication");
6380

64-
List<J.Import> originalImports = cu.getImports();
81+
cu = addImportIfNotExists(cu, "List", "java.util");
82+
cu = addImportIfNotExists(cu, "Collection", "java.util");
83+
}
84+
originalImports = cu.getImports();
6585

6686
cu = (J.CompilationUnit) new ChangeType(
6787
"org.springframework.security.core.AuthenticationManager",
@@ -86,22 +106,218 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon
86106
cu, "BadCredentialsException", "org.springframework.security.authentication");
87107
}
88108

109+
// add java.util.Collections where UserDetails is used
110+
for (J.Import anImport : cu.getImports()) {
111+
String importName = anImport.getQualid().toString();
112+
if (importName.equals("import org.acegisecurity.userdetails.UserDetails")
113+
|| importName.equals("org.springframework.security.core.userdetails.UserDetails")) {
114+
cu = addImportIfNotExists(cu, "Collection", "java.util");
115+
}
116+
}
117+
89118
return super.visitCompilationUnit(cu, ctx);
90119
}
91120

92121
@Override
93122
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
123+
// migration of getAuthentication to getAuthentication2
94124
method = (J.MethodInvocation) new ChangeMethodName(
95125
"jenkins.model.Jenkins getAuthentication()", "getAuthentication2", null, null)
96126
.getVisitor()
97127
.visitNonNull(method, ctx);
128+
98129
// Migrate fireAuthenticated to fireAuthenticated2
99130
if (method.getSimpleName().equals("fireAuthenticated")) {
100131
method = method.withName(method.getName().withSimpleName("fireAuthenticated2"));
101132
}
133+
134+
// migrate loadUserByUsername to loadUserByUsername2
135+
MethodMatcher methodMatcher =
136+
new MethodMatcher("hudson.security.SecurityRealm loadUserByUsername(java.lang.String)", true);
137+
if (methodMatcher.matches(method)) {
138+
JavaType.Method type = method.getMethodType();
139+
140+
if (type != null) {
141+
type = type.withName("loadUserByUsername2");
142+
}
143+
144+
method = method.withName(method.getName()
145+
.withSimpleName("loadUserByUsername2")
146+
.withType(type))
147+
.withMethodType(type);
148+
}
102149
return super.visitMethodInvocation(method, ctx);
103150
}
104151

152+
@Override
153+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
154+
// migration of changing the getAuthorities() return type from GrantedAuthority[] to
155+
// Collection<GrantedAuthority>
156+
// Identify methods named `getAuthorities()`
157+
J.ClassDeclaration enclosingClass = getCursor().firstEnclosing(J.ClassDeclaration.class);
158+
if ("getAuthorities".equals(method.getSimpleName())) {
159+
JavaType returnType = method.getReturnTypeExpression().getType();
160+
161+
// Check if the return type is an array of `GrantedAuthority`
162+
if (returnType instanceof JavaType.Array
163+
&& ((JavaType.Array) returnType)
164+
.getElemType()
165+
.toString()
166+
.contains("GrantedAuthority")) {
167+
168+
List<JRightPadded<Expression>> typeParameters = Collections.singletonList(new JRightPadded<>(
169+
new J.Identifier(
170+
UUID.randomUUID(),
171+
Space.EMPTY,
172+
Markers.EMPTY,
173+
"GrantedAuthority",
174+
JavaType.buildType("org.springframework.security.core.GrantedAuthority"),
175+
null),
176+
Space.EMPTY, // No trailing space
177+
Markers.EMPTY));
178+
179+
JContainer<Expression> typeParametersContainer = JContainer.build(typeParameters);
180+
// Change return type to `Collection<GrantedAuthority>`
181+
method = method.withReturnTypeExpression(new J.ParameterizedType(
182+
UUID.randomUUID(),
183+
Space.EMPTY,
184+
Markers.EMPTY,
185+
new J.Identifier(
186+
UUID.randomUUID(),
187+
Space.SINGLE_SPACE,
188+
Markers.EMPTY,
189+
"Collection",
190+
JavaType.buildType("java.util.Collection"),
191+
null),
192+
typeParametersContainer,
193+
JavaType.buildType("java.util.Collection")));
194+
195+
// Modify method body to return the list directly
196+
if (method.getBody() != null
197+
&& !method.getBody().getStatements().isEmpty()) {
198+
String grantedAuthoritiesFieldName = null;
199+
assert enclosingClass != null;
200+
for (J.VariableDeclarations field : enclosingClass.getBody().getStatements().stream()
201+
.filter(J.VariableDeclarations.class::isInstance)
202+
.map(J.VariableDeclarations.class::cast)
203+
.toList()) {
204+
if (field.getTypeExpression() != null
205+
&& field.getTypeExpression().getType() instanceof JavaType.Parameterized
206+
&& ((JavaType.Parameterized) field.getTypeExpression()
207+
.getType())
208+
.getType()
209+
.toString()
210+
.equals("java.util.List")
211+
&& ((JavaType.Parameterized) field.getTypeExpression()
212+
.getType())
213+
.getTypeParameters()
214+
.get(0)
215+
.toString()
216+
.equals("org.springframework.security.core.GrantedAuthority")) {
217+
grantedAuthoritiesFieldName =
218+
field.getVariables().get(0).getSimpleName();
219+
break;
220+
}
221+
}
222+
223+
if (grantedAuthoritiesFieldName != null) {
224+
method = method.withBody(method.getBody()
225+
.withStatements(Collections.singletonList(new J.Return(
226+
UUID.randomUUID(),
227+
Space.format("\n" + " ".repeat(8)),
228+
Markers.EMPTY,
229+
new J.Identifier(
230+
UUID.randomUUID(),
231+
Space.SINGLE_SPACE,
232+
Markers.EMPTY,
233+
grantedAuthoritiesFieldName,
234+
JavaType.buildType("java.util.List"),
235+
null)))));
236+
} else {
237+
method = method.withBody(method.getBody()
238+
.withStatements(method.getBody().getStatements().stream()
239+
.map(statement -> {
240+
// Check if the statement is a return statement with `new
241+
// GrantedAuthority[0]`
242+
if (statement instanceof J.Return) {
243+
J.Return returnStatement = (J.Return) statement;
244+
if (returnStatement.getExpression() instanceof J.Ternary) {
245+
J.Ternary ternaryExpression =
246+
(J.Ternary) returnStatement.getExpression();
247+
if (ternaryExpression.getFalsePart()
248+
instanceof J.NewArray) {
249+
J.NewArray newArrayExpression =
250+
(J.NewArray) ternaryExpression.getFalsePart();
251+
if (newArrayExpression
252+
.getType()
253+
.toString()
254+
.contains("GrantedAuthority[]")
255+
&& newArrayExpression
256+
.getDimensions()
257+
.get(0)
258+
.toString()
259+
.contains("[0]")) {
260+
LOG.info(
261+
"Found `new GrantedAuthority[0]` inside a ternary expression in getAuthorities method");
262+
// Replace `new GrantedAuthority[0]` with
263+
// `List.of()` in the ternary expression
264+
ternaryExpression = ternaryExpression.withFalsePart(
265+
new J.Identifier(
266+
UUID.randomUUID(),
267+
Space.SINGLE_SPACE,
268+
Markers.EMPTY,
269+
"List.of()",
270+
JavaType.buildType(
271+
"java.util.List"),
272+
null));
273+
}
274+
}
275+
return returnStatement.withExpression(
276+
ternaryExpression); // Update the return statement
277+
// with the modified ternary
278+
}
279+
}
280+
return statement; // Return the unchanged statement if it's not a
281+
// `new GrantedAuthority[0]`
282+
})
283+
.collect(Collectors.toList())));
284+
}
285+
}
286+
}
287+
}
288+
289+
// Migrate loadUserByUsername to loadUserByUsername2 method declaration if the class extends Security
290+
// Realm
291+
MethodMatcher methodMatcher =
292+
new MethodMatcher("hudson.security.SecurityRealm loadUserByUsername(java.lang.String)", true);
293+
if (enclosingClass != null
294+
&& enclosingClass.getExtends() != null
295+
&& enclosingClass.getExtends().getType() != null
296+
&& enclosingClass.getExtends().getType().toString().equals("hudson.security.SecurityRealm")) {
297+
298+
// Check if the overriding method is named loadUserByUsername
299+
if (methodMatcher.matches(method, enclosingClass)) {
300+
JavaType.Method type = method.getMethodType();
301+
302+
if (method.getMethodType().getOverride() != null) {
303+
LOG.info(
304+
"Don't migrate this one to loadUserByUsername2 {}",
305+
method.getMethodType().getOverride().toString());
306+
return super.visitMethodDeclaration(method, ctx);
307+
}
308+
if (type != null) {
309+
type = type.withName("loadUserByUsername2");
310+
}
311+
method = method.withName(method.getName()
312+
.withSimpleName("loadUserByUsername2")
313+
.withType(type))
314+
.withMethodType(type);
315+
}
316+
}
317+
318+
return super.visitMethodDeclaration(method, ctx);
319+
}
320+
105321
private J.CompilationUnit addImportIfNotExists(J.CompilationUnit cu, String className, String packageName) {
106322
boolean importExists = cu.getImports().stream().anyMatch(anImport -> (packageName + "." + className)
107323
.equals(anImport.getQualid().toString()));

0 commit comments

Comments
 (0)