How to test annotation based role-specific security in test annotated with `#WebMvcTest` and `#WithMockUser` - spring-mvc

My Problem is that I can not test the role-based authentication of my annotated Rest Endpoints in my tests. The specified roles seem to make no difference
I am using annotation based security configuration on my REST-controllers like this:
#RestController
#RequestMapping("rest/person")
class PersonRestController(
private val securityService: SecurityService,
private val personService: PersonService,
) {
#GetMapping("/list")
#Secured(UserRole.Role.ROLE_COMPANY)
fun list(): List<Person> {
val companyId = securityService.currentUser.companyId
return personService.findByCompany(companyId)
}
}
In my Web Layer tests I am using #WebMvcTest with a shared config class, that provides all required beans (we have some shared ExceptionHandlers and Interceptors that I would like to test with my Controllers)
My Test looks like this:
#WebMvcTest(PersonRestController::class)
#Import(RestControllerTestConfig::class)
class GroupedScheduleRestControllerTest {
#Autowired
private lateinit var mvc: MockMvc
#MockBean
private lateinit var personService: PersonService
// This bean is provided by RestControllerTestConfig
#Autowired
private lateinit var someSharedService: SomeSharedService
#Test
#WithMockUser(value = "user#company.at")
fun testReturnsEmptyList() {
val response = mvc.perform(MockMvcRequestBuilders.get("/rest/person/list"))
response.andExpect(status().isOk)
.andExpect(jsonPath("$.length()").value(0))
}
}
Now I would like to add a unit test, that verifies, that only a user with the role COMPANY can access this endpoint - but I can't get this to work. My test always runs through when I add WithMockUser, independent of what I pass in for the roles.
And it always fails with a 401 Unauthorized when I remove WithMockUser so some security setup seems to be happening but the #Secured in my RestEndpoint seems to have no effect.
Am I missing some configuration here to notify #WebMvcTest to pick up the Security annotations from the instantiated RestController?

Okay in order for the #Secured annotations to be picked up, I added #EnableGlobalMethodSecurity(securedEnabled = true) to my Configuration class and it worked like a charm 🙈

Related

Unable to set some producer settings for kafka with spring boot

I'm trying to set the retry.backoff.ms setting for kafka in my producer using the DefaultKafkaProducerFactory from org.springframework.kafka.core. Here's what I got:
public class KafkaProducerFactory extends DefaultKafkaProducerFactory {
public KafkaProducerFactory(Map<String, Object> config) {
super(config);
}
#Configuration
public class MyAppProducerConfig {
#Value("${myApp.delivery-timeout-ms:#{120000}}")
private int deliveryTimeoutMs;
#Value("${myApp.retry-backoff-ms:#{30000}}")
private int retryBackoffMs;
Producer<MyKey, MyValue> myAppProducer() {
Map<String, Object> config = new HashMap<>();
config.put(org.apache.kafka.clients.producer.ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, deliveryTimeoutMs);
config.put(org.apache.kafka.clients.producer.ProducerConfig.RETRY_BACKOFF_MS_CONFIG, retryBackoffMs);
final var factory = new KafkaProducerFactory<MyKey, MyValue>(config);
return factory.createProducer(); // calls DefaultKafkaProducerFactory
}
Now when I add the following to my application.yaml
myApp:
retry-backoff-ms = 50
delivery-timeout-ms = 1000
This is what I see in the logging when I start the application:
o.a.k.clients.producer.ProducerConfig : ProducerConfig values:
delivery.timeout.ms = 1000
retry.backoff.ms = 1000
so the delivery.timeout.ms was set, but the retry.backoff.ms wasn't even though I did the exact same for both.
I did find how to set application properties to default kafka producer template without setting from kafka producer config bean, but I didn't see either property listed under integrated properties.
So hopefully someone can give me some pointers.
After an intense debugging session I found the issue. DefaultKafkaProducerFactory is in a shared library between teams and I'm not super familiar with the class since it's my first time touching it.
Turns out the createProducer() call in DefaultKafkaProducerFactory calls another function that is overriden in KafkaProducerFactory which then creates an AxualProducer.
And the AxualProducerConfig always sets retry.backoff.ms to 1000ms.

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);
}
}

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

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! :)

Can asp.net core policies and claims handle resource/activity based authorization?

