Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SecurityContextHolder is not populated in @BeforeAll/PostConstruct within @WithUserDetails #15902

Open
bwgjoseph opened this issue Oct 12, 2024 · 0 comments
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug

Comments

@bwgjoseph
Copy link

Describe the bug

I'm not sure if this is the intended behavior where SecurityContextHolder is not populated or accessible within @BeforeAll/PostConstruct. I searched the repository and found #6591 is quite close to what I experience/encounter.

This is reproduced using Spring Boot 3.3.4 but I encountered it in my project which is using Spring Boot 3.2.5, with Java 21 and JUnit 5

To Reproduce

This is the code snippet to reproduce

@ExtendWith(SpringExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DemoBugTests {
    SoftAssertions softAssertions = new SoftAssertions();

    @PostConstruct
    void postConstruct() {
        // authentication object is null
        // SecurityContextImpl [Null authentication]
        softAssertions.assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); // this fail
    }

    @BeforeAll
    void beforeAll() {
        // authentication object is null
        // SecurityContextImpl [Null authentication]
        softAssertions.assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); // this fail
    }

    @BeforeEach
    void beforeEach() {
        // SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=bwgjoseph, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[admin]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[admin]]]
        softAssertions.assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); // this pass
    }

    @Test
    @WithUserDetails
    void validateAuthentication() {
        // SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=bwgjoseph, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[admin]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[admin]]]
        softAssertions.assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); // this pass
        softAssertions.assertAll();
    }

    @TestConfiguration
    static class SecurityConfiguration {
        @Bean
        UserDetailsService userDetailsService() {
            return new TestUserDetails();
        }
    }

    static class TestUserDetails implements UserDetailsService {

        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return new User("bwgjoseph", "pass", List.of(new SimpleGrantedAuthority("admin")));
        }
    }
}

Expected behavior

I would expect that the authentication object is available within @BeforeAll and PostConstruct method.

I'm not using @PostConstruct actually, but added in this test to see the behavior

Extra Note

I discovered this as my current test is failing where there's a method call to insert certain data in @BeforeAll which uses SpEL within the repository method

@Query(...#{principal}...)
Data findById(...)

So the stack trace looks like the following...

... SpelEvaluationException: EL1022E: A problem occurred whilst attempting to access the property 'principal': The function 'principal' mapped to an object of type 'class org.springframework.security.access.expression.SecurityExpressionRoot' cannot be invoked
// omitted
... SpelEvaluationException: EL1022E: The function 'principal' mapped to an object of type 'class org.springframework.security.access.expression.SecurityExpressionRoot' cannot be invoked

So after some tracing, I found out that it was because the authentication object was null when called in @BeforeAll, and thus, causing the test to fail.

I'm also using custom @WithSecurityContext to provide my own @WithMockXXXUser annotation if that matters.

Let me know if more information is required.

Thanks!

@bwgjoseph bwgjoseph added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Oct 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug
Projects
None yet
Development

No branches or pull requests

1 participant