Add additional info in authetication reply - spring

Working on a REST API application using Grails 3.3.5 and Spring-security-rest 2.0.0-RC1.
When I log in using /api/login I got correctly this as result:
{
"username": "name.surname#acme.com",
"roles": [
"ROLE_USER"
],
"access_token": "qgsbpk05jfrbf33xx08m8r4govkg53d1"
}
I'd like to add other information in the response, like name and surname of the user. Thanks
UPDATE
Thanks to Evgeny that points me in the right direction and reading the manual I implement also a UserDetailsService.
From the manual:
If you want to render additional information in your JSON response,
you have to:
Configure an alternative userDetailsService bean that retrieves the additional information you want, and put it in a principal object.
Configure an alternative accessTokenJsonRenderer that reads that information from the restAuthenticationToken.principal object.

You can override accessTokenJsonRenderer bean
Create class:
class MyRestAuthTokenJsonRenderer implements AccessTokenJsonRenderer {
#Override
String generateJson(AccessToken accessToken){
// create response, see DefaultAccessTokenJsonRenderer.groovy from https://github.com/alvarosanchez/grails-spring-security-rest
return your_formatted_json_response
}
}
override bean in resources.groovy
beans = {
accessTokenJsonRenderer(MyRestAuthTokenJsonRenderer)
}

Related

How to set header variables in GraphQL-SPQR

I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)

How to change Hateoas output format in spring application?

I am currently working on a spring application that offers a REST interface with which CRUD operations on entities of various kinds can be performed. These entities are stored in repositories and thus a major part of the REST interface is automatically generated by spring. When I execute a GET request on such an entity type (e.g. /devices), the result looks as following:
{
"_embedded":{
"devices":[
{
"macAddress": "...",
"ipAddress": "...",
"name": "Device_1",
"id":"5c866db2f8ea1203bc3518e8",
"_links":{
"self":{
...
},
"device":{
...
}
}, ...
]
},
"_links":{
...
},
"page":{
"size":20,
"totalElements":11,
"totalPages":1,
"number":0
}
}
Now I need to implement a similar interface manually, because additional checks are required. I have made use of the spring-hateoas features for this purpose. However, I am unable to achieve the same output structure like the one automatically generated by spring. The corresponding code in my controller class (annotated with RestController) looks as follows:
#GetMapping("/devices")
public Resources<Device> getDevices() {
List<Device> deviceList = getDeviceListFromRepository();
Link selfRelLink = ControllerLinkBuilder.linkTo(
ControllerLinkBuilder.methodOn(RestDeviceController.class)
.getDevices())
.withSelfRel();
Resources<Device> resources = new Resources<>(deviceList);
resources.add(selfRelLink);
return resources;
}
The configuration (excerpt) looks as follows:
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class WebServletConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
...
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer c) {
c.defaultContentType(MediaTypes.HAL_JSON);
}
...
}
However, this is the output of a request:
{
"links":[
{
"rel":"self",
"href":"..."
}
],
"content":[
{
"id":"5c866db2f8ea1203bc3518e8",
"name":"Device_1",
"macAddress": "...",
"ipAddress":"...",
}
]
}
As you can see, instead of an _embedded key there is a content key and the links key misses the leading underscore. These are the main issues I have with this output, more detailed differences compared to the output above are not that important to me. I would like to unify the ouput generated by my application, but I am unable to achieve the output format of the mappings that are automatically generated by spring. I also tried to wrap the resources object into another resource object (like return new Resource<...>(resources)), this did not work as well though.
Do you have any hints for me about what I am doing wrong here? I am quite new to Spring & Co, so please tell me if you need more information about a certain thing. Any help is highly appreciated. Thanks in advance!
Finally I was able to find a solution: The strange output format as showed in the question was generated due to the accept header application/json that was sent by the client. After adding
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true);
configurer.defaultContentType(MediaTypes.HAL_JSON);
}
to class WebServletConfiguration which extends WebMvcConfigurerAdapter everything works as exptected and the output format is now HAL-like. A quite easy fix, but it took me weeks to figure this out. Maybe this answer will help somebody else in the future.

io.undertow.servlet.util.IteratorEnumeration cannot be cast to java.lang.String

I'm trying to authenticate a user from the API key sent in the header without any user details through login. I then get a casting exception because I try to get the principal cast into a String.
I have tried to get the header within the SecurityConfig class but that hasn't worked. I've also tried getting it within the custom filter which is how the current solution sits anyway.
#Bean
public APIKeyAuthFilter authFilter() {
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(authentication -> {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal)){
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
});
return filter;
}
I expect to be able to get the header and inspect the value to compare with existing key but, I get this exception "message": "io.undertow.servlet.util.IteratorEnumeration cannot be cast to java.lang.String",
"trace": "java.lang.ClassCastException: io.undertow.servlet.util.IteratorEnumeration cannot be cast to java.lang.String\n\tat uk.co.nesistec.contractpicturechallenge.config.APISecurityConfig.lambda$authFilter$0(APISecurityConfig.java:46)
I got the example code from this other question.
Securing Spring Boot API with API key and secret
I have discovered all I needed was another class to register the security filter.
import org.springframework.security.web.context
.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer
extends AbstractSecurityWebApplicationInitializer {
//no code needed
}
This is the full example for this scenario for anyone that'll need this. Spring

Spring Data Rest Secure HATEOAS link

Lets say I have an User entity with a ManyToMany mapping to UserGroup entity. If I create repositories for both entities and GET the URI /users/1, I get a response like this:
{
"enabled" : true,
"password" : "xxxxxx",
"username" : "xxxxxx",
"credentialsNonExpired" : true,
"accountNonLocked" : true,
"accountNonExpired" : true,
"_links" : {
"self" : {
"href" : "http://127.0.0.1:45950/users/1"
},
"user" : {
"href" : "http://127.0.0.1:45950/users/1"
},
"userGroups" : {
"href" : "http://127.0.0.1:45950/users/1/userGroups"
}
}
}
The userGroups link here is really useful.
I can list all UserGroups using the /userGroups endpoint.
I would like to protect the /userGroups endpoint and /users/1/userGroups endpoint using different spring-security expressions.
Using the reference here: http://docs.spring.io/spring-data/rest/docs/current/reference/html/#security I understand how to secure the first endpoint:
public interface UserGroupRepository extends PagingAndSortingRepository<UserGroup, Long> {
#PreAuthorize("hasRole('ROLE_ADMIN')")
#Override
Iterable<T> findAll();
}
But how do I secure the second endpoint? Is that even possible currently? Is there some work planned on such a feature. I would love to contribute.
I have also encountered this problem and not found any solution working with Spring's security annotations. As a workaround, I have added something along the line of:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// Whatever config you already have.
.authorizeRequests()
.antMatchers("/users/*/userGroups").hasRole("ADMIN");
}
to my implementation of WebSecurityConfigurerAdapter. While this works, it duplicates the work done when securing the repositories and it is easy to forget adding both the java config and annotations when adding new repositories.
I realize this question is four years old, but this issue has continued to plague me, and I finally found something that works.
The problem essentially comes down to the fact that Spring HATEOAS has no real security controls; while you can configure the repositories with the appropriate method-level security annotations to prevent access via the REST API, you can't stop the HATEAOS representation model assembler from slurping up any objects it can see.
The solution I found was to expose a RepresentationModelProcessor bean for EntityModel<DomainObject>s. In your case, this may look something like
#Configuration
public class SecureHateoasConfig {
public static class UserGroupProcessor implements RepresentationModelProcessor<EntityModel<UserGroup>> {
#Override
public EntityModel<UserGroup> process(EntityModel<UserGroup> model) {
if(SecurityContextHolder.getContext()
.getAuthentication()
.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.anyMatch("ROLE_ADMIN"::equals)) {
return model;
} else {
return null;
}
}
}
#Bean
public UserGroupProcessor userGroupProcessor() {
return new UserGroupProcessor();
}
}
Objects replaced by null seem to get filtered out after the normal processing, so embedded entities simply don't include the objects which don't match your filter!
There's probably a much easier way to filter links if you're only doing role-based security, but I'm afraid I don't know it...
Of course, it's possible to extend this considerably, since your secure object processor need not only apply to EntityModel<UserGroup>! In my case I have an interface all my secure domain objects implement which can be used to make security decisions, so only one RepresentationModelProcessor implementation/bean is required.

How to generically authorize or validate a JSON rest request based on the authenticated user and an attribute of the requestbody

My current Spring3 REST JSON api is authenticated with the default InMemory properties file/basic-authentication authentication manager. That has worked fine thus far, but I need to further validate that an incoming request is allowed to be made for that user. The Role concept seems to work fine as a gateway for entry to a particular controller's url, but it doesn't go far enough to validate that the user is permitted to ask for the data being requested.
In my app, each B2B partner that will be making requests to the API is assigned an applicationId. That partner user account is only allowed to make requests for that applicationId. The applicationId is passed as an attribute of the RequestBody POJO for all the POST API messages. I would like to decline requests that are made for improper applicationIds.
How can I validate that the authenticated user is making a permitted request?
I've started down the path of creating a custom AuthenticationProvider, but I don't know how to get access to the applicationId within the RequestBody bean that hadn't been marshalled into the java bean yet.
Perhaps a custom AuthenticationProvider isn’t the right solution, and a request validator of some sort is needed. If so, how would the validator on the appId attribute get access to the Principal (authenticated user object)
With any solution, I would like it be invisible to the controller, so that requests that do make it to the controller are permitted ones. Also, ideally, the solution should not depend on an engineer to remember some annotation to make the logic work.
Thanks in advance,
JasonV
EDIT 1: By implementing an InitBinder in the controller, and using the #Valid annotation on the RequestBody I was able to validate a request. However, this is not the Droids (er I mean solution) I'm looking for. I need to find a more generic way to handle it without all those Binders and annotations; too much to remember and spread around the application over dozens of request controllers, and it will be forgotten in the future.
The usual way to implement this is using #PreAuthorize.
#PreAuthorize("hasRole('USER') and authentication.principal.approvedAppId == #dto.applicationId")
#RequestMapping...
public ... someMethod(#RequestBody Dto dto, ...)
If you're worried about the repetition of the SpEL, define a new annotation like #PreAuthorizeUser and set the #PreAuthorize as a meta-annotation on it.
I was able to utilize an aspect to solve the problem generically.
I would still like to see if it is possible to do the following:
Get a marshalled RequestBody from the request object in the context of an AuthenticationProvider.
Here is the aspect code for future help to others.
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controllerBean() {
}
#Pointcut(
"execution(org.springframework.http.ResponseEntity *(.., #org.springframework.web.bind.annotation.RequestBody (*),..))")
public void methodPointcut() {
}
#Around("controllerBean() && methodPointcut()")
public Object beforeMethodInControllerClass(ProceedingJoinPoint jp) throws Throwable {
Object[] args = jp.getArgs();
long requestAppId = Long.parseLong(BeanUtils.getProperty(args[0], "applicationId"));
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User principal = (User) auth.getPrincipal();
String username = principal.getUsername();
long[] approvedAppIds = getApprovedAppIdsForUsername(username);
for (long approvedAppId : approvedAppIds) {
if (approvedAppId == requestAppId) {
isAllowedAccess = true;
break;
}
}
if (isAllowedAccess) {
return jp.proceed(args);
} else {
LOGGER.warn("There was an attempt by a user to access an appId they are not approved to access: username="+username+", attempted appId="+requestAppId);
return new ResponseEntity(HttpStatus.FORBIDDEN);
}
}

Resources