I'm reading about Spring Security ACL lib and I am a bit confused about some concepts. Regarding SIDs, it states to my confusion:
ACL_SID allows us to uniquely identify any principal or authority in
the system ("SID" stands for "security identity"). The only columns
are the ID, a textual representation of the SID, and a flag to
indicate whether the textual representation refers to a prncipal name
or a GrantedAuthority.
So is principal the same as role? Is GrantedAuthority an equal concept or a particular role? Does a boolean field principal = true mean that the identifier has semantical value or simply that it is a role within the system?. If so, why do you want to store SIDs that are not accounted in the system?
An ACL (Access Control List) consists of a number of entries (rules), where every entry references a sid, which is a user or a group of users to apply the entry/rule for.
If the principal field is set to true, the sid is a principal, that is a single user. The sid field then contains the username.
If the principal field is set to false, the sid is an authority, for example ROLE_ADMIN. A GrantedAuthority is basically the same as a role (a role is a kind of authority).
Related
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.
I have a RESTful service that exposes resources like /user/{id}
Now, the user can provide the credentials, get the token and access the resource. However, once authenticated, the user can access the resources for any id.
Meaning, user1 can access the URIs like /user/1 as well as user/2 and so on. I ended up using a Principal in the controller methods and started checking the id of the Principal with the id the user is trying to access.
Further, the user has multiple resources associated with it. Say, user1 owns res1 and res2, user2 owns res3 and res4. These can be accessed via /user/1/res/2. I need a way where I can prevent /user/1/res/3 as res3 is owned by user1 and not user2.
But I believe that this problem is very common and I am not really convinced with my solution.
Is there a better way to deal with this problem?
Thanks
You should not be exposing resourse /user/{id} at all if you all user can do is access only their own ID.
If I understand correctly, just exposing /user is enough, find ID of user from Principal or session etc and return result.
If you really want to do it, you can have custom implementation of #PreAuthorize. Got this code from a blog.
#PreAuthorize("isUsersRes(#id)")
#RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
public UsersfindById(#PathVariable long id) {
return Users.findOne(id);
}
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
And implemenation of isUsersRes
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public boolean isMember(Long OrganizationId) {
//user logic
}
Check complete blog here
This is a common problem with varied solutions. Also its not a problem related to REST alone. We have had this ever since apps exist. Employee can see his salary slip, leave records, etc but not another employee's.
One solution I like the most is 'security in depth'. This idea comes from how I have seen this work in banking systems for decades. This needs to get supported in the DB layer first.
You would need a table design like this example (or whatever your app's entity hierarchical structure is):
Organisation
-Dept
--user
And all non-master tables need to have a relation to one of these entities. Example:
Payslip -> user
Leave record -> user
Manager -> dept
HR Manager -> org
etc...
You would need another table to map out the basic access levels (This can get complex if we need to implement different sub access levels)
user1:dept2:org1
user2:dept2:org1
(I have seen some implementations that send this table's info as part of an encrypted access token that is used on every access request if the access has to be sessionless.)
You have not mentioned a framework/language but most languages have a database layer. For example if the DB layer is hibernate-java. There are interceptors (https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String)) that can be used to modify the query thats being executed.
Every query to DB will go with additional where-clause for these relationship keys.
We can get clever with Spring AOP, REST interceptors and a lot of other techniques on top of this basic architecture to enforce this security.
Idea will be that DB layer does not return data thats not accessible to the logged in user principal irrespective of what queries higher layer code uses.
if this is in place, a REST GET call for
/payslip/user1/Jan-2017
will end up with a 404 and not a 403.
Expecting this to be solved by a framework or a superficial set of interceptors is both risky and not future proof. We end up continuously tweaking the interceptors as the url patterns evolve.
Addition to show table examples:
ACL table
user, uid, dept, org
--------------------
jhon, 1 , 1 , 1
mary, 2 , 2 , 1
will, 3 , 2 , 1
Payslip table
--------------
month, net, deductions,..., uid
-------------------------------------
Jan , 200, 15.5 ,..., 3
Feb , 200, 15.5 ,..., 3
Project table
-------------
pname, pstart, pbudget, dept
------------------------------------
mark1, 12/21 , 20000 , 2
markx, 12/31 , 40000 , 2
What you want is user roles and permissions + cross user control. To find out user roles and permissions refer this
Also additionally you may want to cross check their user ID to the resource ID. Since you cannot let user1's resource ID 1 to view by user2, you will need to add userID as part of the resource id ex:- /user/user_id_1.
Otherwise we don't have a logical way to separate which resources are applicable to which users.
I have tried to use OACC framework
createResource("USER", "domain1","kim")
USER : resourceClass
domain1 : domain
kim : value to be stored (externalId)
Sorry if arguments order are mismatched
again I have called for :
createResource("USER", "domain2","kim")
in this case, the system conflicts to store the same name , logically yes, externalId can't be same.
But how can we store the real values into this, a normal Resource instantiate will result in its own ID.
Is there any api to "getRoles" if I pass in a UserName ?
A multi-tenant application will have (users/roles/permission) per domain, 1 user can exist in multiple domains !!
But how can we store the real values into this, a normal Resource instantiate will result in its own ID.
As you noticed, the createResource() method can accept a String externalId parameter, which has to be a globally unique string identifier for the new resource. "Globally unique" here means unique across all domains and resource classes.
The externalId is useful to reference OACC resources by an identifier from your application domain, without having to use or store the unique resourceId OACC generates and returns from createResource().
One way to use non-unique application domain identifiers as external IDs would be to prefix identifiers with an appropriate classifier to make them unique within OACC. Such a prefix could simply be composed of the resource class, domain, or both. To use your example:
createResource("USER", "domain1", "user.domain1.kim");
createResource("USER", "domain2", "user.domain2.kim");
Is there any api to "getRoles" if I pass in a UserName ?
In OACC you can model role assignment by having one resource "inherit" all permissions from another resource by granting it the *INHERIT permission. In other words, there is no explicit concept of roles within OACC and hence no dedicated method to "getRoles".
In any case, the best practice for authorizations is to check against permissions, not against role membership.
For auditing or administrative purposes, if you do need to find the resources a specific user inherits permissions from, OACC provides query methods such as getResourcesByResourcePermissions(). If, for example, all your "roles" were modeled as resources of the resource class ROLE, you could find the ones your user inherits with the following call:
getResourcesByResourcePermissions(userResource,
"ROLE",
ResourcePermissions
.getInstance(ResourcePermissions.INHERIT));
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();
I am developing in Java using Spring for my company. We are developing an application that we have a local LDAP server. The LDAP server will be used to fetch information. However, we do not want the user of the application to know the LDAP structure/schema. This is because the customer/user will have their own LDAP server with their own LDAP structure/schema.
For example, the customer/user will fetch information through a user interface by providing the LDAP server details that will be used to conect to the LDAP server. Once connected, they will be able to fetch information by executing a query. Now, they will not know information about the LDAP structure. The code that will be written, will be done by executing the user query. If the query is run, then it will return that information, otherwise it will give an Exception.
The problem that I am facing is that:
When you use Spring LDAP, there is something called AttributesMapper and ContextMapper. In order to use this, I have to pass in a strongly typed object. So for example:
public class EmployeeAttributesMapper implements AttributesMapper {
public EmployeeAttributesMapper() {
}
/**
* This method maps the Employee Entity to data stored in the LDAP Server through a Attribute.
* #param attrs the name of the Attribute to get
* #return the Employee Entity.
*/
public Object mapFromAttributes(Attributes attrs) throws NamingException {
Employee employee = new Employee();
employee.setFirstName((String) attrs.get("cn").get());
return employee;
}
}
The above code when executed depending on the query, will fetch information only about the cn attribute and nothing else. Is there something else that doesn't require an strongly typed object?
I am not sure what to do.
For example, to search for all employees:
public List getAllEmployees() {
return ldapTemplate.search("", "(objectclass=person)", new EmployeeContextMapper());
}
This will return all the employees but only set the cn attributes. But, on the customer LDAP server they may not have the attribute called cn.
What is the best way to do this? Our code that we write, acts as a proxy between the user interface and the customer LDAP server.
The simplest answer is to abstract the intended use of attributes to the attribute type, which is an OID or an alias. For example, map "Name" to the cn (common name) attribute alias, "First Name" to the givenName attribute, "Last Name" to the sn attribute, and so forth. IETF has many RFCs which describe attributes that have been recommended for use with LDAP directory server databases. inetOrgPerson (RFC2798) is a good example.
If the presentation is separated from the implementation correctly, users need never know that the data comes from an LDAP directory server at all, not to mention what the attribute names are.