
de.arbeitsagentur.opdt.keycloak.cassandra.role.CassandraRoleProvider Maven / Gradle / Ivy
/*
* Copyright 2022 IT-Systemhaus der Bundesagentur fuer Arbeit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.arbeitsagentur.opdt.keycloak.cassandra.role;
import static de.arbeitsagentur.opdt.keycloak.common.MapProviderObjectType.ROLE_AFTER_REMOVE;
import static de.arbeitsagentur.opdt.keycloak.common.MapProviderObjectType.ROLE_BEFORE_REMOVE;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.RoleRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.entities.RoleValue;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.entities.Roles;
import de.arbeitsagentur.opdt.keycloak.cassandra.transaction.CassandraModelTransaction;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import lombok.extern.jbosslog.JBossLog;
import org.keycloak.models.*;
import org.keycloak.models.utils.KeycloakModelUtils;
@JBossLog
public class CassandraRoleProvider implements RoleProvider {
private final RoleRepository roleRepository;
private final KeycloakSession session;
private final Map rolesByRealmId = new HashMap<>();
private final Set rolesChanged = new HashSet<>();
private final Set rolesDeleted = new HashSet<>();
public CassandraRoleProvider(KeycloakSession session, RoleRepository roleRepository) {
this.roleRepository = roleRepository;
this.session = session;
}
public void markChanged(String realmId) {
rolesChanged.add(realmId);
}
public void markDeleted(String realmId) {
rolesDeleted.add(realmId);
}
private Roles getRoles(String realmId) {
if (rolesByRealmId.containsKey(realmId)) {
return rolesByRealmId.get(realmId);
}
Roles roles = roleRepository.getRolesByRealmId(realmId);
rolesByRealmId.put(realmId, roles);
session
.getTransactionManager()
.enlistAfterCompletion(
(CassandraModelTransaction)
() -> {
if (rolesChanged.contains(realmId) && !rolesDeleted.contains(realmId)) {
roleRepository.insertOrUpdate(roles);
}
rolesByRealmId.remove(realmId);
rolesChanged.remove(realmId);
rolesDeleted.remove(realmId);
});
return roles;
}
private Function entityToAdapterFunc(RealmModel realm) {
return origEntity ->
origEntity == null
? null
: new CassandraRoleAdapter(
origEntity.getId(), realm, origEntity, getRoles(realm.getId()), this);
}
@Override
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
if (getRealmRole(realm, name) != null) {
throw new ModelDuplicateException(
"Role with the same name exists: " + name + " for realm " + realm.getName());
}
log.debugf("addRealmRole(%s, %s, %s)%s", realm, id, name, getShortStackTrace());
Roles roles = getRoles(realm.getId());
if (id != null && roles.getRoleById(id) != null) {
throw new ModelDuplicateException("Role exists: " + id);
}
RoleValue role =
RoleValue.builder()
.id(id == null ? KeycloakModelUtils.generateId() : id)
.name(name)
.realmId(realm.getId())
.build();
roles.addRealmRole(role);
markChanged(realm.getId());
return entityToAdapterFunc(realm).apply(role);
}
@Override
public Stream getRealmRolesStream(RealmModel realm) {
return getRealmRolesStream(realm, 0, -1);
}
@Override
public Stream getRealmRolesStream(RealmModel realm, Integer first, Integer max) {
log.debugv("get all realm roles: realmId={0} first={1} max={2}", realm.getId(), first, max);
Roles roles = getRoles(realm.getId());
return roles.getRealmRoles(first, max).stream().map(r -> entityToAdapterFunc(realm).apply(r));
}
@Override
public Stream getRolesStream(
RealmModel realm, Stream ids, String search, Integer first, Integer max) {
log.debugf(
"get all realm roles: realmId=%s search=%s first=%s max=%s",
realm.getId(), search, first, max);
Roles roles = getRoles(realm.getId());
return ids.map(roles::getRoleById)
.filter(
role ->
search == null
|| search.isEmpty()
|| role.getName().toLowerCase().contains(search.toLowerCase())
|| role.getDescription().toLowerCase().contains(search.toLowerCase()))
.map(entityToAdapterFunc(realm))
.filter(Objects::nonNull)
.sorted(Comparator.comparing(RoleModel::getName))
.skip(first == null || first < 0 ? 0 : first)
.limit(max == null || max < 0 ? Long.MAX_VALUE : max);
}
@Override
public boolean removeRole(RoleModel role) {
log.debugf("removeRole roleId=%s", role.getId());
boolean removed;
RealmModel realm =
role.isClientRole()
? ((ClientModel) role.getContainer()).getRealm()
: (RealmModel) role.getContainer();
session.invalidate(ROLE_BEFORE_REMOVE, realm, role);
if (role.isClientRole()) {
Roles roles = getRoles(realm.getId());
removed = roles.removeClientRole(role.getContainerId(), role.getId());
markChanged(realm.getId());
} else {
Roles roles = getRoles(role.getContainerId());
removed = roles.removeRealmRole(role.getId());
markChanged(role.getContainerId());
}
session.invalidate(ROLE_AFTER_REMOVE, realm, role);
return removed;
}
@Override
public void removeRoles(RealmModel realm) {
log.debugf("removeRoles realmId=%s", realm.getId());
getRealmRolesStream(realm).forEach(this::removeRole);
markChanged(realm.getId());
}
@Override
public void removeRoles(ClientModel client) {
log.debugf("removeRoles clientId=%s", client.getId());
getClientRolesStream(client).forEach(this::removeRole);
markChanged(client.getRealm().getId());
}
@Override
public RoleModel addClientRole(ClientModel client, String name) {
return addClientRole(client, null, name);
}
@Override
public RoleModel addClientRole(ClientModel client, String id, String name) {
if (getClientRole(client, name) != null) {
throw new ModelDuplicateException(
"Role with the same name exists: " + name + " for client " + client.getClientId());
}
log.debugf("addClientRole(%s, %s, %s)%s", client.getClientId(), id, name, getShortStackTrace());
Roles roles = getRoles(client.getRealm().getId());
if (id != null && roles.getRoleById(id) != null) {
throw new ModelDuplicateException("Role exists: " + id);
}
RoleValue role =
RoleValue.builder()
.id(id == null ? KeycloakModelUtils.generateId() : id)
.name(name)
.clientId(client.getId())
.build();
roles.addClientRole(client.getId(), role);
markChanged(client.getRealm().getId());
return entityToAdapterFunc(client.getRealm()).apply(role);
}
@Override
public Stream getClientRolesStream(ClientModel client) {
return RoleProvider.super.getClientRolesStream(client);
}
@Override
public Stream getClientRolesStream(ClientModel client, Integer first, Integer max) {
log.debugv("get all client roles: clientId={0} first={1} max={2}", client.getId(), first, max);
Roles roles = getRoles(client.getRealm().getId());
return roles.getClientRoles(client.getId(), first, max).stream()
.map(r -> entityToAdapterFunc(client.getRealm()).apply(r));
}
@Override
public Stream searchForClientRolesStream(
ClientModel client, String search, Integer first, Integer max) {
log.debugf(
"get all client roles: clientId=%s search=%s first=%s max=%s",
client.getId(), search, first, max);
Roles roles = getRoles(client.getRealm().getId());
return roles.getClientRoles(client.getId(), first, max).stream()
.filter(
role ->
search == null
|| search.isEmpty()
|| role.getName().toLowerCase().contains(search.toLowerCase())
|| role.getDescription().toLowerCase().contains(search.toLowerCase()))
.map(entityToAdapterFunc(client.getRealm()));
}
@Override
public Stream searchForClientRolesStream(
RealmModel realm, Stream ids, String search, Integer first, Integer max) {
List result = new ArrayList<>();
realm
.getClientsStream()
.forEach(
client -> {
client
.getRolesStream()
.filter(
role ->
search == null
|| search.isEmpty()
|| role.getName().toLowerCase().contains(search.toLowerCase())
|| client.getClientId().toLowerCase().contains(search.toLowerCase()))
.filter(role -> ids == null || ids.anyMatch(i -> i.equals(role.getId())))
.forEach(result::add);
});
return result.stream();
}
@Override
public Stream searchForClientRolesStream(
RealmModel realm, String search, Stream excludedIds, Integer first, Integer max) {
List result = new ArrayList<>();
realm
.getClientsStream()
.forEach(
client -> {
client
.getRolesStream()
.filter(
role ->
search == null
|| search.isEmpty()
|| role.getName().toLowerCase().contains(search.toLowerCase())
|| client.getClientId().toLowerCase().contains(search.toLowerCase()))
.filter(
role ->
excludedIds == null || excludedIds.noneMatch(i -> i.equals(role.getId())))
.forEach(result::add);
});
return result.stream();
}
@Override
public RoleModel getRealmRole(RealmModel realm, String name) {
log.debugf("getRealmRole realmId=%s name=%s", realm.getId(), name);
Roles roles = getRoles(realm.getId());
RoleValue realmRole =
roles.getRealmRoles().stream()
.filter(r -> Objects.equals(r.getName(), name))
.findFirst()
.orElse(null);
if (realmRole == null) {
return null;
}
return entityToAdapterFunc(realm).apply(realmRole);
}
@Override
public RoleModel getRoleById(RealmModel realm, String id) {
log.debugf("getRoleById realmId=%s id=%s", realm.getId(), id);
Roles roles = getRoles(realm.getId());
RoleValue role = roles.getRoleById(id);
if (role == null) {
return null;
}
return entityToAdapterFunc(realm).apply(role);
}
@Override
public Stream searchForRolesStream(
RealmModel realm, String search, Integer first, Integer max) {
log.debugf(
"get all roles: realmId=%s search=%s first=%s max=%s", realm.getId(), search, first, max);
Roles roles = getRoles(realm.getId());
return roles.getRealmRoles().stream()
.filter(
role ->
search == null
|| search.isEmpty()
|| role.getName().toLowerCase().contains(search.toLowerCase())
|| (role.getDescription() != null
&& role.getDescription().toLowerCase().contains(search.toLowerCase())))
.map(entityToAdapterFunc(realm))
.filter(Objects::nonNull)
.skip(first == null || first < 0 ? 0 : first)
.limit(max == null || max < 0 ? Long.MAX_VALUE : max);
}
@Override
public RoleModel getClientRole(ClientModel client, String name) {
log.debugf("getClientRole clientId=%s name=%s", client.getId(), name);
Roles roles = getRoles(client.getRealm().getId());
RoleValue clientRole =
roles.getClientRoles().getOrDefault(client.getId(), new HashSet<>()).stream()
.filter(r -> Objects.equals(r.getName(), name))
.findFirst()
.orElse(null);
if (clientRole == null) {
return null;
}
return entityToAdapterFunc(client.getRealm()).apply(clientRole);
}
public void preRemove(RealmModel realm) {
removeRoles(realm);
}
public void preRemove(RealmModel realm, RoleModel role) {
getRealmRolesStream(realm).forEach(r -> r.removeCompositeRole(role));
realm
.getClientsStream()
.flatMap(this::getClientRolesStream)
.forEach(r -> r.removeCompositeRole(role));
}
@Override
public void close() {
rolesByRealmId.clear();
rolesChanged.clear();
rolesDeleted.clear();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy