Reputation: 281
I need to make sure that classes annotated by @Path
only call methods in classes annotated by @Secured
. But only method-calls on fields (e.g. injected beans) should be checked. What I have so far is:
ArchRuleDefinition.classes().that()
.areAnnotatedWith(Path.class)
.or().areMetaAnnotatedWith(Path.class)
.should().onlyCallMethodsThat(are(declaredIn(!!!))
So 2 parts are missing:
declaredIn
to check that the methods should be declared in a class that has @Secured
-annotationIs there a way to achieve this?
Upvotes: 0
Views: 1679
Reputation: 3142
There's a simple solution for the second part: With
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.properties.HasOwner.Functions.Get;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
you can use
classes()
.that().areMetaAnnotatedWith(Path.class)
.should().onlyCallMethodsThat(
Get.<JavaClass>owner().is(annotatedWith(Secured.class))
.as("are defined in classes annotated with @Secured")
);
Note that areMetaAnnotatedWith
also includes direct annoations since ArchUnit 0.17.0. If you wanted to test constructor calls as well, you could replace onlyCallMethodsThat
with onlyCallCodeUnitsThat
.
Unfortunately, I don't think that the first part can be solved with the current version ArchUnit 0.23.1: You can rewrite the above rule to test each JavaMethodCall
, which is more specific than the called JavaMethod
:
noClasses()
.that().areMetaAnnotatedWith(Path.class)
.should().callMethodWhere(describe("owner is not @Secured",
methodCall ->
!methodCall.getTargetOwner().isAnnotatedWith(Secured.class)
));
With JavaMethodCall#getOrigin()
, you can find the JavaCodeUnit
where the method call occured, but this does not include information about the object on which the method was called.
If I understood correctly, you want to distinguish the following cases:
@Secured
class SecuredOwner {
void method() {
}
}
class NotSecuredOwner {
void method() {
}
}
@Path
class ThisShouldBeOkay {
SecuredOwner securedOwner = new SecuredOwner();
void call_secured_method_on_field() {
securedOwner.method();
}
void call_not_secured_method_on_local_variable() {
new NotSecuredOwner().method();
}
}
Upvotes: 1