/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.admin;
import org.hamcrest.Matchers;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.admin.client.resource.IdentityProviderResource;
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.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.ErrorRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.AdminEventPaths;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.MailUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.WebDriver;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.ArrayList;
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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.testsuite.Assert.assertNames;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserTest extends AbstractAdminTest {
@Rule
public GreenMailRule greenMail = new GreenMailRule();
@Drone
protected WebDriver driver;
@Page
protected LoginPasswordUpdatePage passwordUpdatePage;
@ArquillianResource
protected OAuthClient oAuthClient;
@Page
protected InfoPage infoPage;
@Page
protected ErrorPage errorPage;
@Page
protected LoginPage loginPage;
public String createUser() {
return createUser("user1", "user1@localhost");
}
public String createUser(String username, String email) {
UserRepresentation user = new UserRepresentation();
user.setUsername(username);
user.setEmail(email);
user.setRequiredActions(Collections.<String>emptyList());
user.setEnabled(true);
return createUser(user);
}
private String createUser(UserRepresentation userRep) {
Response response = realm.users().create(userRep);
String createdId = ApiUtil.getCreatedId(response);
response.close();
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(createdId), userRep, ResourceType.USER);
getCleanup().addUserId(createdId);
return createdId;
}
private void updateUser(UserResource user, UserRepresentation userRep) {
user.update(userRep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(userRep.getId()), userRep, ResourceType.USER);
}
@Test
public void verifyCreateUser() {
createUser();
}
@Test
public void createDuplicatedUser1() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("user1");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
assertAdminEvents.assertEmpty();
// Just to show how to retrieve underlying error message
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
Assert.assertEquals("User exists with same username", error.getErrorMessage());
response.close();
}
@Test
public void createDuplicatedUser2() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("user2");
user.setEmail("user1@localhost");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
}
@Test
public void createDuplicatedUser3() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("User1");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
}
@Test
public void createDuplicatedUser4() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("USER1");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
}
@Test
public void createDuplicatedUser5() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("user2");
user.setEmail("User1@localhost");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
}
@Test
public void createDuplicatedUser6() {
createUser();
UserRepresentation user = new UserRepresentation();
user.setUsername("user2");
user.setEmail("user1@LOCALHOST");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
}
@Test
public void createDuplicatedUser7() {
createUser("user1", "USer1@Localhost");
UserRepresentation user = new UserRepresentation();
user.setUsername("user2");
user.setEmail("user1@localhost");
Response response = realm.users().create(user);
assertEquals(409, response.getStatus());
response.close();
assertAdminEvents.assertEmpty();
}
private void createUsers() {
for (int i = 1; i < 10; i++) {
UserRepresentation user = new UserRepresentation();
user.setUsername("username" + i);
user.setEmail("user" + i + "@localhost");
user.setFirstName("First" + i);
user.setLastName("Last" + i);
createUser(user);
}
}
@Test
public void searchByEmail() {
createUsers();
List<UserRepresentation> users = realm.users().search(null, null, null, "user1@localhost", null, null);
assertEquals(1, users.size());
users = realm.users().search(null, null, null, "@localhost", null, null);
assertEquals(9, users.size());
}
@Test
public void searchByUsername() {
createUsers();
List<UserRepresentation> users = realm.users().search("username1", null, null, null, null, null);
assertEquals(1, users.size());
users = realm.users().search("user", null, null, null, null, null);
assertEquals(9, users.size());
}
@Test
public void search() {
createUsers();
List<UserRepresentation> users = realm.users().search("username1", null, null);
assertEquals(1, users.size());
users = realm.users().search("first1", null, null);
assertEquals(1, users.size());
users = realm.users().search("last", null, null);
assertEquals(9, users.size());
}
@Test
public void count() {
createUsers();
Integer count = realm.users().count();
assertEquals(9, count.intValue());
}
@Test
public void delete() {
String userId = createUser();
Response response = realm.users().delete(userId);
assertEquals(204, response.getStatus());
response.close();
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userResourcePath(userId), ResourceType.USER);
}
@Test
public void deleteNonExistent() {
Response response = realm.users().delete("does-not-exist");
assertEquals(404, response.getStatus());
response.close();
assertAdminEvents.assertEmpty();
}
@Test
public void searchPaginated() {
createUsers();
List<UserRepresentation> users = realm.users().search("username", 0, 1);
assertEquals(1, users.size());
assertEquals("username1", users.get(0).getUsername());
users = realm.users().search("username", 5, 2);
assertEquals(2, users.size());
assertEquals("username6", users.get(0).getUsername());
assertEquals("username7", users.get(1).getUsername());
users = realm.users().search("username", 7, 20);
assertEquals(2, users.size());
assertEquals("username8", users.get(0).getUsername());
assertEquals("username9", users.get(1).getUsername());
users = realm.users().search("username", 0, 20);
assertEquals(9, users.size());
}
@Test
public void getFederatedIdentities() {
// Add sample identity provider
addSampleIdentityProvider();
// Add sample user
String id = createUser();
UserResource user = realm.users().get(id);
assertEquals(0, user.getFederatedIdentity().size());
// Add social link to the user
FederatedIdentityRepresentation link = new FederatedIdentityRepresentation();
link.setUserId("social-user-id");
link.setUserName("social-username");
Response response = user.addFederatedIdentity("social-provider-id", link);
assertEquals(204, response.getStatus());
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederatedIdentityLink(id, "social-provider-id"), link, ResourceType.USER);
// Verify social link is here
user = realm.users().get(id);
List<FederatedIdentityRepresentation> federatedIdentities = user.getFederatedIdentity();
assertEquals(1, federatedIdentities.size());
link = federatedIdentities.get(0);
assertEquals("social-provider-id", link.getIdentityProvider());
assertEquals("social-user-id", link.getUserId());
assertEquals("social-username", link.getUserName());
// Remove social link now
user.removeFederatedIdentity("social-provider-id");
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederatedIdentityLink(id, "social-provider-id"), ResourceType.USER);
assertEquals(0, user.getFederatedIdentity().size());
removeSampleIdentityProvider();
}
private void addSampleIdentityProvider() {
List<IdentityProviderRepresentation> providers = realm.identityProviders().findAll();
Assert.assertEquals(0, providers.size());
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
rep.setAlias("social-provider-id");
rep.setProviderId("social-provider-type");
realm.identityProviders().create(rep);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.identityProviderPath(rep.getAlias()), rep, ResourceType.IDENTITY_PROVIDER);
}
private void removeSampleIdentityProvider() {
IdentityProviderResource resource = realm.identityProviders().get("social-provider-id");
Assert.assertNotNull(resource);
resource.remove();
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.identityProviderPath("social-provider-id"), ResourceType.IDENTITY_PROVIDER);
}
@Test
public void addRequiredAction() {
String id = createUser();
UserResource user = realm.users().get(id);
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
UserRepresentation userRep = user.toRepresentation();
userRep.getRequiredActions().add("UPDATE_PASSWORD");
updateUser(user, userRep);
assertEquals(1, user.toRepresentation().getRequiredActions().size());
assertEquals("UPDATE_PASSWORD", user.toRepresentation().getRequiredActions().get(0));
}
@Test
public void removeRequiredAction() {
String id = createUser();
UserResource user = realm.users().get(id);
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
UserRepresentation userRep = user.toRepresentation();
userRep.getRequiredActions().add("UPDATE_PASSWORD");
updateUser(user, userRep);
user = realm.users().get(id);
userRep = user.toRepresentation();
userRep.getRequiredActions().clear();
updateUser(user, userRep);
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
}
@Test
public void attributes() {
UserRepresentation user1 = new UserRepresentation();
user1.setUsername("user1");
user1.singleAttribute("attr1", "value1user1");
user1.singleAttribute("attr2", "value2user1");
String user1Id = createUser(user1);
UserRepresentation user2 = new UserRepresentation();
user2.setUsername("user2");
user2.singleAttribute("attr1", "value1user2");
List<String> vals = new ArrayList<>();
vals.add("value2user2");
vals.add("value2user2_2");
user2.getAttributes().put("attr2", vals);
String user2Id = createUser(user2);
user1 = realm.users().get(user1Id).toRepresentation();
assertEquals(2, user1.getAttributes().size());
assertAttributeValue("value1user1", user1.getAttributes().get("attr1"));
assertAttributeValue("value2user1", user1.getAttributes().get("attr2"));
user2 = realm.users().get(user2Id).toRepresentation();
assertEquals(2, user2.getAttributes().size());
assertAttributeValue("value1user2", user2.getAttributes().get("attr1"));
vals = user2.getAttributes().get("attr2");
assertEquals(2, vals.size());
assertTrue(vals.contains("value2user2") && vals.contains("value2user2_2"));
user1.singleAttribute("attr1", "value3user1");
user1.singleAttribute("attr3", "value4user1");
updateUser(realm.users().get(user1Id), user1);
user1 = realm.users().get(user1Id).toRepresentation();
assertEquals(3, user1.getAttributes().size());
assertAttributeValue("value3user1", user1.getAttributes().get("attr1"));
assertAttributeValue("value2user1", user1.getAttributes().get("attr2"));
assertAttributeValue("value4user1", user1.getAttributes().get("attr3"));
user1.getAttributes().remove("attr1");
updateUser(realm.users().get(user1Id), user1);
user1 = realm.users().get(user1Id).toRepresentation();
assertEquals(2, user1.getAttributes().size());
assertAttributeValue("value2user1", user1.getAttributes().get("attr2"));
assertAttributeValue("value4user1", user1.getAttributes().get("attr3"));
user1.getAttributes().clear();
updateUser(realm.users().get(user1Id), user1);
user1 = realm.users().get(user1Id).toRepresentation();
assertNull(user1.getAttributes());
}
private void assertAttributeValue(String expectedValue, List<String> attrValues) {
assertEquals(1, attrValues.size());
assertEquals(expectedValue, attrValues.get(0));
}
@Test
public void sendResetPasswordEmail() {
UserRepresentation userRep = new UserRepresentation();
userRep.setUsername("user1");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
try {
user.executeActionsEmail(actions);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("User email missing", error.getErrorMessage());
}
try {
userRep = user.toRepresentation();
userRep.setEmail("user1@localhost");
userRep.setEnabled(false);
updateUser(user, userRep);
user.executeActionsEmail(actions);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("User is disabled", error.getErrorMessage());
}
try {
userRep.setEnabled(true);
updateUser(user, userRep);
user.executeActionsEmail("invalidClientId", "invalidUri", actions);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("invalidClientId not enabled", error.getErrorMessage());
}
}
@Test
public void sendResetPasswordEmailSuccess() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertEquals("Your account has been updated.", driver.getTitle());
driver.navigate().to(link);
assertEquals("We're sorry...", driver.getTitle());
}
@Test
public void sendResetPasswordEmailSuccessTwoLinks() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(2, greenMail.getReceivedMessages().length);
int i = 1;
for (MimeMessage message : greenMail.getReceivedMessages()) {
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
i++;
assertEquals("Your account has been updated.", driver.getTitle());
}
for (MimeMessage message : greenMail.getReceivedMessages()) {
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
errorPage.assertCurrent();
}
}
@Test
public void sendResetPasswordEmailSuccessTwoLinksReverse() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(2, greenMail.getReceivedMessages().length);
int i = 1;
for (int j = greenMail.getReceivedMessages().length - 1; j >= 0; j--) {
MimeMessage message = greenMail.getReceivedMessages()[j];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
i++;
assertEquals("Your account has been updated.", driver.getTitle());
}
for (MimeMessage message : greenMail.getReceivedMessages()) {
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
errorPage.assertCurrent();
}
}
@Test
public void sendResetPasswordEmailSuccessLinkOpenDoesNotExpireWhenOpenedOnly() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
driver.manage().deleteAllCookies();
driver.navigate().to("about:blank");
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertEquals("Your account has been updated.", driver.getTitle());
}
@Test
public void sendResetPasswordEmailSuccessTokenShortLifespan() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
final AtomicInteger originalValue = new AtomicInteger();
RealmRepresentation realmRep = realm.toRepresentation();
originalValue.set(realmRep.getActionTokenGeneratedByAdminLifespan());
realmRep.setActionTokenGeneratedByAdminLifespan(60);
realm.update(realmRep);
try {
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String link = MailUtils.getPasswordResetEmailLink(message);
setTimeOffset(70);
driver.navigate().to(link);
errorPage.assertCurrent();
assertEquals("An error occurred, please login again through your application.", errorPage.getError());
} finally {
setTimeOffset(0);
realmRep.setActionTokenGeneratedByAdminLifespan(originalValue.get());
realm.update(realmRep);
}
}
@Test
public void sendResetPasswordEmailSuccessWithRecycledAuthSession() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
// The following block creates a client and requests updating password with redirect to this client.
// After clicking the link (starting a fresh auth session with client), the user goes away and sends the email
// with password reset again - now without the client - and attempts to complete the password reset.
{
ClientRepresentation client = new ClientRepresentation();
client.setClientId("myclient2");
client.setRedirectUris(new LinkedList<>());
client.getRedirectUris().add("http://myclient.com/*");
client.setName("myclient2");
client.setEnabled(true);
Response response = realm.clients().create(client);
String createdId = ApiUtil.getCreatedId(response);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientResourcePath(createdId), client, ResourceType.CLIENT);
user.executeActionsEmail("myclient2", "http://myclient.com/home.html", actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
}
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(2, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[greenMail.getReceivedMessages().length - 1];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertEquals("Your account has been updated.", driver.getTitle());
driver.navigate().to(link);
assertEquals("We're sorry...", driver.getTitle());
}
@Test
public void sendResetPasswordEmailWithRedirect() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
ClientRepresentation client = new ClientRepresentation();
client.setClientId("myclient");
client.setRedirectUris(new LinkedList<>());
client.getRedirectUris().add("http://myclient.com/*");
client.setName("myclient");
client.setEnabled(true);
Response response = realm.clients().create(client);
String createdId = ApiUtil.getCreatedId(response);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientResourcePath(createdId), client, ResourceType.CLIENT);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
try {
// test that an invalid redirect uri is rejected.
user.executeActionsEmail("myclient", "http://unregistered-uri.com/", actions);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("Invalid redirect uri.", error.getErrorMessage());
}
user.executeActionsEmail("myclient", "http://myclient.com/home.html", actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
String link = MailUtils.getPasswordResetEmailLink(message);
driver.navigate().to(link);
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertEquals("Your account has been updated.", driver.getTitle());
String pageSource = driver.getPageSource();
// check to make sure the back link is set.
Assert.assertTrue(pageSource.contains("http://myclient.com/home.html"));
driver.navigate().to(link);
assertEquals("We're sorry...", driver.getTitle());
}
@Test
public void sendVerifyEmail() throws IOException, MessagingException {
UserRepresentation userRep = new UserRepresentation();
userRep.setUsername("user1");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
try {
user.sendVerifyEmail();
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("User email missing", error.getErrorMessage());
}
try {
userRep = user.toRepresentation();
userRep.setEmail("user1@localhost");
userRep.setEnabled(false);
updateUser(user, userRep);
user.sendVerifyEmail();
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("User is disabled", error.getErrorMessage());
}
try {
userRep.setEnabled(true);
updateUser(user, userRep);
user.sendVerifyEmail("invalidClientId");
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
Assert.assertEquals("invalidClientId not enabled", error.getErrorMessage());
}
user.sendVerifyEmail();
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
String link = MailUtils.getPasswordResetEmailLink(greenMail.getReceivedMessages()[0]);
driver.navigate().to(link);
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
driver.navigate().to("about:blank");
driver.navigate().to(link); // It should be possible to use the same action token multiple times
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
}
@Test
public void updateUserWithNewUsername() {
switchEditUsernameAllowedOn(true);
String id = createUser();
UserResource user = realm.users().get(id);
UserRepresentation userRep = user.toRepresentation();
userRep.setUsername("user11");
updateUser(user, userRep);
userRep = realm.users().get(id).toRepresentation();
assertEquals("user11", userRep.getUsername());
// Revert
switchEditUsernameAllowedOn(false);
}
@Test
public void updateUserWithoutUsername() {
switchEditUsernameAllowedOn(true);
String id = createUser();
UserResource user = realm.users().get(id);
UserRepresentation rep = new UserRepresentation();
rep.setFirstName("Firstname");
user.update(rep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep, ResourceType.USER);
rep = new UserRepresentation();
rep.setLastName("Lastname");
user.update(rep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep, ResourceType.USER);
rep = realm.users().get(id).toRepresentation();
assertEquals("user1", rep.getUsername());
assertEquals("user1@localhost", rep.getEmail());
assertEquals("Firstname", rep.getFirstName());
assertEquals("Lastname", rep.getLastName());
// Revert
switchEditUsernameAllowedOn(false);
}
@Test
public void updateUserWithNewUsernameNotPossible() {
String id = createUser();
UserResource user = realm.users().get(id);
UserRepresentation userRep = user.toRepresentation();
userRep.setUsername("user11");
updateUser(user, userRep);
userRep = realm.users().get(id).toRepresentation();
assertEquals("user1", userRep.getUsername());
}
@Test
public void updateUserWithNewUsernameAccessingViaOldUsername() {
switchEditUsernameAllowedOn(true);
createUser();
try {
UserResource user = realm.users().get("user1");
UserRepresentation userRep = user.toRepresentation();
userRep.setUsername("user1");
updateUser(user, userRep);
realm.users().get("user11").toRepresentation();
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(404, e.getResponse().getStatus());
} finally {
switchEditUsernameAllowedOn(false);
}
}
@Test
public void updateUserWithExistingUsername() {
switchEditUsernameAllowedOn(true);
enableBruteForce(true);
createUser();
UserRepresentation userRep = new UserRepresentation();
userRep.setUsername("user2");
String createdId = createUser(userRep);
try {
UserResource user = realm.users().get(createdId);
userRep = user.toRepresentation();
userRep.setUsername("user1");
user.update(userRep);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(409, e.getResponse().getStatus());
// TODO adminEvents: Event queue should be empty, but it's not because of bug in UsersResource.updateUser, which sends event earlier than transaction commit.
// assertAdminEvents.assertEmpty();
assertAdminEvents.poll();
} finally {
enableBruteForce(false);
switchEditUsernameAllowedOn(false);
}
}
@Test
public void resetUserPassword() {
String userId = createUser("user1", "user1@localhost");
CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue("password");
cred.setTemporary(false);
realm.users().get(userId).resetPassword(cred);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId), ResourceType.USER);
String accountUrl = RealmsResource.accountUrl(UriBuilder.fromUri(getAuthServerRoot())).build(REALM_NAME).toString();
driver.navigate().to(accountUrl);
assertEquals("Log in to admin-client-test", driver.getTitle());
loginPage.login("user1", "password");
assertTrue(driver.getTitle().contains("Account Management"));
}
@Test
public void resetUserInvalidPassword() {
String userId = createUser("user1", "user1@localhost");
try {
CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue(" ");
cred.setTemporary(false);
realm.users().get(userId).resetPassword(cred);
fail("Expected failure");
} catch (ClientErrorException e) {
assertEquals(400, e.getResponse().getStatus());
e.getResponse().close();
assertAdminEvents.assertEmpty();
}
}
@Test
public void testDefaultRequiredActionAdded() {
// Add UPDATE_PASSWORD as default required action
RequiredActionProviderRepresentation updatePasswordReqAction = realm.flows().getRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
updatePasswordReqAction.setDefaultAction(true);
realm.flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
// Create user
String userId = createUser("user1", "user1@localhost");
UserRepresentation userRep = realm.users().get(userId).toRepresentation();
Assert.assertEquals(1, userRep.getRequiredActions().size());
Assert.assertEquals(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), userRep.getRequiredActions().get(0));
// Remove UPDATE_PASSWORD default action
updatePasswordReqAction = realm.flows().getRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
updatePasswordReqAction.setDefaultAction(true);
realm.flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
}
@Test
public void roleMappings() {
RealmResource realm = adminClient.realms().realm("test");
// Enable events
RealmRepresentation realmRep = RealmBuilder.edit(realm.toRepresentation()).testEventListener().build();
realm.update(realmRep);
realm.roles().create(RoleBuilder.create().name("realm-role").build());
realm.roles().create(RoleBuilder.create().name("realm-composite").build());
realm.roles().create(RoleBuilder.create().name("realm-child").build());
realm.roles().get("realm-composite").addComposites(Collections.singletonList(realm.roles().get("realm-child").toRepresentation()));
Response response = realm.clients().create(ClientBuilder.create().clientId("myclient").build());
String clientUuid = ApiUtil.getCreatedId(response);
response.close();
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role").build());
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role2").build());
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-composite").build());
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-child").build());
realm.clients().get(clientUuid).roles().get("client-composite").addComposites(Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-child").toRepresentation()));
response = realm.users().create(UserBuilder.create().username("myuser").build());
String userId = ApiUtil.getCreatedId(response);
response.close();
// Admin events for creating role, client or user tested already in other places
assertAdminEvents.clear();
RoleMappingResource roles = realm.users().get(userId).roles();
assertNames(roles.realmLevel().listAll(), "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
// Add realm roles
List<RoleRepresentation> l = new LinkedList<>();
l.add(realm.roles().get("realm-role").toRepresentation());
l.add(realm.roles().get("realm-composite").toRepresentation());
roles.realmLevel().add(l);
assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userRealmRoleMappingsPath(userId), l, ResourceType.REALM_ROLE_MAPPING);
// Add client roles
List<RoleRepresentation> list = Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-role").toRepresentation());
roles.clientLevel(clientUuid).add(list);
assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), list, ResourceType.CLIENT_ROLE_MAPPING);
list = Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-composite").toRepresentation());
roles.clientLevel(clientUuid).add(list);
assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), ResourceType.CLIENT_ROLE_MAPPING);
// List realm roles
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium", "realm-composite-role", "sample-realm-role");
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
// List client roles
assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite");
assertNames(roles.clientLevel(clientUuid).listAvailable(), "client-role2");
assertNames(roles.clientLevel(clientUuid).listEffective(), "client-role", "client-composite", "client-child");
// Get mapping representation
MappingsRepresentation all = roles.getAll();
assertNames(all.getRealmMappings(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertEquals(2, all.getClientMappings().size());
assertNames(all.getClientMappings().get("myclient").getMappings(), "client-role", "client-composite");
assertNames(all.getClientMappings().get("account").getMappings(), "manage-account", "view-profile");
// Remove realm role
RoleRepresentation realmRoleRep = realm.roles().get("realm-role").toRepresentation();
roles.realmLevel().remove(Collections.singletonList(realmRoleRep));
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userRealmRoleMappingsPath(userId), Collections.singletonList(realmRoleRep), ResourceType.REALM_ROLE_MAPPING);
assertNames(roles.realmLevel().listAll(), "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
// Remove client role
RoleRepresentation clientRoleRep = realm.clients().get(clientUuid).roles().get("client-role").toRepresentation();
roles.clientLevel(clientUuid).remove(Collections.singletonList(clientRoleRep));
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), Collections.singletonList(clientRoleRep), ResourceType.CLIENT_ROLE_MAPPING);
assertNames(roles.clientLevel(clientUuid).listAll(), "client-composite");
}
@Test
public void defaultMaxResults() {
UsersResource users = adminClient.realms().realm("test").users();
for (int i = 0; i < 110; i++) {
users.create(UserBuilder.create().username("test-" + i).build()).close();
}
assertEquals(100, users.search("test", null, null).size());
assertEquals(105, users.search("test", 0, 105).size());
assertEquals(111, users.search("test", 0, 1000).size());
}
private void switchEditUsernameAllowedOn(boolean enable) {
RealmRepresentation rep = realm.toRepresentation();
rep.setEditUsernameAllowed(enable);
realm.update(rep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM);
}
private void enableBruteForce(boolean enable) {
RealmRepresentation rep = realm.toRepresentation();
rep.setBruteForceProtected(enable);
realm.update(rep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM);
}
}