KEYCLOAK-12281 Fix export/import for users that have custom credential algorithms with no salt
This commit is contained in:
parent
b90a0307ea
commit
f0d95da52d
3 changed files with 51 additions and 2 deletions
|
@ -1,18 +1,40 @@
|
|||
package org.keycloak.models.credential.dto;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.Base64;
|
||||
|
||||
public class PasswordSecretData {
|
||||
|
||||
public static final Logger logger = Logger.getLogger(PasswordSecretData.class);
|
||||
|
||||
private final String value;
|
||||
private final byte[] salt;
|
||||
|
||||
@JsonCreator
|
||||
public PasswordSecretData(@JsonProperty("value") String value, @JsonProperty("salt") byte[] salt) {
|
||||
public PasswordSecretData(@JsonProperty("value") String value, @JsonProperty("salt") String salt) {
|
||||
this(value, decodeSalt(salt));
|
||||
}
|
||||
|
||||
public PasswordSecretData(String value, byte[] salt) {
|
||||
this.value = value;
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
private static byte[] decodeSalt(String salt) {
|
||||
try {
|
||||
return Base64.decode(salt);
|
||||
} catch (IOException ioe) {
|
||||
// Could happen under some corner cases that value is still placeholder value "__SALT__" . For example when importing JSON from
|
||||
// previous version and using custom hash provider without salt support.
|
||||
logger.tracef("Can't base64 decode the salt %s . Fallback to null salt", salt);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.keycloak.admin.client.resource.UserResource;
|
|||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.models.credential.dto.PasswordCredentialData;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||
|
@ -39,6 +41,7 @@ import org.keycloak.representations.idm.ClientMappingsRepresentation;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
|
@ -58,6 +61,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
|||
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||
import org.keycloak.testsuite.util.RealmRepUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -72,6 +76,8 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
|
@ -82,7 +88,7 @@ public class ExportImportUtil {
|
|||
|
||||
// In the old testsuite, this method exists as a public method of ImportTest from the model package.
|
||||
// However, model package is not ready to be migrated yet.
|
||||
public static void assertDataImportedInRealm(Keycloak adminClient, KeycloakTestingClient testingClient, RealmRepresentation realm) {
|
||||
public static void assertDataImportedInRealm(Keycloak adminClient, KeycloakTestingClient testingClient, RealmRepresentation realm) throws IOException {
|
||||
Assert.assertTrue(realm.isVerifyEmail());
|
||||
Assert.assertEquals((Integer)3600000, realm.getOfflineSessionIdleTimeout());
|
||||
Assert.assertEquals((Integer)1500, realm.getAccessTokenLifespanForImplicitFlow());
|
||||
|
@ -179,6 +185,13 @@ public class ExportImportUtil {
|
|||
// user with creation timestamp as string in import
|
||||
Assert.assertEquals(new Long(123655), loginclient.getCreatedTimestamp());
|
||||
|
||||
UserRepresentation hashedPasswordUser = findByUsername(realmRsc, "hashedpassworduser");
|
||||
CredentialRepresentation password = realmRsc.users().get(hashedPasswordUser.getId()).credentials().stream()
|
||||
.filter(credential -> PasswordCredentialModel.TYPE.equals(credential.getType()))
|
||||
.findFirst().get();
|
||||
PasswordCredentialData credentialData = JsonSerialization.readValue(password.getCredentialData(), PasswordCredentialData.class);
|
||||
Assert.assertEquals(1234, credentialData.getHashIterations());
|
||||
|
||||
List<RoleRepresentation> realmRoles = realmRolesForUser(realmRsc, admin);
|
||||
Assert.assertEquals(1, realmRoles.size());
|
||||
Assert.assertEquals("admin", realmRoles.iterator().next().getName());
|
||||
|
|
|
@ -150,6 +150,20 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"username": "hashedpassworduser",
|
||||
"createdTimestamp" : "123655",
|
||||
"enabled": true,
|
||||
"credentials": [
|
||||
{
|
||||
"id": "b0c22f34-f6fb-4165-8fd3-54d3cb4599d1",
|
||||
"type": "password",
|
||||
"createdDate": 1576751136227,
|
||||
"secretData": "{\"value\":\"pass\",\"salt\":\"__SALT__\"}",
|
||||
"credentialData": "{\"hashIterations\":1234,\"algorithm\":\"pbkdf2-sha256\"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"username": "admin",
|
||||
"enabled": true,
|
||||
|
|
Loading…
Reference in a new issue