/* * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den * Ministerpräsidenten des Landes Schleswig-Holstein * Staatskanzlei * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung * * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); * Sie dürfen dieses Werk ausschließlich gemäß * dieser Lizenz nutzen. * Eine Kopie der Lizenz finden Sie hier: * * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * * Sofern nicht durch anwendbare Rechtsvorschriften * gefordert oder in schriftlicher Form vereinbart, wird * die unter der Lizenz verbreitete Software "so wie sie * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - * ausdrücklich oder stillschweigend - verbreitet. * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ package de.ozgcloud.user; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import jakarta.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import de.ozgcloud.user.keycloak.KeycloakApiProperties; @Mapper(unmappedTargetPolicy = ReportingPolicy.WARN) public abstract class UserResourceMapper { @Inject KeycloakApiProperties properties; @Inject RealmResource realm; @Mapping(target = "createdAt", expression = "java(mapCreatedAt(userRes))") @Mapping(target = "email", expression = "java(mapEmail(userRes))") @Mapping(target = "firstName", expression = "java(mapFirstName(userRes))") @Mapping(target = "lastName", expression = "java(mapLastName(userRes))") @Mapping(target = "fullName", expression = "java(mapFullName(userRes))") @Mapping(target = "fullNameReversed", expression = "java(mapFullNameReversed(userRes))") @Mapping(target = "username", expression = "java(mapUsername(userRes))") @Mapping(target = "externalId", expression = "java(mapId(userRes))") @Mapping(target = "keycloakUserId", expression = "java(mapKeycloakUserId(userRes))") @Mapping(target = "organisationsEinheitIds", expression = "java(mapOrganisationsEinheitIds(userRes))") @Mapping(target = "roles", expression = "java(mapRoles(userRes))") @Mapping(target = "lastSyncTimestamp", ignore = true) @Mapping(target = "deleted", ignore = true) public abstract User toKopUser(UserResource userRes); Date mapCreatedAt(UserResource userRes) { var createdAt = userRes.toRepresentation().getCreatedTimestamp(); return Optional.ofNullable(createdAt).map(Date::new).orElse(new Date()); } Set<String> mapOrganisationsEinheitIds(UserResource userRes) { var groups = userRes.groups(); return Stream.concat( getOrganisationsEinheitIdsFromGroups(groups), getOrganisationsEinheitIdsFromUser(userRes) ) .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); } private Stream<String> getOrganisationsEinheitIdsFromGroups(List<GroupRepresentation> groups) { return groups.stream() .map(this::mapGroup) .filter(Objects::nonNull) .map(attributeMap -> attributeMap.get(properties.organisationsEinheitIdKey())) .filter(Objects::nonNull) .flatMap(Collection::stream); } private Stream<String> getOrganisationsEinheitIdsFromUser(UserResource userRes) { return getUserAttributes(userRes) .map(attributes -> attributes.get(properties.organisationsEinheitIdKey())) .orElse(Collections.emptyList()) .stream(); } private Map<String, List<String>> mapGroup(GroupRepresentation group) { var groupFromRealm = realm.getGroupByPath(group.getPath()); return Optional.ofNullable(groupFromRealm).map(GroupRepresentation::getAttributes).orElse(null); } List<String> mapRoles(UserResource userRes) { var roleRepresentation = Optional.ofNullable(userRes.roles().getAll().getClientMappings()) .filter(map -> map.containsKey(properties.client())) .map(map -> map.get(properties.client())) .map(ClientMappingsRepresentation::getMappings) .orElseGet(Collections::emptyList); return roleRepresentation.stream().map(RoleRepresentation::getName).toList(); } String mapId(UserResource userRes) { var userRepresentation = userRes.toRepresentation(); return Optional.ofNullable(userRepresentation.getAttributes()) .map(attributes -> attributes.get(properties.ldapIdKey())) .map(List::getFirst) .orElseGet(userRepresentation::getId); } String mapKeycloakUserId(UserResource userRes) { return userRes.toRepresentation().getId(); } String mapEmail(UserResource userRes) { return userRes.toRepresentation().getEmail(); } String mapFirstName(UserResource userRes) { return userRes.toRepresentation().getFirstName(); } String mapLastName(UserResource userRes) { return userRes.toRepresentation().getLastName(); } String mapUsername(UserResource userRes) { return userRes.toRepresentation().getUsername(); } String mapFullName(UserResource userRes) { return String.join(" ", Stream.of(userRes.toRepresentation().getFirstName(), userRes.toRepresentation().getLastName()) .filter(Objects::nonNull).toArray(String[]::new)); } String mapFullNameReversed(UserResource userRes) { return String.join(" ", Stream.of(userRes.toRepresentation().getLastName(), userRes.toRepresentation().getFirstName()) .filter(Objects::nonNull).toArray(String[]::new)); } private Optional<Map<String, List<String>>> getUserAttributes(UserResource userResource) { return Optional.ofNullable(userResource.toRepresentation().getAttributes()); } }