Skip to content

Commit

Permalink
Fix updating of client protocolMappers
Browse files Browse the repository at this point in the history
  • Loading branch information
jkroepke committed Jun 10, 2020
1 parent 5459534 commit 1d8d84b
Show file tree
Hide file tree
Showing 22 changed files with 847 additions and 105 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Updating `protocolMappers` on `clients`

### Removed

- `import.file` parameter
Expand Down
18 changes: 18 additions & 0 deletions contrib/example-config/moped.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,24 @@
],
"webOrigins": [
"*"
],
"protocolMappers": [
{
"name": "BranchCodeMapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"consentRequired": false,
"config": {
"aggregate.attrs": "false",
"userinfo.token.claim": "true",
"multivalued": "false",
"user.attribute": "branch",
"id.token.claim": "false",
"access.token.claim": "true",
"claim.name": "branch",
"jsonType.label": "String"
}
}
]
}
],
Expand Down
1 change: 1 addition & 0 deletions contrib/native/test-with-import-files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ while read -r file; do
done < <(
find src/test/resources/import-files \
-type f \
-name '*.json' \
! -path '*cli*' \
-and ! -path '*exported-realm*' \
-and ! -name '*invalid*' \
Expand Down
4 changes: 2 additions & 2 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

| Feature | Since | Description |
| ------------------------------------------ | ----- | -------------------------------------------------------------------------------------------------------- |
| Create clients | 1.0.0 | Create client configuration while creating or updating realms |
| Update clients | 1.0.0 | Update client configuration while updating realms |
| Create clients | 1.0.0 | Create client configuration (inclusive protocolMappers) while creating or updating realms |
| Update clients | 1.0.0 | Update client configuration (inclusive protocolMappers) while updating realms |
| Add roles | 1.0.0 | Add roles while creating or updating realms |
| Update roles | 1.0.0 | Update role properties while updating realms |
| Add composites to roles | 1.3.0 | Add role with realm-level and client-level composite roles while creating or updating realms |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
import de.adorsys.keycloak.config.util.ResponseUtil;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -60,8 +63,12 @@ public Optional<ClientRepresentation> tryToFindClient(String realm, String clien
return maybeClient;
}

public ClientRepresentation getClient(String realm, String clientId) {
return loadClient(realm, clientId);
public ClientRepresentation getClientByClientId(String realm, String clientId) {
return loadClientByClientId(realm, clientId);
}

public ClientRepresentation getClientById(String realm, String id) {
return loadClientById(realm, id).toRepresentation();
}

public String getClientSecret(String realm, String clientId) {
Expand All @@ -85,8 +92,7 @@ public void update(String realm, ClientRepresentation clientToUpdate) {
clientResource.update(clientToUpdate);
}


private ClientRepresentation loadClient(String realm, String clientId) {
private ClientRepresentation loadClientByClientId(String realm, String clientId) {
List<ClientRepresentation> foundClients = realmRepository.loadRealm(realm)
.clients()
.findByClientId(clientId);
Expand All @@ -98,8 +104,20 @@ private ClientRepresentation loadClient(String realm, String clientId) {
return foundClients.get(0);
}

private ClientResource loadClientById(String realm, String id) {
ClientResource client = realmRepository.loadRealm(realm)
.clients()
.get(id);

if (client == null) {
throw new KeycloakRepositoryException("Cannot find client by id '" + id + "'");
}

return client;
}

final ClientResource getClientResource(String realm, String clientId) {
ClientRepresentation client = loadClient(realm, clientId);
ClientRepresentation client = loadClientByClientId(realm, clientId);
return realmRepository.loadRealm(realm)
.clients()
.get(client.getId());
Expand All @@ -119,4 +137,35 @@ public final List<ClientRepresentation> getClients(String realm) {
.clients()
.findAll();
}

public void addProtocolMappers(String realm, String clientId, List<ProtocolMapperRepresentation> protocolMappers) {
ClientResource clientScopeResource = loadClientById(realm, clientId);
ProtocolMappersResource protocolMappersResource = clientScopeResource.getProtocolMappers();

for (ProtocolMapperRepresentation protocolMapper : protocolMappers) {
Response response = protocolMappersResource.createMapper(protocolMapper);
ResponseUtil.throwOnError(response);
}
}

public void removeProtocolMappers(String realm, String clientId, List<ProtocolMapperRepresentation> protocolMappers) {
ClientResource clientScopeResource = loadClientById(realm, clientId);
ProtocolMappersResource protocolMappersResource = clientScopeResource.getProtocolMappers();

List<ProtocolMapperRepresentation> existingProtocolMappers = clientScopeResource.getProtocolMappers().getMappers();
List<ProtocolMapperRepresentation> protocolMapperToRemove = existingProtocolMappers.stream().filter(em -> protocolMappers.stream().anyMatch(m -> Objects.equals(m.getName(), em.getName()))).collect(Collectors.toList());

for (ProtocolMapperRepresentation protocolMapper : protocolMapperToRemove) {
protocolMappersResource.delete(protocolMapper.getId());
}
}

public void updateProtocolMappers(String realm, String clientId, List<ProtocolMapperRepresentation> protocolMappers) {
ClientResource clientScopeResource = loadClientById(realm, clientId);
ProtocolMappersResource protocolMappersResource = clientScopeResource.getProtocolMappers();

for (ProtocolMapperRepresentation protocolMapper : protocolMappers) {
protocolMappersResource.update(protocolMapper.getId(), protocolMapper);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public void addClientRoles(String realm, String groupId, String clientId, List<S
GroupResource groupResource = loadGroupById(realm, groupId);
RoleMappingResource rolesResource = groupResource.roles();

ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
RoleScopeResource groupClientRolesResource = rolesResource.clientLevel(client.getId());

List<RoleRepresentation> clientRoles = roleRepository.searchClientRoles(realm, clientId, roleNames);
Expand All @@ -139,7 +139,7 @@ public void removeClientRoles(String realm, String groupId, String clientId, Lis
GroupResource groupResource = loadGroupById(realm, groupId);
RoleMappingResource rolesResource = groupResource.roles();

ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
RoleScopeResource groupClientRolesResource = rolesResource.clientLevel(client.getId());

List<RoleRepresentation> clientRoles = roleRepository.searchClientRoles(realm, clientId, roleNames);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private MultivaluedHashMap<String, RoleRepresentation> findClientComposites(Stri

private Set<RoleRepresentation> findClientComposites(String realm, String clientId, Supplier<RoleResource> roleSupplier) {
RoleResource roleResource = roleSupplier.get();
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);

return roleResource.getClientRoleComposites(client.getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public List<RoleRepresentation> findRealmRoles(String realm, Collection<String>
}

public final Optional<RoleRepresentation> tryToFindClientRole(String realm, String clientId, String roleName) {
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
RealmResource realmResource = realmRepository.loadRealm(realm);

List<RoleRepresentation> clientRoles = realmResource.clients()
Expand All @@ -113,7 +113,7 @@ public RoleRepresentation findClientRole(String realm, String clientId, String r
}

public List<RoleRepresentation> searchClientRoles(String realm, String clientId, List<String> roles) {
ClientRepresentation foundClient = clientRepository.getClient(realm, clientId);
ClientRepresentation foundClient = clientRepository.getClientByClientId(realm, clientId);

ClientResource clientResource = realmRepository.loadRealm(realm)
.clients()
Expand All @@ -127,7 +127,7 @@ public List<RoleRepresentation> searchClientRoles(String realm, String clientId,
}

public void createClientRole(String realm, String clientId, RoleRepresentation role) {
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
RolesResource rolesResource = realmRepository.loadRealm(realm)
.clients()
.get(client.getId())
Expand Down Expand Up @@ -177,7 +177,7 @@ public void removeRealmRolesForUser(String realm, String username, List<RoleRepr
}

public void addClientRolesToUser(String realm, String username, String clientId, List<RoleRepresentation> clientRoles) {
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
UserResource userResource = userRepository.getUserResource(realm, username);

RoleScopeResource userClientRoles = userResource.roles()
Expand All @@ -187,7 +187,7 @@ public void addClientRolesToUser(String realm, String username, String clientId,
}

public void removeClientRolesForUser(String realm, String username, String clientId, List<RoleRepresentation> clientRoles) {
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
UserResource userResource = userRepository.getUserResource(realm, username);

RoleScopeResource userClientRoles = userResource.roles()
Expand All @@ -197,7 +197,7 @@ public void removeClientRolesForUser(String realm, String username, String clien
}

public List<String> getUserClientLevelRoles(String realm, String username, String clientId) {
ClientRepresentation client = clientRepository.getClient(realm, clientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, clientId);
UserResource userResource = userRepository.getUserResource(realm, username);

List<RoleRepresentation> roles = userResource.roles()
Expand All @@ -215,7 +215,7 @@ final RoleResource loadRealmRole(String realm, String roleName) {
}

final RoleResource loadClientRole(String realm, String roleClientId, String roleName) {
ClientRepresentation client = clientRepository.getClient(realm, roleClientId);
ClientRepresentation client = clientRepository.getClientByClientId(realm, roleClientId);

return realmRepository.loadRealm(realm)
.clients()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import de.adorsys.keycloak.config.model.RealmImport;
import de.adorsys.keycloak.config.repository.ClientRepository;
import de.adorsys.keycloak.config.util.CloneUtil;
import de.adorsys.keycloak.config.util.ProtocolMapperUtil;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -82,7 +84,8 @@ private void updateClientIfNeeded(String realm, ClientRepresentation clientToUpd
}

private boolean areClientsEqual(String realm, ClientRepresentation clientToUpdate, ClientRepresentation existingClient) {
if (CloneUtil.deepEquals(clientToUpdate, existingClient, "id", "secret")) {
// Clients are never equal except every properties if defined in import realm
if (CloneUtil.deepEquals(clientToUpdate, existingClient, "id", "secret", "access")) {
String clientSecret = clientRepository.getClientSecret(realm, clientToUpdate.getClientId());
return clientSecret.equals(clientToUpdate.getSecret());
}
Expand All @@ -93,5 +96,25 @@ private boolean areClientsEqual(String realm, ClientRepresentation clientToUpdat
private void updateClient(String realm, ClientRepresentation existingClient, ClientRepresentation clientToImport) {
ClientRepresentation patchedClient = CloneUtil.patch(existingClient, clientToImport, "id");
clientRepository.update(realm, patchedClient);

List<ProtocolMapperRepresentation> protocolMappers = patchedClient.getProtocolMappers();
if (protocolMappers != null) {
String clientId = patchedClient.getId();
updateProtocolMappers(realm, clientId, protocolMappers);
}
}

private void updateProtocolMappers(String realm, String clientId, List<ProtocolMapperRepresentation> protocolMappers) {
ClientRepresentation existingClient = clientRepository.getClientById(realm, clientId);

List<ProtocolMapperRepresentation> existingProtocolMappers = existingClient.getProtocolMappers();

List<ProtocolMapperRepresentation> protocolMappersToAdd = ProtocolMapperUtil.estimateProtocolMappersToAdd(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToRemove = ProtocolMapperUtil.estimateProtocolMappersToRemove(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToUpdate = ProtocolMapperUtil.estimateProtocolMappersToUpdate(protocolMappers, existingProtocolMappers);

clientRepository.addProtocolMappers(realm, clientId, protocolMappersToAdd);
clientRepository.removeProtocolMappers(realm, clientId, protocolMappersToRemove);
clientRepository.updateProtocolMappers(realm, clientId, protocolMappersToUpdate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
import de.adorsys.keycloak.config.properties.ImportConfigProperties.ImportManagedProperties.ImportManagedPropertiesValues;
import de.adorsys.keycloak.config.repository.ClientScopeRepository;
import de.adorsys.keycloak.config.util.CloneUtil;
import de.adorsys.keycloak.config.util.ProtocolMapperUtil;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -160,63 +160,12 @@ private void updateProtocolMappers(String realm, String clientScopeId, List<Prot

List<ProtocolMapperRepresentation> existingProtocolMappers = existingClientScope.getProtocolMappers();

List<ProtocolMapperRepresentation> protocolMappersToAdd = estimateProtocolMappersToAdd(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToRemove = estimateProtocolMappersToRemove(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToUpdate = estimateProtocolMappersToUpdate(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToAdd = ProtocolMapperUtil.estimateProtocolMappersToAdd(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToRemove = ProtocolMapperUtil.estimateProtocolMappersToRemove(protocolMappers, existingProtocolMappers);
List<ProtocolMapperRepresentation> protocolMappersToUpdate = ProtocolMapperUtil.estimateProtocolMappersToUpdate(protocolMappers, existingProtocolMappers);

clientScopeRepository.addProtocolMappers(realm, clientScopeId, protocolMappersToAdd);
clientScopeRepository.removeProtocolMappers(realm, clientScopeId, protocolMappersToRemove);
clientScopeRepository.updateProtocolMappers(realm, clientScopeId, protocolMappersToUpdate);
}

private List<ProtocolMapperRepresentation> estimateProtocolMappersToRemove(List<ProtocolMapperRepresentation> protocolMappers, List<ProtocolMapperRepresentation> existingProtocolMappers) {
List<ProtocolMapperRepresentation> protocolMappersToRemove = new ArrayList<>();

if (existingProtocolMappers == null) {
return protocolMappersToRemove;
}

for (ProtocolMapperRepresentation existingProtocolMapper : existingProtocolMappers) {
if (protocolMappers.stream().noneMatch(m -> Objects.equals(m.getName(), existingProtocolMapper.getName()))) {
protocolMappersToRemove.add(existingProtocolMapper);
}
}

return protocolMappersToRemove;
}

private List<ProtocolMapperRepresentation> estimateProtocolMappersToAdd(List<ProtocolMapperRepresentation> protocolMappers, List<ProtocolMapperRepresentation> existingProtocolMappers) {
List<ProtocolMapperRepresentation> protocolMappersToAdd = new ArrayList<>();

if (existingProtocolMappers == null) {
return protocolMappers;
}

for (ProtocolMapperRepresentation protocolMapper : protocolMappers) {
if (existingProtocolMappers.stream().noneMatch(em -> Objects.equals(em.getName(), protocolMapper.getName()))) {
protocolMappersToAdd.add(protocolMapper);
}
}

return protocolMappersToAdd;
}

private List<ProtocolMapperRepresentation> estimateProtocolMappersToUpdate(List<ProtocolMapperRepresentation> protocolMappers, List<ProtocolMapperRepresentation> existingProtocolMappers) {
List<ProtocolMapperRepresentation> protocolMappersToUpdate = new ArrayList<>();

if (existingProtocolMappers == null) {
return protocolMappersToUpdate;
}

for (ProtocolMapperRepresentation protocolMapper : protocolMappers) {
Optional<ProtocolMapperRepresentation> existingProtocolMapper = existingProtocolMappers.stream().filter(em -> Objects.equals(em.getName(), protocolMapper.getName())).findFirst();
if (existingProtocolMapper.isPresent()) {
ProtocolMapperRepresentation patchedProtocolMapper = CloneUtil.patch(existingProtocolMapper.get(), protocolMapper);
protocolMappersToUpdate.add(patchedProtocolMapper);
}
}

return protocolMappersToUpdate;
}

}
Loading

0 comments on commit 1d8d84b

Please sign in to comment.