Data validation across different microservices - java

I've already read lots of topics about it, but still haven't found the better approach.
I have a User. One User may have many Posts. Users and Posts are different microservices. I'm using Spring Boot.
When the front-end call my Posts microservice sending a POST request to /posts/user/1, I need to check if the given userId (1) exists on my Users database. If no, throw an exception telling the front-end that the user doesn't exist. If yes, then insert the given request body as a Post.
The question is: how should I check this information at my backend? We don't want to let this responsibility with the front-end, since javascript is client-side and a malicious user could bypass this check.
Options:
REST communication between the microservices. (Posts microservice call Users microservice asking if the given id exists on his side)
Give Posts microservice access to Users microservice's database
I understand that communication between them will create coupling, but I'm not sure if giving Posts access to Users database is the best option.
Feel free to suggest any options.

You have an option to do interprocess communication between Post and User microservices through RESTful approach.
In case if you just want to check the existence of the resource and don't want any body in response then you should perfer using HEAD http method. Therefore your API endpoint hosted at User microservice will look like -
HEAD user/{userId}
Call this API from Post microservice.
Return 200 / OK if user exist
Return 404 / Not Found if user does not exist
Click here and here to get more details on HEAD method usage and use cases.

For this very particular use case, if you have a security layer, you can(should) make use of user access token, to ensure, that request is processed for the right user, which can be done by validating the token and relying on the fact that if user has token he exist. (As its just not about if user exist)
For any logic other than that, say you want to check if he is allowed to post or other such restrictions it is required to make a call to the user service.
Talking about giving access to the database, it will be against one basic guideline of microservices. Doing so will form a tight coupling between you and user. Its ok to call user service in this case which can decide how to serve this request.
User service on its part should provide ways to answer your queries within the SLA by caching or other mechanisms.
One more thing that you can explore is BFF (Backend for Frontend)
You rightly said you should not expose backend services to frontend or add any logic there, but often frontend pages may not be comfortable in accepting that content on same page is answered via n different back end services and there might be some logic to stitch such queries and thats where you can make use of BFF.
Backend server (in my case node) which take of things like these requiring frontend to make just one call(or less calls) for a given page and at the same time hiding your backend services within.

You're right, you must do a validation at the back end since, I suppose, it's a REST service and requests can be send not only from the UI.
Suppose you have a service implementation:
#Service
class UsersServiceImpl implements UsersService {
private final Users users;
public UsersServiceImpl(Users users) {
this.users = users;
}
#Override
public void addPost(long userId, Post post) {
User user = users.get(userId);
if (user == null) {
throw new UserNonExistent(userId);
}
user.addPost(post);
}
}
where Users is an interface representing a users database and UserNonExistent is a RuntimeException. Then in your controller you can do the following:
#RestController
class UsersController {
private final UsersService usersService;
public UsersController(UsersService usersService) {
this.usersService = usersService;
}
#PostMapping("/posts/user/{userId}")
public void addPostToUser(#PathVariable String userId, #RequestBody Post post) {
usersService.addPost(userId, post);
}
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "User does not exist")
#ExceptionHandler({UsersService.UserNonExistent.class})
public void handleUserNonExistentException() {
// Nothing to do
}
}
If the supplied user ID is invalid handleUserNonExistentException() method will be invoked and it will return a BAD REQUEST HTTP status code.

Related

Spring Boot - make sure data belongs to current logged in user

I have a Spring Boot REST API that I'm building. Im slightly stuck on the correct way to design my API in a way that protects each individual users' data. For example, consider the following database relations:
User -> (Has Many) Projects -> (Has Many) Tasks. (A User has-many Projects, and a Project has-many tasks).
For example, if I design my endpoints in the following way:
GET /api/v1/projects/{projectId}
POST /api/v1/projects/{projectId}/tasks
Just as a simple example for the above, how can I make sure, when creating new tasks for a certain project, that the project belongs to the logged in user?
Currently, I am using JWT tokens via Spring Security as my authentication strategy, and included in the payload of the token I have my Users' id. So with every request I can retrieve the user, but surely that's incredibly inefficient to be making so many requests to the database and check if the user actually has a given project.
Some solution I was thinking about is to simply have endpoints designed like this:
/api/v1/users/{userId}/projects/{projectId}/tasks
And then I can use the user id in the JWT payload and compare it to the user id in the request parameter. But then that would mean with every new relation in my database, the length of the url is going to be massive :) Also I guess it would mean all the business logic would be inside the User service for the whole application, right? Which seems a little odd to me... but maybe I'm wrong.
Im not sure if thats an issue or not, but just trying to design the API to be as elegant as possible.
Thanks again!
Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.
To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.
And when possible combine multiple database queries into one, like for one of your examples:
GET /api/v1/projects/{projectId}
in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.
Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.
Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:
The token should only have required information for the user to access the API, it shouldn't have business logic
Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed
EDIT:
On the spring side I have used the #PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.
#RestController
public class MyController {
#PostMapping
#PreAuthorize("#mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}
#Service
public class MySecurityService {
/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.
SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized
make this check efficient
*/
return false;
}
}
The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.
There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).
Hi so if I understood it correctly you want to automatically assign the task that is going to be created with "POST /api/v1/projects/{projectId}/tasks" to the current logged in user.
You could try to add a Parameter 'Principal principal' to your rest controller. The Principal is the user that is sending the request.
After you have your Prinicipal, you could write a simple convert method(for example: convertPrincipalToUser(Principal principal) which returns you the user. Finally you can add your user to the corresponding task)
Here is some more information about it:
https://www.baeldung.com/get-user-in-spring-security

