I have secured resources with #Auth annotation on User entity.
But I want to control the flow of data, so that requests without header will be also valid (by default, Dropwizard Auth returns 401 Unauthorized).
I think that the best for it would be my custom annotation, which will be applied to User parameter, and, if from SecurityContext userPrincipal is null, then I can return some default User or just pass the null further.
How to implement such an annotation? Or maybe You have other suggestions?
EDIT
I think that I found the code that may suit my needs :
User user = (User) context.getUserPrincipal();
But how can I make paramether annotation from it, so that this would be executed to every annotated object?
Related
I am new to Spring boot. I need to implement role based authorization in spring boot. I am having different roles and multiple users will be mapped to each role. I will be setting different access (read, add, delete, edit)
Whenever an api gets called, need to check the access and allow permission.
I am planning to use interceptor to call method having the query to get the access from DB and deny or access the api.
Is there any other better way I can use for the same?
If you are using Spring Security you can handle it with method security annotations like #PreAuthorize, #PostAuthorize .. even combine them to new annotations.
First your User need to implements UserDetails then you should implement getAuthorities() method according to your Role and Authority structure Spring Security basically checks what getAuthority() method returns if returned value prefixed with "ROLE_" like "ROLE_ADMIN" it will be processed as ROLE if it does not prefixed with "ROLE_" it will be processed as Authority
you can use method annotation for checking authority and role like following example:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasAuthority("READ")")
and Spring Security will check your granted Authorities by getAuthorities() implementation of your User then, according to your annotation it will be checked automatically by Spring Security behalf of you.
For clarity you can check
https://www.baeldung.com/spring-security-granted-authority-vs-role
For quick working implementation you can check article below(I would not directly use it but you can understand the idea. Also you can use permissions but simple solution probably the solution below.):
https://www.baeldung.com/role-and-privilege-for-spring-security-registration
For authorization, there can be these two ways as well:
OAuth (Reference - https://medium.com/#bvulaj/mapping-your-users-and-roles-with-spring-boot-oauth2-a7ac3bbe8e7f)
Spring Security Roles and Privileges(Reference- https://www.baeldung.com/role-and-privilege-for-spring-security-registration)
You can create a custom annotation to handle request for each role. I you can read this article for more details about how to implement.
And in api will have format:
#GetMapping(...)
#YouCustomAnnotation("roleName")
public void doSomeThing(){
}
This api will be called if role of user matched with role define in annotation and server will return 404 code if user's role not match.
We are using Keycloak v6.0.0 and Spring Framework. We want to define some access conditions based on #PathVariable value and #RequestBody fields value in Keycloak admin panel, but we can't find any way to do this. How can we access these fields value and define Resource or Policy based on it?
I think that it will help you the Admin REST API. Also, I've write a Keycloak-Client in Kotlin for some basic APIs like: signUp, login and logout. Maybe it will help you.
First of all, I know about #PreAuthorize annotations and about Expression based access control.
For the sake of learning (as well as for many reasons), what I would like to have is this:
Users are authenticated and their roles are provided by an LDAP directory and populated into the Principal object when they authenticate. This works, as in "it is currently in place in a project".
An annotation (chosen to be #AccessControl) implements the paradigm that access control is entirely tied to roles. The annotation can be set on a class/type (the REST controller), in which case it applies to any method on which there's not also another such annotation, or on a method (a REST endpoint). The deepest annotation always wins, whether it is restricting or relaxing the authorization constraint.
The access control logic, which is a bit more complex than what I could get from the expression based access control, would be enforced by another piece of code. It is also a bit more maintainable but I guess this is only in my eyes.
As an example, a controller would have, except for an #AccessControl annotation over a method, endpoints that can only be accessed by users with ADMIN in their list of roles:
#RestController
#RequestMapping("/admin")
#AccessControl({ Roles.ADMIN })
public class AdminController {
...
}
My current indecision, after reading a lot these past days is more about whether to write a custom request filter or rather an AOP advice.
With a custom request filter, I find myself unable (for the moment) to determine which method of which controller the request is going to be mapped to. The annotations are out of my reach.
With an AOP advice, I don't know (yet) how to reply to the client with a 403 Forbidden status.
My questions stem directly from these two points:
How can I get the controller method that will be called for a client request?
How can I return an HTTP status code from an AOP advice and effectively end the processing of the request when the client is not authorized?
It turned out to be much simpler than I initially thought and I completed it in less than a day, using the AOP option.
This is the code of the AccessControl annotation, comments removed:
#Documented
#Inherited
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
public #interface AccessControl {
public String[] value() default {};
}
It can be placed either on a controller (see my original post/question) or on a controller method:
#RestController
#RequestMapping("/admin")
#AccessControl({ Roles.ADMIN })
public class AdminController {
// This endpoint has open access: no authorization check will happen.
#AccessControl
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getNoCheck(#PathVariable Integer id) {
return service.get(id);
}
// This endpoint specifically allows access to the "USER" role, which is lower
// than ADMIN in my hierarchy of roles.
#AccessControl(Roles.USER)
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getCheckUser(#PathVariable Integer id) {
return service.get(id);
}
// The authorization check defaults to checking the "ADMIN" role, because there's
// no #AccessControl annotation here.
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getCheckRoleAdmin(#PathVariable Integer id) {
return service.get(id);
}
}
In order to perform the actual verification, two questions must be answered:
first, which methods are to be processed?
second, what is checked?
Question 1: which methods are to be processed?
To me, the answer was something like "all REST endpoints in my code". Since my code lies in a specific root package, and since I'm using the RequestMapping annotation in Spring, the concrete answer comes in the form of a Pointcut specification:
#Pointcut("execution(#org.springframework.web.bind.annotation.RequestMapping * *(..)) && within(my.package..*)")
Question 2: what exactly is checked at runtime?
I will not put the entire code here but basically, the answer consists in comparing the user's roles with the roles required by the method (or its controller if the method itself bears no access control specification).
#Around("accessControlled()")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
...
// Get the roles specified in the access control rule that applies (from the method annotation, or from the controller annotation).
// Get the user roles from the UserDetails previously saved when the user went through the authentication process.
// Check authorizations: does the user have one role that is required? If no, throw an exception. If yes, don't do anything.
// No exception has been thrown: let the method proceed and return its results.
}
What was bothering me in my initial thinking was the exception. Since I already had an exception mapper class that bears the #ControllerAdvice annotation, I just reused that class to map my specific AccessControlException to a 403 Forbidden status code.
For retrieving the user's roles, I used SecurityContextHolder.getContext().getAuthentication() to recover the authentication token, then authentication.getPrincipal() to retrieve the custom user details object, which has a roles field that I normally set up during the authentication process.
The code above is not to be used as-is (for instance, path mapping collisions will happen), but this is just to convey the general idea.
I want to provide an approach you can use if you want to follow the AOP advice root:
Concerning this point if using AOP:
How can I return an HTTP status code from an AOP advice and
effectively end the processing of the request when the client is not
authorized? solution:
In your aspect class, using at Around Advice kindly do the following:
#Around("execution(* net.my.package.AdminController.*(..)) && args(.., principal)")
public ResponseEntity<?> processRequest(final ProceedingJoinPoint joinPoint, final Principal principal) {
final String controllerMethodName = joinPoint.getSignature().getName();
LOGGER.info("Controller Method name : {}", controllerMethodName);
final boolean isAuthSuccessful = authenticationService.authenticate(principal);//Pass auth details here
if(!isAuthSuccessful) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Request declined"); //End request if auth failed
} else {
try {
return (ResponseEntity<?>)joinPoint.proceed(); //Continue with request
} catch (Throwable e) {
LOGGER.error("Error In Aspect :", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("failed request");
}
}
}
Well, the above code has comments addressing the challenges you are facing. But for this code to work ensure to do the following:
Make Sure all your controller methods you want to intercept do return ResponseEntity
You can change the #Around aspect to use a Point cut with value of #annotation for your #AccessControl annotation and you are free to chain the conditions in the #Around aspect
Make sure you find a way to pass data to this aspect so that you have a way to validate user credentials
I'm trying to achieve object-based access control using annotations. Would like to verify user's access to a specific object with minimal code.
Assume i have a spring mvc request handler:
public String updateForm(Form form){
//update form
formService.updateForm(form);
}
I would like to add annotation which can validate based on spring security's logged in user if the user has access.
#ValidateAuth
public String updateForm(Form form){
//dont allow if no auth
//update form
formService.updateForm(form);
}
Now the annotation should consider the form's id(form.getId()) as well as logged in user principal to check if the auth is allowed. I'm trying to see if there is a way to do it via aop where we can read a argument's value as well in a neat way.
Kindly help, thanks in advance.
Spring Security has out-of-the-box support for ACL.
It is covered in this section of the Spring Security reference guide.
When configured you can use the #PreAuthorize tag with an expression to allow or disallow access to the method. You could also filter results from a method.
#PreAuthorize("hasPermission(#form, 'admin')")
public String updateForm(Form form){ ... }
You can also make it a bit more complex
#PreAuthorize("hasRole("ADMINISTRATOR") and hasPermission(#form, 'admin')")
public String updateForm(Form form){ ... }
I have attempted to implement the solutions provided in this question.
However, none of them are working for me. In my abstract base controller, I have the following method (I also tried in helper class with static methods, but same problem):
public User getUser() {
User user =
(User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
the getAuthentication() is returning null, even though in my jsps, the following is working perfectly:
<sec:authentication property="principal.firstname" />
In that my custom user object is accesible.
Why does getAuthentication return null ?
Also, I tried passing in the Principal as parameter to my specific controller method, this is null resolves to null too ...
Why is my principal null, but yet the jsp is working ?
Make sure you don't have filters = "none" in <intercept-url> for your controller's URL, it disables Spring Security filters, thus security context doesn't get populated.
If you need to make your controller accessible for everyone, use other approaches.