Apache Shiro - LDAP for Authentication and Properties/Ini for Authorization - apache

i´m trying to add some authentication and authorization functionality to my small web application. therefore i´m using apache shiro.
my plan: using an existing ldap server for user authentication and using a properties or ini file for authorization.
here´s a small example:
user x wants to use the application
he enters his username and his password
the ldap server is used for authentication --> user + pwd correct?
if authentication is verified and correct, a properties file or ini file is used to check if the user is permitted, to start some functions inside the application.
i hope you know what i´m trying to do.
now i´m not sure how to implement this feature. is it enough to use an ini file or is it required to implement my own realm?! is there an example implementation?
i´m grateful for every information
and sorry for my bad english :/

Yes, you have to implement a realm but this is not difficult. You just have to extend JndiLdapRealm and override the queryForAuthorizationInfo method.
This method returns an AuthorizationInfo interface type. In your case the easiest is to return an instance of SimpleAuthorizationInfo which implements this interface.
You must initialize the AuthorizationInfo with the roles and/or permissions for the authenticated user. When this method is called, the user is already authenticated but not authorized.
Inside this method you can read the authorization information from any data source that you want, it can be a properties or ini file, properties associated with the user in the LDAP server, a database or anything that pleases you.
A realm implementation could be:
package example.shiro.realm.ldap;
import javax.naming.NamingException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.subject.PrincipalCollection;
public class JndiLdapAuthzRealm extends JndiLdapRealm {
private List<String> getRoles(String userName) {
List<String> roles = new ArrayList<>();
// TODO: get roles from data source and fill list
roles.add("user");
roles.add("admin");
return roles;
}
private List<String> getPermissions(String userName) {
List<String> perms = new ArrayList<>();
// TODO: get permissions from data source and fill list
perms.add("myapp:run");
perms.add("myapp:file:create");
return perms;
}
#Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
LdapContextFactory ldapContextFactory) throws NamingException {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String userName = principals.getPrimaryPrincipal().toString();
info.addRoles(getRoles(userName));
info.addStringPermissions(getPermissions(userName));
return info;
}
}
In your case, rewrite the getRoles and getPermissions to get the roles and permissions for the authenticated user from the properties or ini file.
In shiro.ini:
[main]
ldapRealm = example.shiro.realm.ldap.JndiLdapAuthzRealm
ldapRealm.userDnTemplate = uid={0},cn=users,cn=accounts,dc=example,dc=com
ldapRealm.contextFactory.url = ldap://192.168.0.10

Related

Store password in Keycloak

I have created a custom user storage provider which will migrate users from legacy system to keycloak's local storage on demand basis.
All the details of the migrated user is being stored in Keycloak except password.
userModel.setEmail(email);
userModel.setEnabled(isEnabled);
userModel.setEmailVerified(isEmailVerified);
userModel.setFirstName(firstName);
userModel.setLastName(lastName);
I am using the above code to store all the information of the user, but I didn't find any method/class in which stores the password.
Can anyone please help me with it?
P.S. I am using Keycloak-3.3.0-Final version.
You can use
session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password(passwordNew, false));
where session is the current KeycloakSession which you have access to in your custom user storage provider.
Thanks to Boomer's answer I managed to make it work in my implementation where the isValid function - which sends the POST request to validate the password - needed to trigger the update of password in Keycloak database.
#Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
UserCredentialModel cred = (UserCredentialModel)input;
// sending a POST request
Response response = userService.validateLogin(user.getUsername(), new EventivalUserCredentialsDto(cred.getValue()));
boolean isValid = HttpStatus.SC_OK == response.getStatus();
if (isValid) {
// save the password to local (keycloak's native) database
session.userCredentialManager().updateCredential(realm, user, cred);
// unset the federation link to never ask again - Import Implementation Strategy
user.setFederationLink(null);
}
return isValid;
}

Provide authorisation using apache shiro for Rest service method in java application

