Drools KnowledgeAgent and Guvnor: authentication fails - authentication

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!

Related

Keycloak Spring UMA denied

I am trying to implement authorization for this use case:
user can access only his own resources
admin can access everything
I am trying out Keycloak and it's resource server. For testing purposes and to understand these scopes and permissions and stuff, I have created test client weather-api and one resource Weather with url /weatherforecast.
Then I have scope weather:read and policy that every user with role weatherer can read that resource.
Now when I try to evaluate on a user with that role, I get PERMIT:
and another user without this role gets DENY.
so I guess my policies and permissions are set correctly.
When I try to use this from my service with user-managed-access disabled, I get permit too.
But when I enable user-managed-access, it fails.
I see in debug log that it gets permissions token:
{
...
"permissions": [
{
"scopes": [
"weather:read"
],
"rsid": "ae5ac493-b7dc-481e-9204-a664d1558a51"
}
],
...
}
but then the next message is
Policy enforcement result for path [http://192.168.0.9:5001/weatherforecast] is : DENIED
I tried to debug Keycloak library and found something I don't really understand.
In KeycloakAdapterPolicyEnforcer this part of code:
#Override
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
AccessToken original = accessToken;
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
return true;
}
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade, claims);
if (accessToken == null) {
return false;
}
...
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
return null;
}
...
}
so when UserManagedAccess is not null, requestAuthorizationToken returns null and then it acts like the user is unauthorized with HTTP 401.
What am I missing here? Why it works only without UMA?
I have looked at these (app-authz-uma-photoz, devconf2019-authz) examples and haven't noticed what I am missing.
Except they are actually creating some resources for users from the Java app, I'm not. But I guess it shouldn't matter if I'm protecting user created resources or single "pre-made" URL, right? It should depend only on correct permissions and since they evaluate to PERMIT so I don't see why this doesn't work.
And one more question. Isn't this UMA thing overkill for just "user can access his own, admin can access everything" case when there will never be any sharing between users? I was thinking about some simpler way that could work without creating user resources in Keycloak but I couldn't think of anything, I believe I still need to have connected user ID with some resource ID to make this working.

Logging out the user from other computers (that he logged in before) when he logs in from another computer

I have a web application that employees log in to do stuff. What Im trying to achieve is: When a user logs in from a computer, if he is already logged in on another computer, that must be logged out. The web app is MVC Asp.Net Core 2.2 Code first. I have added a signInManager in startup and edited the PasswordSignInAsync method. I login the system from two different devices. When I click something on the screen from the first computer that I loggedin, it redirects to logout. It seems like working. But Im not sure if this is the right way of doing this. The code I added is: await UserManager.UpdateSecurityStampAsync(user); Inside PasswordSignInAsync method.
Inside the startup class ConfigureServices method I added
'services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddSignInManager<SignInManagerX>()'
Then in SignInManagerX class which is inherited from SignInManager I overrided the PasswordSignInAsync
public override async Task<SignInResult>
PasswordSignInAsync(ApplicationUser user, string password,
bool isPersistent, bool lockoutOnFailure)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckPasswordSignInAsync(user, password,
lockoutOnFailure);
//Here is added
if (attempt.Succeeded)
{
await UserManager.UpdateSecurityStampAsync(user);
}
//Add end
return attempt.Succeeded
? await SignInOrTwoFactorAsync(user, isPersistent)
: attempt;
}
Is this the right way ? or I should add a table to db for logins which holds the info if the user is already logged in on another Ip. Then Logging out that user from all computers if the last and current login attempt is true ?
Yes , the primary purpose of the SecurityStamp is to enable sign out everywhere.
The basic idea is that whenever something security related is changed on the user, like a password, it is a good idea to automatically invalidate any existing sign in cookies, so if your password/account was previously compromised, the attacker no longer has access.
Reference : https://stackoverflow.com/a/19505060/5751404
You can set validateInterval to TimeSpan.Zero for immediate logout .

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;
}

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

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

WebForms - what event to use to authenticate ticket passed in the request body?

That might sound quite trivial, but I could not find a good answer: when the request is coming with a validation ticket in the body of the request, what event is best suited to authenticate the request (and then create FormsAuthenticationTicket and auth cookie for subsequent calls)?
One option is in Page_PreInit in BasePage, another Application_AuthenticateRequest in Global.asax, and yet another FormsAuthentication_OnAuthenticate in Global.asax.
Any link pointing to the solution will be very helpful.
Pawel
I have not been able to find a link with a definitive answer on this, but from personal experience and from reading the ASP.NET Application Life Cycle it seems that Application_BeginRequest is the best option. I have an application in production for several years using this event for the scenario you describe (transforming an application-specific ticket into an ASP.NET forms authentication ticket).
The problem with using Application_AuthenticateRequest and the others you mention is that it will be too late for controls later in that same request cycle to use the forms authentication cookie that you create.
Here is a simple example. You'd need to fill your custom logic for how the ticket gets validated.
protected void Application_BeginRequest(Object sender, EventArgs e)
{
if (!GetRequestHasValidTicket() )
{
string bodyToken = Request.Form["MyTokenName"];
//custom logic to authenticate token
bool tokenIsValid = true;
if (tokenIsValid)
{
System.Web.Security.FormsAuthentication.SetAuthCookie("myusername", false);
}
}
}
private bool GetRequestHasValidTicket()
{
FormsAuthenticationTicket ticket = null;
try
{
ticket = System.Web.Security.FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
catch { }
return ticket != null;
}
A small problem with BeginRequest is that Context.User is not set yet, so you have to manually check for the valid ticket in order to avoid adding the forms auth ticket to every response. If your application logic is such that the ticket only shows up in the body of the very first request, then you might not need this extra check.