diff --git a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
index d597cf3..95c7ca3 100755
--- a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
@@ -155,4 +155,101 @@ public class CredentialRepresentation {
public void setConfig(MultivaluedHashMap<String, String> config) {
this.config = config;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode());
+ result = prime * result + ((config == null) ? 0 : config.hashCode());
+ result = prime * result + ((counter == null) ? 0 : counter.hashCode());
+ result = prime * result + ((createdDate == null) ? 0 : createdDate.hashCode());
+ result = prime * result + ((device == null) ? 0 : device.hashCode());
+ result = prime * result + ((digits == null) ? 0 : digits.hashCode());
+ result = prime * result + ((hashIterations == null) ? 0 : hashIterations.hashCode());
+ result = prime * result + ((hashedSaltedValue == null) ? 0 : hashedSaltedValue.hashCode());
+ result = prime * result + ((period == null) ? 0 : period.hashCode());
+ result = prime * result + ((salt == null) ? 0 : salt.hashCode());
+ result = prime * result + ((temporary == null) ? 0 : temporary.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CredentialRepresentation other = (CredentialRepresentation) obj;
+ if (algorithm == null) {
+ if (other.algorithm != null)
+ return false;
+ } else if (!algorithm.equals(other.algorithm))
+ return false;
+ if (config == null) {
+ if (other.config != null)
+ return false;
+ } else if (!config.equals(other.config))
+ return false;
+ if (counter == null) {
+ if (other.counter != null)
+ return false;
+ } else if (!counter.equals(other.counter))
+ return false;
+ if (createdDate == null) {
+ if (other.createdDate != null)
+ return false;
+ } else if (!createdDate.equals(other.createdDate))
+ return false;
+ if (device == null) {
+ if (other.device != null)
+ return false;
+ } else if (!device.equals(other.device))
+ return false;
+ if (digits == null) {
+ if (other.digits != null)
+ return false;
+ } else if (!digits.equals(other.digits))
+ return false;
+ if (hashIterations == null) {
+ if (other.hashIterations != null)
+ return false;
+ } else if (!hashIterations.equals(other.hashIterations))
+ return false;
+ if (hashedSaltedValue == null) {
+ if (other.hashedSaltedValue != null)
+ return false;
+ } else if (!hashedSaltedValue.equals(other.hashedSaltedValue))
+ return false;
+ if (period == null) {
+ if (other.period != null)
+ return false;
+ } else if (!period.equals(other.period))
+ return false;
+ if (salt == null) {
+ if (other.salt != null)
+ return false;
+ } else if (!salt.equals(other.salt))
+ return false;
+ if (temporary == null) {
+ if (other.temporary != null)
+ return false;
+ } else if (!temporary.equals(other.temporary))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!value.equals(other.value))
+ return false;
+ return true;
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 6ed677f..c7b9945 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -52,6 +52,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.provider.ProviderFactory;
@@ -162,8 +163,9 @@ public class UsersResource {
try {
UserModel user = session.users().addUser(realm, rep.getUsername());
Set<String> emptySet = Collections.emptySet();
- UserResource.updateUserFromRep(user, rep, emptySet, realm, session, false);
+ UserResource.updateUserFromRep(user, rep, emptySet, realm, session, false);
+ RepresentationToModel.createCredentials(rep, session, realm, user);
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, user.getId()).representation(rep).success();
if (session.getTransactionManager().isActive()) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index 567b284..edcb938 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -17,6 +17,7 @@
package org.keycloak.testsuite.admin;
+import org.apache.commons.collections.map.SingletonMap;
import org.hamcrest.Matchers;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page;
@@ -29,9 +30,14 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleMappingResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.common.util.Base64;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.credential.CredentialModel;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -58,6 +64,8 @@ import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.WebDriver;
+import com.google.common.collect.ImmutableMap;
+
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.ws.rs.ClientErrorException;
@@ -65,12 +73,15 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -169,6 +180,73 @@ public class UserTest extends AbstractAdminTest {
}
@Test
+ public void createUserWithHashedCredentials() {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername("user_creds");
+ user.setEmail("email@localhost");
+
+ CredentialRepresentation hashedPassword = new CredentialRepresentation();
+ hashedPassword.setAlgorithm("my-algorithm");
+ hashedPassword.setCounter(11);
+ hashedPassword.setCreatedDate(1001l);
+ hashedPassword.setDevice("deviceX");
+ hashedPassword.setDigits(6);
+ hashedPassword.setHashIterations(22);
+ hashedPassword.setHashedSaltedValue("ABC");
+ hashedPassword.setPeriod(99);
+ hashedPassword.setSalt(Base64.encodeBytes("theSalt".getBytes()));
+ hashedPassword.setType(CredentialRepresentation.PASSWORD);
+
+ user.setCredentials(Arrays.asList(hashedPassword));
+
+ createUser(user);
+
+ CredentialModel credentialHashed = fetchCredentials("user_creds");
+ assertNotNull("Expecting credential", credentialHashed);
+ assertEquals("my-algorithm", credentialHashed.getAlgorithm());
+ assertEquals(11, credentialHashed.getCounter());
+ assertEquals(Long.valueOf(1001), credentialHashed.getCreatedDate());
+ assertEquals("deviceX", credentialHashed.getDevice());
+ assertEquals(6, credentialHashed.getDigits());
+ assertEquals(22, credentialHashed.getHashIterations());
+ assertEquals("ABC", credentialHashed.getValue());
+ assertEquals(99, credentialHashed.getPeriod());
+ assertEquals("theSalt", new String(credentialHashed.getSalt()));
+ assertEquals(CredentialRepresentation.PASSWORD, credentialHashed.getType());
+ }
+
+ @Test
+ public void createUserWithRawCredentials() {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername("user_rawpw");
+ user.setEmail("email.raw@localhost");
+
+ CredentialRepresentation rawPassword = new CredentialRepresentation();
+ rawPassword.setValue("ABCD");
+ rawPassword.setType(CredentialRepresentation.PASSWORD);
+ user.setCredentials(Arrays.asList(rawPassword));
+
+ createUser(user);
+
+ CredentialModel credential = fetchCredentials("user_rawpw");
+ assertNotNull("Expecting credential", credential);
+ assertEquals(PasswordPolicy.HASH_ALGORITHM_DEFAULT, credential.getAlgorithm());
+ assertEquals(PasswordPolicy.HASH_ITERATIONS_DEFAULT, credential.getHashIterations());
+ assertNotEquals("ABCD", credential.getValue());
+ assertEquals(CredentialRepresentation.PASSWORD, credential.getType());
+ }
+
+ private CredentialModel fetchCredentials(String username) {
+ return getTestingClient().server(REALM_NAME).fetch(session -> {
+ RealmModel realm = session.getContext().getRealm();
+ UserModel user = session.users().getUserByUsername(username, realm);
+ List<CredentialModel> storedCredentialsByType = session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialRepresentation.PASSWORD);
+ System.out.println(storedCredentialsByType.size());
+ return storedCredentialsByType.get(0);
+ }, CredentialModel.class);
+ }
+
+ @Test
public void createDuplicatedUser3() {
createUser();