How to make spring cloud contract tests on controller with secured methods with annotation #PreAuthorize("hasRole('ADMIN')")? - spring-mvc

I'm writing Spring Cloud Contract tests on MVC Controller whith has methods with annotation #PreAuthorize("hasRole('ADMIN')"). What do I need to provide for passing the security of methods?
I need to test security of controller.
Oauth2 security using in project.
I'm junior dev (many things I dont know yet) and it would be greate if you provide me extended answers.
Thanks.
I've created base test class where provided mockMvc in setup() method with annotation #Before.
Base test class has the looking form:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = AdminController.class)
#ContextConfiguration(classes = {AdminControllerTestConfig.class, TestConfig.class, AdminControllerTestProperty.class})
#ComponentScan(basePackageClasses = AdminController.class)
#TestPropertySource(locations = "/AdminControllerTest.properties")
#AutoConfigureStubRunner(workOffline = true)
public class AdminControllerContractBaseTest {
//code
}
setup method for autogenerated contract tests
#Before
public void setup() {
Admin admin = createAdminUser();
when(adminRepository.findOneByCredentialsId(id)).thenReturn(admin);
RestAssuredMockMvc.standaloneSetup(new AdminController(adminService, credentialsService));
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
tests methods for autogenerated test classes
#Test
public void shouldFindAdminUserById() {
}
#Test
public void shouldNotFindAdminUserById() {
}
#Test
public void userDoesNotLoggedIn() {
}
I know that I'm not providing any deteils about login user for security but I didn't find info about how to do it in my situation with contract tests.
I've tried to set annotation #WithMockUser(roles = "ADMIN") on test methods but it doesn't works.
Maybe I didn't configure enough for passing tests?
By the way I have email and password of user who has access to this API.
In autogenerated tests on method below which according to contract
ResponseOptions response = given().spec(request)
.get("/api/admin/a")
I expected response code 200 after passing tests but actual code is 401

You might want to write an integration test with #SpringBootTest instead of using Spring Cloud Contract to test your security.
Spring Cloud Contract has been created to verify the in- and output of your communication layer and to let you (the producer) know if you're going to break your API so others (the consumers) have a problem using your exposed API.
Please take a look at this sample on how you can setup your Spring Cloud Contract tests without starting the whole application context.
Hope that helps! :)

Related

Spring 5 MVC test custom validator

