Unit Testing Spring Data JPA Repositories with Spring Security - spring

I have a Spring Data JPA repository, and the unit tests work fine, as long as the Spring Security dependency (spring-boot-starter-security) is not added, with corresponding method authorization annotations on the repository. Once added I get an AuthenticationCredentialsNotFound exception when running the unit test.
How do I "Authenticate" calls to repository methods in the unit test?

I found an example of what to do here:
https://github.com/spring-projects/spring-data-examples/blob/master/jpa/security/src/test/java/example/springdata/jpa/security/SecurityIntegrationTests.java
My tests run fine now as long as prior to calling methods on the repository, I first authenticate the user that has the required role like this:
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(
"admin",
"password",
Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"))));
// Make calls to repository methods that require the admin role

Also in case you need to authenticate set more than one role (Courtesy of Rob Winch - Spring Security Lead):
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(
ADMIN,
PASSWORD,
AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN")
));

Related

How to mock JwtDecoder in Spring Boot for integration testing of authenticated controllers? [duplicate]

This question already has answers here:
How do I test main app without spring security?
(2 answers)
Closed 4 days ago.
In my Spring Boot application, there are some authenticated controllers.
The operation mode is "OAuth2 resource server", so my application relies on some arbitrary OAuth2 authorization server. (Let's say it's Keycloak, though it should not affect the way of mocking)
So, the question is:
What is the right way to mock JwtDecoder, in order to be able to pass some static strings as the bearer tokens?
(Please remember, it's a third party server responsible for the token issuing; So I cannot rely on it in tests. I want to mock it away to be able to run tests offline for example)
An example of what I expect to happen:
I mock JwtDecoder (let's pretend I've created some map of <token string, UserData>)
I make a MockMvc-based http call to the authenticated controller with this static string in the Authorization header (Authorization: Bearer STATIC_STRING). The controller test is decorated with #SpringBootTest and #AutoConfigureMockMvc.
I expect to have JwtAuthenticationToken filled with data from UserData from the map of the mocked JwtDecoder.
I've already tried to just create a bean of JwtDecoder implementation with all the described features. I see this bean is added to the configuration, but still whole test ends up in AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext. So I presume that JwtDecoder is never called (checked that with the debugger), and the whole testing setup is misconfigured, but I don't know for sure what to change.
What do I miss?
With MockMvc (as well as WebTestClient in reactive apps) the Authorization header is just ignored (not decoded, validated, introspected or whatever).
The test security context is to be set directly (SecurityContextHolder.createEmptyContext().setAuthentication(auth);) or with the help of either:
test annotations (like #WithMockUser, but this one is not quite adapted to mocking OAuth2 identities). Refer to those that I created in this lib for OAuth2
Request post-processors for MockMvc
mutators for WebTestClients
More details in this other answers:
How to write unit test for SecurityConfig for spring security
How do I test main app without spring security?

Integration test for API that has #Secured

I have a spring REST API that is secured by spring security and Keycloak, and I am using keycloak testcontainers for the integration test. I can add a user to keycloak and get a token to use while testing most of the APIs.
The question is, If I have an API that is annotated with #Secured to limit its access to only users with a specific role, how to assign that role to the created user or mock this role to run the integration test
You may simply want to add the role to the user, e.g. like this:
RoleRepresentation role = new RoleRepresentation("rolename", "role desc", false);
KeycloakContainer container = new KeycloakContainer();
RealmResource realm = container.getKeycloakAdminClient().realm("realm");
realm.roles().create(role);
realm
.users()
.get("user")
.roles()
.realmLevel()
.add(List.of(role));

Why is the JPA repository called from spring schedular not able to get the authentication from Security Context

I have a springboot application where with authentication available in SecurityContext post login. Any call from Rest Controller to persist any entity, getCurrentAuditor() method is called which returns the current principle which is used for auto updating the created date column.
I created an schedular using spring "awaitility" dependency. However, this schedular calls an update on a entity. When update is called and spring authentication is checked, it comes as null, even though i have logged in from front end. From front end i am able to persist other entities and gets the authentication object as well.
As per my understanding, this might be happening because the schedular starts as soon as Springboot kicks in and making save request independently. If that understanding is correct, how should i resolve this?
If the Scheduler can use a "system" user for update the entity, you can do something like the following and in the scheduler code perform the authentication:
public void authenticate() {
Authentication auth = authenticationManager.authenticate(getBatch());
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
}
public UsernamePasswordAuthenticationToken getBatch() {
return UsernamePasswordAuthenticationTokenBuilder.anUsernamePasswordAuthenticationToken()
.withCredentials(batchProperties.getPassword()).withUserCode(batchProperties.getUser()).withUserDto(
userDtoFactory.getBatch()).build();
}

How to test REST in spring app with spring security

I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...

How to make a non web thread run with the Spring Security Anonymous Use?

I am using Spring Security 3.1 and I have some code which I execute on web application strartup from the init method of a Servlet. Problems is that there is no valid Authentication object at the time my servlet init method execute. My servlet is configured to run after the Spring has been initialized and spring security is full configured.
How do I make the code in my init method run as the anonymous user in spring security?
How about:
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken(key, login, authorities))
where any non-empty string should do as a key, login like "anonymous", authorities - whichever you want him to have, at least one.

Resources