in my simple web application, i have two web service method. one is storeName()and another one is viewAllNames() methods.
Admin role can be access to both methods. But user role should be access viewAllNames() method.
I am going to use Apache shiro, maven with web based project and rest service. Provide authorization only for those two methods.
My actual rest url is :
http://localhost:8080/SimpleRest/simpleservice/store/jackreobert --> admin only
http://localhost:8080/SimpleRest/simpleservice/viewall/ --> user only
How to configure shiro.ini, web.xml and annotaion/xml.
For shiro approach, do i need pass any other information into web service url, how to achieve this.
SimpleService.java
package com.simple.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.json.JSONException;
#Path("/simpleservice")
public class SimpleService {
#Path("/store/{name}")
#GET
#Produces("application/json")
public Response storeName(#PathParam("name") String name) throws JSONException {
/**
* Here insert logic
*/
String result = "Given name: " + name + " successfully stored";
return Respo}nse.status(200).entity(result).build();
}
#Path("/viewall")
#GET
#Produces("application/json")
public Response viewAllNames() throws JSONException {
/**
* Here retrieve logic
*/
String result = "All names are taken";
return Response.status(200).entity(result).build();
}}
Thanks for reading for my post.
Help me,
Thanks in advance.
My suggestion is to use Permissions. Protect your application resources with permissions. Assign permissions to roles and assign Roles to Users.
As of Shiro 1.4.0-RC2 there is official JAX-RS support, take a look at the sample.

Apache shiro with jersey

