mocking authentication spring security - spring

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.

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.

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

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 {
}

Related

Spring authorization at package level; AOP?

we have a spring boot app with a java package that has spring controllers with endpoints for admin-like functionality. right now they all start with the same request mapping.
so one way i could do authorization of every endpoint in the package is by WebSecurityConfigurerAdapter implementation...
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
but i was thinking it would be nice to use AOP somehow to target the package of the admin controllers to PreAuthorize all controller methods in the package. just in case someone on the project creates a new controller in the proejct with a different request mapping it would automatically be projected. also, if we decided to PreAuthorize at the #Service level instead of the controller level then this way could be used as well.
so, is it possible to PreAuthorize at the package level with AOP or some other way?
Spring provides default AOP interceptor for #Secured and #PreAuthorized annotations, but it works only on class or method level, and AFAIK not intended to be expanded to package level.
To put it simply, Spring intercepts calls to certain methods or all class methods with these annotations and check whether SecurityContextHolder holds Authority object and whether its collection of GrantedAuthority matches any of the annotation's value field values. So, you can do the same thing using AOP, for example like this:
#Aspect
#Component
public class AdminServiceAOPAuthorization {
private static final List<String> ALLOWED_ROLES = List.of("ADMIN", "SUPER_ADMIN");
#Pointcut("within(com.example.service.admin.*)") // <- any method in any class of the package
public void adminServiceLayer() {}
#Before("adminServiceLayer()")
public void authorize(JoinPoint jp) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
// throw some exception depending on the logic
}
boolean authorized = authentication.getAuthorities().stream()
.anyMatch(ga -> ALLOWED_ROLES.contains(ga.getAuthority()));
if (!authorized) {
throw new AccessDeniedException("Access denied");
// log or whatever
}
}
}
For better performance I'd advise to use it at the service layer to let Spring create proxy using implemented interface (if you use them, of course) instead of proxying controller class.

Spring MockMvc not taking roles into account

