Supporting client roles for users on Keycloak API service
This commit is contained in:
parent
0c6e800118
commit
08a5b49d1d
|
@ -0,0 +1,7 @@
|
|||
package eu.eudat.service.keycloak;
|
||||
|
||||
public enum KeycloakRole {
|
||||
|
||||
Admin, DatasetTemplateEditor, Manager, User
|
||||
|
||||
}
|
|
@ -16,5 +16,7 @@ public interface KeycloakService {
|
|||
void removeUserFromAdministratorsGroup(@NotNull UUID subjectId);
|
||||
void addUserToTenantAuthorityGroup(UUID subjectId, TenantEntity tenant, String key);
|
||||
void removeUserFromTenantAuthorityGroup(UUID subjectId, TenantEntity tenant, String key);
|
||||
void assignClientRoleToUser(UUID subjectId, String clientId, KeycloakRole role);
|
||||
void removeClientRoleFromUser(UUID subjectId, String clientId, KeycloakRole role);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,29 +4,31 @@ import com.google.common.collect.Lists;
|
|||
import eu.eudat.configurations.keycloak.KeycloakResourcesConfiguration;
|
||||
import eu.eudat.data.TenantEntity;
|
||||
import gr.cite.commons.web.keycloak.api.KeycloakAdminRestApi;
|
||||
import gr.cite.commons.web.keycloak.api.configuration.KeycloakClientConfiguration;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class KeycloakServiceImpl implements KeycloakService {
|
||||
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(KeycloakServiceImpl.class));
|
||||
private final KeycloakAdminRestApi api;
|
||||
private final MyKeycloakAdminRestApi api;
|
||||
private final KeycloakResourcesConfiguration configuration;
|
||||
private final KeycloakClientConfiguration clientConfiguration;
|
||||
|
||||
@Autowired
|
||||
public KeycloakServiceImpl(KeycloakAdminRestApi api, KeycloakResourcesConfiguration configuration) {
|
||||
public KeycloakServiceImpl(MyKeycloakAdminRestApi api, KeycloakResourcesConfiguration configuration, KeycloakClientConfiguration clientConfiguration) {
|
||||
this.api = api;
|
||||
this.configuration = configuration;
|
||||
//logger.info("Keycloak service initialized. Tenant authorities configured -> {}", configuration.getProperties().getAuthorities().size());
|
||||
this.clientConfiguration = clientConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,6 +82,30 @@ public class KeycloakServiceImpl implements KeycloakService {
|
|||
removeUserFromGroup(subjectId, group.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignClientRoleToUser(UUID subjectId, String clientId, KeycloakRole role) {
|
||||
if (clientId == null) clientId = clientConfiguration.getProperties().getClientId();
|
||||
UserRepresentation user = api.users().findUserById(subjectId.toString());
|
||||
user.getClientRoles().computeIfAbsent(clientId, k -> Lists.newArrayList());
|
||||
Set<String> clientRoles = new HashSet<>(Set.copyOf(user.getClientRoles().get(clientId)));
|
||||
clientRoles.add(role.name());
|
||||
user.getClientRoles().get(clientId).clear();
|
||||
user.getClientRoles().get(clientId).addAll(clientRoles);
|
||||
api.users().updateUser(subjectId.toString(), user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClientRoleFromUser(UUID subjectId, String clientId, KeycloakRole role) {
|
||||
if (clientId == null) clientId = clientConfiguration.getProperties().getClientId();
|
||||
UserRepresentation user = api.users().findUserById(subjectId.toString());
|
||||
user.getClientRoles().computeIfAbsent(clientId, k -> Lists.newArrayList());
|
||||
Set<String> clientRoles = new HashSet<>(Set.copyOf(user.getClientRoles().get(clientId)));
|
||||
clientRoles.remove(role.name());
|
||||
user.getClientRoles().get(clientId).clear();
|
||||
user.getClientRoles().get(clientId).addAll(clientRoles);
|
||||
api.users().updateUser(subjectId.toString(), user);
|
||||
}
|
||||
|
||||
public List<GroupRepresentation> getUserGroups(UUID subjectId) {
|
||||
return api.users().getGroups(subjectId.toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package eu.eudat.service.keycloak;
|
||||
|
||||
import gr.cite.commons.web.keycloak.api.configuration.KeycloakClientConfiguration;
|
||||
import gr.cite.commons.web.keycloak.api.modules.ClientsModule;
|
||||
import gr.cite.commons.web.keycloak.api.modules.GroupsModule;
|
||||
import gr.cite.commons.web.keycloak.api.modules.Modules;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.KeycloakBuilder;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MyKeycloakAdminRestApi {
|
||||
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(MyKeycloakAdminRestApi.class));
|
||||
|
||||
private final KeycloakClientConfiguration configuration;
|
||||
|
||||
protected RealmResource realm;
|
||||
|
||||
@Autowired
|
||||
public MyKeycloakAdminRestApi(KeycloakClientConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
try (Keycloak client = this.initClient()) {
|
||||
this.realm = client.realm(configuration.getProperties().getRealm());
|
||||
}
|
||||
logger.info("Custom Keycloak client initialized. Keycloak serving from {}", configuration.getProperties().getServerUrl().replace("/auth", "").replaceAll("https?://", ""));
|
||||
}
|
||||
|
||||
public MyUsersModule users() {
|
||||
return MyModules.getUsersModule(this.realm);
|
||||
}
|
||||
|
||||
public GroupsModule groups() {
|
||||
return Modules.getGroupsModule(this.realm);
|
||||
}
|
||||
|
||||
public ClientsModule clients() {
|
||||
return Modules.getClientsModule(this.realm);
|
||||
}
|
||||
|
||||
private Keycloak initClient() {
|
||||
return KeycloakBuilder.builder().serverUrl(this.configuration.getProperties().getServerUrl()).realm(this.configuration.getProperties().getRealm()).username(this.configuration.getProperties().getUsername()).password(this.configuration.getProperties().getPassword()).clientId(this.configuration.getProperties().getClientId()).clientSecret(this.configuration.getProperties().getClientSecret()).grantType("password").build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package eu.eudat.service.keycloak;
|
||||
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
|
||||
public class MyModules {
|
||||
|
||||
public static MyUsersModule getUsersModule(RealmResource realm) {
|
||||
return new MyUsersModule(realm);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package eu.eudat.service.keycloak;
|
||||
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MyUsersModule {
|
||||
private final RealmResource realm;
|
||||
|
||||
MyUsersModule(RealmResource realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public List<UserRepresentation> getUsers() {
|
||||
return this.realm.users().list();
|
||||
}
|
||||
|
||||
public UserRepresentation findUserById(String id) {
|
||||
return this.realm.users().get(id).toRepresentation();
|
||||
}
|
||||
|
||||
public List<UserRepresentation> findUsersByUsername(String username) {
|
||||
return this.realm.users().search(username);
|
||||
}
|
||||
|
||||
public UserRepresentation findUserByUsername(String username) {
|
||||
return this.realm.users().search(username, true).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public UserRepresentation addUser(UserRepresentation user) {
|
||||
return this.realm.users().create(user).readEntity(UserRepresentation.class);
|
||||
}
|
||||
|
||||
public void addUserToGroup(String userId, String groupId) {
|
||||
this.realm.users().get(userId).joinGroup(groupId);
|
||||
}
|
||||
|
||||
public void removeUserFromGroup(String userId, String groupId) {
|
||||
this.realm.users().get(userId).leaveGroup(groupId);
|
||||
}
|
||||
|
||||
public List<GroupRepresentation> getGroups(String userId) {
|
||||
return this.realm.users().get(userId).groups();
|
||||
}
|
||||
|
||||
public void updateUser(String userId, UserRepresentation user) {
|
||||
UserRepresentation existing = this.realm.users().get(userId).toRepresentation();
|
||||
existing.setFirstName(user.getFirstName());
|
||||
existing.setLastName(user.getLastName());
|
||||
existing.setEnabled(user.isEnabled());
|
||||
existing.setAttributes(user.getAttributes());
|
||||
existing.setClientRoles(user.getClientRoles());
|
||||
this.realm.users().get(userId).update(existing);
|
||||
}
|
||||
}
|
|
@ -25,6 +25,8 @@ import eu.eudat.model.persist.UserPersist;
|
|||
import eu.eudat.model.persist.UserRolePatchPersist;
|
||||
import eu.eudat.query.UserQuery;
|
||||
import eu.eudat.query.UserRoleQuery;
|
||||
import eu.eudat.service.keycloak.KeycloakRole;
|
||||
import eu.eudat.service.keycloak.KeycloakService;
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.deleter.DeleterFactory;
|
||||
|
@ -80,6 +82,9 @@ public class UserServiceImpl implements UserService {
|
|||
private final JsonHandlingService jsonHandlingService;
|
||||
private final QueryFactory queryFactory;
|
||||
private final UserScope userScope;
|
||||
|
||||
private final KeycloakService keycloakService;
|
||||
|
||||
@Autowired
|
||||
public UserServiceImpl(
|
||||
EntityManager entityManager,
|
||||
|
@ -91,8 +96,8 @@ public class UserServiceImpl implements UserService {
|
|||
MessageSource messageSource,
|
||||
EventBroker eventBroker,
|
||||
JsonHandlingService jsonHandlingService,
|
||||
QueryFactory queryFactory,
|
||||
UserScope userScope) {
|
||||
QueryFactory queryFactory,
|
||||
UserScope userScope, KeycloakService keycloakService) {
|
||||
this.entityManager = entityManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.deleterFactory = deleterFactory;
|
||||
|
@ -104,6 +109,7 @@ public class UserServiceImpl implements UserService {
|
|||
this.jsonHandlingService = jsonHandlingService;
|
||||
this.queryFactory = queryFactory;
|
||||
this.userScope = userScope;
|
||||
this.keycloakService = keycloakService;
|
||||
}
|
||||
|
||||
//region persist
|
||||
|
@ -211,6 +217,7 @@ public class UserServiceImpl implements UserService {
|
|||
item.setRole(roleName);
|
||||
item.setCreatedAt(Instant.now());
|
||||
this.entityManager.persist(item);
|
||||
this.keycloakService.assignClientRoleToUser(data.getId(), null, KeycloakRole.valueOf(roleName));
|
||||
}
|
||||
foundIds.add(item.getId());
|
||||
}
|
||||
|
@ -218,6 +225,7 @@ public class UserServiceImpl implements UserService {
|
|||
this.entityManager.flush();
|
||||
|
||||
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
|
||||
toDelete.forEach(x -> this.keycloakService.removeClientRoleFromUser(data.getId(), null, KeycloakRole.valueOf(x.getRole())));
|
||||
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
|
||||
|
||||
this.entityManager.flush();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
keycloak-client:
|
||||
serverUrl: ${KEYCLOAK_API_SERVER_URL}
|
||||
realm: ${KEYCLOAK_API_REALM}
|
||||
username: ${KEYCLOAK_API_USERNAME}
|
||||
password: ${KEYCLOAK_API_PASSWORD}
|
||||
clientId: ${KEYCLOAK_API_CLIENT_ID}
|
||||
clientSecret: ${KEYCLOAK_API_CLIENT_SECRET}
|
||||
serverUrl: ${KEYCLOAK_API_SERVER_URL:}
|
||||
realm: ${KEYCLOAK_API_REALM:}
|
||||
username: ${KEYCLOAK_API_USERNAME:}
|
||||
password: ${KEYCLOAK_API_PASSWORD:}
|
||||
clientId: ${KEYCLOAK_API_CLIENT_ID:}
|
||||
clientSecret: ${KEYCLOAK_API_CLIENT_SECRET:}
|
||||
|
||||
keycloak-resources:
|
||||
authorities: null
|
||||
|
|
Loading…
Reference in New Issue