Im building a web service app using jersey.
For authorization/authentication im using apache shiro.
I found some tutorials showing how to use apache shiro in a web app. They show the login method using a .jsp page that have a username and password field and than this .jsp page is configured in shiro.ini like this:
[main]
shiro.loginUrl = /login.jsp
[urls]
/login.jsp = authc
/logout = logout
I Wanna know how to make this authentication without a any .jsp page, because my project have only web services. So i think that i need a login service, than i created one:
#POST
#Path("/login")
public Response login(#FormParam("username") final String username, #FormParam("password") final String password, #FormParam("remember") final boolean remember) {
final Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
final UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
token.setRememberMe(remember);
currentUser.login(token);
} catch (final AuthenticationException e) {
return Response.status(Status.BAD_REQUEST).entity("Usuário ou senha inválido").build();
}
}
And this is my shiro.ini conf:
[urls]
/security/login = anon
/security/isAuthenticated = anon
/** = authcBasic
Once that the user wont be authenticated to log in i include /security/login = anon.
Is this the correct way to authenticated a user with apache shiro in a webservice environment?
You don't need a login service. Actually, authenticating and using the service should be two different things. What you need to do is:
Know what pages do you want to authenticate
Configure Shiro to authenticate those pages through your authentication method.
your shiro.ini will look to something like this:
[main]
myRealm = com.my.package.MyRealm
myAuthc = com.my.package.MyAuthenticationFilter
[urls]
/public/** = anon
/** = myAuthc
You will need to implement both the realm and the filter. You can implement the filter using AuthenticatingFilter or even one of the sub-classes, like BasicHttpAuthenticationFilter. The realm can be implemented using the AuthenticatingRealm class.
More on realms here and more on Shiro on web here. Notice that to make your filter available what you will need to do is basically set up the filter on the web.xml
After coding the realm and the filter, your code should work as expected. As defined on the shiro.ini any path that starts with public/ will not be authenticated and all the other paths will be authenticated through your com.my.package.MyAuthenticationFilter. Please notice that order matters: if you define the /** = myAuthc line first it will authenticate everything, including paths that start with /public/.

how to obtain a list of all currently logged-in users (including rememberme cookies) in grails with spring security

I'm building a grails app that has the spring-security-core 1.2.7.3 plugin as well as spring-security-ui 0.2 plugin, and would like to obtain a list of ALL the users that are currently logged in (ie have a currently active session). Users can login either through a login controller (daoAuthenticationProvider) or automatically through a rememberMe cookie.
I have implemented the code below, using ConcurrentSessionControlStrategy to create a sessionRegistry:
in /conf/spring/resources.groovy:
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
beans = {
userDetailsService(lablore.MyUserDetailsService)
sessionRegistry(SessionRegistryImpl)
sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
maximumSessions = -1
}
concurrentSessionFilter(ConcurrentSessionFilter){
sessionRegistry = sessionRegistry
expiredUrl = '/login/concurrentSession'
}
}
In /plugins/spring-security-core/conf/DefaultSecurityConfig.groovy
useHttpSessionEventPublisher = true
In the controller:
controller{
def sessionRegistry
action(){
def loggedInUsers = sessionRegistry.getAllPrincipals()
}
}
It works well for
-users that login through the login page
-users that logout through a 'logout' link
-users who's session expires
HOWEVER, it does NOT work for users that authenticate automatically with a rememberMe cookie. It doesn't see that they have a newly created session.
If I understand correctly, this is because the RememberMeAuthenticationFilter is 'further up' in the filter chain compared to the ConcurrentSessionFilter, which is the one running the sessionRegistry? Or, I messed something up with my configurations....
Any help on how to get this to work would be great !
Thanks!!
The ConcurrentSessionControlStrategy is deprecated,
Use the ConcurrentSessionControlAuthenticationStrategy instead
Alternatively,
You can implement the HttpSessionListener interface which has the sessionCreated(HttpSessionEvent event) and sessionDestroyed(HttpSessionEvent event) methods, But you have to add the class you used
Implementations of this interface are notified of changes to the list of active sessions in a web application. To receive notification events, the implementation class must be configured in the deployment descriptor for the web application.
You can either add the implementation class to your deployment descriptor like so(i.e you web.xml file)
<listener>
<listener-class>com.hazelcast.web.SessionListener</listener-class>
</listener>
or by using the WebXmlConfig plugin in grails
Your implementation class could look like below, see Online users with Spring Security also
class WebSessionListener implements HttpSessionListener{
sessionCreated(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
sessionDestroyed(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
}

Drools KnowledgeAgent and Guvnor: authentication fails

This one is really driving me nuts.
I have a Guvnor inside JBoss AS (more on versions later). I have edited the components.xml to enable authentication (with JAAS, I have the users and passwords set up just fine) and role based permission. I have an 'admin' user with full privileges and an 'agent' user, with read-only permissions. So far so good, in Guvnor I can see the users and proper privileges, from browsers I can login with the 'admin' user to upload rules and such, and I can download changeset.xmls and binaries with the 'agent' user.
Now, from a Java application I set up a knowledge agent. In the UrlResource, I set up the username ('agent') and password. The changeset downloads just fine, however, the changeset.xml refers to other resources (such as PKG). Downloading them fails (HTTP 401). Seems like Drools forgets about my credentials in the way.
Editing the changeset.xml by hand, and adding enableBasicAuthentcation, username and password - it works fine. But this is not the way to go, really.
I have been looking for either solution: a) see some option panel in Guvnor, so that I can set up what to embed in changeset.xml automatically when deploying packages b) find a way, so the credentials are passed around in my Java project so everything works.
Now, I tried Drools 5.1.1, 5.2.FINAL, 5.3.CR1, looked through the documentation of these versions. The only remark I found was in the 5.3 docs: "The User ID and Password in the change-set should be consistent with the requirements of the Authenticator configured in components.xml." - thank you, I understand, but how to do that? "Please refer to the "Security - Authentication and basic access" section of the "Administration Guide" for more details." I did and found nothing.
So really, what am I missing, or what am I doing wrong? Is really the only way to solve this is not to use authentication? Or edit changeset.xmls by hand at every change?
Any help would be greatly appreciated.
There's a workaround posted here
[JBRULES-3465]
https://issues.jboss.org/browse/JBRULES-3465
with the constraint it only works for one username per agent
I already got this bug while using KnowledgeAgent, It seems that Url resources are not set correctly (i'm talking about 5.1.1 version)
As a workaround, you can set up a new Agent Event Listener so force use of username/password this way (it's an inner class in my exemple):
private static final class AuthKnowledgeAgentEventListener extends
DefaultKnowledgeAgentEventListener {
private final String username;
private final String password;
private AuthKnowledgeAgentEventListener(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public void beforeChangeSetApplied(BeforeChangeSetAppliedEvent event) {
// Obliged to do this to get UrlResources done correctly...
ChangeSet changeSet = event.getChangeSet();
for (Resource res : changeSet.getResourcesAdded()) {
if (res instanceof UrlResource) {
setupUrlResource((UrlResource) res);
}
}
for (Resource res : changeSet.getResourcesModified()) {
if (res instanceof UrlResource) {
setupUrlResource((UrlResource) res);
}
}
// maybe this is needed for deleted resources, i didn't check
}
private void setupUrlResource(UrlResource resource) {
if (starter.droolsAuthenticationEnabled) {
resource.setBasicAuthentication("enabled");
resource.setUsername(username);
resource.setPassword(password);
}
}
}
and then you set this event listener to you agent:
agent.addEventListener(new AuthKnowledgeAgentEventListener("myusername","mypassword"));
And that's it!