I have API endpoints which require a user to hold a specific role. Therefore, in some of my tests I attempt to reach these endpoints and expect a 401 error, however I get 200. I am using MockMvc to perform the calls.
The following are some snippets of the controller class with one of the methods that I am testing:
#RestController
public class MyController {
#GetMapping("/getcurrentuser")
public User getCurrent() {
...code
}
}
The following is my test class (only showing the respective test method and variables):
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#ContextConfiguration(classes = MyController.class)
public class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testGetCurrentFailedDueToIncorrectRole() throws Exception {
mockMvc.perform(get("/api/getcurrentuser")
.with(user(USER_NAME).password(PASSWORD)))
.andExpect(status().isUnauthorized());
}
}
I have also have a spring security config class, however I'm not sure if it's being brought into context in this test (sorry I'm still fairly new to spring and unit testing). Inside this class I have the following line of code:
.antMatchers("/api/**").hasAnyRole("ADMIN", "READ_ONLY")
The test showed previously fails, as I said I get 200. Now at this point I think that I'm doing something wrong in the configuration of this test and that is why roles are not being accounted for. Or maybe I am confused on how the ".with" part works.
Any form of help would be appreciated.
If you are using Spring Boot, you might want to try using #SpringBootTest and #AutoConfigureMockMvc.
https://spring.io/guides/gs/testing-web/
Exact opposite problem (may be useful to go off of)

Spring: mocking security enhanced service

We'are imlementing part of our security at service layer, so I add #PreAuthorize annotation to some methods of MyService.
At MyServiceSecurityTest I want to test only security role-permission matrix, without any business logic. For that reason I have to mock MyService. the problem is that both Mockito and Spring security use CGLIB proxies, and my service is not enhanced with #PreAuthorize after Mockito.mock(MyService.class).
Is there any way to mock service and preserve #PreAuthorize logic?
Example:
#Service
public class MyService implements IMyService {
#Override
#PreAuthorize("hasAuthority('SYSOP')")
public void someMethod(ComplexDTO dto) {
// lots of logic and dependencies, require lots of stubbing.
}
}
In order to avoid initialization of all dependencies of MyService#someMethod and building ComplexDTO at MyServiceSecurityTest I want to mock MyServiceSecurityTest but preserve #PreAuthorize checks.
You need to do integration tests and not unit tests. In general, you do not see mock classes in integration tests, at least you would not mock the class you are testing, in this I case I guess its the MyService class.
Setting up integration tests involves reading up on, but the short example below should get you on the right path
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("myProfile")
public class MyServiceIT {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testMyService() {
logger.info("testMyService");
//user TestRestTemplate to call your service.
}
}
EDIT: In this integration test, Spring boots up normally. That means all the annotations for security are processed and all the beans it needs to create are created and properly injected. One thing you may have to control is the Spring profile.... that can be done with the #ActiveProfiles("myProfile") annotation, which I just added to the example.

Spring Security HttpSecurity Configuration Testing

I have a Spring Boot + Spring Security application that has severalantMatchers paths; some fullyAuthenticated(), some permitAll().
How to I write a test that verifies SecurityConfiguration has my endpoints under /api/** (and ultimately others) secured correctly?
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
//...
.antMatchers("/api/**").fullyAuthenticated()
}
}
Using spring-boot-1.5.2.RELEASE, spring-security-core-4.2.2-release.
Clarification1: I want to as-directly-as-possible test the SecurityConfiguration, as opposed to transitively testing via one of the /api/** endpoints, which may have their own #PreAuthorize security.
Clarification2: I would like something similar to this WebSecurityConfigurerAdapterTests.
Clarification3: I would like to #Autowire something at the Spring Security layer, ideally HttpSecurity, to test.
So you want to ensure that if someone changes .antMatchers("/api/**") to .antMatchers("/WRONG_PATH/**") then you have a test that will figure it out ?
The rules you define using HttpSecurity will end up configuring a FilterChainProxy with one or more SecurityFilterChain, each with a list of filters. Each filter, such as UsernamePasswordAuthenticationFilter
(used for form-based login), will have a RequestMatcher defined in the super class AbstractAuthenticationProcessingFilter. The problem is that RequestMatcher is an interface which currently have 12 different implementations, and this includes AndRequestMatcher and OrRequestMatcher, so the matching logic is not always simple. And most importantly RequestMatcher only has one method boolean matches(HttpServletRequest request), and the implementation often does not expose the configuration, so you will have to use reflection to access the private configurations of each RequestMatcher implementation (which could change in the future).
If you go down this path, and autowire FilterChainProxy into a test and use reflection to reverse-engineer the configuration, you have to consider all the implementation dependencies you have. For instance WebSecurityConfigurerAdapter has a default list of filters, which may change between releases, and unless disable it, and when it is disabled you have to define every filter explicitly. In addition new filters and RequestMatchers could be added over time, or the filter chain generated by HttpSecurity in one version of Spring Security may be slightly different in the next version (maybe not likely, but still possible).
Writing a generic test for your spring security configuration, is technically possible, but it is not exactly an easy thing to do, and the Spring Security filters certainly were not designed to support this. I have worked extensively with Spring Security since 2010, and I have never had the need for such a test, and personally I think it would be a waste of time trying to implement it. I think the time will be much better spent writing a test framework that makes it easy to write integration tests, which will implicitly test the security layer as well as the business logic.
I see below test case can help you achieve what you want. It is an Integration Test to test the Web Security configuration and we have similar testing done for all our code that is TDD driven.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class WebConfigIT {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Before
public void setup() throws Exception {
mockMvc = webAppContextSetup(webApplicationContext)
.addFilter(springSecurityFilterChain)
.build();
}
#Test
public void testAuthenticationAtAPIURI() throws Exception {
mockMvc.perform(get("/api/xyz"))
.andExpect(status.is3xxRedirection());
}
This though looks like doing an explicit testing of the end-point (which is anyways a testing one have to do if doing TDD) but this is also bringing the Spring Security Filter Chain in context to enable you test the Security Context for the APP.
MockMVC should be enough to verify you security configuration since the only thing it mocks is the Http layer. However if you really wish to test your Spring Boot application, Tomcat server and all, you need to use #SpringBootTest, like this
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class NoGoServiceTest {
#LocalServerPort
private int port;
private <T> T makeDepthRequest(NoGoRequest request, NoGoResponse response, String path, Class<T> responseClass) {
testService.addRequestResponseMapping(request, response);
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Bearer " + tokenProvider.getToken());
RequestEntity<NoGoRequest> requestEntity = new RequestEntity<>(request, headers, HttpMethod.POST, getURI(path));
ResponseEntity<T> responseEntity = template.exchange(requestEntity, responseClass);
return responseEntity.getBody();
}
#SneakyThrows(URISyntaxException.class)
private URI getURI(String path) {
return new URI("http://localhost:" +port + "/nogo" + path);
}
// Test that makes request using `makeDepthRequest`
}
This code is a part on a test taken from an open source project (https://github.com/maritime-web/NoGoService). The basic idea is to start the test on a random port, which Spring will then inject into a field on the test. This allows you to construct URLs and use Springs RestTemplate to make http request to the server, using the same DTO classes as your Controllers. If the authentication mechanism is Basic or Token you simply have to add the correct Authorization header as in this example.
If you use Form authentication, then it becomes a bit harder, because you first have to GET /login, then extract the CSRF token and the JSessionId cookie, and the POST them with the credentials to /login, and after login you have to extract the new JSessionId cookie, as the sessionId is changed after login for security reasons.
Hope this was what you needed.
If you want to programatically know which endpoints exist, you can autowire the List of RequestHandlerProvider into your test and filter them based on the path they are exposed on.
#Autowired
List<RequestHandlerProvider> handlerProviders;
#Test
public void doTest() {
for (RequestHandlerProvider handlerProvider : handlerProviders) {
for (RequestHandler requestHandler : handlerProvider.requestHandlers()) {
for (String pattern : requestHandler.getPatternsCondition().getPatterns()) {
// call the endpoint without security and check that you get 401
}
}
}
}
Using the RequestHandlerProvider is how SpringFox determines which endpoint are available and their signature, when it build the swagger definition for an API.
Unless you spend a long time building the correct input for each endpoint you will not get 200 OK back from the endpoint when including a valid security token, so you probably have to accept 400 as a correct response.
If you are already worried some developer would make security related mistakes when introducing a new endpoint, I would be equally worried about the logic of the endpoint, which is why I think you should have an integration test for each of them, and that would test your security as well.
Thinking outside the box a little, and answering the question in a different way, would it not be easier to simply define a static String[], e.g.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String[] FULLY_AUTH_PUBLIC_URLS = {"/api/**", "/swagger-resources/**", "/health", "/info" };
protected void configure(HttpSecurity http) throws Exception {
http
//...
.antMatchers(FULLY_AUTH_PUBLIC_URLS).fullyAuthenticated()
}
}
...
And then if the purpose of the test is to ensure that no changes are made to the public urls simply test the known list?
The assumption here is that Spring Security works and has been tested so the only thing we are testing for is that the list of public URLs has not been changed. If they have changed a test should fail highlighting to the developer that there are dragons changing these values? I understand this does not cover the clarifications but assuming the supplied static public URLs are known to be accurate then this approach would provide a unit testable back stop if this is needed.

Mockito spies - verify() calls the method instead of checking

I'm trying to use Springockito and spies to verify that calls were made/not made on a service method during an end-to-end test. I'm autowiring the service that the process will also get, and spy on it. Although myService instance is instrumented, verify() does not verify previous calls, but makes a call to the original method and passes a null parameter. Why is this?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = PatchedSpringockitoContextLoader.class, locations = {
"classpath:/config.xml"
})
...
#Autowired
#WrapWithSpy
private MyService myService;
...
#Before
public void setup() {
initMocks(this);
...
}
...
#Test
public void test() {
// run the process that may or may not call the service
verify(myService, never()).myMethod(any(MyParam.class));
}
What might be happening here is that your spied object uses annotations (e.g #Transactional) that requires Spring to add an AOP proxy around your spy, which causes Mockito to malfunction.
I've had the same issue as yours although I do not use Spock, and I solved it by getting a reference to the proxied mock or spy from the Spring proxy.
Check out the suggested hack in this GitHub issue report.
I am not using Spring Boot, so I wrapped the workaround code in a #BeforeClass method.

Resources