I'm setting up unit tests for my Spring MVC Controllers and I am attempting to leverage the Spring MVC test framework. For each endpoint in my controller, I want to ensure that only users with designated authorities have access. My problem is that I use a custom User implementation and get class cast exceptions when using the mockMvc tooling around this.
For each request, I expect it to look something like this:
mockMvc.perform(MockMvcRequestBuilders.get(path).with(user("user").authorities(authorities)))
.andExpect(status().isOk())
.andExpect(authenticated().withUsername("user"));
I would like to somehow tweak the above statement to specify my custom user principal. See Spring's user method below. My first thought was I would override the class UserRequestPostProcessor and tweak it so that it's using my custom user type instead of the standard Spring Security user, but this class is declared as final and cannot be subclassed. Is there any support for overriding the default behavior and using a custom user type??
public static UserRequestPostProcessor user(String username) {
return new UserRequestPostProcessor(username);
}
From what I have seen, I am a candidate for annotating my tests with #WithSecurityContext so that I can set my custom user principal in the Authentication. My concern here is that I would be limited to testing one user for each method and this doesn't scale for what I am trying to do.
How can I test requests made by multiple custom users?
I learned of two ways to do this:
I could create a class that implements UserDetails and extends my
custom user principal class. I could then pass this as a parameter
to the user method.
I can scratch the user method altogether, and pass in my
Authentication already set with my custom user principal.
I went with the latter.
protected void performTest(HttpMethod method, String path, Object[] pathVariables, UserRoleEnum role,
boolean expectAllowed) throws Exception {
mockMvc.perform(buildRequest(method, path, pathVariables).with(authentication(createAuthentication(role))))
.andExpect(expectAllowed ? status().isNotFound() : status().isForbidden())
.andExpect(authenticated().withUsername("user"));
}
note - the buildRequest and createAuthentication methods are helper methods that I created, whereas all the other methods are provided by Spring. The helper methods return MockHttpServletRequestBuilder and Authentication respectively.
Related
I am following up from this question:
How to configure Spring Boot Security so that a user is only allowed to update their own profile
Imagine I had an end-point /user/edit/{id}, I want this to be accessible if the user either tries to edit themslves (eg: a user with ID 1 accessing /user/edit/1 but not being able to access user/edit/2) or, if they are an admin, to be able to edit any user.
Is there any way I can achieve this in the security configuration?
.antMatchers("/user/edit/**").hasRole("ADMIN")
Would restrict this to admin users, I want either admin or the id matching the user's id.
The only thing I can think of is inside the controller having something like
#GetMapping("/edit/{id}")
public void edit(#PathVariable("id") int id, Principal principal) {
User u = (User) userDetailsService.loadUserByUsername(principal.getName());
if(u.getId() == id || u.getRoles().contains("ADMIN")) {
//accept uer
}
}
But I was under the impression we shouldn't encode access logic in our controller?
It is possible to use Spring Security's Method Security Expressions to do this. Example copied from the docs:
#PreAuthorize("#c.name == authentication.name")
public void doSomething(#P("c") Contact contact);
Read the sections preceding, as there is some configuration needed. Also note that if an expression is used repeatedly you can define your own security annotations.
I was under the impression we shouldn't encode access logic in our
controller?
"Should" is maybe too strong a word, IMHO. Security expressions are powerful, and in theory would allow you to keep all security checks separate from the controller logic. Easier to spot when a check is wrong, or missing. Easier to compare with the Swagger annotations too, if you are using those to document your endpoints.
But it can get trickier when you have to do something like filter rows returned so that the user only sees some of the results. Spring Security can do that using #PostFilter. But sometimes it isn't optimal. For example, if you know that certain rows aren't going to be returned you may be able to run a faster query, rather than filter out rows after the fact.
My first Spring Security project had queries like that, so ever since I have tended to use controller logic instead of security annotations. But that's not a good reason to never use annotations! So by all means use security expressions when you can, but if you have trouble with them or other considerations arise, integrating security with your controller logic isn't so bad IMHO.
To control role access in your controller you can use annotations like #Secured or #PreAuthorize.
To use the #Secured, put in you security config class:
#EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
And now you can use it in your controller:
#Secured("ROLE_ADMIN")
#PostMapping
public Account post(Account account, double amount){
// ...
}
To use the #PreAuthorize, put in you security config class:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}
And now you can use it in your controller:
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#PostMapping
public Account post(Account account, double amount){
// ...
}
For more information you can check here the spring docs.
i started a project on spring boot using a rest a webservice, when i shared it between my team they puted some comments :
get method need to be grouped Ex : get/users & get/users/{id} will be get/users/{id}
remove put method & just use post Ex: post/users/0 add | post/users/{id} update
make a helper class for Jdbc Template and call it in the repository classes to centralize the code
pls guys help me to solve this i'm so confused, and thank you
get method need to be grouped Ex : get/users & get/users/{id} will be
get/users/{id}
I do not agree with this. /get/users will be returning List<User> and get/users/{id} will return User that matches with {id}
remove put method & just use post Ex: post/users/0 add |
post/users/{id} update
Post should be used when you create a new resource. POST is not idempotent. Each time you call a post a new resource will be created.
e.g. Calling POST /Users will create a new User every-time.
PUT on other hands works like upsert. Create if the resource is not present and update/replace if present. Put is idempotent and doesn't change the resource's state even if it's called multiple times.
make a helper class for Jdbc Template and call it in the repository
classes to centralize the code
Helper classes help to separate the concerns and achieve single responsibility.
However, JdbcTemplate is a ready to use abstraction of JDBC. I don't see any point in creating Helper. You can create a DataAccessObject (DAO) or Repository which has-a JdbcTemplate. Like the two Dao shown below
public class UserDao {
#Autowired
private JdbcTemplate jdbcTemplate;
public User findUserById(String id){}
public void addUser(User user){}
}
// -------
public class BooksDao{
#Autowired
private JdbcTemplate jdbcTemplate;
public List<Book> getAllBooksByType(String type){}
public void Book getBookByName(String name){}
}
Now, your Dao objects can be called from Controller or if you need to modify data before/after DB operation, best is to have a Service layer between Controller and Dao.
Don't bother too much about recommendations or rules. Stick to the basic OOPS concepts. Those are really easy to understand and implement.
Always:
Encapsulate data variables and methods working on those variables together
Make sure your class has a Single Responsibility
Write smaller and testable methods (if you can't write tests to cover your method, then something is wrong with your method)
Always keep the concerns separate
Make sure your objects are loosely coupled. (You are already using spring so just use the spring's auto-wiring)
If I use Spring, which of these two methods is more correct.
Can I use the new() operator even if I use dipendency injection?.Can I mix both?
I would like to have some clarification on these concepts.
Thanks
First method:
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(new User());
return "index";
}
Second Method:
#Autowired
User user;
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(user);
return "index";
}
By using dependency injection does not mean that the use of new operator is automatically prohibited throughout your code. It's just different approaches applied to different requirements.
A web application in spring is composed of a number of collaborating beans that are instantiated by the framework and (unless overriding the default scope) are singletons. This means that they must not preserve any state since they are shared across all requests (threads). In other words if you autowire the User object (or any other model attribute), it is created on application context initialization and the same instance is given to any user request. This also means that if a request modifies the object, other requests will see the modification as well. Needless to say this is erroneous behavior in multithreaded applications because your User object (or other model attribute) belongs to the request, so it must have the very narrow scope of a method invocation, or session at most.
You can also have spring create beans with different scopes for you, but for a simple scenario of a model attribute initialization, the new operator is sufficient. See the following documentation if interested in bean scopes : Bean scopes
So in your use case, the second method is totally wrong.
But you can also delegate the creation of your model attributes to spring if they are used as command objects (i.e. if you want to bind request parameters to them). Just add it in the method signature (with or without the modelattribute annotation).
So you may also write the above code as
#RequestMapping(method=RequestMethod.GET)
public String create(#ModelAttribute User user){
return "index";
}
see also : Supported method argument types
If you want your beans to be "managed" by Spring (for e.g. to use with Dependency Injection or PropertySources or any other Spring-related functionality), then you do NOT create new objects on your own. You declare them (via XML or JavaConfig) and let Spring create and manage them.
If the beans don't need to be "managed" by Spring, you can create a new instance using new operator.
In your case, is this particular object - User - used anywhere else in code? Is it being injected into any other Spring bean? Or is any other Spring bean being injected in User? How about any other Spring-based functionality?
If the answer to all these questions is "No", then you can use the first method (create a new object and return it). As soon as the create() method execution is complete, the User object created there would go out of scope and will be marked for GC. The User object created in this method will eventually be GC-ed.
Things can be injected in two ways in a Spring MVC applications. And yes, you can you can mix injection and creation if doing right.
Components like the controller in your example are singletons managed by the application context. If you inject anything to them it is global, not per request or session! So a user is not the right thing to inject, a user directory can be. Be aware of this as you are writing a multithreaded application!
Request related things can be injected to the method like the used locale, the request, the user principal may be injected as parameters, see a full list at Spring MVC Documentation.
But if you create a model attribute you may use new() to create it from scratch. I will not be filled by spring but to be used by your view to display data created by the controller. When created in the request mapped method that is ok.
I'm building a straight forward AJAX / JSON web service with Spring. The common data flow is:
some DTO from browser
v
Spring #Controller method
v
Spring #Service method
I'm looking for the most easy way to handle data validation.
I know the #Valid annotation which works pretty well inside #Controller methods.
Why does #Valid not work within #Service methods?
I mean: A service method can be used by any other service and controller. So wouldn't it make much more sense to validate at #Service level?
Let's take this simple example:
MyDTO.java:
public class MyDTO {
#NotNull
public String required
#Min(18)
public int age;
}
MyServiceImpl.java:
public MyDomainObject foo(MyDTO myDTO) {
// persist myDTO
// and return created domain object
}
MyController.java:
#Autowired
MyService myService;
#Autowired // some simple bean mapper like Dozer or Orika
Mapper mapper; // for converting domain objects to DTO
#RequestMapping(...)
public MyDomainObjectDTO doSomething(#RequestBody MyDTO myDTO) {
mapper.map(myService.foo(myDTO), MyDomainObjectDTO.class);
}
Is it common practice that the service method receives the DTO?
If yes: What's the best practice to validate that DTO inside the service method?
If no: Should maybe the controller manipulate the Domain object and just let the service save that object? (this seems pretty useless to me)
In my opinion the service should be responsible for only data consistency.
How do you solve this?
My answer? Both.
The service must check its own contract for validity.
The controller is part of the UI. It should validate and bind for a better user experience, but the service should not rely on it.
The service cannot know how it's being called. What if you wrap it as a REST service?
The service also knows about business logic violations in a way that no UI can. It needs to validate to make sure that the use case is fulfilled appropriately.
Double bag it; do both.
See my other answer: Check preconditions in Controller or Service layer
If you really want to do validation like error handling in your Service layer similar to Spring MVC you can use javax.validation and AspectJ (to advice the methods to validate) which is what I do because I like making reflection do the work and declarative programming (annotations).
Spring MVC doesn't need to do AspectJ/AOP to do the error handling because the methods are being called through reflection (url routing/dispatching).
Finally for you MVC code you should know that #Valid is sort of unofficially deprecated. Instead consider #Validated which will leverage more of the javax.validation features.
I'd like to be able to create a base controller in my Spring app that, among other things, determines if a user is a registered user or not. This base controller, following the template design pattern, would contain an abstract protected method that controller subclasses would implement.
The abstract method would have passed to it an instance of User, registered or otherwise. However, I have no idea how I would do this since it seems that by using controllers purely using the #Controller annotation each controller is free to define their request handling method however they like.
Would creating some sort of user service class that is injected into each controller and used to validate a user be one way to get around this? This begs the question (at least for me) how does such a controller get a hold of a HttpServletRequest or the Session object?
Thanks.
Define an abstract BaseController, with no annotations
Define concrete and abstract methods
Call these methods from subclasses (which are annotated with #Controller) whenever needed.
I think the Base Controller is not a good idea if the only code it is to have is for UserAuthentication...instead use Spring security. This is the best option.
Alternatively, you can have methods like this...take a look at the Spring reference..
#Controller("loginController")
public class LoginController {
#RequestMapping(value="/login.do", method=RequestMethod.POST)
public String login(Model model, HttpServletRequest request) {
String userIdFromRequest = (String)request.getParameter("userId");
String password = (String)request.getParameter("password");
boolean verified = ...send userIdFromRequest and password to the user service for
verification...
if (verified){
request.getSession().setAttribute("userId", userIdFromRequest);
}
}
//More Methods
}
Did it help?
-SB
The basic problem is that annotational bootstrapping is not polymorphic. I found this paper useful: http://sanguinecomputing.com/design-pattern-for-hierarchical-controller-organization-with-annotational-configuration-spring-mvc-3/