Accessing URL with params in Spring Security - java

I am new in Spring Security and have an issue with authenticating specific URLs with path variables or parameters.
Let's say I have some URL: /messages/1 where 1 is an user's ID.
What I want to do is to let ONLY user with ID=1 accessing page with this URL.
Spring Security allows accessing some content if user is authenticated, but how can I make authentication more narrow?

Assuming that you have an id property in your UserDetails:
#RequestMapping(value="/messages/{userId}", method=HttpMethod.GET)
#PreAuthorize("#userId == authentication.principal.id")
public List<Message> getMessages(#PathVariable String userId) {
// retrieve from DAO here
}
Make sure you have enabled pre/post-annotations in your security config.

Related

Different response based on logged user role

In my RestController I have POST method which returns different object based on user role:
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(#RequestBody LocalRequest localRequest){
return status(OK).body(localService.findLocalBylocalId(localRequest));
}
Service method:
public LocalDto findLocalBylocalId(LocalRequest localRequest) {
Role role = userRoleRepository.findByUsername(localRequest.getUsername());
if(role == Role.ROLE_ADMIN) //return localDto infromation object for ADMIN
else if(role == Role.ROLE_USER) //return localDto information for USER
}
LocalRequest contains username of current logged in user.
The problem is when user will authenticate and hit this endpoint he can pass to RequestBody admin's username. In this case he will get access to admin resources even if he is logged as USER.
How to avoid this situation and modify the code?
Should I create two different endpoints - one secured only for USER, and one secured only for ADMIN?
Should I pass Principal object instead passing username in POST method as parameter? Can I pass Princpal object if I am using jwt mechanism?
You can access the currently authenticated username by specifying Principal as an argument. For example:
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(Principal principal){
return status(OK).body(localService.findLocalBylocalId(principal.getName()));
}
This works because Spring MVC will resolve the Principal argument from the HttpServletRequest.getUserPrincipal() method and Spring Security overrides the method to align with the currently logged in user. You would then need to update your LocalDto to accept a username instead of a Localrequest.
Alternatively, you can also resolve the entire Spring Security Authentication in the same way since the HttpServletRequest.getUserPrincipal() will be an Authentication.
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(Authentication authentication){
return status(OK).body(localService.findLocalBylocalId(principal.getName()));
}
This gives you access to the roles without needing to look them up again. The disadvantage of this approach is that you are now relying on Spring Security's API directly.
You can also consider using the #AuthenticationPrincipal annotation to decouple yourself from Spring Security. This approach is the best if you need access to more than just the username because you can still be decoupled from Spring Security, but it also involves more work (i.e for username/password authentication you need a custom UserDetailsService, custom UserDetails). Because the amount of work/variables here, it is difficult to provide more guidance than a link to the documentation without further details.

oAuth Token for Anonymous User in Spring boot