I have custom validator that uses a service
public class ProductCodeValidator implements ConstraintValidator<ProdutCode, Set<String>> {
...
#Override
public boolean isValid(Set<String> pkdCodes, ConstraintValidatorContext context) {
return pkdCodes != null && service.count(pkdCodes) == pkdCodes.size();
}
My goal is to use it inside of a controller MVC test, with a mocked service, to control validation output.
In order to test it within a controller request, I tried to use #WebMvcTest. But then security pops in, giving me 403 everywhere. In order to disable it I added:
#AutoConfigureMockMvc(addFilters = false) // to avoid security
#WebMvcTest(ProdutDataController.class)
class EditCompanyRegistryDataControllerTest {
#MockBean
private ProductService pkdService;
#Autowired
private MockMvc mvc;
But now Spring ignores also form validation, returning status 200/201 instead of 400, when validation should be triggered.
Question:
1 Is there a way to disable security only, without the validation in MVC tests?.
2 Is it possible to register custom validators using MockMvcBuilders.standaloneSetup(...) builder? This way I could ignore spring context, and focus on standalone setup with mocks.
Use Spring Security Test Support
There really isn't a good way to disable just Spring Security because that is not recommend. Instead it is recommended to use Spring Security's MockMvc test support. This ensures that your tests are aligned with your configuration.
With Spring Boot, the integration with Spring Security is automatically added to the #Autowired MockMvc instance. Then you have the choice of using either Annotations or RequestProcessor to run your test.
Using Annotations:
#Test
#WithMockUser
public void requestProtectedUrlWithUser() throws Exception {
mvc
.perform(get("/"))
...
}
This will run as the user with username "user" and role "ROLE_USER". You can also override the attributes with something like: #WithMockUser(username="rob", roles="ADMIN").
Alternatively, you can also use a RequestPostProcessor.
mvc
.perform(get("/").with(user("user")))
You can also override the default roles and password:
mvc
.perform(get("/admin").with(user("admin").password("pass").roles("USER","ADMIN")))
For more details including additional customization, best practices, etc refer to the links I provided.
Standalone Setup
The standalone setup doesn't configure anything automatically (including validation and security). You an explicitly configure the validator, but you must ensure it is the same instance as that is configured in your application.
One way to do this is to #Autowire the Validator instance.
#SpringBootTest
class TheTest {
#Autowired
Validator validator;
#Test
void standaloneTest() {
MockMvc mvc = MockMvcBuilders
.standaloneSetup(new MyController())
.setValidator(this.validator).build();
}
Alternatively, you can create your own instance of the Validator directly. This complicates things as you must keep this in sync with how your Validator is created in Spring. The default is to use OptionalValidatorFactoryBean:
MockMvc mvc = MockMvcBuilders
.standaloneSetup(new MyController())
.setValidator(new OptionalValidatorFactoryBean())
.build();

aop aspects as mock in spring test

I came across an interesting article: AOP Aspects as mocks in JUnit
Since I have requirement to mock multiple final and private static variables, I am planning to use AOP in place of reflection or PowerMockito as they are causing issues with SpringJUnit4ClassRunner.
Is there any way I can use #Aspect for test classes without using the annotation #EnableAspectJAutoProxy? (I want to use an aspect targeting class X only in one test case.)
This is a sample of what I want to do.
The question is answered(adding for discussion on what could be done)
//External class
public final class ABC(){
public void method1() throws Exception {}
}
#Service
public void DestClass() {
private static final ABC abc = new ABC();
public Object m() {
// code (...)
try {
abc.method1();
}
catch(Exception e) {
// do something (...)
return null;
}
// more code (...)
}
}
Spring framework allows to programmatically create proxies that advise target objects , without configuring through #EnableAspectJAutoProxy or <aop:aspectj-autoproxy>
Details can be found in the documentation section : Programmatic Creation of #AspectJ Proxies and the implementation is pretty simple.
Example code from the documentation.
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// add an aspect, the class must be an #AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied must be an #AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();
Please note that with Spring AOP , only method executions can be adviced. Excerpt from the documentation
Spring AOP currently supports only method execution join points
(advising the execution of methods on Spring beans). Field
interception is not implemented, although support for field
interception could be added without breaking the core Spring AOP APIs.
If you need to advise field access and update join points, consider a
language such as AspectJ.
The document shared with the question is about aspectj and without providing the sample code to be adviced it is hard to conclude if the requriement can acheived through Spring AOP. The document mentions this as well.
One example of the integration of AspectJ is the Spring framework,
which now can use the AspectJ pointcut language in its own AOP
implementation. Spring’s implementation is not specifically targeted
as a test solution.
Hope this helps.
--- Update : A test case without using AOP ---
Consider the external Class
public class ABCImpl implements ABC{
#Override
public void method1(String example) {
System.out.println("ABC method 1 called :"+example);
}
}
And the DestClass
#Service
public class DestClass {
private static final ABC service = new ABCImpl();
protected ABC abc() throws Exception{
System.out.println("DestClass.abc() called");
return service;
}
public Object m() {
Object obj = new Object();
try {
abc().method1("test");
} catch (Exception e) {
System.out.println("Exception : "+ e.getMessage());
return null;
}
return obj;
}
}
Following test class autowires the DestClass bean with overridden logic to throw exception . This code can be modified to adapt to your requirement.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { DestClassSpringTest.TestConfiguration.class })
public class DestClassSpringTest {
#Configuration
static class TestConfiguration {
#Bean
public DestClass destClass() {
return new DestClass() {
protected ABC abc() throws Exception {
// super.abc(); // not required . added to demo the parent method call
throw new Exception("Custom exception thrown");
}
};
}
}
#Autowired
DestClass cut;
#Test
public void test() {
Object obj = cut.m();
assertNull(obj);
}
}
Following will be the output log
DestClass.abc() called // this will not happen if the parent method call is commented in DestClassSpringTest.TestConfiguration
Exception : Custom exception thrown
The article you are referring to is using full AspectJ, not Spring AOP. Thus, you do not need any #EnableAspectJAutoProxy for that, just
either the AspectJ load-time weaver on the command line when running your test via -javaagent:/path/to/aspectjweaver.jar
or the AspectJ compiler activated when compiling your tests (easily done via AspectJ Maven plugin if you use Maven)
Both approaches are completely independent of Spring, will work in any project and even when using Spring also work when targeting execution of third party code because no dynamic proxies are needed unlike in Spring AOP. So there is no need to make the target code into a Spring bean or to create a wrapper method in your application class for it. When using compile-time weaving you can even avoid weaving into the third party library by using call() instead of execution() pointcut. Spring AOP only knows execution(), AspectJ is more powerful.
By the way: Unfortunately both your question and your comment about the solution you found are somewhat fuzzy and I do not fully understand your requirement. E.g. you talked about mocking final and private static variables, which would also be possible in other ways with AspectJ by using set() and/or get() pointcuts. But actually it seems you do not need to mock the field contents, just stub the results of method calls upon the objects assigned to those fields.

Provider test integration with pact broker for Spring Boot junit5 + configuration in application properties

The pact-jvm-provider-spring states that for junit5 provider test, it is not required to use the spring library.
However, #PactBroker annotation depends on the system properties. Is there a way to get this working for application properties via the Spring Property Resolver. I tried to create something similar to SpringEnvironmentResolver.kt and used it in the context setup. But that did not work.
#Provider("api-provider-app")
#PactBroker
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PactVerificationTest {
#LocalServerPort
private int port;
#Autowired
private Environment environment;
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void testTemplate(Pact pact, Interaction interaction, HttpRequest request,
PactVerificationContext context) {
context.setTarget(new HttpTestTarget("localhost", port));
context.setValueResolver(new SpringResolver(environment));
context.verifyInteraction();
}
}
I get the following error
Invalid pact broker host specified ('${pactbroker.host:}'). Please provide a valid host or specify the system property 'pactbroker.host'.
Update
After some more searching found out that the setTarget was not working and that needs to be moved to #BeforeEach method.
#BeforeEach
void setContext(PactVerificationContext context) {
context.setValueResolver(new SpringResolver(environment));
context.setTarget(new HttpTestTarget("localhost", port));
}
The following snippet helped it work with #PactFolder annotation. But the #PactBroker with properties is still not working
There is a new module added to Pact-JVM that extends the JUnit5 support to allow values to be configured in the Spring Context. See https://github.com/DiUS/pact-jvm/tree/master/provider/pact-jvm-provider-junit5-spring. It will be released with the next version of Pact-JVM, which will be 4.0.7.

Cloud datastore dynamic namespace

Requirement
For Cloud, datastore needs to change namespace dynamically. (example store kind as per company Name)
Used Spring cloud DataRepository with Springboot for same
Issue
We need to declare spring.cloud.gcp.datastore.namespace in application.properties which is static.
Is there any way to change this dynamically with CRUDReposity of spring cloud
Thanks in advance
You can change anything you want in your application.properties at runtime using Spring Cloud Config.
Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments. The concepts on both client and server map identically to the Spring Environment and PropertySource abstractions, so they fit very well with Spring applications but can be used with any application running in any language.
Just as a quick example on how you can use this , you should firstly add the dependency : eg gradlecompile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.1.RELEASE', then you need to add the #RefreshScope on the desired configuration bean.
You will be able to view your current config at a certain endpoint, like "applicationConfig: [classpath:/application.properties]": {
"my.property": "value1",
etc
And then you can change the properties as you wish doing a POST request like :
curl -X POST http://localhost:8080 -d my.property=value2
There is also a nice article about dynamically reloading the properties in a Spring application here. It is nice because they actually display more ways that you can achieve that.
You can use DatastoreNamespaceProvider which can dynamically return needed namespace.
Was added in this PR PR
Also see this discussion here and this recommendation
#Component
#RequiredArgsConstructor
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class HeaderValueProvider implements Supplier<String>, DatastoreNamespaceProvider {
private final HttpServletRequest httpServletRequest;
#Override
public String get() {
return httpServletRequest.getHeader("someHeader");
}
}
And this
#Component
public class UserContextProvider implements DatastoreNamespaceProvider, Consumer<UUID> {
private static final ThreadLocal<UUID> USER_CONTEXT = new ThreadLocal<>();
#Override
public String get() {
return ofNullable(USER_CONTEXT.get())
.map(UUID::toString)
.orElse(null);
}
#Override
public void accept(UUID uuid) {
USER_CONTEXT.set(uuid);
}
}

Spring Boot - OAuth2 - Scope Limit Resources at the Field Level

Is there an annotation or DI based approach for implementing resource field level filtering based on oauth2 scope in Spring?
We have a spring boot based resource server that has oauth2 scope protected endpoints. This works fine to scope protect endpoints, however we want to be able to filter sensitive information from the resources we expose based on scope. E.g. I only want to expose last 4 of a person's SSN when the client scope allows it.
So far the only way I've found to do this on the resource server is like this:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2SecurityExpressionMethods expressionMethods = new OAuth2SecurityExpressionMethods(authentication);
boolean hasScope = expressionMethods.hasScope("xyz.read");
if(hasScope) {
resource.setSsn(entity.getSsn());
}
So when scope "xyz.read" is not present the resource will look like this:
{
"name": "blah"
}
But when scope "xyz.read" is present the resource will look like this:
{
"name": "blah",
"ssn": "123-45-2347"
}
Having to reach out and grab the authentication object from the security context holder and construct a new OAuth2SecurityExpressionMethods every time we want to check scope seems like we're missing something. However as this is a 'pure' OAuth2 resource server we've not discovered a better way to accomplish this.
This is what our resource server configuration looks like (and it does work fine):
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/health").permitAll()
.antMatchers("/info").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/**").access("#oauth2.hasScope('xyz.read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("resource-id");
}
}
You could make use of the #JsonView annotation, where the view names reflect authentication or access levels. Have a look at this tutorial: http://www.baeldung.com/jackson-json-view-annotation.
The goal: when the object is serialized, the view will dictate which fields should be shown/serialized.

Resources