Custom User, roles, permissions implementing UserDetail and UserDetailService with Spring Security - java

I am looking to use Spring Security w/ MySql to store Users, Roles, Permissions (authorities). I have done the following:
ApplicationUser (id, firstName, lastName, username, password, roles)
ApplicationRole (id, name, description, permissions) implements GrantedAuthority
ApplicationPermission (id, name, description) implements GrantedAuthority
I created a class that implements UserDetailService. In the loadUserByUsername method I doing the following:
ApplicationUser applicationUser = userRepository.findByUsername(username);
if(applicationUser == null) {
throw new UsernameNotFoundException(username);
}
// Extract role/role permission grantedAuthorities from db user
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (Role role: applicationUser.getRoles()) {
grantedAuthorities.add(role);
grantedAuthorities.addAll(role.getPermissions());
}
return new User(applicationUser.getUsername(), applicationUser.getPassword(), grantedAuthorities);
However, this still leaves me a bit confused and here are a few of my questions...
Is it necessary to implement GrantedAuthority on my ApplicationRole
and ApplicationPermission class. I don't see others doing it in many
examples that I have seen but it appears to have made my life easier
so I can just get them and pass them in as is.
I have not
implemented UserDetails with my ApplicationUser class. If I were to
do this what would be the purpose? Is the only reason so I would be
able to customize the getAuthorities, isAccountNonExpired,
isAccountNonLocked, isCredentialsNonExpired, isEnabled methods? Is
there even a default implementation of these methods or only if I
create my versions. Why would I need to implement getAuthorities
here if I am already doing it with loadbyUserName in
UserDetailsServiceImpl?
What is the purpose of SimpleGrantedAuthority? I see it implements GrantedAuthority and only accepts a String name. However, I don't feel like I need to use it since I implemented GrantedAuthority with my ApplicationRole and ApplicationPermission classes.
I think my problem is that I am confused as to when/how to properly implement UserDetail and UserDetailService making sure I am loading the authorities from the database and setting them each time a user logs in. Additionally, is there any point in implementing GrantedAuthority with my ApplicationRole and ApplicationPermission class the way I am?

It's not necessary and I wouldn't recommend it either. It's good to keep these different aspects of your application separate. As far as I know it's a common practice to fetch the roles in your custom UserDetailsService and wrap their textual representation in a SimpleGrantedAuthority (i.e. their name). Note that to differentiate between roles and authorities within Spring Security you have to prefix roles with the ROLE_ tag by default (see hasRole and hasAuthority access-control expressions).
Having a custom UserDetails implementation is not necessary but might have benefits, for example you can store extra fields that are specific to your application. The default implementation is org.springframework.security.core.userdetails.User, most of the time you can just extend this one.
See #1
is there any point in implementing GrantedAuthority with my ApplicationRole and ApplicationPermission class the way I am?
It wouldn't make any sense, no. Many use-cases are covered by the existing implementations, but for the most simple use-cases you can just stick to SimpleGrantedAuthority.

Related

Should duplicate values in aggregate roots be checked in the domain layer or the application layer?

I am new to DDD, and I have ran into a problem with unique constraints. I have a problem where one of the fields (a value object) on my aggregate root cannot be a duplicate value. For example, a user has a unique username.
My domain layer contains:
public class User {
private UUID id;
private Username username;
private User(UUID id, Username username) {
this.id = id;
this.username = username;
}
public void rename(Username username) {
if (!username.equals(username)) {
this.username = username;
EventBus.raise(new UserRenamedEvent(username));
}
}
public UUID getId() {
return id;
}
public Username getUsername() {
return username;
}
public static User create(UUID id, Username username) {
User user = new User(id, username);
EventBus.raise(new UserCreatedEvent(user));
return user;
}
}
Username:
public record Username(String name) {
// Validation on username
}
As well as a simple CRUD repository interface, implemented in the infrastructure layer.
My application layer contains:
UserSerivce:
public interface UserService {
UUID createUser(Username username);
// Get, update and delete functions...
}
And UserServiceImpl:
public class UserServiceImpl implements UserService {
public UUID createUser(Username username) {
// Repository returns an Optional<User>
if (userRepository.findByName(username).isPresent()) {
throw new DuplicateUsernameException();
}
User user = User.create(UUID.randomUUID(), username);
repsitory.save(user);
return user.getId();
}
}
This solution doesn't feel right, as preventing duplicate usernames is domain logic, and should not be in the application layer. I have also tried creating a domain service to check for duplicate usernames, but this also feels wrong as the application service has access to the repository and can do this by itself.
If the user was part of an aggregate I would do the validation at the aggregate root level, but as user is the aggregate this isn't possible. I would really like to know the best place to validate the unique constraint.
EDIT: I decided to take VoiceOfUnreasons advice and not worry about it too much. I put the logic to check for duplicates in the application service, as it makes for readable code and works as expected.
This solution doesn't feel right, as preventing duplicate usernames is domain logic, and should not be in the application layer.
There are at least two common answers.
One is to accept that "domain layer" vs "application layer" is a somewhat artificial distinction, and to not get too hung up on where the branching logic happens. We're trying to ship code that meets a business need; we don't get bonus points for style.
Another approach is to separate the act of retrieving some information from the act of deciding what to do with it.
Consider:
public UUID createUser(Username username) {
return createUser(
UUID.randomUUID(),
username,
userRepository.findByName(username).isPresent()
);
}
UUID createUser(UUID userId, Username username, boolean isPresent) {
if (isPresent) {
throw new DuplicateUsernameException();
}
User user = User.create(userId, username);
repository.save(user);
return user.getId();
}
What I'm hoping to make clear here is that we actually have two different kinds of problems to address. The first is that we'd like to separate the I/O side effects from the logic. The second is that our logic has two different outcomes, and those outcomes are mapped to different side effects.
// warning: pseudo code ahead
select User.create(userId, username, isPresent)
case User(u):
repository.save(u)
return u.getId()
case Duplicate:
throw new DuplicateUsernameException()
In effect, User::create isn't returning User, but rather some sort of Result that is an abstraction of all of the different possible outcomes of the create operation. We want the semantics of a process, rather than a factory.
So we probably wouldn't use the spelling User::create, but instead something like CreateUser::run or CreateUser::result.
There are lots of ways you might actually perform the implementation; you could return a discriminated union from the domain code, and then have some flavor of switch statement in the application code, or you could return an interface, with different implementations depending on the result of the domain logic, or....
It largely depends on how important it is that the domain layer is "pure", how much additional complexity you are willing to take on to get it, including how you feel about testing the designs, which idioms your development team is comfortable with, and so on.
It should be noted that one can reasonably argue that the definition of "unique" itself belongs in the domain, rather than in the application.
In that case, the design is mostly the same, except that instead of passing "the answer" to the domain code, we pass the capability of asking the question.
select User.create(
userId,
username,
SomeServiceWrapperAround(
userRepository::findByName
))
Or we define a protocol, where the domain code returns representations of questions, and the application code does the I/O and passes representations of the answers back to the domain model.
(and, in my experience, begin questioning whether all this ceremony is actually making the design "better")
This solution doesn't feel right, as preventing duplicate usernames is
domain logic, and should not be in the application layer.
Correct.
I have also tried creating a domain service to check for duplicate
usernames, but this also feels wrong as the application service has
access to the repository and can do this by itself.
Yes, the application service could do the work by itself, but you have taken a conscious decision to create a dedicated layer for those 'tricky' bits where the domain aggregate cannot do the work on its own and you do not want to leak domain knowledge into the application service.
There's nothing wrong with this. Just don't let 'Domain Service' become a your default approach whenever something looks a bit tricky. You'll end up with an anaemic domain model and all the logic sitting in Domain Services. But, sometimes, a Domain Service is the only pragmatic solution and feel free to use them when all else fails.
The other alternative is to search up "Domain Events". They keep a better separation in my view but demands more effort and plumbing.
Here's a C# introduction, but just as applicable to the world of java.
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation

How to design rest endpoint URLs for sub resources

Let's consider we have two entities. User entity can have many offers and an Offer must have one User.
class User {
...
#OneToMany(mappedBy = "user", orphanRemoval = true)
private Set<Offer> offers;
}
class Offer {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user")
private User user;
}
At this moment there are two controllers. UserController and the OrderController. The UserController is mapped under /api/v1/users/ and the OrderController is mapped under /api/v1/orders/.
What should an endpoint look like that fetches the user's offer list?
Should it be in the same controller? I do have by functionality project structure.
How to modify or delete an Offer for a particular User? In case we would have /api/v1/users/{username}/offers/{offerId} to delete or update an offer, should we have also /api/v1/offers/{offerId} endpoint that allows to edit or remove an offer? Perhaps it is worth having it for an administrator?
General rules of thumb that I use when creating my endpoints are:
URLs need to be clean and easy to understand
They should be as short as possible while still informative.
Try to build it in a such manner that it allows you to reuse it within the reasonable amount
Think about the user experience(whether this is called from browser or mobile app etc.)
I am not sure if there is a written rule exactly how one should build an URL though.
In your specific case I would use /users/{username}/offers/{offerId} only if this is the only place you are exposing and using offers, since you are separating your code by functionality.
If you have any further logic around offers and/or have beans that have such logic I would create a separate controller for Offers which would be under /offers.
Concerning your last question. This very much depends on what you are trying to achieve. If you need to be able to update/delete/create offers then it makes sense to have such functionality. Even if it only used by the administrator. You can restrict the access to the endpoint. How to do that depends on the way you are authorize your users and the information that you have on them. Most people use roles.
If you decide to have the full CRUD functionality I would suggest to use a single path with combination of request methods.
Personally I would create the following:
#RestController
#RequestMapping(value = "/users")
class UserController {
#GetMapping("{userId}/offers")
public Set<Offer> getAllOffers(#PathVariable("userId") String userId){
...
}
#GetMapping("{userId}/offers")
public Offer getOffer(#PathVariable("userId") String userId, #RequestParam(required = true) String offerId){
...
}
#PutMapping("{userId}/offers")
public Offer createOffer(#PathVariable("userId") String userId, #RequestBody Offer offer){
...
}
#PostMapping("{userId}/offers")
public Offer updateOffer(#PathVariable("userId") String userId, #RequestBody Offer offer){
...
}
#DeleteMapping("{userId}/offers")
public void deleteOffer(#PathVariable("userId") String userId, #RequestParam(required = true) String offerId){
...
}
}
In this scenario I think the POST/PUT for create and update will be cleaner as there will be no duplication of information. To be precise the IDs.
I agree that it should be in the same 'UserController', it makes sense because offers belong to the user, so having an endpoint like:
#GetMapping("{user}/offers")
public Set<OfferDTO> getOffers(#PathVariable("user") String user) {
return offerService.getOffers(user);
}
You can define special DTOs for getting the meta-data from the offers if you wanted to display them in a list for example, and you could display them as a list to your user.
You could set up a similar endpoint for updating the offer which could be a POST endpoint, and a DELETE endpoint for deleting. You might want to think about what would happen if the user is looking at an offer when you delete it though, like making an asynchronous task for deleting the offer in a background thread and updating the UI to inform the user that the offer is deleted.
Spring has some really nice annotations for security stuff (check this and this), you could write your own annotation for administrator endpoints:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAuthority('" + ROLE_ADMIN + "')")
public #interface IsAdmin {}
Then annotate your method like this:
#DeleteMapping("/{user}/{offer}/delete")
#IsReceiverAdmin
public void delete(#PathVariable("user") String user, #PathVariable("offer") String offer){
return offerService.delete(user, offer);
}
Of course the implementation of the service layer would be quite important, but it could be as simple as calling your repositories and performing the operations there :)

How to restrict user functionality

I need to restrict edtiting functionality in my Vaadin 12 app basing on user assigned roles
Currently we have page level security, i.e user can see only pages basing on assigned roles, like #Secured(Role.VIEW_PAGE)
User must see view button when view role assigned and see edit button when edit role assigned.
You can use this method for this, which accepts one Role and looks if the users authorities contain this Role. It basically does the same as SecurityUtils.isAccessGranted(securedClass) only it looks for a specific role and not the roles defined in the #Secured annotation of a view.
public static boolean userHasRole(Role role){ // your own Role class/enum
Authentication userAuthentication = SecurityContextHolder.getContext().getAuthentication();
List<String> allowedRoles = Arrays.asList(role.name());
return userAuthentication.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.anyMatch(allowedRoles::contains);
}
A good place to define this method is the already mentioned SecurityUtils class if you already have it. If not, now is a good time to create one ;).
You can now call the method like this in your view:
if(SecurityUtils.userHasRole(Role.EDIT_PAGE)){
add(new Button("Edit"));
} else {
add(new Button("View"));
}
If you wish, you can of course change this method to accept a list of roles instead of only one.

What methods the DAO layer should implement?

