How to know all roles a user has in Spring security - java

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();

Related

Spring JPA Multiple join conditions

I am working on my first Spring Boot application and I am having a bit of a struggle within the joining of two tables/entities.
I need to join my user_roles table to the users table so that the user will get its correct roles directly from the database (postgres).
The thing that is my the fact that the roles are depending on the current organization that the user is in. The app allows a user to work for multiple companies at once, and switch between them. The roles a user has in organization 1 may not apply to organization 2.
The user has a roles field in the database which should be mapped to the correct roles. The user also has a 'current_organization_id' field, which contains the current user organization.
I think the solution wouldn't be that difficult but my brain is not solving this case yet.
Roles could be mapped on 'user_id' (UUID) and 'cur_org_id' (UUID) with an table like 'user_role_within_org'. But I don't know how to do that within JPA.
An explanation how your solution is solving this would be nice, it is important to understand code and not just copy paste it ;)
Thanks in advance,
Jesse
Current database model
Try storing a Map of Organization->List in the User.
#Entity
class User {
#ManyToMany
Map<Organization, List<Role>> roles;
}
To get the roles in current organization try the following JPQL:
SELECT VALUE(r)
FROM User u JOIN u.roles r
WHERE KEY(r).id = u.currentOrganization.id

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

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.

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.

login and security system with Mysql

I need to make a java program with a security and a login system for a project at school
I was thinking of using a enum for the security role in java with a security level:
public enum Role {
lECTOR(0), COORDINATOR(1), ADMINISTRATOR(2);
private int securityLevel;
Role(int securityLevel) {
this.securityLevel = securityLevel;
}
public int returnSecurityLevel() {
return securityLevel;
}
In mysql I'm thinking of making two tables
table users with a userid, username, password and security_id
roles with a security_id, rolename this table I would link to the enum
depending on the securitylevel I would then say what a specific user can or can't do.
Is this a good way to work or not, any suggestions are more then welcome.
You should consider if you really need a separate table for the enum. Maybe just have a column for it in the user table.
The only reason to keep it a separate table is if a user can have multiple roles.

Querying many to many association where the entity doesn't have a certain value using Hibernate Criteria API

I got two classes User and Role which are mapped to each other using a many to many associations. Now I'm trying to query all users which doesn't have a certain role using the Hibernate Criteria API. But I'm a little bit stuck.
To query users with a certain role I use the following query, which works just fine.
Session session = getSessionFactory().getCurrentSession();
Criteria mainCrit = session.createCriteria(boClass);
return mainCrit.createAlias("roles", "r").add( Restrictions.eq("r.name", roleName)).list();
Now I'm a little bit confused how to reverse the query and get all user that don't have a certain role. If possible I want to explicit exclude a certain role and don't query for all roles chaining them with OR as there may be more roles added later dynamically.
UPDATE
To get a better understanding of my scenario you can see the association I'm trying to query in another question.
Furthermore I would also add that the name property of the Role class is an Enum, don't know if this is important or changes the way to query the database.
There's perhaps a more elegant way, but the only one I've found is to query for all the users for which there doesn't exist any role in the user's roles which has the given role name.
This is done like this:
Criteria c = session.createCriteria(User.class, "user");
DetachedCriteria roleOfUserWithName = DetachedCriteria.forClass(Role.class, "role");
roleOfUserWithName.createAlias("role.users", "userOfTheRole");
roleOfUserWithName.add(Restrictions.eqProperty("userOfTheRole.id", "user.id"));
roleOfUserWithName.add(Restrictions.eq("role.name", roleName);
roleOfUserWithName.setProjection(Projections.id());
c.add(Subqueries.notExists(roleOfUserWithName));
It's equivalent to the following HQL query:
select user from User user where not exists (
select role.id from Role role inner join role.users userOfTheRole
where userOfTheRole.id = user.id
and role.name = :roleName);

Categories