Skip to content

Commit adaa368

Browse files
authored
Merge pull request #67 from jenkinsci/post-for-do-fill
Add architecture rule for `doFill` methods.
2 parents 1e28a23 + 7226322 commit adaa368

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

src/test/java/io/jenkins/plugins/util/PluginArchitectureRules.java

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@
22

33
import java.util.Arrays;
44
import java.util.List;
5+
import java.util.Set;
56
import java.util.stream.Collectors;
67

78
import com.tngtech.archunit.base.DescribedPredicate;
9+
import com.tngtech.archunit.core.domain.JavaCall;
810
import com.tngtech.archunit.core.domain.JavaClass;
11+
import com.tngtech.archunit.core.domain.JavaMethod;
912
import com.tngtech.archunit.core.domain.JavaModifier;
13+
import com.tngtech.archunit.lang.ArchCondition;
1014
import com.tngtech.archunit.lang.ArchRule;
15+
import com.tngtech.archunit.lang.ConditionEvents;
16+
import com.tngtech.archunit.lang.SimpleConditionEvent;
1117

1218
import org.kohsuke.stapler.DataBoundConstructor;
1319
import org.kohsuke.stapler.DataBoundSetter;
1420
import org.kohsuke.stapler.bind.JavaScriptMethod;
1521
import org.kohsuke.stapler.verb.POST;
1622
import hudson.model.AbstractProject;
1723
import hudson.model.Descriptor;
24+
import hudson.util.ComboBoxModel;
1825
import hudson.util.FormValidation;
26+
import hudson.util.ListBoxModel;
1927
import jenkins.model.Jenkins;
2028

2129
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.*;
@@ -89,14 +97,72 @@ public final class PluginArchitectureRules {
8997
methods().that().areDeclaredInClassesThat().areAssignableTo(Descriptor.class)
9098
.and().haveNameMatching("do[A-Z].*")
9199
.and().haveRawReturnType(FormValidation.class)
92-
.and().haveRawParameterTypes(new FormValidationSignaturePredicate())
100+
.and().haveRawParameterTypes(ofAllowedValidationMethodSignatures())
93101
.should().beAnnotatedWith(POST.class)
94102
.andShould().bePublic();
95103

104+
/**
105+
* List model methods that are used as AJAX end points must use @POST and have a permission check.
106+
*/
107+
public static final ArchRule USE_POST_FOR_LIST_AND_COMBOBOX_FILL =
108+
methods().that().areDeclaredInClassesThat().areAssignableTo(Descriptor.class)
109+
.and().haveNameMatching("doFill[A-Z].*")
110+
.and().haveRawReturnType(ofAllowedClasses(ComboBoxModel.class, ListBoxModel.class))
111+
.should().beAnnotatedWith(POST.class)
112+
.andShould().bePublic()
113+
.andShould(checkPermissions());
114+
115+
private static FormValidationSignaturePredicate ofAllowedValidationMethodSignatures() {
116+
return new FormValidationSignaturePredicate();
117+
}
118+
119+
private static HavePermissionCheck checkPermissions() {
120+
return new HavePermissionCheck();
121+
}
122+
123+
private static DescribedPredicate<JavaClass> ofAllowedClasses(final Class<?>... classes) {
124+
return new AllowedClasses(classes);
125+
}
126+
96127
private PluginArchitectureRules() {
97128
// prevents instantiation
98129
}
99130

131+
private static class HavePermissionCheck extends ArchCondition<JavaMethod> {
132+
HavePermissionCheck() {
133+
super("should have a permission check");
134+
}
135+
136+
@Override
137+
public void check(final JavaMethod item, final ConditionEvents events) {
138+
Set<JavaCall<?>> callsFromSelf = item.getCallsFromSelf();
139+
140+
if (callsFromSelf.stream().anyMatch(
141+
javaCall -> javaCall.getTarget().getOwner().getFullName().equals(JenkinsFacade.class.getName())
142+
&& "hasPermission".equals(javaCall.getTarget().getName()))) {
143+
return;
144+
}
145+
events.add(SimpleConditionEvent.violated(item,
146+
String.format("JenkinsFacade not called in %s in %s",
147+
item.getDescription(), item.getSourceCodeLocation())));
148+
}
149+
}
150+
151+
private static class AllowedClasses extends DescribedPredicate<JavaClass> {
152+
private final List<String> allowedClassNames;
153+
154+
AllowedClasses(final Class<?>... classes) {
155+
super("raw return type of any of %s", Arrays.toString(classes));
156+
157+
allowedClassNames = Arrays.stream(classes).map(Class::getName).collect(Collectors.toList());
158+
}
159+
160+
@Override
161+
public boolean apply(final JavaClass input) {
162+
return allowedClassNames.contains(input.getFullName());
163+
}
164+
}
165+
100166
private static class FormValidationSignaturePredicate extends DescribedPredicate<List<JavaClass>> {
101167
FormValidationSignaturePredicate() {
102168
super("do* method signatures that should be guarded by @POST");

0 commit comments

Comments
 (0)