Skip to content

Commit

Permalink
adorsys#1015 Added option to store checksum for each imported file
Browse files Browse the repository at this point in the history
- changed set language level to 17
  • Loading branch information
Stephan Schrader committed May 10, 2024
1 parent 554c4a1 commit 152105d
Show file tree
Hide file tree
Showing 14 changed files with 355 additions and 34 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

- Added option to calculate checksum for each import file ([#1015](https://github.com/adorsys/keycloak-config-cli/issues/1015))

## [5.12.0] - 2024-03-28
- Added support for managing message bundles

Expand Down
40 changes: 21 additions & 19 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
</issueManagement>

<properties>
<java.version>11</java.version>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

Expand Down
17 changes: 11 additions & 6 deletions src/main/java/de/adorsys/keycloak/config/model/RealmImport.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class RealmImport extends RealmRepresentation {
private Map<String, Map<String, String>> messageBundles;

private String checksum;
private String source;

@Override
@SuppressWarnings("java:S1168")
Expand All @@ -54,12 +55,6 @@ public void setAuthenticationFlowImports(List<AuthenticationFlowImport> authenti
this.authenticationFlowImports = authenticationFlowImports;
}

@SuppressWarnings("unused")
@JsonSetter("userProfile")
public void setUserProfile(Map<String, List<Map<String, Object>>> userProfile) {
this.userProfile = userProfile;
}

public Map<String, Map<String, String>> getMessageBundles() {
return messageBundles;
}
Expand All @@ -83,4 +78,14 @@ public String getChecksum() {
public void setChecksum(String checksum) {
this.checksum = checksum;
}

@JsonIgnore
public String getSource() {
return source;
}

@JsonIgnore
public void setSource(String source) {
this.source = source;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,19 @@ public static class ImportBehaviorsProperties {
@NotNull
private final boolean skipAttributesForFederatedUser;

public ImportBehaviorsProperties(boolean syncUserFederation, boolean removeDefaultRoleFromUser, boolean skipAttributesForFederatedUser) {
@NotNull
private final boolean checksumWithCacheKey;

@NotNull
private final ChecksumChangedOption checksumChanged;

public ImportBehaviorsProperties(boolean syncUserFederation, boolean removeDefaultRoleFromUser, boolean skipAttributesForFederatedUser,
boolean checksumWithCacheKey, ChecksumChangedOption checksumChanged) {
this.syncUserFederation = syncUserFederation;
this.removeDefaultRoleFromUser = removeDefaultRoleFromUser;
this.skipAttributesForFederatedUser = skipAttributesForFederatedUser;
this.checksumWithCacheKey = checksumWithCacheKey;
this.checksumChanged = checksumChanged;
}

public boolean isSyncUserFederation() {
Expand All @@ -360,6 +369,18 @@ public boolean isRemoveDefaultRoleFromUser() {
public boolean isSkipAttributesForFederatedUser() {
return skipAttributesForFederatedUser;
}

public boolean isChecksumWithCacheKey() {
return checksumWithCacheKey;
}

public ChecksumChangedOption getChecksumChanged() {
return checksumChanged;
}

public enum ChecksumChangedOption {
CONTINUE, FAIL
}
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ private Pair<String, List<RealmImport>> readRealmImportFromImportResource(Import
} catch (Exception e) {
throw new InvalidImportException("Unable to parse file '" + location + "': " + e.getMessage(), e);
}
realmImports.forEach(realmImport -> realmImport.setChecksum(contentChecksum));
realmImports.forEach(realmImport -> {
realmImport.setChecksum(contentChecksum);
realmImport.setSource(location);
});

return new ImmutablePair<>(location, realmImports);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@

package de.adorsys.keycloak.config.service.checksum;

import de.adorsys.keycloak.config.exception.InvalidImportException;
import de.adorsys.keycloak.config.model.RealmImport;
import de.adorsys.keycloak.config.properties.ImportConfigProperties;
import de.adorsys.keycloak.config.repository.RealmRepository;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.keycloak.representations.idm.RealmRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -51,25 +54,48 @@ public void doImport(RealmImport realmImport) {
Map<String, String> customAttributes = existingRealm.getAttributes();

String importChecksum = realmImport.getChecksum();
customAttributes.put(getCustomAttributeKey(), importChecksum);
String attributeKey = getCustomAttributeKey(realmImport);
customAttributes.put(attributeKey, importChecksum);
realmRepository.update(existingRealm);

logger.debug("Updated import checksum of realm '{}' to '{}'", realmImport.getRealm(), importChecksum);
logger.debug("Updated import checksum of realm '{}' to '{}', attributeKey: '{}'", realmImport.getRealm(), importChecksum, attributeKey);
}

public boolean hasToBeUpdated(RealmImport realmImport) {
RealmRepresentation existingRealm = realmRepository.get(realmImport.getRealm());
Map<String, String> customAttributes = existingRealm.getAttributes();

String readChecksum = customAttributes.get(getCustomAttributeKey());
String readChecksum = customAttributes.get(getCustomAttributeKey(realmImport));
if (readChecksum == null) {
return true;
}

return !Objects.equals(realmImport.getChecksum(), readChecksum);
if (Objects.equals(realmImport.getChecksum(), readChecksum)) {
return false;
}

// checksum has changed
return switch (importConfigProperties.getBehaviors().getChecksumChanged()) {
case CONTINUE -> true;
case FAIL -> throw new InvalidImportException(
"The checksum of import '%s' has changed from: '%s', to: '%s'"
.formatted(realmImport.getSource(), readChecksum, realmImport.getChecksum())
);
};
}

private String getCustomAttributeKey() {
private String getCustomAttributeKey(RealmImport realmImport) {
String attributeSuffix;
if (importConfigProperties.getBehaviors().isChecksumWithCacheKey()) {
attributeSuffix = importConfigProperties.getCache().getKey();
} else {
attributeSuffix = FilenameUtils.getName(realmImport.getSource()) + "_" + DigestUtils.md5Hex(realmImport.getSource());
}

return MessageFormat.format(
ImportConfigProperties.REALM_CHECKSUM_ATTRIBUTE_PREFIX_KEY,
importConfigProperties.getCache().getKey()
attributeSuffix
);
}

}
2 changes: 2 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import.remote-state.encryption-salt=2B521C795FBE2F2425DB150CD3700BA9
import.behaviors.remove-default-role-from-user=false
import.behaviors.skip-attributes-for-federated-user=false
import.behaviors.sync-user-federation=false
import.behaviors.checksum-with-cache-key=true
import.behaviors.checksum-changed=continue
import.managed.authentication-flow=full
import.managed.group=full
import.managed.required-action=full
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import de.adorsys.keycloak.config.extensions.GithubActionsExtension;
import de.adorsys.keycloak.config.properties.ImportConfigProperties.ImportManagedProperties.ImportManagedPropertiesValues;
import de.adorsys.keycloak.config.properties.ImportConfigProperties.ImportBehaviorsProperties.ChecksumChangedOption;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -72,6 +73,8 @@
"import.behaviors.sync-user-federation=true",
"import.behaviors.remove-default-role-from-user=true",
"import.behaviors.skip-attributes-for-federated-user=true",
"import.behaviors.checksum-with-cache-key=true",
"import.behaviors.checksum-changed=fail"
})
class ImportConfigPropertiesTest {

Expand Down Expand Up @@ -112,6 +115,8 @@ void shouldPopulateConfigurationProperties() {
assertThat(properties.getBehaviors().isSyncUserFederation(), is(true));
assertThat(properties.getBehaviors().isRemoveDefaultRoleFromUser(), is(true));
assertThat(properties.getBehaviors().isSkipAttributesForFederatedUser(), is(true));
assertThat(properties.getBehaviors().isChecksumWithCacheKey(), is(true));
assertThat(properties.getBehaviors().getChecksumChanged(), is(ChecksumChangedOption.FAIL));
}

@EnableConfigurationProperties(ImportConfigProperties.class)
Expand Down
Loading

0 comments on commit 152105d

Please sign in to comment.