method-level security-control with some open endpoints (e.g. /api/ping) (#191)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/191
This commit is contained in:
@@ -20,6 +20,7 @@ import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||
import net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@@ -369,6 +370,32 @@ public class ArchitectureTest {
|
||||
.beAnnotatedWith(SecurityRequirement.class).orShould()
|
||||
.beAnnotatedWith(NoSecurityRequirement.class);
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
static final ArchRule everyRestControllerShouldRequireAuthentication =
|
||||
classes()
|
||||
.that().areAnnotatedWith(RestController.class)
|
||||
.should(havePreAuthorizeWithValue("isAuthenticated()"))
|
||||
.because("Every REST controller should require authentication by default, use @PreAuthorize(...) to override this at the endpoint method level.");
|
||||
|
||||
private static ArchCondition<JavaClass> havePreAuthorizeWithValue(String expectedValue) {
|
||||
return new ArchCondition<>("have @PreAuthorize(\"" + expectedValue + "\")") {
|
||||
@Override
|
||||
public void check(JavaClass javaClass, ConditionEvents events) {
|
||||
boolean satisfied = javaClass.tryGetAnnotationOfType(PreAuthorize.class)
|
||||
.map(annotation -> expectedValue.equals(annotation.value()))
|
||||
.orElse(false);
|
||||
|
||||
String message = javaClass.getDescription() +
|
||||
(satisfied ? " has @PreAuthorize(\"" + expectedValue + "\")"
|
||||
: " does not have @PreAuthorize(\"" + expectedValue + "\")");
|
||||
|
||||
events.add(new SimpleConditionEvent(javaClass, satisfied, message));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
static final ArchRule restControllerMethods = classes()
|
||||
@@ -398,7 +425,6 @@ public class ArchitectureTest {
|
||||
.should(haveTableNameEndingWith_rv())
|
||||
.because("it's required that the table names of RBAC entities end with '_rv'");
|
||||
|
||||
|
||||
private static DescribedPredicate<JavaMethod> hasStaticMethodNamed(final String expectedName) {
|
||||
return new DescribedPredicate<>("rbac entity") {
|
||||
@Override
|
||||
|
||||
+21
-4
@@ -113,20 +113,37 @@ class WebSecurityConfigIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessToApiWithoutTokenShouldBeDenied() {
|
||||
void accessToPingApiWithoutTokenShouldBePermitted() {
|
||||
final var result = this.restTemplate.getForEntity(
|
||||
"http://localhost:" + this.serverPort + "/api/ping", String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessToApiWithInvalidTokenShouldBeDenied() {
|
||||
void accessToPongApiWithValidTokenShouldBePermitted() {
|
||||
// given
|
||||
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||
|
||||
// when
|
||||
final var result = restTemplate.exchange(
|
||||
"http://localhost:" + this.serverPort + "/api/ping",
|
||||
"http://localhost:" + this.serverPort + "/api/pong",
|
||||
HttpMethod.GET,
|
||||
httpHeaders(entry("Authorization", "Bearer ST-fake-cas-ticket")),
|
||||
String.class
|
||||
);
|
||||
|
||||
// then
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessToPongApiWithInvalidTokenShouldBeDenied() {
|
||||
// given
|
||||
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||
|
||||
// when
|
||||
final var result = restTemplate.exchange(
|
||||
"http://localhost:" + this.serverPort + "/api/pong",
|
||||
HttpMethod.GET,
|
||||
httpHeaders(entry("Authorization", "Bearer ST-WRONG-cas-ticket")),
|
||||
String.class
|
||||
|
||||
Reference in New Issue
Block a user