I have a client in keycloak for my awx(ansible tower) webpage.
I need only the users from one specific keycloak group to be able to log in through this client.
How can I forbid all other users(except from one particular group) from using this keycloak client?
I found a solution which does not require the scripts extension or any changes on the flow.
The key for this solution are the Client Scopes. An application which wants to to authorize a user needs a scope like email or uid, right? What if you only pass them to an application if a user is in a specific group?
In the following, my client application name is App1.
Solution:
Go to your client roles (realm -> Clients -> click App1 -> Roles)
Click 'Add Role' -> enter Name (e.g. 'access') -> click 'Save'
Go to Client Scopes (realm -> Client Scopes)
Click on the scope which is needed by your client application (e.g. 'email')
Assign Client Role 'access' in 'Scope' Tab by choosing client application 'App1' in Drop Down 'Client Roles'
Now, you won't be able to log into your client application App1 anymore, as the role 'access' is not assigned to any user or group. You can try.
Let's create a new group and assign the role and a user to it.
Create Group (realm -> Groups -> Click 'New' -> enter Name 'App1 Users' -> Click Save)
In the Group, choose 'Role Mappings', choose 'App1' in Client Roles drop down, and assign the role 'access'
Assign User to 'App1 Users' (realm -> Users -> Click on User -> Groups -> Select 'App1 Users -> Click Join)
Voila, the chosen user can log into App1.
On Keycloak admin console, go to Clients menu, select your client. On the client configuration page, set Authorization Enabled: On, click Save. A new Authorization tab should appear, go to it, then to the Policies tab underneath, click Create Policy and select Group-based policy. There, you can restrict access to specific groups, assuming you have defined your groups via the Groups menu already.
--EDIT 2019-11-08--
As mentioned in comments, Client Protocol must be set to openid-connect and Access Type must be set to confidential, in order to make the Authorization Enabled option visible.
Follow-up to Allan's answer: His approach is working (for me ;-) ), though I had some struggle on how to deploy it. This is how I did it:
Bundle script in a JAR file as documented here, deploy it by copying to standalone/deployments/ (see manual link)
Enable scripts: Start Keycloak with -Dkeycloak.profile.feature.scripts=enabled
In your realm, create a new flow. Duplicate the Browser flow in a required subflow, and add the script authenticator as final (required) element:
Now add to all clients which should be restricted a client role feature:authenticate. Users which don't bear that role won't get access to the application.
If it can help, here is a script which helps implementing this behaviour for any client: if the client contains a given role (here it is called feature:authenticate), then the script checks whether the user has the role and shows an error page (a new template that needs to be deployed in the theme) if not.
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
var MANDATORY_ROLE = 'feature:authenticate';
var username = user ? user.username : "anonymous";
var client = session.getContext().getClient();
LOG.debug("Checking access to authentication for client '" + client.getName() + "' through mandatory role '" + MANDATORY_ROLE + "' for user '" + username + "'");
var mandatoryRole = client.getRole(MANDATORY_ROLE);
if (mandatoryRole === null) {
LOG.debug("No mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return context.success();
}
if (user.hasRole(mandatoryRole)) {
LOG.info("Successful authentication for user '" + username + "' with mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return context.success();
}
LOG.info("Denied authentication for user '" + username + "' without mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return denyAccess(context, mandatoryRole);
}
function denyAccess(context, mandatoryRole) {
var formBuilder = context.form();
var client = session.getContext().getClient();
var description = !mandatoryRole.getAttribute('deniedMessage').isEmpty() ? mandatoryRole.getAttribute('deniedMessage') : [''];
var form = formBuilder
.setAttribute('clientUrl', client.getRootUrl())
.setAttribute('clientName', client.getName())
.setAttribute('description', description[0])
.createForm('denied-auth.ftl');
return context.failure(AuthenticationFlowError.INVALID_USER, form);
}
I solved it like this:
Create a new role in Keycloak.
Assign this role to the group.
Create a new authentication script in Kycloak. Configure which role is allowed upon login (e.g. user.hasRole(realm.getRole("yourRoleName"))).
In the client's settings, under "Authentication Flow Overrides", choose the authentication script that was just created.
You can use this extension to restrict access to a specific group: https://github.com/thomasdarimont/keycloak-extension-playground/tree/master/auth-require-group-extension
according docu https://www.keycloak.org/docs/6.0/server_admin/#executions u have to active that feature to add some custom scripts with "add execution".
bin/standalone.sh|bat -Dkeycloak.profile.feature.scripts=enabled
#Allan solution with feature:authenticate looks good to me
I tried Allan's solution and it is working fine using Keycloak 11.0.3 but it has some cons mentioned below. Here is my solution for the authenticator script which does not grant access for users if they are not member at least one of the given groups. In such case a unique error message is shown.
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
var allowed_groups = ['foo', 'bar'];
var username = user ? user.username : "anonymous";
var groups = user.getGroups();
var group_array = groups.toArray();
for (var i in group_array) {
var gn = group_array[i].getName();
if (allowed_groups.indexOf(gn) >= 0) {
LOG.info("Access granted for user '" + username + "' for being member of LDAP group '" + gn + "'");
return context.success();
}
}
LOG.info("Access denied for user '" + username + ". for not being member of any of the following LDAP groups: " + allowed_groups);
context.failure(AuthenticationFlowError.IDENTITY_PROVIDER_DISABLED, context.form().setError(
"User doesn't have the required LDAP group membership to view this page", null).createForm("error.ftl"));
return;
}
There are two minor user experience related cons with this solution worth mentioning:
When a not logged in user tries to connect to a client which access gets denied by the authenticator script the whole authentication flow is considered failure. This means the user doesn't get logged in into Keycloak despite the fact they provided the correct credentials
When a logged in user tries to connect to a client which access gets denied by the authenticator script the Keycloak login page is displayed (without showing any error message) which is deceptive as the user can have the false feeling they are not logged in
In addition if you maintain multiple clients and you need to have different groups (or roles) checked per client then you have to implement as many new authentication flows as many different checks you need. In short the solution works, but it has some disadvantages. I believe a simple feature such as restricting the access based on groups or roles is essential for an identity and access management system and should be supported natively!
2021 year - Keycloak 7.4.1.GA
I solved it like this for SAML2:
Add new Authentication flow (Just copy existing one)
Add execution "Group Access Observer" and set it as Required
Actions -> Config on Group Access Observer line
Fill group name
Go to your client and change Authentication flow to created now.
Best Regards
With Keycloak >= 13.x you may want to try the "Allow/Deny Access" authenticators with conditions. You can assign a role to a group and build the condition based on the role.
If that is not flexible enough, try out this library that I have build to solve exactly that issue.
My query url is:
var url = 'https://api.mongolab.com/api/1/databases/database/collections/collection?'
+ 'q={'
+ '\"visible\": true'
+ ', \"date\": ' + JSON.stringify( jsonDate )
+ ', \"country\": \"' + country + "\""
+ '}'
+ '&s={"date": -1}'
+ '&apiKey=' + this.key;
I have and option to sort after with Backbone or Jquery, but I hope to do it with query.
The url you generated is correct, however it is not a valid URL unless you do proper URL encoding of special characters like spaces.
Use encodeURIcomponent of your query parameters, and it should work.
Or use jQuery to pass your parameter as javascript object in jQuery.get method.
Query is working. Problem was in PhoneGap framework, because I've open platform related JS/HTML code and made changes in there. But changes should be made in special www directory, one with source for every platforms.
I would prefer using cursor.sort(sort).
cursor.sort(date)
check this http://docs.mongodb.org/manual/reference/method/cursor.sort/
I have a form written in classic ASP with some light client-side validation. Everything works well except for one thing - the form fails when there's an apostrophe. One of the fields may have apostrophes often (last name field - form would fail if user's last name was O'Brien, for example).
How do I fix this?
You'll have to examine your ASP code. If you see any code that looks like
string SQL =
"SELECT user_id, first_name,last_name FROM users WHERE username = "
+ myUserName;
where myUserName comes from the user, then you are definitely vulnerable.
The fix is NOT to try to escape the input (i.e., replace all "'" with "''") but to use a completely different method as outlined in this article on SQL Injection and how to avoid it
In a nutshell, try something like the following from the bobby-tables site
String username = "joe.bloggs";
SqlCommand sqlQuery = new SqlCommand(
"SELECT user_id, first_name,last_name FROM users WHERE username = ?username",
sqlConnection);
sqlQuery.Parameters.AddWithValue("?username", username);
After hours of debugging and trying to find out why the file transfer was not working using aSmack, while normal messaging was, I finally managed to pin it down to this.
The Openfire server is sending the Rosters' JID missing the / at the end when I follow the method given in the Smack documentation to get a user's Roster list.
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry r : entries) {
Log.v("Gabriel","Receiving: " + r.getUser());
}
For example if I receive a message from the user gabriel, I get the "From" as:
gabriel#dragonov/Smack
But the r.getUser() returns to the user as
gabriel#dragonov
Even
connection.getRoster().getPresence(contactName).getFrom()
is returning is as "gabriel#dragonov".
This is causing the File transfer to fail, but oddly not the regular messaging. However when I manually add the /Smack at the end of
OutgoingFileTransfer transferr = manager.createOutgoingFileTransfer(contactJID+"/Smack");
it works.
My question is, how can I receive the full JID with the resource part included or configure the file transfer so that it doesn't fail when using a bare JID?
Edit:
I have tried the following method:
Log.v("Gabriel", entries.size() + " buddy(ies):");
for (RosterEntry r : entries) {
Log.v("Pat","adding: " + r.getType() + " " + r.getName());
contacts.add(r.getUser());
}
for (String contact : contacts){
Iterator<org.jivesoftware.smack.packet.Presence> presences = connection.getRoster().getPresences(contact);
Log.v("Gabriel", contact+" has: ");
while(presences.hasNext()){
Log.v("Gabriel",presences.next().getFrom());
}
}
But I am still getting the bare ID.
The output:
gabriel#dragonov has:
gabriel#dragonov
Use Iterator<Presence> Roster.getPresences(String user) to get the presence information from all known resources of a user. For this Presence instances getFrom() should return a full JID which you can use in FileTransferManager.createOutgoingFileTransfer().
I have created SMACK-430, regarding the use of a full JID in createOutgoingFileTranfer(). It really should throw an exception so that smack users don't have to debug hours to find the reason (although it's stated in the method javadoc). SMACK-430 also explains why FileTransferManager needs a full JID and can not be used with a bare JID.
And I need to secure some area's on my web store for admin use.
The problem is the authentication of the user: the salt + hash is failing.
This is my code for creating a password (using PHP5.x):
$salt = rand(0, 999999999999);<br>
$passEncr = sha1($pass1 + $salt);
This variable $passEncr is inserted into the database together with its salt.
At the login page I've got the following check:
$password = $_POST['password']; // hash+salt in the database
$storedSalt = $row['salt']; // salt from database<br>
if (sha1($password + $storedSalt) == $row['password'])
Now the problem I'm experiencing is that some hashes appear to be the same.
If I try to log in with an alphanumeric password, I succeed, no matter what the content of that password is.
Full login check here: http://pastebin.com/WjVnQ4aF
Can someone please explain what I'm doing wrong?
Well, SQL injection, using SHA for passwords instead of bcrypt are the first things I see, not using OpenId so you can get out of the business of storing passwords is another.
As for the passwords being the same, I would check the database -- see what you are storing, that will tell you where your problem lies.