I'm looking into asp.net core and the new security policies and claims functionality. Having just looked at it I don't see how it is much better than the existing authorize attribute logic in the past where hard-coded roles or users are decorated on controllers, methods etc. To me the issues has just been moved from hard-coding in attributes to hard-coding policies.
Ideally I would like to perform activity/resource based authorization where everything would be database driven. Each activity or resource would be stored in the database and a permission/role would be assigned to the resource.
While researching the topic I found this fantastic article by Stefan Wloch that pretty much covers exactly what I'm looking to do.
http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE
So my question is with the new core features how does it prevent us from having to hard-code and recompile when the time comes to change what roles/permissions are allowed to access a controller or method in a controller? I understand how claims can be used to store anything but the policy portion seems susceptible to change, which gets us back to square one. Don't get me wrong, loving asp.net core and all the great changes, just looking for more information on how to handle authorization.
There are at least 2 things that need to be consider in implementing what you want. The first one is how to model the Controller-Action access in database, the second one is to apply that setting in asp.net core Identity.
The first one, there are too many possibilities depend on the application itself, so lets create a Service interface named IActivityAccessService that encapsulate. We use that service via dependency injection so that anything that we need can be injected to it.
As for the second one, it can be achieved by customize AuthorizationHandler in a policy-based authorization. The first step is to setup things in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();
next we create the ActivityAccessHandler:
public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
readonly IActivityAccessService _ActivityAccessService;
public ActivityAccessHandler (IActivityAccessService r)
{
_ActivityAccessService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (_ActivityAccessService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
public class ActivityAccessRequirement : IAuthorizationRequirement
{
//since we handle the authorization in our service, we can leave this empty
}
Since we can use dependency injection in AuthorizationHandler, it is here that we inject the IActivityAccessService.
Now that we have access to what resource is being requested, we need to know who is requesting it. This can be done by injecting IHttpContextAccessor. Thus services.AddHttpContextAccessor() is added in code above, it is for this reason.
And for the IActivityAccessService, you could do something like:
public class ActivityAccessService : IActivityAccessService
{
readonly AppDbContext _context;
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_context = d;
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
//get the user object from the ClaimPrincipals
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//get user roles if necessary
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that the code in IsAuthorize body above is an example. While it will works, people might say it's not a good practice. But since IActivityAccessService is just a common simple service class, we can inject anything that wee need to it and modify the IsAuthorize method signature in any way that we want to. For example, we can just pass the filterContext.RouteData instead.
As for how to apply this to a controller or action:
[Authorize(Policy = "ActivityAccess")]
public ActionResult<IActionResult> GetResource(int resourceId)
{
return Resource;
}
hope this helps

Injecting DbContext in constructor of Web api 2 controller

I am creating a small proof of concept asp.net web api 2 service with entity framework code first. The controller's constructor looks like
public AccountController: ApiController
{
private readonly DbContext context;
public AccountController(DbContext _context){
context = _context;
}
public AccountController(){context = new ApplicationContext();}
}
I need to unit test my controllers. How can I mock the DbContext class. Is there a simple way of doing this? I want to avoid all that repository pattern with lot of interfaces. Because it will be a way overkill for this prototype.
Its usually something like this if you use Nunit and Moq.
[TestFixture]
public class AccountControllerTest
{
private Mock<DbContext> mockContext;
private AccountController sut;
[SetUp]
public void TestSetup()
{
mockContext = new Mock<DbContext>();
var account = new Account() { Id = 123, Name = "Test Account" };
mockContext.SetUp(x => x.GetAccountOnContext()).Returns(account);
sut = new Controller(mockContext.Object) { Request = new HttpRequestMessage() };
sut.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
}
[Test]
public void ControllerMethod_GetLogin_Test()
{
// assuming GetLogin calls GetAccount on DbContext()
var response = sut.GetLogin(someAccount);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
mockContext.Verify();
}
}
You basically want to mock out your external dependencies and test just what the SUT (System Under Test) is supposed to do. I would also strongly encourage to look at Fakes instead of mocks. In general fakes result in less brittle tests.
So in this case, you could have a FakeDbContext() that you can pass to the tests. The FakeDbContext() will behave more like the actual DbContext() but will do all those operations in-memory, so that your tests don't have a dependency with a real database.
Depending on the database you use, you can also look at starting an embedded version of the real database as a part of your tests. Just have to make sure to do the necessary stopping and clean up of the test database records after the test run is complete in the TearDown() method.

Resources