-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add service account realm role mapping (#31)
- Loading branch information
1 parent
72a1a66
commit 68d2413
Showing
4 changed files
with
129 additions
and
2 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
...bs/keycloak/configurator/commands/configure/boundary/ServiceAccountRealmRoleImporter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package com.cycrilabs.keycloak.configurator.commands.configure.boundary; | ||
|
||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import jakarta.enterprise.context.ApplicationScoped; | ||
|
||
import org.keycloak.representations.idm.RoleRepresentation; | ||
import org.keycloak.representations.idm.UserRepresentation; | ||
|
||
import com.cycrilabs.keycloak.configurator.commands.configure.entity.ServiceUserRealmRoleMappingDTO; | ||
import com.cycrilabs.keycloak.configurator.shared.control.JsonUtil; | ||
import com.cycrilabs.keycloak.configurator.shared.entity.EntityType; | ||
|
||
import io.quarkus.logging.Log; | ||
|
||
@ApplicationScoped | ||
public class ServiceAccountRealmRoleImporter extends AbstractImporter { | ||
@Override | ||
public EntityType getType() { | ||
return EntityType.SERVICE_ACCOUNT_REALM_ROLE; | ||
} | ||
|
||
@Override | ||
protected Object importFile(final Path file) { | ||
final String[] fileNameParts = file.toString().split(PATH_SEPARATOR); | ||
final String realmName = fileNameParts[fileNameParts.length - 4]; | ||
final String serviceUsername = fileNameParts[fileNameParts.length - 2]; | ||
|
||
Log.debugf( | ||
"Importing service account realm roles '%s' for service user '%s' of realm '%s'.", | ||
file.getFileName(), serviceUsername, realmName); | ||
|
||
final UserRepresentation user = loadUserByUsername(realmName, serviceUsername); | ||
if (user == null) { | ||
return null; | ||
} | ||
|
||
Log.debugf("Found service user '%s' of realm '%s'.", user.getUsername(), realmName); | ||
|
||
importServiceUserRealmRoleMappings(file, realmName, user); | ||
|
||
return null; | ||
} | ||
|
||
private UserRepresentation loadUserByUsername(final String realmName, final String username) { | ||
try { | ||
final List<UserRepresentation> userRepresentations = keycloak.realm(realmName) | ||
.users() | ||
.searchByUsername(username, Boolean.TRUE); | ||
if (userRepresentations.size() == 1) { | ||
return userRepresentations.getFirst(); | ||
} | ||
|
||
Log.warnf("Found %d users '%s' of realm '%s'. Skipping import.", | ||
Integer.valueOf(userRepresentations.size()), username, realmName); | ||
} catch (final Exception e) { | ||
Log.errorf("Could not find user '%s' of realm '%s': %s", username, realmName, | ||
e.getMessage()); | ||
} | ||
return null; | ||
} | ||
|
||
private void importServiceUserRealmRoleMappings(final Path file, final String realmName, | ||
final UserRepresentation serviceUser) { | ||
final ServiceUserRealmRoleMappingDTO serviceUserRealmRoleMappings = | ||
JsonUtil.loadEntity(file, ServiceUserRealmRoleMappingDTO.class); | ||
final List<String> roles = serviceUserRealmRoleMappings.getRoles(); | ||
|
||
Log.debugf("Importing realm roles '%s' for service user '%s' of realm '%s'.", | ||
roles.toString(), serviceUser.getUsername(), realmName); | ||
final Map<String, RoleRepresentation> availableRealmRoles = keycloak.realm(realmName) | ||
.roles() | ||
.list() | ||
.stream() | ||
.collect(Collectors.toMap(RoleRepresentation::getName, Function.identity())); | ||
Log.debugf("Found %d roles of realm '%s'.", Integer.valueOf(availableRealmRoles.size()), | ||
realmName); | ||
|
||
keycloak.realm(realmName) | ||
.users() | ||
.get(serviceUser.getId()) | ||
.roles() | ||
.realmLevel() | ||
.add(roles.stream() | ||
.filter(availableRealmRoles::containsKey) | ||
.map(availableRealmRoles::get) | ||
.toList()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
...ilabs/keycloak/configurator/commands/configure/entity/ServiceUserRealmRoleMappingDTO.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.cycrilabs.keycloak.configurator.commands.configure.entity; | ||
|
||
import java.util.List; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class ServiceUserRealmRoleMappingDTO { | ||
private List<String> roles; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters