Lost authentication when using GraphQL subscriptions with Spring Security - java

I'm using spring-boot-starter-graphql for my GraphQL server application. I protected the application with basic authentication using Spring security.
GraphQL subscription are used to return a type with a subtype. As soon as subscription returns a type, authentication gets lost from security context.
Here is short version of my configuration:
type Post {
id: String!
title: String!
author: Author!
}
type Author {
id: String!
name: String!
}
type Subscription {
getNewPost: Post
}
#SchemaMapping(typeName = "Post", field = "author")
public Author getAuthor(Post post) {
return this.appService.getAuthorById(post.getAuthorId());
}
#Bean
Many<Post> publisher() {
return Sinks.many().multicast().directBestEffort();
}
#SubscriptionMapping("getNewPost")
public Publisher<Post> getNewPost() {
return this.publisher.asFlux();
}
public Post createPost(CreatePostInput postInput) {
...
this.publisher.tryEmitNext(post);
...
}
Everything works as expected until subscription returns first object. After that security context becomes empty and any following request gets redirected to login page.
I noticed two things:
In case that object that subscription returns (Post in this example) doesn't contain nested object (Author), everything works as it should
With Java Kickstart everything works as it should
I created an example application for reproducing this issue: https://github.com/stojsavljevic/graphql-security-issue
Any help is very much appreciated!

According to Rossen Stoyanchev's presentation at Spring IO last May (which you can watch here), Spring for GraphQL has a layered architecture that has a Web transport layer at the top (be it HTTP or Websocket), then comes the GraphQL engine, and then the Data Controllers.
It's at this last level that we find the Components, like a mutation or a query service, or also one of this "service" operations invoked by a Controller method.
Spring for GraphQL makes sure that the Security context (e.g. the SecurityFilterChain) gets propagated through the GraphQL engine layer and remains available.
For that to work in your example, according to what I saw in your Github repository I believe you should use a #Secured("ROLE_ADMIN") annotation in your methods.
#Secured("ROLE_ADMIN")
public Post createPost(CreatePostInput postInput) {
Post post = new Post("1", postInput.getTitle(), postInput.getAuthorId());
// not actually saving anything
// publish newly created Post
this.publisher.tryEmitNext(post);
return post;
}
Make sure to enable method level security as well.
Hope it helps.
Note: For the Security part of Rossen's talk go to 24:05 onwards.

Related

Add Entire Protobuf Message to Model in Spring Boot

I'm starting to play around with Spring Boot to learn how it functions for a MVC web application.
At a high-level the goal is to have a controller that will handle GET requests by issuing a request to an external gRPC server which will return a Order protobuf message. The Order data will be added to the Model and served via a template with Thymeleaf.
I am new to the Spring framework as a whole so the approach is likely incorrect but what I was doing is:
#Controller
public class OrderController {
#GetMapping("/order")
public String getOrder(#RequestParam(name = "order_number") String orderNumber, Model model) {
// Code for getting Order proto message from external server here
model.addAttribute("name", Order.getDate());
model.addAttribute("total", Order.getTotal());
model.addAttribute("number", Order.getNumber());
....
return "order";
}
}
However, this seems pretty tedious (especially for larger messages). Is there something I am missing that would allow me to add these fields en-masse?
Sorry, I cannot comment on question (SO restriction), hence asking here - cannot you just put your order object in model (rather than individual fields) and access it's fields/methods in view (Thymeleaf)?
In controller
model.addAttribute("order", order);
in view, I am not sure as I have not used Thymeleaf but should be simillar to
<span th:text="${order.getName()}" />

Grails 2.4.5 Rest API GET Request: Can't get Response Body from CUrl and from React UI, but can get it from Postman and Browser

Grails 2.4.5 REST Controller returning List of non Domain object DTO
What's the best way to turn a method/action into a REST Resource accessible through a GET request, without returning a domain class/entity, but with a non-domain/entity DTO class?
With SpringBoot, it will look like this:
#RestController
class EmployeeController { ...}
/**
**I am not sure that even in Spring/SpringBoot you can return a list of non entity class DTO.
** Can you return a random non entity
**/
#GetMapping("/employees")
List<EmployeeDTO> all() {
return repository.findAll();
}
For my Grails project, I am trying to return a list of DTO object having data from different domain/entities. Since the class is not a Grails domain class, how do you do that, since it doesn't follow the Grails convention?
I have the code as below:
class ClassNameController extends RestfulController{
/**
** Grails usually wants you to use call the parent constructor and and put a domain class as
** a parameter like super (DomainClass).
** In my case, since I have a non-conventional Controller which combines different data
** sources/domains/entities, how do I implement my method/action to turn it into a Grails REST
** Resource?
** Is there an annotation like #RestController, #ResponseBody and #GetMapping in Grails or can they be used in Grails too, since Grails 2.4.5 is built on top of Spring?
**/
/**
** I used just one of the multiple domain/entity classes as a parameter to the parent constructor,
** in order to follow that convention like this below.
**/
ClassNameController(){
super(OneDomainClassOutOfTheManyDomainClassesInsideDTOClass)
}
List<ClassDTO> getAllDTOs(){
//allClassDTOs() is a private method inside same controller returning a list of all classDTOs
List<ClassDTO> classDTOsList = allClassDTOs()
respond classDTOsList, status: 200
}
With that code, I can see the data displayed in Postman as well as inside the browser. However, I am not able to see the data when testing with CUrl, somehow looking at the log, Grails considers the request as successful and redirect to the home page. I am not sure why that's the case. I even removed any security to see if it was due to the Oauth Access token, but I can see that it's not the case, as even with #Secured(['IS_AUTHENTICATED_FULLY']), I can see in the logs of Spring Security that the user has been authenticated successfully and the authorization access has been granted successfully.
Pretty much, I am looking for a sample example of a simple Grails REST API that doesn't use the Grails convention and returns a non domain object.

Read JWT token in Spring Boot RouterFunction

In Spring Boot Controller implementation we can get the JwtAuthenticationToken as a parameter in our method. Same token can be read, manipulated and validated for authorization like below
#PostMapping("/hello")
#PreAuthorize("hasAuthority('SCOPE_Internal') or hasAuthority('ROLE_User')")
public Mono<String> testHello(JwtAuthenticationToken token) {
log.info("token is " + token.getTokenAttributes().toString());
return Mono.just("OK");
}
We are using reactive Spring Boot and we have replaced our controllers with RouterFunction. We are wondering how above feature - Authorization and get the token in our router method calls.
public RouterFunction<ServerResponse> route() {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
When we tried passing the JwtAuthenticationToken in the router method call, it threw
Could not autowire. No beans of 'JwtAuthenticationToken' type found.
public RouterFunction<ServerResponse> route(JwtAuthenticationToken jwtAuthenticationToken) {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
We came up this solution if it makes any sense, or valid. We ran into same issue lately as we began a journey of converting our legacy and synchronous spring boot server app to an asynchronous one. The JwtAuthenticationToken which we use to get some added attribute to the token used by the system works out of the box when we used the RestController and pass it as an argument in the protected endpoint method. But with Router Function we could not get it to work. After 1 day of painstaking research on google, stackoverflow, spring's reactive application resource server docs, we could not get any head way. However, this post got us thinking even more. so we came up with this solution:
#Slf4j
#Component
#RequiredArgsConstructor
public class FitnessAccountWebHandler {
private final FitnessAccountService accountService;
public Mono<ServerResponse> getAccountByUserId(ServerRequest request) {
String userId = request.pathVariable(AccountResourceConfig.USER_ID);
// This will give you the JwtAuthenticationToken if a Principal is
// returned or empty if no authentication is found
Mono<JwtAuthenticationToken> authentication = request
.principal()
.cast(JwtAuthenticationToken.class);
return authentication.flatMap(auth -> {
log.info("Subject: {}", auth.getName());
return accountService.getSomething(userId)
.flatMap(ServerResponse.ok()::bodyValue)
.switchIfEmpty(ServerResponse.notFound().build());
});
}
}
Hope this helps someone and save some time.

Keycloak annotation based Resource configuration

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

How do you unit-test controllers that oauth?

I like Spring MVC because you can unit test your controllers.
But testing controllers that oauth is another thing. For instance if I want to get the authorization url because I want to Oauth to GData, I would have to deploy the web-app because Google will only accept authorization requests from my domain (the url of my web app), not my development environment whose domain is localhost:8080.
So right now the only way I am testing if my code works is deploying the code and printing out the data that I need to have printed.
My Controller, which is a multi-action controller
public ModelAndView authorize(HttpServletRequest request,
HttpServletResponse response) {
Provider provider = getProvider(request.getAttribute("provider"));
String authUrl = provider.getAuthUrl();
page.put("authUrl", authUrl);
return new ModelAndView("setup","model",page);
}
The provider code, all my dependencies are injected
public String getAuthUrl()
{
oAuthParameters.setScope("http://docs.google.com/feeds/");
try {
oAuthHelper.getUnauthorizedRequestToken(oAuthParameters);
} catch (OAuthException e) {
page.put("authUrl", CANNOT_CONNECT_TO_GOOGLE);
}
String oAuth_Callback="[callback url]";
try {
oAuth_Callback.concat("?oauth_token_secret=").concat(
java.net.URLEncoder.encode
(oAuthParameters.getOAuthTokenSecret(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
page.put("authUrl",INTERNAL_ERROR);
}
oAuthParameters.setOAuthCallback(oAuth_Callback);
String authUrl = oAuthHelper.createUserAuthorizationUrl(oAuthParameters);
return authUrl;
}
It sounds like you have one component (a controller) doing multiple things.
I would break this into
The controller
The OAuth service that communicates with Google
The latter should be injected into your controller, as with just about everything else in Spring.
This allows you, in a unit test, to mock out how your controller behaves when the OAuth component returns different values.
For actually testing integration with Google, you could do two things:
Unit testing of the service that parses the Google OAuth response - mock out the code that does the actual message transport so that you can test how your message parser behaves when google returns a certain type of XML (I'm assuming this is done with XML, but same principle applies regardless of technology) vs another type.
Actual integration tests of the component that sends and receives to google - this might be harder because of the limitations you mentioned.
So, even if they restrict access to certain domains, then you can unit test most of the pieces of the puzzle, and hopefully only have one small segment that has to be "in the wild" to be tested.
Or, could you register a different account for a domain in your test environment? Either way, you should still break up this code into smaller components.

Categories