Team
Using Spring boot I am able to accomplish workflows, where oAuth server can generate the token for the logged-in user. However, how does the oAuth server generate a unique token for each anonymous user?
For e.g. if 2 concurrent users are trying to access the resource server from their respective browsers, then I would like the resource server to identify the 2 different users in each subsequent requests that they make. For that, I would like to generate different tokens for each anonymous user. Is that possible and if yes then how?
It is simple thing, you are making it complex.
I would like to generate different tokens for each anonymous user. Is that possible
Once you generate valid token, user will become authenticated user.
You might think about validating token and setting ROLE='ROLE_ANONYMOUS'. As I told it will be like making simple thing to complex. Making authenticated user to Anonymous user is not good approach. You can create a new role like ROLE_SEMIANONYMOUS and grant authorities which were open for ANONYMOUS user.
(but this approach doesn't make any sense for me so explaining alternative to achieve your requirement of identifying AnonymousUser)
As "Anonymous User = UnAuthenticated User".
For anonymous user if you print principal
String principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
principal = "anonymousUser"
You can create a session for anonymous user for identifying him as given below
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if(principal instanceof String && ((String)principal).equalsIgnoreCase("anonymousUser"))
{
if(request.getSession(false) == null)
{
HttpSession session = request.getSession();
session.setMaxInactiveInterval(300); // As per Your requirement
// store session id from session and ip address from request in to DB or cache if required and use it
}
else
{
//identify anonymous user against session id and stored details
}
}
You can achieve this by registering a filter
In convetional spring order of filter should be after springSecurityFilterChain.
In spring boot you can achieve it by FilterRegistrationBean

REST service to return only current user-related resources

I have a REST service implemented using Spring MVC (RestControllers) with token based security (using Spring Security). How can i filter resources depending on user identity? Let's say user has some reports. How can I let authorized user by performing a call to /reports to see only his reports?
Obviously i can make userId to be a request parameter or path variable, but something tells me that this is a bad practice.
I assume i can achieve that using Spring Security features, but how exactly could i do that and, more important, where is the most appropriate place to apply such filtering? Should controllers perform calls to services passing user identity or should it be somehow retrieved at repositories level (I use Spring Data JPA)?
Thanks in advance
You have Authentication object whenever a user is successfully logged in.
It contains Object principal Object credentials and Set authorities.
All you need to do is override UserDetailsService to add new parameters for your authenticated user. Add your userId in authentication as shown in blog
Now when you do
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this will return you the User object of the spring security.
You can get the user id from here and use this in controller to do necessary actions.

User Authorization for Cloud Endpoints

I'm working on a cloud endpoints backend and want to restrict certain operations to admin users.
My current code works like this:
#ApiMethod(httpMethod = "PATCH", name = "item.update", path = "items")
public Item update(Item newObject, User user)
throws UnauthorizedException, OAuthRequestException {
OAuthService oAuthService = OAuthServiceFactory.getOAuthService();
if (!oAuthService.isUserAdmin()) {
throw new UnauthorizedException("Only admin users can modify content.");
}
...
}
I know app engine has a concept of user roles, but I'm curious if Endpoints
do. I've tried using the OAuthService.isUserAdmin() call but that doesn't
seem to be working out very well and the docs have a big old warning saying
Note: You should not confuse Endpoints auth with the auth for
non-Endpoints App Engine web apps described in the article on configuration settings
https://developers.google.com/appengine/articles/auth in the Admin
Console, where you also specify the user login requirement in your
web.xmlhttps://developers.google.com/appengine/docs/java/config/webxml#Security_and_Authentication
file. That approach is not used with Endpoints."
Do I have to create some sort of authorization myself that uses the User object that's passed into the update method? Any thoughts?
I had similar issues. Indeed OAuth user service has nothing to do with AppEngine user service. What I ended up doing was having a dedicated user type entity in my datastore where I store a specific flag (regular/admin) for each user. This flag is updated when I use AppEngine user service (i.e. so that the administrators I specified in the console get the proper admin flag).
In my endpoints API I get the current user authDomain and id, look up in my datastore to check whether it has the admin flag. The key of my user entity is composed of "authDomain:userId" and as I only support google user for now, it looks like (gmail.com:123456789)
This means that an administrator has to login once using the AppEngine UserService (i.e. a dedicated webpage in my case) so that the flag is properly updated
I needed to do the same thing and validate some endpoint to grant access only to admin members listed in the project console and used the same implementation presented above, but the oAuthService.isUserAdmin() accept one or more string parameters, this parameters are scopes that you specify and the Oauth uses to get user informations, in my case i just set this parameter and it works like the code bellow.
OAuthService authService = OAuthServiceFactory.getOAuthService();
User user;
try {
com.google.appengine.api.users.User currentUser =
authService.getCurrentUser(Constants.EMAIL_SCOPE);
if (currentUser != null && authService.isUserAdmin(Constants.EMAIL_SCOPE)) {
user = new User(currentUser.getEmail());
return user;
}
...
The EMAIL_SCOPE constant is defined by
public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
In my case i implemented an authenticator, to pass user information to endpoint only if it's admin user, you can read more about the authenticators if you want.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator

How to add a role filter on request combined with OAuth

I would like to develop a portal which contains some modules
The portal and each module consume data provided by a webservice based on Jersey and secured with OAuth 1.0
For the moment I have almost implement the OAuth provider
A user can connect to the portal and access to a module
Each app or module has a specific access token to consume resource
What I want to do is to add a role implementation
For example for the module1, the user can have 2 roles (role1 and role2) but can't use the 2 roles in parallel
First the user uses the access (module1 / user1 / role1) and he will have a token and later the user uses the access (module1 / user1 / role2) and he will have an other token
Depending on the role, I would like to filter the request with a RolesAllowed annotation for example
I have read this article: http://objecthunter.congrace.de/tinybo/blog/articles/89
When the user is authenticated to the web service I could persist in a database the username, and the role used for the module and the RolesAllowedResourceFilterFactory could use the realm to check if the user is in the role and can access to the resource
But can I by-passed the auth method?
Anyway I really need your help to implement this role filter thing
I will try to give you more details if you need
Thanks
The Jersey oauth filter sets the security context based on what access token was used. You just have to make sure your custom implementation of the oauth provider assigns a token with the right return values from the isInRole() method when called with various roles. The role for a given token can be established during the token authorization flow (e.g. the client can request a particular role using a custom parameter that it passes to the server when requesting a request token (this gets passed in the parameters parameter to the provider.newRequestToken() method).
The security context that the oauth filter sets will delegate to the token isInRole() method when determining the roles - and the RolesAllowedResourceFilterFactory relies on the security context. So, everything should work as expected if OAuthToken.isInRole() returns the right value. Are you facing any issues?
I know it is an old post but I was facing similar issue. In my case I solved it exactly the same way as Martin described. During token authorisation I set allowed roles:
String verifier = provider.authorizeToken(token, sContext.getUserPrincipal(), roles);
where provider is #Context DefaultOAuthProvider, token is DefaultOAuthProvider.Token and roles is a Set of roles I want to allow the access by this token:
Set<String> roles = new HashSet<String>();
roles.add("someRole");
Then in my service I just use a #Context SecurityContext method isUserInRole("someRole") which gives me true in case the user is in specified role and false if not:
if (sContext.isUserInRole("someRole")) {
....
}
Hope it will help somebody.

Categories