I'm using Keycloak version 3.4.3.Final community version (I know it's an older version and cannot update it because of business reasons) and it's corresponding java client, I have created a client with flag serviceAccountEnabled set to true and need to assign realm-management client's roles to it i.e. from section service-account-roles.
I'm able to create client successfully but not able to assign realm-management client's roles to it as it requires fetching of service-account-user which is returning null everytime, below is my code snippet
Code to create client
KeycloakTransaction tx = session.getTransactionManager();
tx.begin();
ClientModel clientModel = myRealm.addClient("myClient-id");
clientModel.setClientId("myClient-id");
clientModel.setClientAuthenticatorType("client-secret");
clientModel.setStandardFlowEnabled(false);
clientModel.setWebOrigins(Collections.emptySet());
clientModel.setRedirectUris(Collections.emptySet());
clientModel.setDirectAccessGrantsEnabled(false);
clientModel.setImplicitFlowEnabled(false);
clientModel.setServiceAccountsEnabled(true);
clientModel.setPublicClient(false);
clientModel.setProtocol("openid-connect");
clientModel.setFullScopeAllowed(false);
//need to inject this from env
clientModel.setSecret("12345");
clientModel.updateClient();
commitOrRollbackTransaction(tx);
Code to assign realm-management roles to client's service-account-user
KeycloakTransaction tx = session.getTransactionManager();
tx.begin();
RealmModel myRealm = keycloakSession.realms().getRealmByName("myRealm");
ClientModel clientModel = myRealm.getClientByClientId("myClient-id");
ClientModel realmManagementClient = myRealm.getClientByClientId("realm-management");
Set<RoleModel> roles = realmManagementClient.getRoles();
UserModel serviceAccountUser = keycloakSession.users().getServiceAccount(clientModel);
if(serviceAccountUser != null){
roles.stream().forEach(r -> serviceAccountUser.grantRole(r));
}
tx.commit();
The issue is, I do not get service-account-user for the client I created in previous step, however I've verified that user got created, also I can fetch service-account-user for existing clients, it seems more like a transaction issue but I'm performing client creation and roles assignment in two different sessions.
Can someone please point me where I'm mistaking or point me to direction how to fetch service-account-user.
Got it working using RealmManager and ClientManager keycloak API.
Related
Is possible to have user in one realm to manage another realm in keycloak?
My goal is to have 2 realms - adminRealm & userRalm. In adminRealm should be users, which will be able to log in to our admin app and there they could create via Keycloak rest api "ordinary user" which will be placed into userRealm.
Currently my solution working over one realm, where I have admin user which is able to log into my admin app and there he can create users in the same realm. But if I want create users to another realm, I get 403 error. So is there any way how to allow admin user to manage another realm (eg create users etc.)?
You should use master realm for storing admin accounts. Non master realms are isolated from each other. If you look to the clients list in master realm you should see that every realm represented by client with OIDC id like "foo-realm". This clients represents administration REST API for corresponding realms, and users with granted roles from this clients could perform admin requests to corresponding apis.
For example you have foo realm which will contain ordinary application users. To achieve your goal to introduce admin accounts that will be able to manage users from foo you have to create foo-admin user in master realm and grant him foo-realm.realm-admin role. Now this user has total control over foo realm and no control over master realm. You also can map foo-realm.realm-admin role to some group in master realm and add users to it (so if any changes appears in future you will have to change only group role settings)
In case you use terraform your solution would look like this:
data "keycloak_realm" "master" {
realm = "master"
}
data "keycloak_openid_client" "realm_management" {
realm_id = data.keycloak_realm.master.id
client_id = "foo-realm"
}
data "keycloak_role" "query_users" {
realm_id = data.keycloak_realm.master.id
client_id = data.keycloak_openid_client.realm_management.id
name = "query-users"
}
data "keycloak_role" "manage_users" {
realm_id = data.keycloak_realm.master.id
client_id = data.keycloak_openid_client.realm_management.id
name = "manage-users"
}
resource "keycloak_user_roles" "user_admin_roles" {
realm_id = data.keycloak_realm.master.id
user_id = keycloak_user.users_admin.id
role_ids = [
data.keycloak_role.query_users.id,
data.keycloak_role.manage_users.id,
]
}
I m trying to understand the admin client api of Keycloak, especially around joins.
There is this post that adresses a similar need for getting users per role.
Keycloak - Get all Users mapped to roles
How would we do this with the admin client?
Because for now I am retrieving all users and checking if the roles match:
List<UserRepresentation> userRepresentations = keycloak.realm(realm).users().search("", 0, 1000); //get all users :(
for (UserRepresentation userRepresentation : userRepresentations ) {
List<String> userRoles = userRepresentation.getRealmRoles();
if(userRoles != null && !Collections.disjoint(userRoles, roles)){
result.add(KeycloakUserTransformer.userRepresentationToSimpleUserDTO(userRepresentation));
}
}
And the thing is, userRoles list is always empty :S. But actually, we have ~2500 users in keycloak users.
EDIT:
I am using the keycloak admin client v.2.0. I guess the newer versions support this.
Thanks in advance.
With latest admin client you can easily get
RoleResource roleResource = keycloak.realm("realm_name").roles().get("role_name");
Set<UserRepresentation> users = roleResource.getRoleUserMembers();
round 2: trying to make this clearer:
I have two totally and completely separate services (both laravel 5.3).
One is an authentication service that has access to a user mysql database, permissions and role.
The other is a resource service. It couldn't care less about the user table and does not have access to any user table.
That means that any type of Auth::loginById()... would never work in this service because there is no user table.
I want to use JWT to have users access the resource service API, and have the user Auth to act as if a user is authenticated - but again - all I have are the claims inside the JWT - no user table. The claims include some information about the user - id, name, etc. I want to do something like this:
$userObj = JWTAuth::parseToken()->getPayload()->claims; // { id:4, name: Birdman, email: birdy414141#gmail.com}
Auth::login($userObj)
and then have access to the user object like usual
echo Auth::user()->name // Birdman
Has anyone tried anything like this?
I have found that doing this more or less works:
$u = new User();
$u->user_id = (JWTAuth::parseToken()->getPayload()->get('sub'));
$u->name = (JWTAuth::parseToken()->getPayload()->get('name'));
Obviously I can't $u->save() here because again - there is no user database.
I can do this though:
Auth::login($u);
and then I can later call Auth::user()->name properly...
I'm asking if I'm doing something exotic here or is this good stuff. Where will this fail?
I have a Mobile Service with Model classes and DTO classes. I mapped these using Fluent API and got it to work to perform CRUD operations, but I have a problem with the JSON responses returned in some instances.
Take for example a User entity with user name and password. To register a user I have my PostUserDTO method like this:
// POST tables/UserDTO
public async Task<IHttpActionResult> PostUserDTO(UserDTO item)
{
string hashString = PasswordHash.CreateHash(item.Password);
item.Password = hashString;
UserDTO current = await InsertAsync(item);
current.Password = "";
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
On the method I read the password property sent from the client and then proceed to hash it with a salt, replace the value in the object sent and save it to the database, then I proceed to empty the password so the hashed result isn't returned.
In this case the best practice would be to omit the password property from the response, this also should happen when retrieving all of the users in the table, I only want to return specific information, some information generated by my server should stay out from the response.
How can I select or alter the information from the responses? Do I have to create other DTOs for every kind of response I desire?
In geometrix site if I need to get the results for page which has only user X has access
Following query pulls all the records, but I need to restrict for only X user
http://myserver.com:4502/bin/querybuilder.feed?orderby=%40jcr%3acontent%2fjcr%3acreated&orderby.index=true&orderby.sort=desc&path=%2fcontent%2fgeometrixx%2fen&type=cq%3aPage
What are the parameters that need to be included in the url
ACL mechanism, responsible for authorization (deciding if a user has access to a resource) is quite complicated. Privileges are inherited from ancestor nodes, there are groups (and a group may be member of another group), etc. That's why it is not possible to write a query that will list nodes available for a particular user.
However, you may create a resource resolver working on behalf of any user and use it to query the repository - you'll get only resources available to the resource resolver "owner". Example:
final String user = "my-user";
final String query = "SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE([/content/geometrixx/en]) ORDER BY [jcr:created]";
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(ResourceResolverFactory.USER_IMPERSONATION, user);
ResourceResolver resolver = resourceResolverFactory.getAdministrativeResourceResolver(authInfo);
Iterator<Resource> result = resolver.findResources(query, "JCR-SQL2");