I'm being writing web application right now and got stuck at the stage of designing the DAO layer. Already have surfed through variaty of articles on that subject but still haven't reached the clarity.
And my question is:
What kind of methods are allowed to be declared and implemented in DAO classes?
Are they just limited set of CRUD operations (create, read, update, delete)? Is it considered a good practice to extend this set with some custom methods fitting in your concrete needs in developing a business logic layer?
For example:
I have an entity class named User which fields fully reflect a corresponding database table. Let's suppose I'm about to validate user's login and password while authorization. What approach would be more appropriate in that situation? Should I call generic CRUD method List<User> findAll() to retrieve all users and validate these concrete login and password in method of business logic (something like boolean validateUser(String login, String password, List<User> users)). Or should I add some not CRUD method like boolean checkUser(String login, String password) directly in DAO class?
I'm being writing web application right now and got stuck at the stage of designing the DAO layer.
Are you writing it by hand using plain old servlets or using a web framework (e.g Spring MVC) to ease your pain?
And my question is: What kind of methods are allowed to be declared and implemented in DAO classes? Are they just limited set of CRUD operations (create, read, update, delete)?**
Generally speaking, yes -- the methods should be limited to CRUD operations.
Is it considered a good practice to extend this set with some custom methods fitting in your concrete needs in developing a business logic layer?**
Within reason, yes.
For example: I have an entity class named User which fields are fully reflect a corresponding database table. Let's suppose I'm about to validate user's login and password while authorization. What approach would be more appropriate in that situation? Should I call generic CRUD method List findAll() to retrieve all users and validate these concrete login and password in method of business logic (something like boolean validateUser(String login, String password, List users)). Or should I add some not CRUD method like boolean checkUser(String login, String password) directly in DAO class?
In addition to the standard CRUD methods that your DAO classes are expected to have, you can add a helper method like:
User findUser(String login) whose job is to return a populated User object for the specified login parameter, or null if the login is non-existent.
User findUser(String login) should leverage List<User> findAll() which should already exist with the rest of the CRUD methods in the DAO class. It could be implemented as follows:
public User findUser(String login) {
User user = null;
final SearchCriteria criteria = buildSearchCriteria(login); // construct a search criteria from the login parameter
List<User> users = findAll(criteria);
if (null != users) {
assert (users.size() == 1) : "More than one user was matched - login must be unique.";
user = users.get(0);
}
return user;
}
To summarize, you only need 2 methods to implement the authorization logic:
User findUser(String login) in your DAO layer and;
boolean checkUser(String login, String password) which will be in your frontend layer. If you are not using any web framework, this method will be implemented in your servlet otherwise this method will go inside your controller class (if you are using an MVC framework).
short answer is no: dont add any business logic in dao layer. Just let every tier has its own responsibility, so when someone else (or even you) needs changes they will know where to look.
EDIT:
Other answers:
Q:What kind of methods are allowed to be declared and implemented in DAO classes?
A:Methods that allow you to access database objects or their properties. i.e. public User getOldUsers(), public boolean isUserExist(Long userId)etc...
Q:Are they just limited set of CRUD operations (create, read, update, delete)?
A:Yes additionally you can control persistence or transaction properties
Q:Generic CRUDS?
A:Almost all the projects I work on we use generic CRUDS (AbstractDao classes) and add additional methods

How to know all roles a user has in Spring security

I use Spring security so log in on the site I'm building to study Spring. I used Hibernate to map User object to other objects that allow user to see or not pages, according to its role. It works.
I was asking if I can know by using authorities-by-username-query, all the role a single user has, instead of checking my User object.
This is my query:
authorities-by-username-query="select u1.utente,
u1.email, u2.descrizione, u3.id_autorizzazioni,
u3.autorizzazione from autenticazione u1,
autorizzazione u2, autorizzazioni
u3 where u1.utente = u3.utente and
u2.id_autorizzazione = u3.autorizzazione and u1.email =?"
If I write that query on mysql console I get 3 records, so the query it's right.
This is my database
I used
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
but I always get a collection that have size 1 instead of 3 (I have 3 roles in the db for the user, as you can see in the pic). I don't understand if it depends on my db or my authorities-by-username-query. Probably one of the two things is wrong
You can use SecurityContextHolder.getContext().getAuthentication().getAuthorities() to obtain a collection of the currently logged in user's roles.
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
You have the collection of roles in the authorities variable.
Little bit more excercise
private boolean hasRole(String role) {
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>)
SecurityContextHolder.getContext().getAuthentication().getAuthorities();
boolean hasRole = false;
for (GrantedAuthority authority : authorities) {
hasRole = authority.getAuthority().equals(role);
if (hasRole) {
break;
}
}
return hasRole;
}
You can use two different approaches:
query for the user roles (of any user) in the database
"look" in the Session to get the roles of the current user
For the first approach it is possible to invoke the UserDetailsService.loadUserByUsername(String username) method.
For the second approach: SecurityContextHolder.getContext().getAuthentication().getAuthorities();

Categories