Ensure a particular user can only see their own user details - Using Spring

Working with Spring / Spring security on a small project at the moment and having difficulty implementing this feature. Ideally I only want user1 to view the details of user1 and not those of 2,3 or 4 for example.
I've implemented Spring Security with Roles and understand that I can retrieve a UserDetails object or a principle, I'm not sure exactly but I know I can retrieve the details of the current logged in user using one of, what appears to be many methods.
This is what I'm currently using as a proof of concept when we go to the Admin/home page:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Gamer gamer = gamerService.findGamerByEmail(auth.getName());
System.out.println("Auth: " + auth.getName());
System.out.println("Gamer: " + gamer.getName() + gamer.getId() + gamer.getEmail());
The security config takes care of whether or not the current user can access because of the roles assigned to it.
I believe I should be able to go to the url of /mysite/viewUserDetails and have that page display information of the current user but I cannot find any examples of this, I've found plenty of example that prove a logged in user can view a page but none that specify checks in place to ensure user1 can only view user1's details.
On an older page I do this to display information for a particular user but I understand this to be bad practice-
<a th:href="#{/gamer/{gamerid}/games/excited (gamerid=${gamer.id}) }">
*Worth noting that this isn't using any form of login/registration to pull out this info, I'm simple using the id I pass in as part of the DB query.
It maps onto :
#RequestMapping("/gamer/{gamerid}/games/excited")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
So my question becomes, and I hope you can point in the right direction, How can I implement a solution where a user can only view his/her details and how should this be represented via the form and connecting controllers as passing ids in the url is kinda ugly (I could use a guid but...)
Thanks so much in advance.
It's actually quite an easy choice. Either you have an entry point like:
#RequestMapping("/gamer/{gamerid}/games/excited")
and you manually check that the user in session can access the requested resource, or you have something like
#RequestMapping("/my-games")
that automatically reads the user id from the security context.
More than a security choice, I'd pick one depending on code reuse and future use-cases (for example the same page/set of pages can be seen by more than one user).
Have a look at #PreAuthorize annotation. It is possbile to annotate given endpoint with it and create custom logic in a bean. Then you can use custom method to allow or disallow the endpoint to proceed :
#Controller
public class HomeController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping("/gamer/{gamerid}/games/excited")
#PreAuthorize("#authenticationService.hasAccess(#gamerid)")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
}
Service class :
#Service
public class AuthenticationService {
public boolean hasAccess(String tgamerid) {
//implement logic here
return true;
}
}
Method hasAccess in the AuthenticationService should return boolean. #PreAuthorize will be launched before controller handler method is invoked. The controller above is just an example. You can pass Authentication object in SPeL expression in #PreAuthorize annotation to service method or get it from security context inside service class, to implement logic which fits your needs. More information can be found here and in Spring Docs.

Jersey rest framework - authorization - some doubts

