Limit requests per t in Spring Boot - java

I am writing a REST service in spring boot and I've come across a problem I can't solve. Been googling for hours but most examples seem to bee way too complicated for me because I'm new to Spring Boot.
Let's assume a user can save an object into my database:
#RequestMapping(value = "/person", method = RequestMethod.GET)
public void saveName (#RequestParam String name){
personRepository.save(new Person(name));
}
Now I have been trying to find a way so an anonymous user can only make a limited amount of entries per minute.

You need to use Spring Security for this problem. You can check if the user is anonymous through isAnonymous() annotation and to limit it you can use a table to capture the attempts. Since you are new to Spring Boot. It's better to brush up on Spring Security as it gives you a fine-grained control over it.
If you do not want spring security, then, the same approach applies, use a table to capture unauthenticated user attempts. Also, there is a default session id, you could make use of this instead of going through the hassle of getting Ip addresses.

Even though the client is not an authenticated user, it still has its own session, so you can store in the session how many entries he has added. Don't store an interger but a collection of times, so you can remove the old ones if they are older than a minute.

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

How to get more/optional data for users using keyclaok rest api?

We are using Keycloak for SSO purpose, in particular we are able to use the REST API /admin/realms/{realm}/users to get the basic user details in a Keycloak realm, the response we get is UserRepresentation which seems to have provision for realmRoles and clientRoles as well but by default they are not required/false.
We have a new requirement to fetch the roles of all users, I see there are additional API exposed to get these roles: /auth/admin/realms/realm/users/user-id/role-mappings/realm/, but this means firing another request, and if we have 2k users that means 2k more request.
My question is as UserRepresentation also have properties realmRoles and clientRoles but seems to be optional by default, how can I enable these while firing the request /admin/realms/{realm}/users, and avoid additional request to get roles.
I'm afraid that getting the data you need in one request is not possible: just by looking at the source code of getting all users in UsersResource you can see that realmRoles and clientRoles are never populated.
Having that said, there is one thing that you can do - write your own REST Resource by implementing SPI. In fact, in the past I had a similar problem with groups resource and I ended up writing my own resource. In this case you will need to write custom resource with just one method - getting all users with roles. You can just copy-paste current keycloak logic and add extra bits or extend built-in UsersResource. This, however, is not a single bullet - on the long run you will be required to maintain your own code and upgrades to latest keycloak may not be that simple if some interface will change.

Spring boot #Cacheble with Ehcache

I'm using Spring boot with Ehcache for caching some data in the application.
The application is a rest service that caches some data that has high usage.
The code in our controllers looks like:
#Cacheable("CategoryModels")
#GetMapping("/category/{companyId}")
public List<CategoryViewModel> getAllCategories(#PathVariable(value = "companyId", required = true) long companyId,
#RequestHeader("user") String user) {
//custom code here
}
Now in some situations the users are getting different data sets back from the server. Can someone explain this in the above situation?
If data is changed in the database I refresh the cache and the program will auto update the updated data to the
For refreshing the cache I use a custom written method:
Cache categoryCache = (Cache) manager.getCache("CategoryModels").getNativeCache();
categoryCache.removeAll();
categoryController.getAllCategories(company.getCompanyId(), null);
I have the same behavior on other caches that are used and refreshed on the same way the above cache is used.
You should try to parametrize your cache definition with :
#Cacheable(value="CategoryModels", key="{ #root.methodName, #companyId, #user.id }")
It may be a couple of things. First off the default key resolver that spring provides does not consider anything but the names of the parameters. The cleanest way to fix this kid to write your own key revolver that considers both class and method, without this it could be possible to get back data from a completely different method that happens to share the same parameter list.

Get logged in userId from a 'me' relative id in /users/me spring boot api

We have a bunch of secured web services (using spring security) defined like this:
#GetMapping("/users/{userId}")
public User getUserInfo(#PathVariable String userId) {
...
#GetMapping("/users/{userId}/addresses")
public User getUserInfo(#PathVariable String userId) {
If I want to get the logged in user, I can add the Principal principal or HttpServletRequest request to the method and spring will support the data for the logged in user, or also I can use SecurityContextHolder.getContext().getAuthentication().
So far so good, if we want to get the logged in user for the api /users/1234 we have no problem. However we got a requirement to support a relative userId value being me where the api would be /users/me.
We could resolve this by adding in each api just an if statement and replacing the userId me with the real logged in userId. We don't like this implementation since we have a boilerplate code in 20 apis. So, another solution we tried was by just adding an aspect to replace the me by the real userId but I don't like using reflection for solving this problem.
So, i was wondering if there is a "spring boot" way of doing this? I haven't found any feature supporting this behavior.
I think the best solution would be to change that URL. If statement is ver explicit too though. There are interceptors as well you could use but I don't see them as being much different to using aspects, pretty evil stuff. But if you do use aspects I would suggest that you have an annotation such as #Me which you could annotated method inputs such as userId with, which would replace them with the id of the principal if the parameter is equal to me.

Spring Access Control

I working on Spring MVC app. The app funcionality is accessible through ReST API which jsp containing ajax logic consume. I am using spring security with defined roles (USER, COMPANY, ADMIN). Methods use requestMapping with responseBody such as:
www.app.com/auth/{userId}/request/{requestId}
It, of course, support GET for obtaining resource and POST for its creating or updating.
The problem is that after succesful login with, for example, userId = 1 I want GET request with requestId = 99. But when I run WebDev client for Chrome, I can also access another resource with easy request in format
www.app.com/auth/5/request/{requestId}
So basically, I can access resources, which I am not allowed to see. I hope you got the idea, where I am heading.
My question is - What is the best approach to secure this?
I was thinking about storing logged user Id (Integer) in session and comparing it everytime request for resource is made, but it seems to me that I am pulling the wrong end of rope :)
Thank you for any advice
You should have a look into the Expression-Based Access Control section of the spring security documentation.
Example copied from the documentation:
#PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact) {
..
}
This would check if name of the contact is equal to the name of the currently logged in user.
Using this this feature you can build much more sophisticated access rules than you could do with simple roles. However, this does not exclude using roles. You can still keep roles for basic security checks.

Categories

Resources