I am new to spring, I would like to ask if the setup below is possible with spring-security:
USER->USER_TYPE->PERMISSION
User has User Type;
A User Type has Permission(s);
Honestly, I've been reading ACL but still I am not sure if I can use it to this type of setup.
TIA
In Spring Security the nomenclature is USER => GROUP => AUTHORITY. ACLs are a even more granular method of handling authorization which I believe is unnecessary for your described use case.
There is a pre-defined database schema for storing this information as well as a UserDetailsService implementation for populating your security context/principal. Look at the documentation on Authority Groups for more information.
Related
I am new to Spring boot. I need to implement role based authorization in spring boot. I am having different roles and multiple users will be mapped to each role. I will be setting different access (read, add, delete, edit)
Whenever an api gets called, need to check the access and allow permission.
I am planning to use interceptor to call method having the query to get the access from DB and deny or access the api.
Is there any other better way I can use for the same?
If you are using Spring Security you can handle it with method security annotations like #PreAuthorize, #PostAuthorize .. even combine them to new annotations.
First your User need to implements UserDetails then you should implement getAuthorities() method according to your Role and Authority structure Spring Security basically checks what getAuthority() method returns if returned value prefixed with "ROLE_" like "ROLE_ADMIN" it will be processed as ROLE if it does not prefixed with "ROLE_" it will be processed as Authority
you can use method annotation for checking authority and role like following example:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasAuthority("READ")")
and Spring Security will check your granted Authorities by getAuthorities() implementation of your User then, according to your annotation it will be checked automatically by Spring Security behalf of you.
For clarity you can check
https://www.baeldung.com/spring-security-granted-authority-vs-role
For quick working implementation you can check article below(I would not directly use it but you can understand the idea. Also you can use permissions but simple solution probably the solution below.):
https://www.baeldung.com/role-and-privilege-for-spring-security-registration
For authorization, there can be these two ways as well:
OAuth (Reference - https://medium.com/#bvulaj/mapping-your-users-and-roles-with-spring-boot-oauth2-a7ac3bbe8e7f)
Spring Security Roles and Privileges(Reference- https://www.baeldung.com/role-and-privilege-for-spring-security-registration)
You can create a custom annotation to handle request for each role. I you can read this article for more details about how to implement.
And in api will have format:
#GetMapping(...)
#YouCustomAnnotation("roleName")
public void doSomeThing(){
}
This api will be called if role of user matched with role define in annotation and server will return 404 code if user's role not match.
I have a Spring REST-API which is secured by SpringSecurity.
I have different ways to authenticate in that application.
Customer (db)
Employees (ldap)
Other Applications/Services (inMemory)
Each of these are covered by a different authentication provider, which properly assigns the access roles. (Works)
However I need to know which authentication provider was used to authenticate the user, so I can write it to the logs, history...
User CUSTOMER/max.pain created a new entry.
And of course the username does not really contain that prefix, neither in the db, nor in ldap.
I considered adding a special role based on the authentication provider used, but extracting that information over and over is annoying and probably has bad performance and might cause problems in the future. (user.getGrantedAuthorities().findStartingWith("AP_"))
I also tried using org.springframework.security.config.annotation.SecurityConfigurerAdapter.addObjectPostProcessor(ObjectPostProcessor<?>) but i cannot use it to wrap the AuthenticationProvider with a custom AuthenticationProvider class, because it requires the use the same class for whatever reason.
Is there an inbuild method I could use to achieve that or do I have to use a custom implementation/configuration?
Prozess steps:
Login: EMPLOYEE/always.calm
Don't check CUSTOMER AuthenticationProvider
Check EMPLOYEE AuthenticationProvider
Don't check SERVICE AuthenticationProvider
authentication.getPrincipal().getAP() = EMPLOYEE
Using a Jersey service, what would be the most elegant method to authenticate a user with more than just username and password? say, a Business Id, and username as the Principals, and password as the only Credential.
I have a postgres DB with a users table, the same username string can exist for multiple Business entities. (A unique constraint belongs to two columns, Business id and username together).
I Use Basic authentication, having the client send an additional HTTP Header for the Business id. how do I go on from here?
Now, Regarding Authorization, how would I set up Roles and Permissions, if roles are created specifically for each business entity (each business can define it's own roles and connect it to permissions.)? Permissions are static.
does apache shiro (or any other pluggable security extension) offer a solution in this case?
Thanks.
You might consider.
Implement auth logic in the good old servlet filter. JAX-RS application is just normal WWW aplication, so Filters fits well as a simple authorization mechanism.
JAX-RS interceptors (PreProcessInterceptor) where you can implement your auth logic as you need (calling database, etc.) This is "more idiomatic" while working with JAX-RS.
Use Spring Security. This is a good option if you are ready to learn a little it of Spring Framework. Spring Security provides full featured authentication and access control mechanism, so you can implement whatever you need. Note that the rest of application does not neeed to use Spring.
You might also use CDI decorators (example) to implement auth logic, but that would be sort of exotic given still low CDI adoption.
Personaly I would go with 1. or 2, for simple cases and 3 for something more advanced.
(Old question! a response for new users) As you tagged SHIRO in your question, you can just add your own logic by extanding org.apache.shiro.realm.jdbc.JdbcRealm and #Override: getRoleNamesForUser(...) , getPermissions(..), doGetAuthenticationInfo(..) This is an example:
#Override
protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
Set<String> roleNames = new LinkedHashSet<>();
Collection<UserRole> roles = /* Get roles from your DB, this example use JPA entity, **but you put here any logic you want**...*/
for(UserRole userRole:roles){
roleNames.add(userRole.getRole().getName());
}
return roleNames; // return roles so Shiro is 'aware' of roles to add them to current user
}
*note that same logic applies for other methods that you override.
** You don't need 2 http calls to log the user, you can just user Shiro programatic auth.
here is a Complete example with Shiro annotation enabled..and more
I'd like to know how to combine these two authentication steps :
check the user/password in an LDAP
add principals (roles) found in a DB to the subject.
The LDAP user repository have no idea about application-specific roles and I don't want to manage the passwords in the application DB. So I need both.
JAAS configuration file allows to have additional LoginModules :
<name used by application to refer to this entry> {
<LoginModule> <flag> <LoginModule options>;
<optional additional LoginModules, flags and options>;
};
but I can't find example that explains how I works.
Is it the good method ?
Thanks
=========================================
Here is my answer :
Indeed we can have additional LoginModules. The JAAS configuration file is :
Sample {
com.sun.security.auth.module.LdapLoginModule Requisite
userProvider="ldap://acme.org:389/OU=Users,OU=_ACME,DC=acmegis,DC=acme,DC=org"
authIdentity="{USERNAME}"
userFilter="(userPrincipalName={USERNAME})"
storePass=true
sample.module.SampleLoginModule required debug=true;
};
Here we have two LoginModules :
The Sun's LdapLoginModule that checks user/password,
and mine sample.module.SampleLoginModule that query my db and fills the principals.
The important parameter is storePass=true that asks the LdapLoginModule to store the username and password in the module's shared state. (see http://docs.oracle.com/javase/6/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/LdapLoginModule.html).
So the next LoginModules can get the username passed to the initialize method in the sharedState Map argument. They should have nothing to do in login() and the query in DB to fill the Principales is done in commit() (like Shimi Bandiel said).
I don't use it yet but there is a DatabaseServerLoginModule developed by JBoss (see http://community.jboss.org/wiki/DatabaseServerLoginModule) that supports authentication and role mapping. Used with password-stacking=useFirstPass we should have the answer to my need without write any line-code (but a beautiful JAAS configuration file).
B.R.
You should implement a LoginModule which in the login method you access the LDAP and check username/password and in the commit method you access the DB and fill the principals.
There is no need here to use multiple LoginModule
It is great!
But implement the LoginModule give you more power to customize the way you interact with LDAP server.
I also struggle the same problem as you.
But remember when implement the LoginModule, you should add the role in login() function , not in commit(), otherwise your subjet will not get the principal.
I need to make a legacy application start using spring security 3.
This app already has its security data model with:
Very simple by far. I can write my custom usersByUsernameQuery and authoritiesByUsernameQuery.
The thing is that there is another table indicating the operation (i.e. #Service layer method) that a Role can execute:
So the administrator can enable/disable a role from accessing an operation through a web interface, without redeploying the app.
I still can annotate the business methods with #Secure('ROLE_ADMIN') for example, but my custom UserDetailsService must know at least the method name that is being secured, so I can perform the right query.
So, the question is: is there a way that my custom UserDetailsService can intercept the method's name that is being secured?
It sounds like your access-decision is based on the "operation role", rather than the user roles, so it might be better to use the "operational role" directly in the Spring Security constraints. That is essentially an RBAC approach, where there is a mapping between the user roles and the operations they are allowed to perform.
You would address the issue in the AuthenticationProvider rather than the UserDetailsService, by adding a mapping layer in there which translates the user roles (supplied by the UserDetailsService) into the rights that the user has within the application. These would make up the collection of authorities that are returned in the Authentication object created by the AuthenticationProvider.
The mapping layer would directly use the data which your administration interface provides.
You might want to take a look at this presentation, by Mike Weisner, which covers similar material, amongst other things.
Not also that Spring Security 3.1 will include an additional GrantedAuthorityMapper strategy to make it easier to plug in a mapping of this kind.