I'd like to know if it is safe to inject the persistent entity via #AuthenticationPrincipal in a method annotated with #RequestMapping.
My concern is following:
#GetMapping("/orders")
public List<OrderDto> getOrders(#AuthenticationPrincipal User user) { ... }
in the code above, the method should return all orders for currently logged user (which it does), however, if I modify the code as follows
#GetMapping("/orders")
public List<OrderDto> getOrders(User user) { ... }
then I'm able to retrieve orders for all users (by changing the user's role to admin in the http request). Is something like this possible while using #AuthenticationPrincipal? How does the #AuthenticationPrincipal annotation ensure that the possible attacker would not be able to inject some malicious code to the entity?
To clarify, the User entity is annotated with #Entity.
I'm currently using Spring Security 5.1.4.
Related
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 pretty new to the concept of patterns. I am practising my dependency injection skills as well as using DAO principles. The code I have written works but I feel that it can be written in a more elegant fashion. I've tried restructuring it a pattern I saw but that complicated things so not sure if I implemented it correctly. As a general rule when a web application communicates with a database and throws out result, how should one structure their project?
I've heard of the MVC principle but that doesn't necessarily add database to the mix.
This is what I have so far:
A class containing a controller in a Controller package:
#RestController
public class ResponseController {
#Autowired
MongoBase dbConnection;
#RequestMapping(value = "/jsonresult", method = RequestMethod.GET)
#ResponseBody
public String jsonresult(#RequestParam(value = "id", required = true) String id){
return dbConnection.documentToJSON(id, Constants.database,Constants.collection);
}
#RequestMapping(value = "/alljsonresult", method = RequestMethod.GET)
#ResponseBody
public String alljsonresult(){
return dbConnection.allDocumentToJSON(Constants.database,Constants.collection);
}}
A class containing CRUD methods to the database in a Database package:
#Component
public class MongoBase {
#Autowired
MongoClient mongoClient;
public MongoBase() {
try {
mongoClient = new MongoClient("localhost", 27017);
} catch (Exception e) {
e.printStackTrace();
}
}
public void printAllCollection(String databaseName, String collectionName) {
...
}
So is there a better way/more efficient way of writing thi? Also I feel I haven't fully implemented DI in the Monogbase class since it contains the new keyword..
If you are using springboot, then you don't need this old style
also don't need to create mongoClient bean your self, spring boot help you in it
You just need to add following properties in application.properties file
#mongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=app1
Also declares a spring-boot-starter-data-mongodb in your pom or gradle
it's a cool and super awesome dependency for accessing Data with MongoDB
you can read about it from here[https://spring.io/guides/gs/accessing-data-mongodb/]
suppose you have a domain
#Document(collection = "domain")
public class User {
#Id
private long id;
#Indexed(unique = true)
private String domain;
private boolean displayAds;
//getters and setters
}
Now if we need to perform curd operation on this domain, extends MongoRepository, you have CRUD function automatically. Spring data come with many magic findBy queries, review the official Spring data MongoDB – Query methods for detail.
public interface UserRepository extends MongoRepository<User, Long> {
Domain findFirstByDomain(String domain);
Domain findByDomainAndDisplayAds(String domain, boolean displayAds);
//Supports native JSON query string
#Query("{domain:'?0'}")
Domain findCustomByDomain(String domain);
#Query("{domain: { $regex: ?0 } })")
List<Domain> findCustomByRegExDomain(String domain);
}
UserRepository extends the MongoRepository interface and plugs in the type of values and id it works with: User and Long. Out-of-the-box, this interface comes with many operations, including standard CRUD operations (create-read-update-delete).
now you can easly use it in your controller
#RestController
public class ResponseController {
#Autowired
UserRepository userRepository;
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
User create(#RequestBody #Valid User user) {
return userRepository.create(user);
}
}
also you can do with it lot of things. you just need to go throw with it doc.
Also you you can use mongoTemplate for execute the query
#Autowired
MongoTemplate mongoTemplate;
When I build web applications I typically define the full chain as follows:
Client Side:
View - This is the V in MVC where you control visuals & user action derived workflow.
Controller - This is the C in MVC where workflow is managed. Most Client processing will go here and multiple Client calls can be made to get/send data or perform lookups.
Client - This is where you make a call to a REST web service and parse/deserialize the results/handle exceptions.
Server Side:
RestController (Sometimes Called Resource) - This is your REST API endpoint. Here you extract & validate a request.
Service - This is where most of your server logic will go. Sometimes you might have to make multiple data access calls or call other service functions.
DataAccessObject (Sometimes Called Provider) - This is your database interaction to pull data from your database into a model. CRUD operations (Create Read Update Delete)
Example Scenario:
Lets say we want to submit data & permissions for a given user
UserView.jsp - User types in user & permission data and hits submit.
UserController.java - validates User & permission data, does any necessary lookups, then calls UserClient.
UserClient.java - Builds the REST request and calls the /user/create REST endpoint.
UserRestController.java - Unpackages/Validates the request, then calls UserManagementService
UserManagementService.java - Server Logic happens here! Lets say I have two tables in my database. A User table and a Permissions table. I want to store the user information in the User table and the permission information in the permission table so I will call the UserDAO for the user data and the PermissionDAO for the permission data.
UserDAO & PermissionDAO - Saves The passed models to their respective tables.
Return to Service, Return to RestController, Return to Client (Parse Response), Return to Controller (Move the Workflow forward with a redirect or a success message).
Conclusion:
This may seem like a lot of in-between steps but this design provides a ton of flexibility, especially if your building large, complex web services. Each component has a specific purpose, follows an easy naming convention, and splits complex logic into smaller, simpler steps.
I'd like to recommend some improvements to your solution:
You're using Spring. You should not create any injected beans using new. Let Spring instantiate and manage those beans for you.
Use the #Repository annotation to mark your persistence class.
Make your repository class interface based.
Don't embed "Mongo" into the class name. You happen to be using Mongo as your persistence provider now, but you may change your mind later. Don't reveal implementation information in your class names - hide it.
The Controller is part of the UI. It uses repositories and/or services to fulfill use cases. It's perfectly correct to use Spring to inject the repository into the Controller.
I'd recommend that you use Spring Boot, an opinionated version of Spring. Have a look at their guides to learn how to use it properly.
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?
Consider an Struts 2 + Spring 4 project.
For each login the User object is put in session. As a very simple action it will look
public class LoginProcess implements ServletRequestAware {
#Inject
private AuthenticationServices authenticationServices;
public String execute() {
//The login method makes a new User and fills its setters
User newUser = authenticationServices.login(....);
getServletRequest().getSession().setAttribute("USER_SESSION", user);
}
}
As we manually make a new User object so it is not managed spring bean, and we can't use spring features in User class: #Inject , #Value ,...
I tried to change user as:
#Named
#Scope(value="session")
public class User { ...
#Inject
private AccountServices accountServices;
}
and inject the User in instead of calling new User, but I get the error:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
Well although it describes the error, but I can not find how can I fix it, and I am not sure if this is the correct way at all. It seems that I can only use spring session scope been when I am using spring mvc
Any comments ?!
Why I need this ?! (Simplified situation)
The user object has a getAccounts() methods which get all user accounts. Getting user accounts is an expensive operation, and it is possible that a user does not require its accounts during its login.
So, instead of get user accounts as soon as user logs in, we let the get method get user accounts if it does not have it:
public class User() {
private Accounts accounts;
#Inject
private AccountServices accountServices;
Accounts getAccounts() {
if (accounts == null) {
accounts = accountServices.getUserAccountsFromDB(...)
}
return accounts;
}
Don't create a new instance of User by yourself, instead get a bean from Spring context.
For example you can achieve it by implementing ApplicationContextAware interface and calling one of getBean methods.
User user = applicationContext.getBean(User.class);
// populate user and put it into session
In that way it is a Spring managed bean an all required properties should be injected.
BUT consider changing your User to a simple POJO and moving all business logic (such as fetching users accounts) to some more appropriate place, in that way your model layer will be cleaner and easily testable.
I'm using spring security and I've 2 methods in my MovieService-
#PreAuthorize("hasRole('ROLE_DIRECTOR')")
public Map<String, Movie> getAllMovies() {
.......
.......
}
And another method is -
public Movie getMovieByMovieCode(String movieCode) {
Map<String, Movie> movies = getAllMovies();
Movie movie = movies.get(movieCode);
return movie;
}
As shown in code, I'm calling getAllMovies() method from inside the getMovieByMovieCode() method. So, if a user who does NOT have ROLE_DIRECTOR role, it tries to access getMovieByMovieCode() method and from inside this method, getAllMovies() method is also accessed.
But the expected behavior is that this user should not be able to access getAllMovies() method as this user does not have role ROLE_DIRECTOR.
How can I achieve this behavior? Do I need to use <dispatcher> inside filter mapping?
Spring Security authorization using annotations is implemented using Spring AOP proxies. That, among other things, means that whenever you are calling methods on the current object, you are referencing the object itself, not the proxy. That is why authentication is not done. The simple workaround would be to annotate the getMovieByMovieCode with the #PreAuthorize annotation too.
Here is some helpfull reading on AOP proxies: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies