Add tests for AuthorizeReturnObject
See gh-253
This commit is contained in:
committed by
Moritz Halbritter
parent
7bf63cd4ac
commit
8614b25139
@@ -0,0 +1,53 @@
|
||||
package com.example.security.method;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.smoketest.support.assertj.AssertableOutput;
|
||||
import org.springframework.aot.smoketest.support.junit.ApplicationTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ApplicationTest
|
||||
public class AuthorizedObjectTests {
|
||||
|
||||
@Test
|
||||
void anonymousCanOnlyCallPermittedMethods(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> assertThat(output)
|
||||
// @formatter:off
|
||||
.hasSingleLineContaining("testAuthorizedObject(): object.getUserProperty() correctly allowed as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): object.getAdminProperty() correctly allowed as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): object.getUserPropertyJsr250() correctly allowed as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): object.getUserPropertySecured() correctly allowed as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserProperty() correctly denied as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getAdminProperty() correctly denied as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertyJsr250() correctly denied as anonymous")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertySecured() correctly denied as anonymous"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void userCanOnlyCallPermittedMethods(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> assertThat(output)
|
||||
// @formatter:off
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserProperty() correctly allowed as user")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getAdminProperty() correctly denied as user")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertyJsr250() correctly allowed as user")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertySecured() correctly allowed as user"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminCanOnlyCallPermittedMethods(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> assertThat(output)
|
||||
// @formatter:off
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserProperty() correctly denied as admin")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getAdminProperty() correctly allowed as admin")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertyJsr250() correctly denied as admin")
|
||||
.hasSingleLineContaining("testAuthorizedObject(): authorized.getUserPropertySecured() correctly denied as admin"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.example.security.method;
|
||||
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
|
||||
public class AuthorizableObject {
|
||||
|
||||
private final String property = "value";
|
||||
|
||||
@HasRole("ADMIN")
|
||||
public String getAdminProperty() {
|
||||
return this.property;
|
||||
}
|
||||
|
||||
@HasRole("USER")
|
||||
public String getUserProperty() {
|
||||
return this.property;
|
||||
}
|
||||
|
||||
@RolesAllowed("USER")
|
||||
public String getUserPropertyJsr250() {
|
||||
return this.property;
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
public String getUserPropertySecured() {
|
||||
return this.property;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.example.security.method;
|
||||
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AuthorizedReturnValueService {
|
||||
|
||||
@AuthorizeReturnObject
|
||||
public AuthorizableObject getAuthorizedObject() {
|
||||
return new AuthorizableObject();
|
||||
}
|
||||
|
||||
public AuthorizableObject getObject() {
|
||||
return new AuthorizableObject();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,11 +17,14 @@
|
||||
package com.example.security.method;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.UserDetailsManager;
|
||||
@@ -38,15 +41,19 @@ public class CLR implements CommandLineRunner {
|
||||
|
||||
private final PostAuthorizeProtectedService postAuthorizeProtectedService;
|
||||
|
||||
private final AuthorizedReturnValueService authorizedReturnValueService;
|
||||
|
||||
private final UserDetailsManager userDetailsManager;
|
||||
|
||||
public CLR(PreAuthorizeProtectedService preAuthorizeProtectedService,
|
||||
SecuredProtectedService securedProtectedService, Jsr250ProtectedService jsr250ProtectedService,
|
||||
PostAuthorizeProtectedService postAuthorizeProtectedService, UserDetailsManager userDetailsManager) {
|
||||
PostAuthorizeProtectedService postAuthorizeProtectedService,
|
||||
AuthorizedReturnValueService authorizedReturnValueService, UserDetailsManager userDetailsManager) {
|
||||
this.preAuthorizeProtectedService = preAuthorizeProtectedService;
|
||||
this.securedProtectedService = securedProtectedService;
|
||||
this.jsr250ProtectedService = jsr250ProtectedService;
|
||||
this.postAuthorizeProtectedService = postAuthorizeProtectedService;
|
||||
this.authorizedReturnValueService = authorizedReturnValueService;
|
||||
this.userDetailsManager = userDetailsManager;
|
||||
}
|
||||
|
||||
@@ -66,35 +73,36 @@ public class CLR implements CommandLineRunner {
|
||||
testJsr250Admin();
|
||||
testJsr250PermitAll();
|
||||
testJsr250DenyAll();
|
||||
testAuthorizedObject();
|
||||
}
|
||||
|
||||
private void testAnonymous() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
this.preAuthorizeProtectedService.anonymous();
|
||||
System.out.println("testAnonymous(): preAuthorizeProtectedService.anonymous() worked as anonymous");
|
||||
}
|
||||
|
||||
private void testPostAuthorizeAnonymous() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
this.postAuthorizeProtectedService.anonymous();
|
||||
System.out
|
||||
.println("testPostAuthorizeAnonymous(): postAuthorizeProtectedService.anonymous() worked as anonymous");
|
||||
}
|
||||
|
||||
private void testSecuredAnonymous() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
this.securedProtectedService.anonymous();
|
||||
System.out.println("testSecuredAnonymous(): securedProtectedService.anonymous() worked as anonymous");
|
||||
}
|
||||
|
||||
private void testJsr250Anonymous() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
this.jsr250ProtectedService.anonymous();
|
||||
System.out.println("testJsr250Anonymous(): jsr250ProtectedService.anonymous() worked as anonymous");
|
||||
}
|
||||
|
||||
private void testUser() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.preAuthorizeProtectedService.user();
|
||||
throw new IllegalStateException("testUser(): preAuthorizeProtectedService.user() worked as anonymous");
|
||||
@@ -109,7 +117,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testPostAuthorizeUser() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.postAuthorizeProtectedService.user();
|
||||
throw new IllegalStateException(
|
||||
@@ -125,7 +133,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testSecuredUser() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.securedProtectedService.user();
|
||||
throw new IllegalStateException("testSecuredUser(): securedProtectedService.user() worked as anonymous");
|
||||
@@ -140,7 +148,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testJsr250User() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.jsr250ProtectedService.user();
|
||||
throw new IllegalStateException("testJsr250User(): jsr250ProtectedService.user() worked as anonymous");
|
||||
@@ -155,7 +163,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testAdmin() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.preAuthorizeProtectedService.admin();
|
||||
throw new IllegalStateException("testAdmin(): preAuthorizeProtectedService.admin() worked as anonymous");
|
||||
@@ -179,7 +187,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testPostAuthorizeAdmin() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.postAuthorizeProtectedService.admin();
|
||||
throw new IllegalStateException(
|
||||
@@ -205,7 +213,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testSecuredAdmin() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.securedProtectedService.admin();
|
||||
throw new IllegalStateException("testSecuredAdmin(): securedProtectedService.admin() worked as anonymous");
|
||||
@@ -229,7 +237,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testJsr250Admin() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.jsr250ProtectedService.admin();
|
||||
throw new IllegalStateException("testJsr250Admin(): jsr250ProtectedService.admin() worked as anonymous");
|
||||
@@ -253,7 +261,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testJsr250DenyAll() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
try {
|
||||
this.jsr250ProtectedService.denyAll();
|
||||
throw new IllegalStateException(
|
||||
@@ -283,7 +291,7 @@ public class CLR implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void testJsr250PermitAll() {
|
||||
impersonateAnonymous();
|
||||
impersonateNoAuthentication();
|
||||
this.jsr250ProtectedService.permitAll();
|
||||
System.out.println("testJsr250PermitAll(): jsr250ProtectedService.permitAll() worked as anonymous");
|
||||
|
||||
@@ -296,10 +304,53 @@ public class CLR implements CommandLineRunner {
|
||||
System.out.println("testJsr250PermitAll(): jsr250ProtectedService.permitAll() worked as admin");
|
||||
}
|
||||
|
||||
private void impersonateAnonymous() {
|
||||
private void testAuthorizedObject() {
|
||||
AuthorizableObject object = this.authorizedReturnValueService.getObject();
|
||||
AuthorizableObject authorized = this.authorizedReturnValueService.getAuthorizedObject();
|
||||
|
||||
impersonateAnonymous();
|
||||
expectAllow(object::getAdminProperty).report("testAuthorizedObject", "object.getAdminProperty", "anonymous");
|
||||
expectAllow(object::getUserProperty).report("testAuthorizedObject", "object.getUserProperty", "anonymous");
|
||||
expectAllow(object::getUserPropertyJsr250).report("testAuthorizedObject", "object.getUserPropertyJsr250",
|
||||
"anonymous");
|
||||
expectAllow(object::getUserPropertySecured).report("testAuthorizedObject", "object.getUserPropertySecured",
|
||||
"anonymous");
|
||||
expectDeny(authorized::getAdminProperty).report("testAuthorizedObject", "authorized.getAdminProperty",
|
||||
"anonymous");
|
||||
expectDeny(authorized::getUserProperty).report("testAuthorizedObject", "authorized.getUserProperty",
|
||||
"anonymous");
|
||||
expectDeny(authorized::getUserPropertyJsr250).report("testAuthorizedObject", "authorized.getUserPropertyJsr250",
|
||||
"anonymous");
|
||||
expectDeny(authorized::getUserPropertySecured).report("testAuthorizedObject",
|
||||
"authorized.getUserPropertySecured", "anonymous");
|
||||
|
||||
impersonateUser();
|
||||
expectDeny(authorized::getAdminProperty).report("testAuthorizedObject", "authorized.getAdminProperty", "user");
|
||||
expectAllow(authorized::getUserProperty).report("testAuthorizedObject", "authorized.getUserProperty", "user");
|
||||
expectAllow(authorized::getUserPropertyJsr250).report("testAuthorizedObject",
|
||||
"authorized.getUserPropertyJsr250", "user");
|
||||
expectAllow(authorized::getUserPropertySecured).report("testAuthorizedObject",
|
||||
"authorized.getUserPropertySecured", "user");
|
||||
|
||||
impersonateAdmin();
|
||||
expectAllow(authorized::getAdminProperty).report("testAuthorizedObject", "authorized.getAdminProperty",
|
||||
"admin");
|
||||
expectDeny(authorized::getUserProperty).report("testAuthorizedObject", "authorized.getUserProperty", "admin");
|
||||
expectDeny(authorized::getUserPropertyJsr250).report("testAuthorizedObject", "authorized.getUserPropertyJsr250",
|
||||
"admin");
|
||||
expectDeny(authorized::getUserPropertySecured).report("testAuthorizedObject",
|
||||
"authorized.getUserPropertySecured", "admin");
|
||||
}
|
||||
|
||||
private void impersonateNoAuthentication() {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
}
|
||||
|
||||
private void impersonateAnonymous() {
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(new TestingAuthenticationToken("principal", "credentials"));
|
||||
}
|
||||
|
||||
private void impersonateUser() {
|
||||
UserDetails user = this.userDetailsManager.loadUserByUsername("user");
|
||||
SecurityContextHolder.getContext()
|
||||
@@ -314,4 +365,30 @@ public class CLR implements CommandLineRunner {
|
||||
new TestingAuthenticationToken(user, user.getPassword(), List.copyOf(user.getAuthorities())));
|
||||
}
|
||||
|
||||
private interface ResultReporter {
|
||||
|
||||
void report(String testMethod, String method, String role);
|
||||
|
||||
}
|
||||
|
||||
private ResultReporter expectAllow(Supplier<?> test) {
|
||||
try {
|
||||
test.get();
|
||||
return (t, m, r) -> System.out.printf("%s(): %s() correctly allowed as %s%n", t, m, r);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
return (t, m, r) -> System.out.printf("%s(): %s() incorrectly denied as %s%n", t, m, r);
|
||||
}
|
||||
}
|
||||
|
||||
private ResultReporter expectDeny(Supplier<?> test) {
|
||||
try {
|
||||
test.get();
|
||||
return (t, m, r) -> System.out.printf("%s(): %s() incorrectly allowed as %s%n", t, m, r);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
return (t, m, r) -> System.out.printf("%s(): %s() correctly denied as %s%n", t, m, r);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.security.method;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PreAuthorize("hasRole('{value}')")
|
||||
public @interface HasRole {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.example.security.method;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
@@ -28,6 +29,11 @@ import org.springframework.security.provisioning.UserDetailsManager;
|
||||
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
|
||||
class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
static AnnotationTemplateExpressionDefaults annotationTemplateExpressionDefaults() {
|
||||
return new AnnotationTemplateExpressionDefaults();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
UserDetailsManager userDetailsManager() {
|
||||
|
||||
Reference in New Issue
Block a user