I know that there are already similar questions, here and here, regarding this problem but every solution proposed failed to help me. There is also mention to this library in most of those answers but (with all due respect) I would like to avoid depending on an external library just to be able to test a simple controller.
So, I have a very simple api that is accessed using a bearer token generated by keycloak and I would like to test the controller. Something along these lines:
#Test
#DisplayName("Should be ok")
#WithMockUser
void whenCalled_shouldBeOk() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
mockMvc.perform(
post("/api/url/something")
.content("{}")
.contentType(APPLICATION_JSON)
.with(authentication(authentication))
).andExpect(status().isOk());
}
The problem is that I will always get a null pointer exception thrown by the KeycloakDeploymentBuilder for it's missing the adapter config. In our SecurityConfig we extend the KeycloakWebSecurityConfigurerAdapter and do all the required configurations for the app to work but I am failing to mock/by-pass this process in the test. Normally I find my way around this authentication problems in the tests (when keycloak isn't used) with #WithMockUser annotation but not this time.
Isn't there way to mock the adapter or the filter process in order to by-pass this issue?
I have tried everything that was answered in the other questions (except the library) with no luck. If you have any clue that could be of help or at least point me in the correct direction (since this can be due to a lack of knowledge on spring security from my part) that would very much appreciated.
2023 update
The deprecated Keycloak adapters for Spring (where KeycloakAuthenticationToken is defined) are not compatible with spring-boot 3. Alternatives in the accepted answer to "Use Keycloak Spring Adapter with Spring Boot 3"
Original answer
As I already wrote in my answer to the first question you linked, #WithMockUser populates the security context with a UsernamePaswordAuthenticationToken when your code / conf probably expect a KeycloakAuthenticationToken.
If you read carefully the same answer, you'll find an alternative to using my lib to do this: manually populate the security-context with a KeycloakAuthenticationToken instance or mock in each test.
Minimal sample with Mockito I added to my repo:
#Test
public void test() {
final var principal = mock(Principal.class);
when(principal.getName()).thenReturn("user");
final var account = mock(OidcKeycloakAccount.class);
when(account.getRoles()).thenReturn(Set.of("offline_access", "uma_authorization"));
when(account.getPrincipal()).thenReturn(principal);
final var authentication = mock(KeycloakAuthenticationToken.class);
when(authentication.getAccount()).thenReturn(account);
// post(...).with(authentication(authentication))
// limits to testing secured #Controller with MockMvc
// I prefer to set security context directly instead:
SecurityContextHolder.getContext().setAuthentication(authentication);
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Maybe, after you mesure how much this clutters your tests (there are very few claims set here) you'll reconsider using my lib (or copying from it as it's opensource).
With my annotation, above sample becomes:
#Test
#WithMockKeycloakAuth
public void test() throws Exception {
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Regarding spring test config with Keycloak involved, you could dig a bit into the tests of spring-addons-keycloak module. You'd find a complete app using KeycloakAuthenticationToken with unit tests (and working test conf).
Last (and maybe off-topic) you could read the repo main README and consider using a more generic OIDC implementation than Keycloak's one. I provide one, along with test annotation, and wrote tutorials on how to extend it to your app specific needs.
Related
I am trying to write a few tests for my Spring controller. The endpoints are secured with Keycloak (open id connect).
I tried mocking an authenticated user using the #WithMockUser annotation but I need to retrieve claims from the token (preferred_username) and I end up getting a null pointer exception from here:
return Long.parseLong(((KeycloakPrincipal) authentication.getPrincipal()).getKeycloakSecurityContext().getToken().getPreferredUsername());
Is there any way to mock the Keycloak token? I came across this similar question but I do not want to use the suggested external library.
Thank you guys in advance, any help would be greatly appreciated as I have been stuck on this problem for a while.
I came across this similar question but I do not want to use the suggested external library.
Well, you'd better reconsider that.
Are you using the deprecated Keycloak adapters?
If yes, and if you still don't want to use spring-addons-keycloak, you'll have to manualy populate test security context with a KeycloakAuthenticationToken instance or mock:
#Test
public void test() {
final var principal = mock(Principal.class);
when(principal.getName()).thenReturn("user");
final var account = mock(OidcKeycloakAccount.class);
when(account.getRoles()).thenReturn(Set.of("offline_access", "uma_authorization"));
when(account.getPrincipal()).thenReturn(principal);
final var authentication = mock(KeycloakAuthenticationToken.class);
when(authentication.getAccount()).thenReturn(account);
// post(...).with(authentication(authentication))
// limits to testing secured #Controller with MockMvc
// I prefer to set security context directly instead:
SecurityContextHolder.getContext().setAuthentication(authentication);
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
You'll soon understand why this #WithMockKeycloakAuth was created.
If you already migrated to something else than Keycloak adapters, solution with manualy setting test-security context still applies, just adapt the Authentication instance. If your authentication type is JwtAuthenticationToken, you can use either:
jwt() request post processor for MockMvc I wrote (it is available from spring-security-test)
#Test
void testWithPostProcessor() throws Exception {
mockMvc.perform(get("/greet").with(jwt().jwt(jwt -> {
jwt.claim("preferred_username", "Tonton Pirate");
}).authorities(List.of(new SimpleGrantedAuthority("NICE_GUY"), new SimpleGrantedAuthority("AUTHOR")))))
.andExpect(status().isOk())
.andExpect(content().string("Hi Tonton Pirate! You are granted with: [NICE_GUY, AUTHOR]."));
}
#WithMockJwtAuth, same author, different lib
#Test
#WithMockJwtAuth(authorities = { "NICE_GUY", "AUTHOR" }, claims = #OpenIdClaims(preferredUsername = "Tonton Pirate"))
void testWithPostProcessor() throws Exception {
mockMvc.perform(get("/greet"))
.andExpect(status().isOk())
.andExpect(content().string("Hi Tonton Pirate! You are granted with: [NICE_GUY, AUTHOR]."));
}
Note that only second option will work if you want to unit-test a secured #Component that is not a #Controller (a #Service or #Repository for instance).
My two cent advices:
drop Keycloak adapters now: it will disapear soon, is not adapted to boot 2.7+ (web-security config should not extend WebSecurityConfigurerAdapter any more) and is way too adherent to Keycloak. Just have a look at this tutorial to see how easy it can be to configure and unit-test a JWT resource-server (with identities issued by Keycloak or any other OIDC authorization-server)
if your team does not let you abandon Keycloak adapters yet, use #WithMockKeycloakAuth, you'll save tones of time and your test code will be way more readable.
I have looked at a few tutorials, and am not quite sure how to proceed with writing a test case for my controller method using JUnit 5. I have read up on TestRestTemplate and Mock functions, but still feel at a loss as to how to begin. I am using a MySQL database, the application contains two classes Product and Feedback, it has a OneToMany relationship, with the owning entity being Feedback.
How do I write a test for the method below?
#PostMapping("/view/{id}")
public ModelAndView addReview(#PathVariable("id") int id, #RequestParam("review") String review, HttpServletRequest request) {
Product product = dao.findById(id).get();
Feedback feedback = new Feedback(review);
product.getFeedbacks().add(feedback);
feedback.setProduct(product);
dao.save(product);
List<Feedback> fs = product.getFeedbacks();
Collections.sort(fs, new FeedbackComparator());
HttpSession session = request.getSession();
session.setAttribute("fs", fs);
return new ModelAndView("/view").addObject("product", product);
}
There are multiple ways to write a test for your Spring MVC controller endpoints:
Use #WebMvcTest and MockMvc to test only your web-layer in isolation.
For such tests, you usually mock all collaborators (in your case your dao) of your controller. Using MockMvc you can then fire requests against a mocked Servlet environment that Spring Boot creates for you. This way you can ensure e.g. your controller endpoints are properly protected (by Spring Security), your path variables and query parameters are mapped as expected, you're returning the correct HTTP response and headers, your Model contains the default attributes, etc.
You can find further information on how to write tests with MockMvc as part of this guide.
Use #SpringBootTest to populate your whole application context, start the real Servlet container (e.g. Tomcat) and invoke your endpoints with the TestRestTemplate or the WebTestClient over HTTP.
This setup requires all your external infrastructure components (database, messaging queues, external systems, etc.) to be available during test execution. This usually takes quite some time and such tests are not as fast as the first variant. Testcontainers can help you a lot here. This way you can ensure the whole use case is working as expected and write integration tests for your application.
So as a general recommendation you should have both kinds of tests and at least ensure your important happy-paths are working as expected with an integration test. For more low-level checks for your controller, the #WebMvcTest annotation comes quite handy.
I'm recently working with microservices, developed as Spring Boot applications (v 2.2) and in my company we're using Keycloak as authorization server.
We chose it because we need complex policies, roles and groups, and we also need the User Managed Authorization (UMA) to share resources between users.
We configured Keycloak with a single realm and many clients (one client per microservice).
Now, I understand that I need to explicitly define Resources within Keycloak and this is fine, but the question is: do I really need to duplicate all of them in my microservice's property file?
All the documentation, examples and tutorials end up with the same thing, that is something like:
keycloak.policy-enforcer-config.enforcement-mode=PERMISSIVE
keycloak.policy-enforcer-config.paths[0].name=Car Resource
keycloak.policy-enforcer-config.paths[0].path=/cars/create
keycloak.policy-enforcer-config.paths[0].scopes[0]=car:create
keycloak.policy-enforcer-config.paths[1].path=/cars/{id}
keycloak.policy-enforcer-config.paths[1].methods[0].method=GET
keycloak.policy-enforcer-config.paths[1].methods[0].scopes[0]=car:view-detail
keycloak.policy-enforcer-config.paths[1].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[1].methods[1].scopes[0]=car:delete
(this second example fits better our case because it also uses different authorization scopes per http method).
In real life each microservice we're developing has dozens of endpoints and define them one by one seems to me a waste of time and a weakness in the code's robustness: we change an endpoint, we need to reconfigure it in both Keycloak and the application properties.
Is there a way to use some kind of annotation at Controller level? Something like the following pseudo-code:
#RestController
#RequestMapping("/foo")
public class MyController {
#GetMapping
#KeycloakPolicy(scope = "foo:view")
public ResponseEntity<String> foo() {
...
}
#PostMapping
#KeycloakPolicy(scope = "bar:create")
public ResponseEntity<String> bar() {
...
}
}
In the end, I developed my own project that provides auto-configuration capabilities to a spring-boot project that needs to work as a resource server.
The project is released under MIT2 license and it's available on my github:
keycloak-resource-autoconf
I have recently started out with Spring and am unsure about how to approach this issue. I have a Spring boot program which makes calls to remote REST APIs. For example an AddressService class with getAddress(String user) method, which makes a HTTP call and returns a JSON response. I would like to set up Spring profiles for development purposes local, dev, uat, prod.
When the program is running with the local profile, I would like to "mock" these external API calls with an expected JSON response, so I can just test logic, but when it is run in any of the other profiles I would like to make the actual calls. How can I go about doing this? From what I read, there's many ways people approach this, using WireMock, RestTemplate, Mockito etc. I'm confused about which is the way to go.
Any advice would be greatly appreciated. Thanks.
WireMock,Mockit is for unittest, to mock the real request. Example here:
How do I mock a REST template exchange?
When you need a running implementation with a mock, i think the easiest way is that you have a interface
public interface AdressAdapter {
public List<Adress> getAddress(String name);
}
And two different implementations depending on the profile.
#Profile("local")
public class DummyAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Mock here something
return null;
}
}
! means NOT locale profile in this case.
#Profile("!local")
public class RealAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Make Restcall
return null;
}
}
What you could do is use different application.properties files depending on your profile. That way, you just change the url to a mock server for your local profile.
So what you have to do is :
Create another application.properties in your resources folder named : application-local.properties.
Change the url of the desired service.
Start your application with the VM option -Dspring.profiles.active=local.
Here is a link that describe well what you want to achieve.
For your mock server, you could use Wiremock, Mountebank, Postman,... that can be start separately and mock specific endpoints to return what you want.
I was developing the RESTful web service with springmvc4 and spring data jpa.Well, I have about 100+ apis for frontend to pull data.What I am want to do is how to test all of my apis automatically with random data.
The apis look like:
#RestController
#Api(tags = "index")
#RequestMapping("/index")
public class IndexController {
#Autowired
private IndexService indexService;
#RequestMapping(value = "/data", method = RequestMethod.GET)
#ApiOperation(value="today's data",notes="today's data",consumes="application/json",produces="application/json")
public Object getTodayData() {
return indexService.getTodayData();
}
#RequestMapping(value = "/chartData", method = RequestMethod.GET)
#ApiOperation(value="charts data",notes="charts data",consumes="application/json",produces="application/json")
public Object getLast7Data() {
return indexService.getLast7Data();
}
}
if I test it with postman one by one,it was waste a lot of time.When we developping,we should make sure the service is ok by ourselves.
I have got a solution but which is not satisfied me well.
here are my solution:
Scaned the controller of the specified package,then use reflection
get the annotation of the class,which could get the value of
#RequestMapping("/index").
Iterate through the method of the class and get the method's
annotation the same way,and get the full url.
Create random data for request, execute request and log the response.
Could anyone provide a solution for this, very appreciate for your help.
I see that you are using swagger in your api, you can use it to generate client code https://github.com/swagger-api/swagger-codegen for automatic testing.
Since you are using the Spring framework, you can try the following :
Use Spring Integration Test for testing the API. It spawns an
instance of your service and tests against it.
Use RestAssured & JUnit to hit the API and assert the response.
Use RequestMappingHandlerMapping.getHandlerMethods(), which you can simply get with Spring injection, e.g. via #Autowired. This will give you a map RequestMappingInfo->HandlerMethod, which contains all the information you need.
You can run the tests as regular JUnit tests, without the need for postman etc. using Spring integration testing support:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(name = "root", locations = "classpath:applicationContext.xml"),
#ContextConfiguration(name = "web", locations = "classpath:xxx-servlet.xml)
})
public class YourTest extends AbstractTransactionalJUnit4SpringContextTests {...}
In this test, use #Autowired WebApplicationContext and pass it to MockMvcBuilders.webAppContextSetup(webApplicationContext) to create a MockMvc instance. It allows to submit HTTP request to the Spring's MockMvc infrastructure via an easy interface.
Note that Spring's MockMvc framework will not run any real app server such as Tomcat. But this might be exactly what you need, since it is much faster. By default, Spring integration testing framework will only initialize your Spring application context once for all the tests with the same Spring configuration (use #DirtiesContext on a test class or method to signal that a new Spring app context is required after a specific test).
If you feel you need to run an actual app server such as Tomcat within your tests, check maven plugins such as tomcat7-maven-plugin.