Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mocking authentication spring security

I have a respository method annotated with @Secured. I am trying to write a unit test for this method, but my test fails because I need authentication to call the method. The method itself, happens to be the save() method. The error I get when I call the method is:

 org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext

I cannot test this method because it requires authentication, and I cannot save a user to authenticate against (I am using hsqldb) because I would need to call this method to save. Any advice on how to unit test a method annotated with @secured or how to mock the authentication.

like image 920
vikash dat Avatar asked Jan 20 '12 19:01

vikash dat


3 Answers

It depends on what you want to test.

  • If you want to test business logic of your application without any security stuff, you can disable security altogether. Assuming that you use SpringJunit4Runner, you can put security configuration into separate file and don't include it into @ContextConfiguration.

  • If you want to test authorization (e.g. correct work of @Secured annotations), you can access SecurityContext directly, bypassing all authentication-related stuff (in this case you can put authentication configuration into separate file and don't load it as well):

    All you need is to put an appropriate Authentication object into SecurityContextHolder:

    @Test
    public void myTest() {
        login(...);
        ...
        logout();
    }
    
    private void login(...) {
        SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken(...)
        )
    }
    
    private void logout() {
        SecurityContextHolder.clearContext();
    }
    
  • Finally, if you want to test authentication, you can run the test with the database containing test data.

like image 84
axtavt Avatar answered Nov 13 '22 16:11

axtavt


a) This is not a unit test, it's an integration test. A unit test would be no problem.

b) I'd try to keep your test setups separated. Why not just deactive spring security in tests where you are not testing security-related features?

c) If all else fails: inject a JdbcTemplate object and create the user data manulally in SQL during test setup. See Support classes for integration testing

like image 20
Sean Patrick Floyd Avatar answered Nov 13 '22 15:11

Sean Patrick Floyd


I'd consider using an annotation such as @WithMockUser to populate test security context with "personas" (test user with specific authorities like 'admin', 'salesman', etc.).

If @Secured is on the repo, it must be because specs were stating something like "user must have this grant to query that", and so security should be unit tested too.

If UsernameAuthenticationToken (what @WithMockUser creates) doesn't meet your needs, then you might choose an other annotation, like this one I wrote, or even write one of your own creating the exact Authentication implementation you want.

Libs here, sample result extracted from there

@RunWith(SpringRunner.class)
@Import(JwtAuthenticationTokenMessageServiceTests.TestConfig.class)
public class JwtAuthenticationTokenMessageServiceTests {

    @Autowired
    private MessageService<JwtAuthenticationToken> messageService;

    @Test
    @WithMockAuthentication(authType = JwtAuthenticationToken.class, authorities = "ROLE_AUTHORIZED_PERSONNEL")
    public void secretWithScopeAuthorizedPersonnelAuthority() {
        assertThat(messageService.getSecret()).isEqualTo("Secret message");
    }

    @TestConfiguration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @Import({ JwtTestConf.class, JwtAuthenticationTokenServletApp.JwtAuthenticationTokenMessageService.class })
    public static class TestConfig {
    }
like image 1
ch4mp Avatar answered Nov 13 '22 17:11

ch4mp