I read about jersey framework for rest service on this page http://howtodoinjava.com/jersey/jersey-restful-client-api-authentication-example/|
And I don't understand one thing.
For instance, when we have
#Path("/users")
public class JerseyService
{
#RolesAllowed("USER")
public String doLogin(#QueryParam("username") String uname,
#QueryParam("password") String result)
It means that user with role user can modify (by this method) ALL the users? Not only himself in the database? I am writing android app and I can imagine situation where someone is using for instance Advanced REST client. He logs on the service and modifying queries in appropriate way and strongly mess my database. For instance write some points to other user or something similar. How can I shut out this situation?
Jersey (and similar Spring Security) operate on Resource Types and Roles.
So, if you permit Role "USER" to operate on resource "Users", you can't block specific user from editing other users with Jersey only.
What you can do is use SecurityContext to get current user, and block dangerous operations if his credentials are different from user being changed.
Here's a good example on SecurityContext:
https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/

Rest Authentication - Token validation with a database look up Vs alternatives

I read through several q&a on stackoverflow for implementing rest authentication. And in one of those questions found a sample code as well.
https://github.com/philipsorst/angular-rest-springsecurity/blob/master/src/main/java/net/dontdrinkandroot/example/angularrestspringsecurity/rest/AuthenticationTokenProcessingFilter.java
Most of the answers talked about having an interceptor and filtering every request based on the auth header (a token and a user id or login id) and comparing it with the ones stored in the database.
I am implementing an Order management system.
And my url looks like http://myapi.com/customers/{customerId}/Orders/{OrderId}
Currently it is http and we're setting up the https soon.
In the URL, I get the customer ID and the order ID. I do a quick look up in the database with the order id and customer id and if it returns some rows, I return a JSON.
Questions I have:
To protect this endpoint, I can have a security interceptor. But every time I'll have to validate the request against the database. What are my alternatives (cache?) to validate or authorize each requests?
This rest end point is consumed by an android app(angular js)client and a website (a php client). For mobile, I should not re generate token each time the user logs in. So I have configured the token expiry to 30 days. However for the website, it is a session token. How should one handle this scenario?
What you need can be solved with Oauth.
Your backend (REST-API) will require authenticated access to your API operations. In turn, your clients/front-end will need to issue authenticated requests when communicating with the backend. This is achieved by sending access tokens.
Although this could seem complex, it will be very useful for you to take a look at Stormpath. We have a quite a straightforward solution for this. Please take a look at Using Stormpath for API Authentication.
As a summary, your solution will look like this:
You will use the Stormpath Java SDK to easily delegate all your user-management needs.
In your front, when the user presses the login button, your front end will send the credentials securely to your backend-end thorough its REST API.
2.1. By the way, Stormpath greatly enhances all the possibilities here. Instead of having your own login page, you can completely delegate the login/register functionality to Stormpath via its IDSite, or you can also delegate it to our Servlet Plugin. Stormpath also supports Google, Facebook, LinkedIn and Github login.
Your backend will then try to authenticate the user against the Stormpath Backend and will return an access token as a result:
/** This code will throw an Exception if the authentication fails */
public void postOAuthToken(HttpServletRequest request, HttpServletResponse response) {
Application application = client.getResource(applicationRestUrl, Application.class);
//Getting the authentication result
AccessTokenResult result = (AccessTokenResult) application.authenticateApiRequest(request);
//Here you can get all the user data stored in Stormpath
Account account = accessTokenResult.getAccount();
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
//Output the json of the Access Token
response.getWriter().print(token.toJson());
response.getWriter().flush();
}
Then, for every authenticated request, your backend will do:
/** This is your (now protected) exposed operation */
public void getOrder(HttpServletRequest request, HttpServletResponse response) {
Application application = client.getResource(applicationRestUrl, Application.class);
OauthAuthenticationResult result = (OauthAuthenticationResult) application.authenticateOauthRequest(request).execute();
System.out.println(result.getApiKey());
System.out.println(result.getAccount());
//Return what you need to return in the response
doGetOrder(request, response);
}
Please take a look here for more information
Hope that helps!
Disclaimer, I am an active Stormpath contributor.

REST Design of User-based access using Jersey

So I am trying to get my head around how to design my REST resources and how security fits into all of this. I am new to REST and user authentication in general so please bear with me - I am sure this is a really stupid question.
Now I know that you can define security roles for users, and restrict access to resources depending on whether they are an admin or not.
Where I am getting confused is say I have a situation where a certain user, 1234 say, has a list of subscriptions. Now only user 1234 should be able to access his subscriptions.
GET /user_id/1234/subscriptions
In this scenario, using roles doesn't make any sense, as you would have to define roles for each user. Do we have to control this access by doing some kind of check in the code to make sure this user has access? For example:
#Path("/user_id/{user_id}/subscriptions")
#GET
public getSubscriptions(#PathParam("user_id" int user_id))
{
if(user_id == "some code here that checks what the user_id of the current user is")
{
return Response.ok(getUserSubscriptionsFromDB(user_id));
}
else
{
return Response.status(Status.UNAUTHORIZED).build();
}
}
Is this how it is supposed to be done, or have i got it all wrong? If that is how you would go about it, what would the actual code in the "" look like? What object would I be interrogating to get my hands on the user_id? [The plan is in the future to use OAUTH2 social login for google, facebook etc.... but I might just use basic authentication too]
You basically got it right. The terminology for this type of authorization is Permission/Activity Based Authorization and is widely used for CRUD operations on recources like RESTful services.
A pseudo code would more look like this:
#Path("/users/{userId}/subscriptions")
#GET
public getSubscriptions(#PathParam("userId" int userId))
{
if(getSubject().isPermitted("subscriptions:read:"+userId)
{
return Response.ok(getUserSubscriptionsFromDB(userId));
}
else
{
return Response.status(Status.UNAUTHORIZED).build();
}
}
You might want to have a look on Apache Shiro which has pretty decent permission based authorization support.
You additionally might want to have a look on best practices for naming your REST resources.

Categories

Resources