mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Move UserTest.java to the new testsuite
Part of: #34494 Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
This commit is contained in:
parent
5c930c1f73
commit
788e981917
@ -1,6 +1,10 @@
|
||||
package org.keycloak.testframework.realm;
|
||||
|
||||
import org.keycloak.models.credential.OTPCredentialModel;
|
||||
import org.keycloak.models.utils.HmacOTP;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -88,6 +92,23 @@ public class UserConfigBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserConfigBuilder federatedLink(String identityProvider, String federatedUserId, String federatedUsername) {
|
||||
FederatedIdentityRepresentation federatedIdentity = new FederatedIdentityRepresentation();
|
||||
federatedIdentity.setUserId(federatedUserId);
|
||||
federatedIdentity.setUserName(federatedUsername);
|
||||
federatedIdentity.setIdentityProvider(identityProvider);
|
||||
|
||||
rep.setFederatedIdentities(Collections.combine(rep.getFederatedIdentities(), federatedIdentity));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserConfigBuilder totpSecret(String totpSecret) {
|
||||
rep.setCredentials(Collections.combine(rep.getCredentials(), ModelToRepresentation.toRepresentation(
|
||||
OTPCredentialModel.createTOTP(totpSecret, 6, 30, HmacOTP.HMAC_SHA1))));
|
||||
rep.setTotp(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best practice is to use other convenience methods when configuring a user, but while the framework is under
|
||||
* active development there may not be a way to perform all updates required. In these cases this method allows
|
||||
|
||||
@ -134,7 +134,7 @@ public class OAuthClientTest {
|
||||
AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(authzResponse.getCode());
|
||||
oauth.logoutForm().idTokenHint(accessTokenResponse.getIdToken()).open();
|
||||
oauth.loginForm().open();
|
||||
Assertions.assertTrue(loginPage.isActivePage());
|
||||
loginPage.assertCurrent();
|
||||
}
|
||||
|
||||
public static class OAuthUserConfig implements UserConfig {
|
||||
|
||||
@ -2,7 +2,9 @@ package org.keycloak.test.examples;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
|
||||
import org.keycloak.testframework.ui.page.LoginPage;
|
||||
@ -13,6 +15,8 @@ import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class PagesTest {
|
||||
@InjectRealm(ref = "master", attachTo = "master")
|
||||
ManagedRealm masterRealm;
|
||||
|
||||
@InjectWebDriver
|
||||
WebDriver webDriver;
|
||||
@ -25,13 +29,16 @@ public class PagesTest {
|
||||
|
||||
@Test
|
||||
public void testLoginFromWelcome() {
|
||||
masterRealm.admin().users().searchByUsername("admin", true)
|
||||
.stream().findFirst().ifPresent(admin ->
|
||||
masterRealm.admin().users().delete(admin.getId()));
|
||||
|
||||
welcomePage.navigateTo();
|
||||
|
||||
if (welcomePage.isActivePage()) {
|
||||
welcomePage.fillRegistration("admin", "admin");
|
||||
welcomePage.submit();
|
||||
welcomePage.clickOpenAdminConsole();
|
||||
}
|
||||
welcomePage.assertCurrent();
|
||||
welcomePage.fillRegistration("admin", "admin");
|
||||
welcomePage.submit();
|
||||
welcomePage.clickOpenAdminConsole();
|
||||
|
||||
if (webDriver instanceof HtmlUnitDriver) {
|
||||
String pageId = webDriver.findElement(By.xpath("//body")).getAttribute("data-page-id");
|
||||
@ -40,7 +47,7 @@ public class PagesTest {
|
||||
} else {
|
||||
loginPage.waitForPage();
|
||||
|
||||
Assertions.assertTrue(loginPage.isActivePage());
|
||||
loginPage.assertCurrent();
|
||||
|
||||
loginPage.fillLogin("admin", "admin");
|
||||
loginPage.submit();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.keycloak.testframework.ui.page;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
@ -34,8 +35,11 @@ public abstract class AbstractPage {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivePage() {
|
||||
private boolean isActivePage() {
|
||||
return getExpectedPageId().equals(getCurrentPageId());
|
||||
}
|
||||
|
||||
public void assertCurrent() {
|
||||
Assertions.assertEquals(getExpectedPageId(), getCurrentPageId(), "Not on the expected page");
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ErrorPage extends AbstractPage {
|
||||
|
||||
@FindBy(className = "instruction")
|
||||
private WebElement errorMessage;
|
||||
|
||||
@FindBy(id = "backToApplication")
|
||||
private WebElement backToApplicationLink;
|
||||
|
||||
public ErrorPage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return errorMessage.getText();
|
||||
}
|
||||
|
||||
public void clickBackToApplication() {
|
||||
backToApplicationLink.click();
|
||||
}
|
||||
|
||||
public String getBackToApplicationLink() {
|
||||
if (backToApplicationLink == null) {
|
||||
return null;
|
||||
} else {
|
||||
return backToApplicationLink.getAttribute("href");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-error";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfoPage extends AbstractPage {
|
||||
|
||||
@FindBy(className = "instruction")
|
||||
private WebElement infoMessage;
|
||||
|
||||
public InfoPage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-info";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LoginPasswordUpdatePage extends AbstractPage {
|
||||
|
||||
@FindBy(id = "password-new")
|
||||
private WebElement newPasswordInput;
|
||||
|
||||
@FindBy(id = "password-confirm")
|
||||
private WebElement passwordConfirmInput;
|
||||
|
||||
@FindBy(css = "[type=\"submit\"]")
|
||||
private WebElement submitButton;
|
||||
|
||||
@FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
|
||||
private WebElement loginErrorMessage;
|
||||
|
||||
@FindBy(className = "kc-feedback-text")
|
||||
private WebElement feedbackMessage;
|
||||
|
||||
@FindBy(name = "cancel-aia")
|
||||
private WebElement cancelAIAButton;
|
||||
|
||||
public LoginPasswordUpdatePage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public void changePassword(String newPassword, String passwordConfirm) {
|
||||
newPasswordInput.sendKeys(newPassword);
|
||||
passwordConfirmInput.sendKeys(passwordConfirm);
|
||||
|
||||
submitButton.click();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
cancelAIAButton.click();
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return loginErrorMessage != null ? loginErrorMessage.getText() : null;
|
||||
}
|
||||
|
||||
public String getFeedbackMessage() {
|
||||
return feedbackMessage.getText();
|
||||
}
|
||||
|
||||
public boolean isCancelDisplayed() {
|
||||
return cancelAIAButton.isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-login-update-password";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2017 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.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class ProceedPage extends AbstractPage {
|
||||
|
||||
@FindBy(className = "instruction")
|
||||
private WebElement infoMessage;
|
||||
|
||||
@FindBy(linkText = "» Click here to proceed")
|
||||
private WebElement proceedLink;
|
||||
|
||||
public ProceedPage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
public void clickProceedLink() {
|
||||
proceedLink.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-info";
|
||||
}
|
||||
}
|
||||
@ -215,7 +215,7 @@ public class ImpersonationTest {
|
||||
public void testImpersonationWorksWhenAuthenticationSessionExists() throws Exception {
|
||||
// Open the URL for the client (will redirect to Keycloak server AuthorizationEndpoint and create authenticationSession)
|
||||
oauth.openLoginForm();
|
||||
Assertions.assertTrue(loginPage.isActivePage());
|
||||
loginPage.assertCurrent();
|
||||
|
||||
// Impersonate and get SSO cookie. Setup that cookie for webDriver
|
||||
for (Cookie cookie : testSuccessfulImpersonation("realm-admin", managedRealm.getName())) {
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.utils.StripSecretsUtils;
|
||||
import org.keycloak.representations.idm.*;
|
||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testframework.annotations.InjectAdminClient;
|
||||
import org.keycloak.testframework.annotations.InjectAdminEvents;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.events.AdminEvents;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.tests.utils.runonserver.RunHelpers;
|
||||
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
|
||||
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
|
||||
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
|
||||
import org.keycloak.testframework.ui.page.LoginPage;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.userprofile.UserProfileUtil;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class AbstractUserTest {
|
||||
|
||||
@InjectRealm
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@InjectAdminClient
|
||||
Keycloak adminClient;
|
||||
|
||||
@InjectAdminEvents
|
||||
AdminEvents adminEvents;
|
||||
|
||||
@InjectWebDriver
|
||||
WebDriver driver;
|
||||
|
||||
@InjectRunOnServer
|
||||
RunOnServerClient runOnServer;
|
||||
|
||||
@InjectPage
|
||||
LoginPage loginPage;
|
||||
|
||||
protected Set<String> managedAttributes = new HashSet<>();
|
||||
|
||||
{
|
||||
managedAttributes.add("test");
|
||||
managedAttributes.add("attr");
|
||||
managedAttributes.add("attr1");
|
||||
managedAttributes.add("attr2");
|
||||
managedAttributes.add("attr3");
|
||||
managedAttributes.add("foo");
|
||||
managedAttributes.add("bar");
|
||||
managedAttributes.add("phoneNumber");
|
||||
managedAttributes.add("usercertificate");
|
||||
managedAttributes.add("saml.persistent.name.id.for.foo");
|
||||
managedAttributes.add(LDAPConstants.LDAP_ID);
|
||||
managedAttributes.add("LDap_Id");
|
||||
managedAttributes.add("deniedSomeAdmin");
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
managedAttributes.add("test" + i);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void beforeUserTest() throws IOException {
|
||||
|
||||
UserProfileUtil.setUserProfileConfiguration(managedRealm.admin(), null);
|
||||
UPConfig upConfig = managedRealm.admin().users().userProfile().getConfiguration();
|
||||
|
||||
for (String name : managedAttributes) {
|
||||
upConfig.addOrReplaceAttribute(createAttributeMetadata(name));
|
||||
}
|
||||
|
||||
UserProfileUtil.setUserProfileConfiguration(managedRealm.admin(), JsonSerialization.writeValueAsString(upConfig));
|
||||
|
||||
adminEvents.clear();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
managedRealm.admin().identityProviders().findAll()
|
||||
.forEach(ip -> managedRealm.admin().identityProviders().get(ip.getAlias()).remove());
|
||||
|
||||
managedRealm.admin().groups().groups()
|
||||
.forEach(g -> managedRealm.admin().groups().group(g.getId()).remove());
|
||||
}
|
||||
|
||||
protected String createUser() {
|
||||
return createUser("user1", "user1@localhost");
|
||||
}
|
||||
|
||||
protected String createUser(String username, String email) {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername(username);
|
||||
user.setEmail(email);
|
||||
user.setRequiredActions(Collections.emptyList());
|
||||
user.setEnabled(true);
|
||||
|
||||
return createUser(user);
|
||||
}
|
||||
|
||||
protected String createUser(UserRepresentation userRep) {
|
||||
return createUser(userRep, true);
|
||||
}
|
||||
|
||||
protected String createUser(UserRepresentation userRep, boolean assertAdminEvent) {
|
||||
final String createdId;
|
||||
try (Response response = managedRealm.admin().users().create(userRep)) {
|
||||
createdId = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
managedRealm.cleanup().add(r -> r.users().get(createdId).remove());
|
||||
|
||||
StripSecretsUtils.stripSecrets(null, userRep);
|
||||
|
||||
if (assertAdminEvent) {
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userResourcePath(createdId), userRep,
|
||||
ResourceType.USER);
|
||||
}
|
||||
|
||||
return createdId;
|
||||
}
|
||||
|
||||
protected void updateUser(UserResource user, UserRepresentation userRep) {
|
||||
user.update(userRep);
|
||||
List<CredentialRepresentation> credentials = userRep.getCredentials();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.userResourcePath(userRep.getId()), StripSecretsUtils.stripSecrets(null, userRep), ResourceType.USER);
|
||||
userRep.setCredentials(credentials);
|
||||
}
|
||||
|
||||
protected UPAttribute createAttributeMetadata(String name) {
|
||||
UPAttribute attribute = new UPAttribute();
|
||||
attribute.setName(name);
|
||||
attribute.setMultivalued(true);
|
||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||
permissions.setEdit(Set.of("user", "admin"));
|
||||
attribute.setPermissions(permissions);
|
||||
this.managedAttributes.add(name);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
protected CredentialModel fetchCredentials(String username) {
|
||||
return runOnServer.fetch(RunHelpers.fetchCredentials(username));
|
||||
}
|
||||
|
||||
protected List<String> createUsers() {
|
||||
List<String> ids = new ArrayList<>();
|
||||
|
||||
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);
|
||||
|
||||
addAttribute(user, "test", Collections.singletonList("test" + i));
|
||||
addAttribute(user, "test" + i, Collections.singletonList("test" + i));
|
||||
addAttribute(user, "attr", Arrays.asList("common", "common2"));
|
||||
|
||||
ids.add(createUser(user));
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void addAttribute(UserRepresentation user, String name, List<String> values) {
|
||||
Map<String, List<String>> attributes = Optional.ofNullable(user.getAttributes()).orElse(new HashMap<>());
|
||||
|
||||
attributes.put(name, values);
|
||||
managedAttributes.add(name);
|
||||
|
||||
user.setAttributes(attributes);
|
||||
}
|
||||
|
||||
protected void deleteUser(String id) {
|
||||
try (Response response = managedRealm.admin().users().delete(id)) {
|
||||
assertEquals(204, response.getStatus());
|
||||
}
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.userResourcePath(id), ResourceType.USER);
|
||||
}
|
||||
|
||||
protected void addFederatedIdentity(String keycloakUserId, String identityProviderAlias1,
|
||||
FederatedIdentityRepresentation link) {
|
||||
Response response1 = managedRealm.admin().users().get(keycloakUserId).addFederatedIdentity(identityProviderAlias1, link);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE,
|
||||
AdminEventPaths.userFederatedIdentityLink(keycloakUserId, identityProviderAlias1), link,
|
||||
ResourceType.USER);
|
||||
assertEquals(204, response1.getStatus());
|
||||
}
|
||||
|
||||
protected void addSampleIdentityProvider() {
|
||||
addSampleIdentityProvider("social-provider-id", 0);
|
||||
}
|
||||
|
||||
protected void addSampleIdentityProvider(final String alias, final int expectedInitialIdpCount) {
|
||||
List<IdentityProviderRepresentation> providers = managedRealm.admin().identityProviders().findAll();
|
||||
Assertions.assertEquals(expectedInitialIdpCount, providers.size());
|
||||
|
||||
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
||||
rep.setAlias(alias);
|
||||
rep.setProviderId("oidc");
|
||||
|
||||
managedRealm.admin().identityProviders().create(rep);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.identityProviderPath(rep.getAlias()), rep, ResourceType.IDENTITY_PROVIDER);
|
||||
}
|
||||
|
||||
protected String mapToSearchQuery(Map<String, String> search) {
|
||||
return search.entrySet()
|
||||
.stream()
|
||||
.map(e -> String.format("%s:%s", e.getKey(), e.getValue()))
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
protected void switchEditUsernameAllowedOn(boolean enable) {
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
managedRealm.cleanup().add(r -> r.update(rep));
|
||||
rep.setEditUsernameAllowed(enable);
|
||||
managedRealm.admin().update(rep);
|
||||
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
|
||||
}
|
||||
|
||||
protected void switchRegistrationEmailAsUsername(boolean enable) {
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
managedRealm.cleanup().add(r -> r.update(rep));
|
||||
rep.setRegistrationEmailAsUsername(enable);
|
||||
managedRealm.admin().update(rep);
|
||||
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
|
||||
}
|
||||
|
||||
protected static <T> T loadJson(InputStream is, Class<T> type) {
|
||||
try {
|
||||
return JsonSerialization.readValue(is, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to parse json", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,256 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@KeycloakIntegrationTest(config = UserAttributesTest.UserAttributesServerConfig.class)
|
||||
public class UserAttributesTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void countByAttribute() {
|
||||
createUsers();
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("test1", "test2");
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(0));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("test", "test1");
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("test", "test2");
|
||||
attributes.put("attr", "common");
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("attr", "common");
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(9));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("attr", "common");
|
||||
attributes.put(UserModel.EXACT, Boolean.FALSE.toString());
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(9));
|
||||
}
|
||||
|
||||
@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 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertEquals(2, user1.getAttributes().size());
|
||||
assertAttributeValue("value1user1", user1.getAttributes().get("attr1"));
|
||||
assertAttributeValue("value2user1", user1.getAttributes().get("attr2"));
|
||||
|
||||
user2 = managedRealm.admin().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(managedRealm.admin().users().get(user1Id), user1);
|
||||
|
||||
user1 = managedRealm.admin().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(managedRealm.admin().users().get(user1Id), user1);
|
||||
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertEquals(2, user1.getAttributes().size());
|
||||
assertAttributeValue("value2user1", user1.getAttributes().get("attr2"));
|
||||
assertAttributeValue("value4user1", user1.getAttributes().get("attr3"));
|
||||
|
||||
// null attributes should not remove attributes
|
||||
user1.setAttributes(null);
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertNotNull(user1.getAttributes());
|
||||
assertEquals(2, user1.getAttributes().size());
|
||||
|
||||
// empty attributes should remove attributes
|
||||
user1.setAttributes(Collections.emptyMap());
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertNull(user1.getAttributes());
|
||||
|
||||
Map<String, List<String>> attributes = new HashMap<>();
|
||||
|
||||
attributes.put("foo", List.of("foo"));
|
||||
attributes.put("bar", List.of("bar"));
|
||||
|
||||
user1.setAttributes(attributes);
|
||||
|
||||
managedRealm.admin().users().get(user1Id).update(user1);
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertEquals(2, user1.getAttributes().size());
|
||||
|
||||
user1.getAttributes().remove("foo");
|
||||
|
||||
managedRealm.admin().users().get(user1Id).update(user1);
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertEquals(1, user1.getAttributes().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithReadOnlyAttributes() {
|
||||
// Admin is able to update "usercertificate" attribute
|
||||
UserRepresentation user1 = new UserRepresentation();
|
||||
user1.setUsername("user1");
|
||||
user1.singleAttribute("usercertificate", "foo1");
|
||||
String user1Id = createUser(user1);
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
|
||||
// Update of the user should be rejected due adding the "denied" attribute LDAP_ID
|
||||
try {
|
||||
user1.singleAttribute("usercertificate", "foo");
|
||||
user1.singleAttribute("saml.persistent.name.id.for.foo", "bar");
|
||||
user1.singleAttribute(LDAPConstants.LDAP_ID, "baz");
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
Assertions.fail("Not supposed to successfully update user");
|
||||
} catch (BadRequestException expected) {
|
||||
// Expected
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
ErrorRepresentation error = expected.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("updateReadOnlyAttributesRejectedMessage", error.getErrorMessage());
|
||||
}
|
||||
|
||||
// The same test as before, but with the case-sensitivity used
|
||||
try {
|
||||
user1.getAttributes().remove(LDAPConstants.LDAP_ID);
|
||||
user1.singleAttribute("LDap_Id", "baz");
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
Assertions.fail("Not supposed to successfully update user");
|
||||
} catch (BadRequestException bre) {
|
||||
// Expected
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
|
||||
// Attribute "deniedSomeAdmin" was denied for administrator
|
||||
try {
|
||||
user1.getAttributes().remove("LDap_Id");
|
||||
user1.singleAttribute("deniedSomeAdmin", "baz");
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
Assertions.fail("Not supposed to successfully update user");
|
||||
} catch (BadRequestException bre) {
|
||||
// Expected
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
|
||||
// usercertificate and saml attribute are allowed by admin
|
||||
user1.getAttributes().remove("deniedSomeAdmin");
|
||||
updateUser(managedRealm.admin().users().get(user1Id), user1);
|
||||
|
||||
user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
|
||||
assertEquals("foo", user1.getAttributes().get("usercertificate").get(0));
|
||||
assertEquals("bar", user1.getAttributes().get("saml.persistent.name.id.for.foo").get(0));
|
||||
assertFalse(user1.getAttributes().containsKey(LDAPConstants.LDAP_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportUserWithNullAttribute() {
|
||||
RealmRepresentation rep = loadJson(UserAttributesTest.class.getResourceAsStream("testrealm-user-null-attr.json"), RealmRepresentation.class);
|
||||
|
||||
adminClient.realms().create(rep);
|
||||
List<UserRepresentation> users = adminClient.realms().realm("test-user-null-attr").users().list();
|
||||
// there should be only one user
|
||||
assertThat(users, hasSize(1));
|
||||
// test there are only 2 attributes imported from json file, attribute "key3" : [ null ] shouldn't be imported
|
||||
assertThat(users.get(0).getAttributes().size(), equalTo(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRootAttributeWhenOtherAttributesAreSet() {
|
||||
String random = UUID.randomUUID().toString();
|
||||
String userName = String.format("username-%s", random);
|
||||
String email = String.format("my@mail-%s.com", random);
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername(userName);
|
||||
user.setEmail(email);
|
||||
String userId = createUser(user);
|
||||
|
||||
UserRepresentation created = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
assertThat(created.getEmail(), equalTo(email));
|
||||
assertThat(created.getUsername(), equalTo(userName));
|
||||
assertThat(created.getAttributes(), Matchers.nullValue());
|
||||
|
||||
UserRepresentation update = new UserRepresentation();
|
||||
update.setId(userId);
|
||||
// user profile requires sending all attributes otherwise they are removed
|
||||
update.setEmail(email);
|
||||
|
||||
update.setAttributes(Map.of("phoneNumber", List.of("123")));
|
||||
updateUser(managedRealm.admin().users().get(userId), update);
|
||||
|
||||
UserRepresentation updated = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
assertThat(updated.getUsername(), equalTo(userName));
|
||||
assertThat(updated.getAttributes().get("phoneNumber"), equalTo(List.of("123")));
|
||||
|
||||
assertThat(updated.getEmail(), equalTo(email));
|
||||
}
|
||||
|
||||
private void assertAttributeValue(String expectedValue, List<String> attrValues) {
|
||||
Assertions.assertEquals(1, attrValues.size());
|
||||
Assertions.assertEquals(expectedValue, attrValues.get(0));
|
||||
}
|
||||
|
||||
public static class UserAttributesServerConfig implements KeycloakServerConfig {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder builder) {
|
||||
builder.option("spi-user-profile-declarative-user-profile-admin-read-only-attributes", "deniedSomeAdmin");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,579 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.CreatedResponseUtil;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.Base64;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.crypto.hash.Argon2Parameters;
|
||||
import org.keycloak.crypto.hash.Argon2PasswordHashProviderFactory;
|
||||
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.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.testframework.admin.AdminClientFactory;
|
||||
import org.keycloak.testframework.annotations.InjectAdminClientFactory;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.tests.utils.Assert;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@KeycloakIntegrationTest(config = UserCreateTest.UserCreateServerConf.class)
|
||||
public class UserCreateTest extends AbstractUserTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@InjectAdminClientFactory
|
||||
AdminClientFactory clientFactory;
|
||||
|
||||
@Test
|
||||
public void verifyCreateUser() {
|
||||
createUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* See KEYCLOAK-11003
|
||||
*/
|
||||
@Test
|
||||
public void createUserWithTemporaryPasswordWithAdditionalPasswordUpdateShouldRemoveUpdatePasswordRequiredAction() {
|
||||
|
||||
String userId = createUser();
|
||||
|
||||
CredentialRepresentation credTmp = new CredentialRepresentation();
|
||||
credTmp.setType(CredentialRepresentation.PASSWORD);
|
||||
credTmp.setValue("temp");
|
||||
credTmp.setTemporary(Boolean.TRUE);
|
||||
|
||||
managedRealm.admin().users().get(userId).resetPassword(credTmp);
|
||||
|
||||
CredentialRepresentation credPerm = new CredentialRepresentation();
|
||||
credPerm.setType(CredentialRepresentation.PASSWORD);
|
||||
credPerm.setValue("perm");
|
||||
credPerm.setTemporary(null);
|
||||
|
||||
managedRealm.admin().users().get(userId).resetPassword(credPerm);
|
||||
|
||||
UserRepresentation userRep = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
|
||||
Assertions.assertFalse(userRep.getRequiredActions().contains(UserModel.RequiredAction.UPDATE_PASSWORD.name()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser1() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1");
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
|
||||
// Just to show how to retrieve underlying error message
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User exists with same username", error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser2() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setEmail("user1@localhost");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
assertNull(adminEvents.poll());
|
||||
|
||||
// Alternative way of showing underlying error message
|
||||
try {
|
||||
CreatedResponseUtil.getCreatedId(response);
|
||||
Assertions.fail("Not expected getCreatedId to success");
|
||||
} catch (WebApplicationException wae) {
|
||||
MatcherAssert.assertThat(wae.getMessage(), endsWith("ErrorMessage: User exists with same email"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUsernameWithEmail() {
|
||||
createUser("user1@local.com", "user1@local.org");
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1@local.org");
|
||||
user.setEmail("user2@localhost");
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
assertNull(adminEvents.poll());
|
||||
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User exists with same username", error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedEmailWithUsername() {
|
||||
createUser("user1@local.com", "user1@local.org");
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setEmail("user1@local.com");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
assertNull(adminEvents.poll());
|
||||
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User exists with same email", error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//KEYCLOAK-14611
|
||||
@Test
|
||||
public void createDuplicateEmailWithExistingDuplicates() {
|
||||
//Allow duplicate emails
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
rep.setDuplicateEmailsAllowed(true);
|
||||
managedRealm.admin().update(rep);
|
||||
|
||||
//Create 2 users with the same email
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setEmail("user1@localhost");
|
||||
user.setUsername("user1");
|
||||
createUser(user, false);
|
||||
user.setUsername("user2");
|
||||
createUser(user, false);
|
||||
|
||||
//Disallow duplicate emails
|
||||
rep.setDuplicateEmailsAllowed(false);
|
||||
managedRealm.admin().update(rep);
|
||||
|
||||
//Create a third user with the same email
|
||||
user.setUsername("user3");
|
||||
adminEvents.clear();
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User exists with same username or email", error.getErrorMessage());
|
||||
assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithHashedCredentials() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user_creds");
|
||||
user.setEmail("email@localhost");
|
||||
|
||||
PasswordCredentialModel pcm = PasswordCredentialModel.createFromValues("my-algorithm", "theSalt".getBytes(), 22, "ABC");
|
||||
CredentialRepresentation hashedPassword = ModelToRepresentation.toRepresentation(pcm);
|
||||
hashedPassword.setCreatedDate(1001L);
|
||||
hashedPassword.setUserLabel("deviceX");
|
||||
hashedPassword.setType(CredentialRepresentation.PASSWORD);
|
||||
|
||||
user.setCredentials(Arrays.asList(hashedPassword));
|
||||
|
||||
createUser(user);
|
||||
|
||||
CredentialModel credentialHashed = fetchCredentials("user_creds");
|
||||
PasswordCredentialModel pcmh = PasswordCredentialModel.createFromCredentialModel(credentialHashed);
|
||||
assertNotNull(credentialHashed, "Expecting credential");
|
||||
assertEquals("my-algorithm", pcmh.getPasswordCredentialData().getAlgorithm());
|
||||
assertEquals(Long.valueOf(1001), credentialHashed.getCreatedDate());
|
||||
assertEquals("deviceX", credentialHashed.getUserLabel());
|
||||
assertEquals(22, pcmh.getPasswordCredentialData().getHashIterations());
|
||||
assertEquals("ABC", pcmh.getPasswordSecretData().getValue());
|
||||
assertEquals("theSalt", new String(pcmh.getPasswordSecretData().getSalt()));
|
||||
assertEquals(CredentialRepresentation.PASSWORD, credentialHashed.getType());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createUserWithDeprecatedCredentialsFormat() throws IOException {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user_creds");
|
||||
user.setEmail("email@localhost");
|
||||
|
||||
PasswordCredentialModel pcm = PasswordCredentialModel.createFromValues("my-algorithm", "theSalt".getBytes(), 22, "ABC");
|
||||
//CredentialRepresentation hashedPassword = ModelToRepresentation.toRepresentation(pcm);
|
||||
String deprecatedCredential = "{\n" +
|
||||
" \"type\" : \"password\",\n" +
|
||||
" \"hashedSaltedValue\" : \"" + pcm.getPasswordSecretData().getValue() + "\",\n" +
|
||||
" \"salt\" : \"" + Base64.encodeBytes(pcm.getPasswordSecretData().getSalt()) + "\",\n" +
|
||||
" \"hashIterations\" : " + pcm.getPasswordCredentialData().getHashIterations() + ",\n" +
|
||||
" \"algorithm\" : \"" + pcm.getPasswordCredentialData().getAlgorithm() + "\"\n" +
|
||||
" }";
|
||||
|
||||
CredentialRepresentation deprecatedHashedPassword = JsonSerialization.readValue(deprecatedCredential, CredentialRepresentation.class);
|
||||
Assertions.assertNotNull(deprecatedHashedPassword.getHashedSaltedValue());
|
||||
Assertions.assertNull(deprecatedHashedPassword.getCredentialData());
|
||||
|
||||
deprecatedHashedPassword.setCreatedDate(1001l);
|
||||
deprecatedHashedPassword.setUserLabel("deviceX");
|
||||
deprecatedHashedPassword.setType(CredentialRepresentation.PASSWORD);
|
||||
|
||||
user.setCredentials(Arrays.asList(deprecatedHashedPassword));
|
||||
|
||||
createUser(user, false);
|
||||
|
||||
CredentialModel credentialHashed = fetchCredentials("user_creds");
|
||||
PasswordCredentialModel pcmh = PasswordCredentialModel.createFromCredentialModel(credentialHashed);
|
||||
assertNotNull(credentialHashed, "Expecting credential");
|
||||
assertEquals("my-algorithm", pcmh.getPasswordCredentialData().getAlgorithm());
|
||||
assertEquals(Long.valueOf(1001), credentialHashed.getCreatedDate());
|
||||
assertEquals("deviceX", credentialHashed.getUserLabel());
|
||||
assertEquals(22, pcmh.getPasswordCredentialData().getHashIterations());
|
||||
assertEquals("ABC", pcmh.getPasswordSecretData().getValue());
|
||||
assertEquals("theSalt", new String(pcmh.getPasswordSecretData().getSalt()));
|
||||
assertEquals(CredentialRepresentation.PASSWORD, credentialHashed.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithTemporaryCredentials() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user_temppw");
|
||||
user.setEmail("email.temppw@localhost");
|
||||
|
||||
CredentialRepresentation password = new CredentialRepresentation();
|
||||
password.setValue("password");
|
||||
password.setType(CredentialRepresentation.PASSWORD);
|
||||
password.setTemporary(true);
|
||||
user.setCredentials(Arrays.asList(password));
|
||||
|
||||
String userId = createUser(user);
|
||||
|
||||
UserRepresentation userRep = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
Assertions.assertEquals(1, userRep.getRequiredActions().size());
|
||||
Assertions.assertEquals(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), userRep.getRequiredActions().get(0));
|
||||
}
|
||||
|
||||
@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(credential, "Expecting credential");
|
||||
PasswordCredentialModel pcm = PasswordCredentialModel.createFromCredentialModel(credential);
|
||||
assertEquals(Argon2PasswordHashProviderFactory.ID, pcm.getPasswordCredentialData().getAlgorithm());
|
||||
assertEquals(Argon2Parameters.DEFAULT_ITERATIONS, pcm.getPasswordCredentialData().getHashIterations());
|
||||
assertNotEquals("ABCD", pcm.getPasswordSecretData().getValue());
|
||||
assertEquals(CredentialRepresentation.PASSWORD, credential.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser3() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("User1");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser4() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("USER1");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser5() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setEmail("User1@localhost");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser6() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setEmail("user1@LOCALHOST");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDuplicatedUser7() {
|
||||
createUser("user1", "USer1@Localhost");
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setEmail("user1@localhost");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(409, response.getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-7015
|
||||
@Test
|
||||
public void createTwoUsersWithEmptyStringEmails() {
|
||||
createUser("user1", "");
|
||||
createUser("user2", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithFederationLink() {
|
||||
|
||||
// add a dummy federation provider
|
||||
ComponentRepresentation dummyFederationProvider = new ComponentRepresentation();
|
||||
String componentId = KeycloakModelUtils.generateId();
|
||||
dummyFederationProvider.setId(componentId);
|
||||
dummyFederationProvider.setName(DummyUserFederationProviderFactory.PROVIDER_NAME);
|
||||
dummyFederationProvider.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
|
||||
dummyFederationProvider.setProviderType(UserStorageProvider.class.getName());
|
||||
managedRealm.admin().components().add(dummyFederationProvider);
|
||||
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.componentPath(componentId), dummyFederationProvider, ResourceType.COMPONENT);
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1");
|
||||
user.setEmail("user1@localhost");
|
||||
user.setFederationLink(componentId);
|
||||
|
||||
String userId = createUser(user);
|
||||
|
||||
// fetch user again and see federation link filled in
|
||||
UserRepresentation createdUser = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
assertNotNull(createdUser);
|
||||
assertEquals(user.getFederationLink(), createdUser.getFederationLink());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithoutUsername() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setEmail("user1@localhost");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(400, response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User name is missing", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithEmailAsUsername() {
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
switchEditUsernameAllowedOn(false);
|
||||
String id = createUser();
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
assertEquals("user1@localhost", userRep.getEmail());
|
||||
assertEquals(userRep.getEmail(), userRep.getUsername());
|
||||
deleteUser(id);
|
||||
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
switchEditUsernameAllowedOn(true);
|
||||
id = createUser();
|
||||
user = managedRealm.admin().users().get(id);
|
||||
userRep = user.toRepresentation();
|
||||
assertEquals("user1@localhost", userRep.getEmail());
|
||||
assertEquals(userRep.getEmail(), userRep.getUsername());
|
||||
deleteUser(id);
|
||||
|
||||
switchRegistrationEmailAsUsername(false);
|
||||
switchEditUsernameAllowedOn(true);
|
||||
id = createUser();
|
||||
user = managedRealm.admin().users().get(id);
|
||||
userRep = user.toRepresentation();
|
||||
assertEquals("user1", userRep.getUsername());
|
||||
assertEquals("user1@localhost", userRep.getEmail());
|
||||
deleteUser(id);
|
||||
|
||||
switchRegistrationEmailAsUsername(false);
|
||||
switchEditUsernameAllowedOn(false);
|
||||
id = createUser();
|
||||
user = managedRealm.admin().users().get(id);
|
||||
userRep = user.toRepresentation();
|
||||
assertEquals("user1", userRep.getUsername());
|
||||
assertEquals("user1@localhost", userRep.getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithEmptyUsername() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("");
|
||||
user.setEmail("user2@localhost");
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(400, response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User name is missing", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithInvalidPolicyPassword() {
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
String passwordPolicy = rep.getPasswordPolicy();
|
||||
rep.setPasswordPolicy("length(8)");
|
||||
managedRealm.admin().update(rep);
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user4");
|
||||
user.setEmail("user4@localhost");
|
||||
CredentialRepresentation rawPassword = new CredentialRepresentation();
|
||||
rawPassword.setValue("ABCD");
|
||||
rawPassword.setType(CredentialRepresentation.PASSWORD);
|
||||
user.setCredentials(Collections.singletonList(rawPassword));
|
||||
adminEvents.clear();
|
||||
|
||||
try (Response response = managedRealm.admin().users().create(user)) {
|
||||
assertEquals(400, response.getStatus());
|
||||
OAuth2ErrorRepresentation error = response.readEntity(OAuth2ErrorRepresentation.class);
|
||||
Assertions.assertEquals("invalidPasswordMinLengthMessage", error.getError());
|
||||
Assertions.assertEquals("Invalid password: minimum length 8.", error.getErrorDescription());
|
||||
rep.setPasswordPolicy(passwordPolicy);
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
managedRealm.admin().update(rep);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithCreateTimestamp() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1");
|
||||
user.setEmail("user1@localhost");
|
||||
Long createdTimestamp = 1695238476L;
|
||||
user.setCreatedTimestamp(createdTimestamp);
|
||||
|
||||
String userId = createUser(user);
|
||||
|
||||
// fetch user again and see created timestamp filled in
|
||||
UserRepresentation createdUser = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
assertNotNull(createdUser);
|
||||
assertEquals(user.getCreatedTimestamp(), createdUser.getCreatedTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failCreateUserUsingRegularUser() throws Exception {
|
||||
managedRealm.admin().users().create(UserConfigBuilder.create().username("regular-user").password("password").email("regular@local").name("Regular", "User").build());
|
||||
|
||||
try (Keycloak localAdminClient = clientFactory.create()
|
||||
.realm(managedRealm.getName()).username("regular-user").password("password")
|
||||
.clientId(Constants.ADMIN_CLI_CLIENT_ID).build()) {
|
||||
UserRepresentation invalidUser = new UserRepresentation();
|
||||
invalidUser.setUsername("do-not-create-me");
|
||||
|
||||
Response response = localAdminClient.realm("default").users().create(invalidUser);
|
||||
Assert.assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||
|
||||
invalidUser.setGroups(Collections.emptyList());
|
||||
response = localAdminClient.realm("default").users().create(invalidUser);
|
||||
|
||||
Assert.assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUserDoNotGrantRole() {
|
||||
managedRealm.admin().roles().create(RoleBuilder.create().name("realm-role").build());
|
||||
|
||||
try {
|
||||
UserRepresentation userRep = UserConfigBuilder.create().username("alice").password("password").roles("realm-role")
|
||||
.build();
|
||||
String userId = ApiUtil.getCreatedId(managedRealm.admin().users().create(userRep));
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
List<RoleRepresentation> realmMappings = user.roles().getAll().getRealmMappings();
|
||||
|
||||
assertFalse(realmMappings.stream().map(RoleRepresentation::getName).anyMatch("realm-role"::equals));
|
||||
} finally {
|
||||
managedRealm.admin().roles().get("realm-role").remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultCharacterValidationOnUsername() {
|
||||
List<String> invalidNames = List.of("1user\\\\", "2user\\\\%", "3user\\\\*", "4user\\\\_");
|
||||
|
||||
for (String invalidName : invalidNames) {
|
||||
UserRepresentation invalidUser = UserConfigBuilder.create().username(invalidName).email("test@invalid.org").build();
|
||||
Response response = managedRealm.admin().users().create(invalidUser);
|
||||
Assert.assertEquals(400, response.getStatus());
|
||||
Assert.assertEquals("error-username-invalid-character", response.readEntity(ErrorRepresentation.class).getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static class UserCreateServerConf implements KeycloakServerConfig {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder builder) {
|
||||
return builder.dependency("org.keycloak.tests", "keycloak-tests-custom-providers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,360 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.ClientErrorException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.credential.OTPCredentialModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectUser;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.realm.ManagedUser;
|
||||
import org.keycloak.testframework.realm.UserConfig;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserCredentialTest extends AbstractUserTest {
|
||||
|
||||
@InjectOAuthClient
|
||||
OAuthClient oauth;
|
||||
|
||||
@InjectUser(ref = "user-with-one-configured-otp", config = UserCredentialOtp1UserConf.class)
|
||||
ManagedUser userOtp1;
|
||||
|
||||
@InjectUser(ref = "user-with-two-configured-otp", config = UserCredentialOtp2UserConf.class)
|
||||
ManagedUser userOtp2;
|
||||
|
||||
@InjectUser(ref = "john-doh@localhost", config = UserCredentialJohnDohUserConf.class)
|
||||
ManagedUser johnDoh;
|
||||
|
||||
@InjectUser(ref = "test-user@localhost", config = UserCredentialTestUserConf.class)
|
||||
ManagedUser testUser;
|
||||
|
||||
@Test
|
||||
public void resetUserPassword() {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@localhost").build();
|
||||
|
||||
String userId = createUser(userRep);
|
||||
|
||||
CredentialRepresentation cred = new CredentialRepresentation();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
cred.setValue("paSSw0rd");
|
||||
cred.setTemporary(false);
|
||||
|
||||
managedRealm.admin().users().get(userId).resetPassword(cred);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId), ResourceType.USER);
|
||||
|
||||
oauth.openLoginForm();
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
loginPage.fillLogin("user1", "paSSw0rd");
|
||||
loginPage.submit();
|
||||
|
||||
assertTrue(driver.getPageSource().contains("Happy days"));
|
||||
|
||||
AccountHelper.logout(managedRealm.admin(), "user1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetUserInvalidPassword() {
|
||||
String userId = createUser("user1", "user1@localhost");
|
||||
|
||||
try {
|
||||
CredentialRepresentation cred = new CredentialRepresentation();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
cred.setValue(" ");
|
||||
cred.setTemporary(false);
|
||||
managedRealm.admin().users().get(userId).resetPassword(cred);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
e.getResponse().close();
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldFailAfterPasswordDeleted() {
|
||||
String userName = "credential-tester";
|
||||
String userPass = "s3cr37";
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username(userName).password(userPass).name("credential", "tester").email("credential@tester").build();
|
||||
String userId = createUser(userRep);
|
||||
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
loginPage.fillLogin(userName, userPass);
|
||||
loginPage.submit();
|
||||
assertTrue(driver.getPageSource().contains("Happy days"), "Test user should be successfully logged in.");
|
||||
AccountHelper.logout(managedRealm.admin(), userName);
|
||||
|
||||
Optional<CredentialRepresentation> passwordCredential =
|
||||
managedRealm.admin().users().get(userId).credentials().stream()
|
||||
.filter(c -> CredentialRepresentation.PASSWORD.equals(c.getType()))
|
||||
.findFirst();
|
||||
assertTrue(passwordCredential.isPresent(), "Test user should have a password credential set.");
|
||||
managedRealm.admin().users().get(userId).removeCredential(passwordCredential.get().getId());
|
||||
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
loginPage.fillLogin(userName, userPass);
|
||||
loginPage.submit();
|
||||
assertTrue(driver.getCurrentUrl().contains(String.format("/realms/%s/login-actions/authenticate", managedRealm.getName())), "Test user should fail to log in after password was deleted.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCredentials() {
|
||||
// both credentials have a null priority - stable ordering is not guaranteed between calls
|
||||
// Get user user-with-one-configured-otp and assert he has no label linked to its OTP credential
|
||||
UserResource user = userOtp1.admin();
|
||||
CredentialRepresentation otpCred = user.credentials().stream().filter(cr -> "otp".equals(cr.getType()))
|
||||
.findFirst().orElseThrow();
|
||||
Assertions.assertNull(otpCred.getUserLabel());
|
||||
|
||||
// Set and check a new label
|
||||
String newLabel = "the label";
|
||||
user.setCredentialUserLabel(otpCred.getId(), newLabel);
|
||||
Assertions.assertEquals(newLabel, user.credentials().stream().filter(cr -> cr.getId().equals(otpCred.getId()))
|
||||
.findFirst().orElseThrow().getUserLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToSetCredentialUserLabelWhenLabelIsEmpty() {
|
||||
UserResource user = userOtp1.admin();
|
||||
CredentialRepresentation otpCred = user.credentials().get(0);
|
||||
BadRequestException ex = Assertions.assertThrows(BadRequestException.class, () -> {
|
||||
user.setCredentialUserLabel(otpCred.getId(), " ");
|
||||
});
|
||||
|
||||
Response response = ex.getResponse();
|
||||
String body = response.readEntity(String.class);
|
||||
|
||||
Assertions.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
Assertions.assertTrue(body.contains("missingCredentialLabel"));
|
||||
Assertions.assertTrue(body.contains("Credential label must not be empty"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToSetCredentialUserLabelWhenLabelAlreadyExists() {
|
||||
UserResource user = userOtp2.admin();
|
||||
|
||||
List<CredentialRepresentation> credentials = user.credentials().stream()
|
||||
.filter(c -> c.getType().equals(OTPCredentialModel.TYPE))
|
||||
.toList();
|
||||
Assertions.assertEquals(2, credentials.size());
|
||||
|
||||
String firstId = credentials.get(0).getId();
|
||||
String secondId = credentials.get(1).getId();
|
||||
|
||||
user.setCredentialUserLabel(firstId, "Device");
|
||||
user.setCredentialUserLabel(secondId, "Second Device");
|
||||
|
||||
// Attempt to update second credential to use the same label as the first
|
||||
ClientErrorException ex = Assertions.assertThrows(ClientErrorException.class, () -> {
|
||||
user.setCredentialUserLabel(secondId, "Device");
|
||||
});
|
||||
|
||||
Response response = ex.getResponse();
|
||||
Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
|
||||
|
||||
String body = response.readEntity(String.class);
|
||||
Assertions.assertNotNull(body);
|
||||
Assertions.assertTrue(body.contains("Device already exists with the same name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCredentials() {
|
||||
UserResource user = johnDoh.admin();
|
||||
List<CredentialRepresentation> creds = user.credentials();
|
||||
Assertions.assertEquals(1, creds.size());
|
||||
CredentialRepresentation credPasswd = creds.get(0);
|
||||
Assertions.assertEquals("password", credPasswd.getType());
|
||||
|
||||
// Remove password
|
||||
user.removeCredential(credPasswd.getId());
|
||||
Assertions.assertEquals(0, user.credentials().size());
|
||||
|
||||
// Restore password
|
||||
credPasswd.setValue("password");
|
||||
user.resetPassword(credPasswd);
|
||||
Assertions.assertEquals(1, user.credentials().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCRUDCredentialsOfDifferentUser() {
|
||||
// Get credential ID of the OTP credential of the user1
|
||||
UserResource user1 = userOtp1.admin();
|
||||
CredentialRepresentation otpCredential = user1.credentials().stream()
|
||||
.filter(credentialRep -> OTPCredentialModel.TYPE.equals(credentialRep.getType()))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
// Test that when admin operates on user "user2", he can't update, move or remove credentials of different user "user1"
|
||||
UserResource user2 = ApiUtil.findUserByUsernameId(managedRealm.admin(), testUser.getUsername());
|
||||
try {
|
||||
user2.setCredentialUserLabel(otpCredential.getId(), "new-label");
|
||||
Assertions.fail("Not expected to successfully update user label");
|
||||
} catch (NotFoundException nfe) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
user2.moveCredentialToFirst(otpCredential.getId());
|
||||
Assertions.fail("Not expected to successfully move credential");
|
||||
} catch (NotFoundException nfe) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
user2.removeCredential(otpCredential.getId());
|
||||
Assertions.fail("Not expected to successfully remove credential");
|
||||
} catch (NotFoundException nfe) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Assert credential was not removed or updated
|
||||
CredentialRepresentation otpCredentialLoaded = user1.credentials().stream()
|
||||
.filter(credentialRep -> OTPCredentialModel.TYPE.equals(credentialRep.getType()))
|
||||
.findFirst()
|
||||
.get();
|
||||
Assertions.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getUserLabel(), otpCredentialLoaded.getUserLabel()));
|
||||
Assertions.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getPriority(), otpCredentialLoaded.getPriority()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAndMoveCredentials() {
|
||||
UserResource user = userOtp2.admin();
|
||||
List<CredentialRepresentation> creds = user.credentials();
|
||||
List<String> expectedCredIds = Arrays.asList(creds.get(0).getId(), creds.get(1).getId(), creds.get(2).getId());
|
||||
|
||||
// Check actual user credentials
|
||||
assertSameIds(expectedCredIds, user.credentials());
|
||||
|
||||
// Move first credential after second one
|
||||
user.moveCredentialAfter(expectedCredIds.get(0), expectedCredIds.get(1));
|
||||
List<String> newOrderCredIds = Arrays.asList(expectedCredIds.get(1), expectedCredIds.get(0), expectedCredIds.get(2));
|
||||
assertSameIds(newOrderCredIds, user.credentials());
|
||||
|
||||
// Move last credential in first position
|
||||
user.moveCredentialToFirst(expectedCredIds.get(2));
|
||||
newOrderCredIds = Arrays.asList(expectedCredIds.get(2), expectedCredIds.get(1), expectedCredIds.get(0));
|
||||
assertSameIds(newOrderCredIds, user.credentials());
|
||||
|
||||
// Restore initial state
|
||||
user.moveCredentialToFirst(expectedCredIds.get(1));
|
||||
user.moveCredentialToFirst(expectedCredIds.get(0));
|
||||
assertSameIds(expectedCredIds, user.credentials());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expectNoPasswordShownWhenCreatingUserWithPassword() throws IOException {
|
||||
CredentialRepresentation credential = new CredentialRepresentation();
|
||||
credential.setType(CredentialRepresentation.PASSWORD);
|
||||
credential.setValue("password");
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("test");
|
||||
user.setCredentials(Collections.singletonList(credential));
|
||||
user.setEnabled(true);
|
||||
|
||||
createUser(user, false);
|
||||
|
||||
String actualRepresentation = adminEvents.poll().getRepresentation();
|
||||
assertEquals(
|
||||
JsonSerialization.writeValueAsString(user),
|
||||
actualRepresentation
|
||||
);
|
||||
}
|
||||
|
||||
private void assertSameIds(List<String> expectedIds, List<CredentialRepresentation> actual) {
|
||||
Assertions.assertEquals(expectedIds.size(), actual.size());
|
||||
for (int i = 0; i < expectedIds.size(); i++) {
|
||||
Assertions.assertEquals(expectedIds.get(i), actual.get(i).getId());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserCredentialJohnDohUserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("john-doh@localhost");
|
||||
builder.password("password");
|
||||
builder.name("John", "Doh");
|
||||
builder.email("john-doh@localhost");
|
||||
builder.emailVerified();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserCredentialTestUserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("test-user@localhost");
|
||||
builder.password("password");
|
||||
builder.name("Tom", "Brady");
|
||||
builder.email("test-user@localhost");
|
||||
builder.emailVerified();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserCredentialOtp1UserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("user-with-one-configured-otp");
|
||||
builder.password("password");
|
||||
builder.name("Otp", "1");
|
||||
builder.email("otp1@redhat.com");
|
||||
builder.emailVerified();
|
||||
builder.totpSecret("DJmQfC73VGFhw7D4QJ8A");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserCredentialOtp2UserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("user-with-two-configured-otp");
|
||||
builder.password("password");
|
||||
builder.name("Otp", "2");
|
||||
builder.email("otp2@redhat.com");
|
||||
builder.emailVerified();
|
||||
builder.totpSecret("DJmQfC73VGFhw7D4QJ8A");
|
||||
builder.totpSecret("ABCQfC73VGFhw7D4QJ8A");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserDeleteTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
String userId = ApiUtil.getCreatedId(managedRealm.admin().users().create(UserConfigBuilder.create().username("user1").email("user1@localhost.com").build()));
|
||||
AdminEventAssertion.assertSuccess(adminEvents.poll());
|
||||
deleteUser(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteNonExistent() {
|
||||
try (Response response = managedRealm.admin().users().delete("does-not-exist")) {
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,901 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import jakarta.ws.rs.ClientErrorException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.VerificationException;
|
||||
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.models.utils.SystemClientUtil;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectClient;
|
||||
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.mail.MailServer;
|
||||
import org.keycloak.testframework.mail.annotations.InjectMailServer;
|
||||
import org.keycloak.testframework.realm.ClientConfig;
|
||||
import org.keycloak.testframework.realm.ClientConfigBuilder;
|
||||
import org.keycloak.testframework.realm.ManagedClient;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet;
|
||||
import org.keycloak.testframework.remote.timeoffset.TimeOffSet;
|
||||
import org.keycloak.testframework.server.KeycloakUrls;
|
||||
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||
import org.keycloak.testframework.ui.page.ErrorPage;
|
||||
import org.keycloak.testframework.ui.page.InfoPage;
|
||||
import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testframework.ui.page.ProceedPage;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.tests.utils.MailUtils;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserEmailTest extends AbstractUserTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@InjectClient(config = UserEmailTestAppClientConf.class)
|
||||
ManagedClient client;
|
||||
|
||||
@InjectMailServer
|
||||
MailServer mailServer;
|
||||
|
||||
@InjectKeycloakUrls
|
||||
KeycloakUrls keycloakUrls;
|
||||
|
||||
@InjectTimeOffSet
|
||||
TimeOffSet timeOffSet;
|
||||
|
||||
@InjectPage
|
||||
LoginPasswordUpdatePage passwordUpdatePage;
|
||||
|
||||
@InjectPage
|
||||
InfoPage infoPage;
|
||||
|
||||
@InjectPage
|
||||
ProceedPage proceedPage;
|
||||
|
||||
@InjectPage
|
||||
ErrorPage errorPage;
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmail() {
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
userRep.setUsername("user1");
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().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);
|
||||
Assertions.assertEquals("User email missing", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
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);
|
||||
Assertions.assertEquals("User is disabled", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
try {
|
||||
userRep.setEnabled(true);
|
||||
updateUser(user, userRep);
|
||||
|
||||
user.executeActionsEmail(Arrays.asList(
|
||||
UserModel.RequiredAction.UPDATE_PASSWORD.name(),
|
||||
"invalid\"<img src=\"alert(0)\">")
|
||||
);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("Provided invalid required actions", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
|
||||
try {
|
||||
user.executeActionsEmail(
|
||||
"invalidClientId",
|
||||
"invalidUri",
|
||||
Collections.singletonList(UserModel.RequiredAction.UPDATE_PASSWORD.name())
|
||||
);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("Client doesn't exist", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccess() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
|
||||
assertTrue(body.getText().contains("Update Password"));
|
||||
assertTrue(body.getText().contains("your Default account"));
|
||||
assertTrue(body.getText().contains("This link will expire within 12 hours"));
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(body);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertThat(driver.getCurrentUrl(), Matchers.containsString("client_id=" + Constants.ACCOUNT_MANAGEMENT_CLIENT_ID));
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessWithAccountClientDisabled() throws IOException {
|
||||
ClientRepresentation clientRepresentation = managedRealm.admin().clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||
clientRepresentation.setEnabled(false);
|
||||
managedRealm.admin().clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(clientRepresentation.getId()), clientRepresentation, ResourceType.CLIENT);
|
||||
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
userRep.setEnabled(true);
|
||||
userRep.setUsername("user1");
|
||||
userRep.setEmail("user1@test.com");
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(body);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertThat(driver.getCurrentUrl(), Matchers.containsString("client_id=" + SystemClientUtil.SYSTEM_CLIENT_ID));
|
||||
|
||||
clientRepresentation.setEnabled(true);
|
||||
managedRealm.admin().clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(clientRepresentation.getId()), clientRepresentation, ResourceType.CLIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailLinkBasedOnRealmFrontEndUrl() throws Exception {
|
||||
try {
|
||||
updateRealmFrontEndUrl(adminClient.realm("master"), keycloakUrls.getBase());
|
||||
String expectedFrontEndUrl = "https://mytestrealm";
|
||||
updateRealmFrontEndUrl(adminClient.realm(managedRealm.getName()), expectedFrontEndUrl);
|
||||
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
userRep.setEnabled(true);
|
||||
userRep.setUsername("user1");
|
||||
userRep.setEmail("user1@test.com");
|
||||
|
||||
String id = createUser(userRep, false);
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
String link = MailUtils.getPasswordResetEmailLink(body);
|
||||
assertTrue(link.contains(expectedFrontEndUrl));
|
||||
} finally {
|
||||
updateRealmFrontEndUrl(adminClient.realm("master"), null);
|
||||
updateRealmFrontEndUrl(adminClient.realm(managedRealm.getName()), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailWithCustomLifespan() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
|
||||
final int lifespan = (int) TimeUnit.HOURS.toSeconds(5);
|
||||
user.executeActionsEmail(actions, lifespan);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
|
||||
assertTrue(body.getText().contains("Update Password"));
|
||||
assertTrue(body.getText().contains("your Default account"));
|
||||
assertTrue(body.getText().contains("This link will expire within 5 hours"));
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(body);
|
||||
|
||||
String token = link.substring(link.indexOf("key=") + "key=".length());
|
||||
|
||||
try {
|
||||
final AccessToken accessToken = TokenVerifier.create(token, AccessToken.class).getToken();
|
||||
assertThat(accessToken.getExp() - accessToken.getIat(), allOf(greaterThanOrEqualTo(lifespan - 1L), lessThanOrEqualTo(lifespan + 1L)));
|
||||
assertEquals(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID, accessToken.getIssuedFor());
|
||||
} catch (VerificationException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessTwoLinks() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(2, mailServer.getReceivedMessages().length);
|
||||
|
||||
int i = 1;
|
||||
for (MimeMessage message : mailServer.getReceivedMessages()) {
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
|
||||
i++;
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
}
|
||||
|
||||
for (MimeMessage message : mailServer.getReceivedMessages()) {
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
driver.navigate().to(link);
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessTwoLinksReverse() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(2, mailServer.getReceivedMessages().length);
|
||||
|
||||
int i = 1;
|
||||
for (int j = mailServer.getReceivedMessages().length - 1; j >= 0; j--) {
|
||||
MimeMessage message = mailServer.getReceivedMessages()[j];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
|
||||
i++;
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
}
|
||||
|
||||
for (MimeMessage message : mailServer.getReceivedMessages()) {
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
driver.navigate().to(link);
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessLinkOpenDoesNotExpireWhenOpenedOnly() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
driver.manage().deleteAllCookies();
|
||||
driver.navigate().to("about:blank");
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessTokenShortLifespan() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
final AtomicInteger originalValue = new AtomicInteger();
|
||||
|
||||
RealmRepresentation realmRep = managedRealm.admin().toRepresentation();
|
||||
originalValue.set(realmRep.getActionTokenGeneratedByAdminLifespan());
|
||||
realmRep.setActionTokenGeneratedByAdminLifespan(60);
|
||||
managedRealm.admin().update(realmRep);
|
||||
|
||||
try {
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail(actions);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
timeOffSet.set(70);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
assertEquals("Action expired.", errorPage.getError());
|
||||
} finally {
|
||||
timeOffSet.set(0);
|
||||
|
||||
realmRep.setActionTokenGeneratedByAdminLifespan(originalValue.get());
|
||||
managedRealm.admin().update(realmRep);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccessWithRecycledAuthSession() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().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 = managedRealm.admin().clients().create(client);
|
||||
String createdId = ApiUtil.getCreatedId(response);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.clientResourcePath(createdId), client, ResourceType.CLIENT);
|
||||
|
||||
user.executeActionsEmail("myclient2", "http://myclient.com/home.html", actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
}
|
||||
|
||||
user.executeActionsEmail(actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(2, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[mailServer.getReceivedMessages().length - 1];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailWithRedirect() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().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 = managedRealm.admin().clients().create(client);
|
||||
String createdId = ApiUtil.getCreatedId(response);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), 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);
|
||||
Assertions.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
user.executeActionsEmail("myclient", "http://myclient.com/home.html", actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", driver.findElement(By.id("kc-page-title")).getText());
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
|
||||
// check to make sure the back link is set.
|
||||
Assertions.assertTrue(pageSource.contains("http://myclient.com/home.html"));
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailWithRedirectAndCustomLifespan() throws IOException {
|
||||
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().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 = managedRealm.admin().clients().create(client);
|
||||
String createdId = ApiUtil.getCreatedId(response);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.clientResourcePath(createdId), client, ResourceType.CLIENT);
|
||||
|
||||
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
|
||||
final int lifespan = (int) TimeUnit.DAYS.toSeconds(128);
|
||||
|
||||
try {
|
||||
// test that an invalid redirect uri is rejected.
|
||||
user.executeActionsEmail("myclient", "http://unregistered-uri.com/", lifespan, actions);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
user.executeActionsEmail("myclient", "http://myclient.com/home.html", lifespan, actions);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
|
||||
assertTrue(body.getText().contains("This link will expire within 128 days"));
|
||||
assertTrue(body.getHtml().contains("This link will expire within 128 days"));
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
String token = link.substring(link.indexOf("key=") + "key=".length());
|
||||
|
||||
try {
|
||||
final AccessToken accessToken = TokenVerifier.create(token, AccessToken.class).getToken();
|
||||
assertEquals(lifespan, accessToken.getExp() - accessToken.getIat());
|
||||
} catch (VerificationException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", driver.findElement(By.id("kc-page-title")).getText());
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
|
||||
// check to make sure the back link is set.
|
||||
Assertions.assertTrue(pageSource.contains("http://myclient.com/home.html"));
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void sendVerifyEmail() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
|
||||
try {
|
||||
user.sendVerifyEmail();
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.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);
|
||||
Assertions.assertEquals("User is disabled", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
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);
|
||||
Assertions.assertEquals("Client doesn't exist", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
|
||||
user.sendVerifyEmail();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(mailServer.getReceivedMessages()[0]);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||
proceedPage.clickProceedLink();
|
||||
|
||||
Assertions.assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
driver.navigate().to("about:blank");
|
||||
|
||||
driver.navigate().to(link);
|
||||
infoPage.assertCurrent();
|
||||
assertEquals("Your email address has been verified already.", infoPage.getInfo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendVerifyEmailWithRedirect() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
|
||||
String clientId = client.getClientId();
|
||||
String redirectUri = keycloakUrls.getBase() + "/auth/some-page";
|
||||
try {
|
||||
// test that an invalid redirect uri is rejected.
|
||||
user.sendVerifyEmail(clientId, "http://unregistered-uri.com/");
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
user.sendVerifyEmail(clientId, redirectUri);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||
proceedPage.clickProceedLink();
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
Assertions.assertTrue(pageSource.contains(redirectUri));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendVerifyEmailWithRedirectAndCustomLifespan() throws IOException {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").name("User", "One").email("user1@test.com").build();
|
||||
|
||||
String id = createUser(userRep);
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
|
||||
final int lifespan = (int) TimeUnit.DAYS.toSeconds(1);
|
||||
String redirectUri = keycloakUrls.getBase() + "/auth/some-page";
|
||||
try {
|
||||
// test that an invalid redirect uri is rejected.
|
||||
user.sendVerifyEmail(client.getClientId(), "http://unregistered-uri.com/", lifespan);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
user.sendVerifyEmail(client.getClientId(), redirectUri, lifespan);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
|
||||
|
||||
Assertions.assertEquals(1, mailServer.getReceivedMessages().length);
|
||||
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||
|
||||
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||
assertThat(body.getText(), Matchers.containsString("This link will expire within 1 day"));
|
||||
assertThat(body.getHtml(), Matchers.containsString("This link will expire within 1 day"));
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||
String token = link.substring(link.indexOf("key=") + "key=".length());
|
||||
|
||||
try {
|
||||
final AccessToken accessToken = TokenVerifier.create(token, AccessToken.class).getToken();
|
||||
assertEquals(lifespan, accessToken.getExp() - accessToken.getIat());
|
||||
} catch (VerificationException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||
proceedPage.clickProceedLink();
|
||||
|
||||
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
Assertions.assertTrue(pageSource.contains(redirectUri));
|
||||
}
|
||||
|
||||
private void updateRealmFrontEndUrl(RealmResource realm, String url) throws Exception {
|
||||
RealmRepresentation master = realm.toRepresentation();
|
||||
Map<String, String> attributes = Optional.ofNullable(master.getAttributes()).orElse(new HashMap<>());
|
||||
|
||||
if (url == null) {
|
||||
attributes.remove("frontendUrl");
|
||||
} else {
|
||||
attributes.put("frontendUrl", url);
|
||||
}
|
||||
|
||||
realm.update(master);
|
||||
}
|
||||
|
||||
private static class UserEmailTestAppClientConf implements ClientConfig {
|
||||
|
||||
public ClientConfigBuilder configure(ClientConfigBuilder builder) {
|
||||
builder.clientId("test-app-email");
|
||||
builder.secret("password");
|
||||
builder.baseUrl("http://localhost:8080/auth/");
|
||||
builder.redirectUris("http://localhost:8080/auth/*");
|
||||
builder.adminUrl("http://localhost:8080/auth/admin");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.tests.utils.Assert;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED;
|
||||
|
||||
@KeycloakIntegrationTest(config = UserFedarationTest.UserFederationServerConfig.class)
|
||||
public class UserFedarationTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void getFederatedIdentities() {
|
||||
// Add sample identity provider
|
||||
addSampleIdentityProvider();
|
||||
|
||||
// Add sample user
|
||||
String id = createUser();
|
||||
UserResource user = managedRealm.admin().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");
|
||||
addFederatedIdentity(id, "social-provider-id", link);
|
||||
|
||||
// Verify social link is here
|
||||
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");
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.userFederatedIdentityLink(id, "social-provider-id"), ResourceType.USER);
|
||||
assertEquals(0, user.getFederatedIdentity().size());
|
||||
|
||||
removeSampleIdentityProvider();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCredentialLabelForFederatedUser() {
|
||||
// Create user federation
|
||||
ComponentRepresentation memProvider = new ComponentRepresentation();
|
||||
memProvider.setName("memory");
|
||||
memProvider.setProviderId(UserMapStorageFactory.PROVIDER_ID);
|
||||
memProvider.setProviderType(UserStorageProvider.class.getName());
|
||||
memProvider.setConfig(new MultivaluedHashMap<>());
|
||||
memProvider.getConfig().putSingle("priority", Integer.toString(0));
|
||||
memProvider.getConfig().putSingle(IMPORT_ENABLED, Boolean.toString(false));
|
||||
|
||||
String memProviderId = ApiUtil.getCreatedId(managedRealm.admin().components().add(memProvider));
|
||||
managedRealm.cleanup().add(realm -> realm.components().component(memProviderId).remove());
|
||||
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.componentPath(memProviderId), memProvider, ResourceType.COMPONENT);
|
||||
|
||||
// Create federated user
|
||||
String username = "fed-user1";
|
||||
UserRepresentation userRepresentation = new UserRepresentation();
|
||||
userRepresentation.setUsername(username);
|
||||
userRepresentation.setEmail("feduser1@mail.com");
|
||||
userRepresentation.setRequiredActions(Collections.emptyList());
|
||||
userRepresentation.setEnabled(true);
|
||||
userRepresentation.setFederationLink(memProviderId);
|
||||
|
||||
PasswordCredentialModel pcm = PasswordCredentialModel.createFromValues("my-algorithm", "theSalt".getBytes(), 22, "ABC");
|
||||
CredentialRepresentation hashedPassword = ModelToRepresentation.toRepresentation(pcm);
|
||||
hashedPassword.setCreatedDate(1001L);
|
||||
hashedPassword.setUserLabel("label");
|
||||
hashedPassword.setType(CredentialRepresentation.PASSWORD);
|
||||
|
||||
userRepresentation.setCredentials(Arrays.asList(hashedPassword));
|
||||
String userId = ApiUtil.getCreatedId(managedRealm.admin().users().create(userRepresentation));
|
||||
Assert.assertFalse(StorageId.isLocalStorage(userId));
|
||||
|
||||
UserResource user = ApiUtil.findUserByUsernameId(managedRealm.admin(), username);
|
||||
List<CredentialRepresentation> credentials = user.credentials();
|
||||
Assertions.assertNotNull(credentials);
|
||||
Assertions.assertEquals(1, credentials.size());
|
||||
Assertions.assertEquals("label", credentials.get(0).getUserLabel());
|
||||
|
||||
// Update federated credential user label
|
||||
user.setCredentialUserLabel(credentials.get(0).getId(), "updatedLabel");
|
||||
credentials = user.credentials();
|
||||
Assertions.assertNotNull(credentials);
|
||||
Assertions.assertEquals(1, credentials.size());
|
||||
Assertions.assertEquals("updatedLabel", credentials.get(0).getUserLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFederatedIdentities() {
|
||||
String identityProviderAlias = "social-provider-id";
|
||||
String username = "federated-identities";
|
||||
String federatedUserId = "federated-user-id";
|
||||
|
||||
addSampleIdentityProvider();
|
||||
|
||||
UserRepresentation build = UserConfigBuilder.create()
|
||||
.username(username)
|
||||
.federatedLink(identityProviderAlias, federatedUserId, username)
|
||||
.build();
|
||||
|
||||
//when
|
||||
String userId = createUser(build, false);
|
||||
List<FederatedIdentityRepresentation> obtainedFederatedIdentities = managedRealm.admin().users().get(userId).getFederatedIdentity();
|
||||
|
||||
//then
|
||||
assertEquals(1, obtainedFederatedIdentities.size());
|
||||
assertEquals(federatedUserId, obtainedFederatedIdentities.get(0).getUserId());
|
||||
assertEquals(username, obtainedFederatedIdentities.get(0).getUserName());
|
||||
assertEquals(identityProviderAlias, obtainedFederatedIdentities.get(0).getIdentityProvider());
|
||||
}
|
||||
|
||||
private void removeSampleIdentityProvider() {
|
||||
IdentityProviderResource resource = managedRealm.admin().identityProviders().get("social-provider-id");
|
||||
Assertions.assertNotNull(resource);
|
||||
resource.remove();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.identityProviderPath("social-provider-id"), ResourceType.IDENTITY_PROVIDER);
|
||||
}
|
||||
|
||||
public static class UserFederationServerConfig implements KeycloakServerConfig {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||
return config.dependency("org.keycloak.tests", "keycloak-tests-custom-providers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,249 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.realm.GroupConfigBuilder;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.keycloak.tests.utils.Assert.assertNames;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserGroupTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void testGetGroupsForUserFullRepresentation() {
|
||||
String userName = "averagejoe";
|
||||
String groupName = "groupWithAttribute";
|
||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||
attributes.put("attribute1", Arrays.asList("attribute1","attribute2"));
|
||||
|
||||
UserRepresentation userRepresentation = UserConfigBuilder.create()
|
||||
.username(userName).name("average", "joe").password("password")
|
||||
.email("joe@average.com").emailVerified().build();
|
||||
|
||||
GroupRepresentation groupRepresentation = GroupConfigBuilder.create().name(groupName).setAttributes(attributes).build();
|
||||
|
||||
String userId = createUser(userRepresentation);
|
||||
String groupId = createGroup(groupRepresentation).getId();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
user.joinGroup(groupId);
|
||||
|
||||
List<GroupRepresentation> userGroups = user.groups(0, 100, false);
|
||||
|
||||
assertFalse(userGroups.isEmpty());
|
||||
assertTrue(userGroups.get(0).getAttributes().containsKey("attribute1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSearchedGroupsForUserFullRepresentation() {
|
||||
String userName = "averagejoe";
|
||||
String groupName1 = "group1WithAttribute";
|
||||
String groupName2 = "group2WithAttribute";
|
||||
Map<String, List<String>> attributes1 = new HashMap<String, List<String>>();
|
||||
attributes1.put("attribute1", Arrays.asList("attribute1"));
|
||||
Map<String, List<String>> attributes2 = new HashMap<String, List<String>>();
|
||||
attributes2.put("attribute2", Arrays.asList("attribute2"));
|
||||
|
||||
UserRepresentation userRepresentation = UserConfigBuilder.create()
|
||||
.username(userName).name("average", "joe").password("password")
|
||||
.email("joe@average.com").emailVerified().build();
|
||||
|
||||
GroupRepresentation groupRepresentation = GroupConfigBuilder.create().name(groupName1).setAttributes(attributes1).build();
|
||||
GroupRepresentation groupRepresentation2 = GroupConfigBuilder.create().name(groupName2).setAttributes(attributes2).build();
|
||||
|
||||
String userId = createUser(userRepresentation);
|
||||
|
||||
String group1Id = createGroup(groupRepresentation).getId();
|
||||
String group2Id = createGroup(groupRepresentation2).getId();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
user.joinGroup(group1Id);
|
||||
user.joinGroup(group2Id);
|
||||
|
||||
List<GroupRepresentation> userGroups = user.groups("group2", false);
|
||||
assertFalse(userGroups.isEmpty());
|
||||
assertTrue(userGroups.stream().collect(Collectors.toMap(GroupRepresentation::getName, Function.identity())).get(groupName2).getAttributes().containsKey("attribute2"));
|
||||
|
||||
userGroups = user.groups("group3", false);
|
||||
assertTrue(userGroups.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupMembershipPaginated() {
|
||||
String userId = createUser(UserConfigBuilder.create().username("user-a").build());
|
||||
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
GroupRepresentation group = new GroupRepresentation();
|
||||
group.setName("group-" + i);
|
||||
String groupId = createGroup(group).getId();
|
||||
managedRealm.admin().users().get(userId).joinGroup(groupId);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userGroupPath(userId, groupId), group, ResourceType.GROUP_MEMBERSHIP);
|
||||
}
|
||||
|
||||
List<GroupRepresentation> groups = managedRealm.admin().users().get(userId).groups(5, 6);
|
||||
assertEquals(groups.size(), 5);
|
||||
assertNames(groups, "group-5","group-6","group-7","group-8","group-9");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupMembershipSearch() {
|
||||
String userId = createUser(UserConfigBuilder.create().username("user-b").build());
|
||||
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
GroupRepresentation group = new GroupRepresentation();
|
||||
group.setName("group-" + i);
|
||||
String groupId = createGroup(group).getId();
|
||||
managedRealm.admin().users().get(userId).joinGroup(groupId);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userGroupPath(userId, groupId), group, ResourceType.GROUP_MEMBERSHIP);
|
||||
}
|
||||
|
||||
List<GroupRepresentation> groups = managedRealm.admin().users().get(userId).groups("-3", 0, 10);
|
||||
assertThat(managedRealm.admin().users().get(userId).groupsCount("-3").get("count"), is(1L));
|
||||
assertEquals(1, groups.size());
|
||||
assertNames(groups, "group-3");
|
||||
|
||||
List<GroupRepresentation> groups2 = managedRealm.admin().users().get(userId).groups("1", 0, 10);
|
||||
assertThat(managedRealm.admin().users().get(userId).groupsCount("1").get("count"), is(2L));
|
||||
assertEquals(2, groups2.size());
|
||||
assertNames(groups2, "group-1", "group-10");
|
||||
|
||||
List<GroupRepresentation> groups3 = managedRealm.admin().users().get(userId).groups("1", 2, 10);
|
||||
assertEquals(0, groups3.size());
|
||||
|
||||
List<GroupRepresentation> groups4 = managedRealm.admin().users().get(userId).groups("gr", 2, 10);
|
||||
assertThat(managedRealm.admin().users().get(userId).groupsCount("gr").get("count"), is(10L));
|
||||
assertEquals(8, groups4.size());
|
||||
|
||||
List<GroupRepresentation> groups5 = managedRealm.admin().users().get(userId).groups("Gr", 2, 10);
|
||||
assertEquals(8, groups5.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserWithGroups() {
|
||||
String username = "user-with-groups";
|
||||
String groupToBeAdded = "test-group";
|
||||
|
||||
createGroup(GroupConfigBuilder.create().name(groupToBeAdded).build());
|
||||
|
||||
UserRepresentation build = UserConfigBuilder.create()
|
||||
.username(username)
|
||||
.groups(groupToBeAdded)
|
||||
.build();
|
||||
|
||||
//when
|
||||
String userId = createUser(build);
|
||||
List<GroupRepresentation> obtainedGroups = managedRealm.admin().users().get(userId).groups();
|
||||
|
||||
//then
|
||||
assertEquals(1, obtainedGroups.size());
|
||||
assertEquals(groupToBeAdded, obtainedGroups.get(0).getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for #9482
|
||||
*/
|
||||
@Test
|
||||
public void joinParentGroupAfterSubGroup() {
|
||||
String username = "user-with-sub-and-parent-group";
|
||||
String parentGroupName = "parent-group";
|
||||
String subGroupName = "sub-group";
|
||||
|
||||
UserRepresentation userRepresentation = UserConfigBuilder.create().username(username).build();
|
||||
|
||||
GroupRepresentation subGroupRep = GroupConfigBuilder.create().name(subGroupName).build();
|
||||
GroupRepresentation parentGroupRep = GroupConfigBuilder.create().name(parentGroupName).subGroups(subGroupRep).build();
|
||||
|
||||
String userId = createUser(userRepresentation);
|
||||
|
||||
String subGroupId = createGroup(subGroupRep).getId();
|
||||
String parentGroupId = createGroup(parentGroupRep).getId();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
|
||||
//when
|
||||
user.joinGroup(subGroupId);
|
||||
List<GroupRepresentation> obtainedGroups = managedRealm.admin().users().get(userId).groups();
|
||||
|
||||
//then
|
||||
assertEquals(1, obtainedGroups.size());
|
||||
assertEquals(subGroupName, obtainedGroups.get(0).getName());
|
||||
|
||||
//when
|
||||
user.joinGroup(parentGroupId);
|
||||
obtainedGroups = managedRealm.admin().users().get(userId).groups();
|
||||
|
||||
//then
|
||||
assertEquals(2, obtainedGroups.size());
|
||||
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||
assertEquals(subGroupName, obtainedGroups.get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void joinSubGroupAfterParentGroup() {
|
||||
String username = "user-with-sub-and-parent-group";
|
||||
String parentGroupName = "parent-group";
|
||||
String subGroupName = "sub-group";
|
||||
|
||||
UserRepresentation userRepresentation = UserConfigBuilder.create().username(username).build();
|
||||
GroupRepresentation subGroupRep = GroupConfigBuilder.create().name(subGroupName).build();
|
||||
GroupRepresentation parentGroupRep = GroupConfigBuilder.create().name(parentGroupName).subGroups(subGroupRep).build();
|
||||
|
||||
String userId = createUser(userRepresentation);
|
||||
String subGroupId = createGroup(subGroupRep).getId();
|
||||
String parentGroupId = createGroup(parentGroupRep).getId();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
|
||||
//when
|
||||
user.joinGroup(parentGroupId);
|
||||
List<GroupRepresentation> obtainedGroups = managedRealm.admin().users().get(userId).groups();
|
||||
|
||||
//then
|
||||
assertEquals(1, obtainedGroups.size());
|
||||
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||
|
||||
//when
|
||||
user.joinGroup(subGroupId);
|
||||
obtainedGroups = managedRealm.admin().users().get(userId).groups();
|
||||
|
||||
//then
|
||||
assertEquals(2, obtainedGroups.size());
|
||||
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||
assertEquals(subGroupName, obtainedGroups.get(1).getName());
|
||||
}
|
||||
|
||||
private GroupRepresentation createGroup(GroupRepresentation group) {
|
||||
final String groupId;
|
||||
|
||||
try (Response response = managedRealm.admin().groups().add(group)) {
|
||||
groupId = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.groupPath(groupId), group, ResourceType.GROUP);
|
||||
|
||||
group.setId(groupId);
|
||||
return group;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.tests.admin.user;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
||||
import org.keycloak.representations.idm.UserProfileMetadata;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@KeycloakIntegrationTest
|
||||
public class UserProfileTest extends AbstractUserTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@Test
|
||||
public void testUsernameReadOnlyIfEmailAsUsernameEnabled() {
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||
UserRepresentation user = managedRealm.admin().users().get(userId).toRepresentation(true);
|
||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||
assertNotNull(metadata);
|
||||
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
||||
assertNotNull(username);
|
||||
assertTrue(username.isReadOnly());
|
||||
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
||||
assertNotNull(email);
|
||||
assertFalse(email.isReadOnly());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailNotReadOnlyIfEmailAsUsernameEnabledAndEditUsernameDisabled() {
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
assertFalse(rep.isEditUsernameAllowed());
|
||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||
UserRepresentation user = managedRealm.admin().users().get(userId).toRepresentation(true);
|
||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||
assertNotNull(metadata);
|
||||
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
||||
assertNotNull(username);
|
||||
assertTrue(username.isReadOnly());
|
||||
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
||||
assertNotNull(email);
|
||||
assertFalse(email.isReadOnly());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserProfileMetadata() {
|
||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||
UserRepresentation user = managedRealm.admin().users().get(userId).toRepresentation(true);
|
||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||
assertNotNull(metadata);
|
||||
|
||||
for (String name : managedAttributes) {
|
||||
assertNotNull(metadata.getAttributeMetadata(name));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchBasedOnUserProfileSettings() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("test_username");
|
||||
user.setFirstName("test_first_name");
|
||||
user.setLastName("test_last_name");
|
||||
user.setEmail("test_email@test.com");
|
||||
user.setEnabled(true);
|
||||
user.setEmailVerified(true);
|
||||
createUser(user);
|
||||
|
||||
UPConfig upConfig = managedRealm.admin().users().userProfile().getConfiguration();
|
||||
upConfig.getAttribute(UserModel.FIRST_NAME).setPermissions(new UPAttributePermissions());
|
||||
managedRealm.admin().users().userProfile().update(upConfig);
|
||||
List<UserRepresentation> users = managedRealm.admin().users().list();
|
||||
assertThat(users, hasSize(1));
|
||||
user = users.get(0);
|
||||
assertThat(user.getFirstName(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMaxResults() {
|
||||
UserProfileResource upResource = managedRealm.admin().users().userProfile();
|
||||
UPConfig upConfig = upResource.getConfiguration();
|
||||
upConfig.addOrReplaceAttribute(createAttributeMetadata("aName"));
|
||||
upConfig.getAttribute("aName").setPermissions(new UPAttributePermissions(Set.of("user", "admin"), Set.of("user", "admin")));
|
||||
upResource.update(upConfig);
|
||||
|
||||
try {
|
||||
UsersResource users = managedRealm.admin().users();
|
||||
|
||||
for (int i = 0; i < 110; i++) {
|
||||
users.create(UserConfigBuilder.create().username("test2-" + i).attribute("aName", "aValue").build()).close();
|
||||
}
|
||||
|
||||
List<UserRepresentation> result = users.search("test2", null, null);
|
||||
assertEquals(100, result.size());
|
||||
for (UserRepresentation user : result) {
|
||||
assertThat(user.getAttributes(), Matchers.notNullValue());
|
||||
assertThat(user.getAttributes().keySet(), hasSize(1));
|
||||
assertThat(user.getAttributes(), Matchers.hasEntry(is("aName"), Matchers.contains("aValue")));
|
||||
}
|
||||
|
||||
assertEquals(105, users.search("test2", 0, 105).size());
|
||||
assertEquals(110, users.search("test2", 0, 1000).size());
|
||||
} finally {
|
||||
upConfig.removeAttribute("aName");
|
||||
upResource.update(upConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMaxResultsBrief() {
|
||||
UserProfileResource upResource = managedRealm.admin().users().userProfile();
|
||||
UPConfig upConfig = upResource.getConfiguration();
|
||||
upConfig.addOrReplaceAttribute(createAttributeMetadata("aName"));
|
||||
upConfig.getAttribute("aName").setPermissions(new UPAttributePermissions());
|
||||
upResource.update(upConfig);
|
||||
|
||||
try {
|
||||
UsersResource users = managedRealm.admin().users();
|
||||
|
||||
for (int i = 0; i < 110; i++) {
|
||||
users.create(UserConfigBuilder.create().username("test-" + i).attribute("aName", "aValue").build()).close();
|
||||
}
|
||||
|
||||
List<UserRepresentation> result = users.search("test", null, null, true);
|
||||
assertEquals(100, result.size());
|
||||
for (UserRepresentation user : result) {
|
||||
assertThat(user.getAttributes(), nullValue());
|
||||
}
|
||||
} finally {
|
||||
upConfig.removeAttribute("aName");
|
||||
upResource.update(upConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserRequiredActionsTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void addRequiredAction() {
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
|
||||
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.getRequiredActions().add(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
updateUser(user, userRep);
|
||||
|
||||
assertEquals(1, user.toRepresentation().getRequiredActions().size());
|
||||
assertEquals(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), user.toRepresentation().getRequiredActions().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeRequiredAction() {
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
|
||||
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.getRequiredActions().add(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
updateUser(user, userRep);
|
||||
|
||||
user = managedRealm.admin().users().get(id);
|
||||
userRep = user.toRepresentation();
|
||||
userRep.getRequiredActions().clear();
|
||||
updateUser(user, userRep);
|
||||
|
||||
assertTrue(user.toRepresentation().getRequiredActions().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultRequiredActionAdded() {
|
||||
// Add UPDATE_PASSWORD as default required action
|
||||
RequiredActionProviderRepresentation updatePasswordReqAction = managedRealm.admin().flows().getRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
updatePasswordReqAction.setDefaultAction(true);
|
||||
managedRealm.admin().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
|
||||
|
||||
// Create user
|
||||
String userId = createUser("user1", "user1@localhost");
|
||||
|
||||
UserRepresentation userRep = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
Assertions.assertEquals(1, userRep.getRequiredActions().size());
|
||||
Assertions.assertEquals(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), userRep.getRequiredActions().get(0));
|
||||
|
||||
// Remove UPDATE_PASSWORD default action
|
||||
updatePasswordReqAction = managedRealm.admin().flows().getRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
updatePasswordReqAction.setDefaultAction(false);
|
||||
managedRealm.admin().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,254 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.RoleMappingResource;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.realm.ClientConfigBuilder;
|
||||
import org.keycloak.testframework.realm.GroupConfigBuilder;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.events.TestEventsListenerProviderFactory;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.keycloak.tests.utils.Assert.assertNames;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserRoleTest extends AbstractUserTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@Test
|
||||
public void roleMappings() {
|
||||
RealmResource realm = managedRealm.admin();
|
||||
// Enable events
|
||||
RealmRepresentation realmRep = addTestEventListener(managedRealm.admin().toRepresentation());
|
||||
managedRealm.admin().update(realmRep);
|
||||
|
||||
RoleRepresentation realmCompositeRole = RoleBuilder.create().name("realm-composite").singleAttribute("attribute1", "value1").build();
|
||||
|
||||
realm.roles().create(RoleBuilder.create().name("realm-role").build());
|
||||
realm.roles().create(realmCompositeRole);
|
||||
realm.roles().create(RoleBuilder.create().name("realm-child").build());
|
||||
realm.roles().get("realm-composite").addComposites(Collections.singletonList(realm.roles().get("realm-child").toRepresentation()));
|
||||
|
||||
final String clientUuid;
|
||||
try (Response response = realm.clients().create(ClientConfigBuilder.create().clientId("myclient").build())) {
|
||||
clientUuid = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
RoleRepresentation clientCompositeRole = RoleBuilder.create().name("client-composite").singleAttribute("attribute1", "value1").build();
|
||||
|
||||
|
||||
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(clientCompositeRole);
|
||||
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()));
|
||||
|
||||
final String userId;
|
||||
try (Response response = realm.users().create(UserConfigBuilder.create().username("myuser").build())) {
|
||||
userId = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
// Admin events for creating role, client or user tested already in other places
|
||||
adminEvents.clear();
|
||||
|
||||
RoleMappingResource roles = realm.users().get(userId).roles();
|
||||
assertNames(roles.realmLevel().listAll(), Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertNames(roles.realmLevel().listEffective(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
|
||||
// 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);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), 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);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), 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);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), ResourceType.CLIENT_ROLE_MAPPING);
|
||||
|
||||
// List realm roles
|
||||
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertNames(roles.realmLevel().listAvailable(), "realm-child", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
|
||||
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
|
||||
// List realm effective role with full representation
|
||||
List<RoleRepresentation> realmRolesFullRepresentations = roles.realmLevel().listEffective(false);
|
||||
RoleRepresentation realmCompositeRoleFromList = getRoleByName("realm-composite", realmRolesFullRepresentations);
|
||||
assertNotNull(realmCompositeRoleFromList);
|
||||
assertTrue(realmCompositeRoleFromList.getAttributes().containsKey("attribute1"));
|
||||
|
||||
// List client roles
|
||||
assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite");
|
||||
assertNames(roles.clientLevel(clientUuid).listAvailable(), "client-role2", "client-child");
|
||||
assertNames(roles.clientLevel(clientUuid).listEffective(), "client-role", "client-composite", "client-child");
|
||||
|
||||
// List client effective role with full representation
|
||||
List<RoleRepresentation> rolesFullRepresentations = roles.clientLevel(clientUuid).listEffective(false);
|
||||
RoleRepresentation clientCompositeRoleFromList = getRoleByName("client-composite", rolesFullRepresentations);
|
||||
assertNotNull(clientCompositeRoleFromList);
|
||||
assertTrue(clientCompositeRoleFromList.getAttributes().containsKey("attribute1"));
|
||||
|
||||
// Get mapping representation
|
||||
MappingsRepresentation all = roles.getAll();
|
||||
assertNames(all.getRealmMappings(), "realm-role", "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertEquals(1, all.getClientMappings().size());
|
||||
assertNames(all.getClientMappings().get("myclient").getMappings(), "client-role", "client-composite");
|
||||
|
||||
// Remove realm role
|
||||
RoleRepresentation realmRoleRep = realm.roles().get("realm-role").toRepresentation();
|
||||
roles.realmLevel().remove(Collections.singletonList(realmRoleRep));
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.userRealmRoleMappingsPath(userId), Collections.singletonList(realmRoleRep), ResourceType.REALM_ROLE_MAPPING);
|
||||
|
||||
assertNames(roles.realmLevel().listAll(), "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
|
||||
// Remove client role
|
||||
RoleRepresentation clientRoleRep = realm.clients().get(clientUuid).roles().get("client-role").toRepresentation();
|
||||
roles.clientLevel(clientUuid).remove(Collections.singletonList(clientRoleRep));
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), Collections.singletonList(clientRoleRep), ResourceType.CLIENT_ROLE_MAPPING);
|
||||
|
||||
assertNames(roles.clientLevel(clientUuid).listAll(), "client-composite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for KEYCLOAK-10603.
|
||||
*/
|
||||
@Test
|
||||
public void rolesCanBeAssignedEvenWhenTheyAreAlreadyIndirectlyAssigned() {
|
||||
RealmResource realm = managedRealm.admin();
|
||||
|
||||
RoleRepresentation realmCompositeRole = RoleBuilder.create().name("realm-composite").build();
|
||||
realm.roles().create(realmCompositeRole);
|
||||
realm.roles().create(RoleBuilder.create().name("realm-child").build());
|
||||
realm.roles().get("realm-composite")
|
||||
.addComposites(Collections.singletonList(realm.roles().get("realm-child").toRepresentation()));
|
||||
realm.roles().create(RoleBuilder.create().name("realm-role-in-group").build());
|
||||
|
||||
Response response = realm.clients().create(ClientConfigBuilder.create().clientId("myclient").build());
|
||||
String clientUuid = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
RoleRepresentation clientCompositeRole = RoleBuilder.create().name("client-composite").build();
|
||||
realm.clients().get(clientUuid).roles().create(clientCompositeRole);
|
||||
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()));
|
||||
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role-in-group").build());
|
||||
|
||||
GroupRepresentation group = GroupConfigBuilder.create().name("mygroup").build();
|
||||
response = realm.groups().add(group);
|
||||
String groupId = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
response = realm.users().create(UserConfigBuilder.create().username("myuser").build());
|
||||
String userId = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
// Make indirect assignments
|
||||
// .. add roles to the group and add it to the user
|
||||
realm.groups().group(groupId).roles().realmLevel()
|
||||
.add(Collections.singletonList(realm.roles().get("realm-role-in-group").toRepresentation()));
|
||||
realm.groups().group(groupId).roles().clientLevel(clientUuid).add(Collections
|
||||
.singletonList(realm.clients().get(clientUuid).roles().get("client-role-in-group").toRepresentation()));
|
||||
realm.users().get(userId).joinGroup(groupId);
|
||||
// .. assign composite roles
|
||||
RoleMappingResource userRoles = realm.users().get(userId).roles();
|
||||
userRoles.realmLevel().add(Collections.singletonList(realm.roles().get("realm-composite").toRepresentation()));
|
||||
userRoles.clientLevel(clientUuid).add(Collections
|
||||
.singletonList(realm.clients().get(clientUuid).roles().get("client-composite").toRepresentation()));
|
||||
|
||||
// check state before making the direct assignments
|
||||
assertNames(userRoles.realmLevel().listAll(), "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertNames(userRoles.realmLevel().listAvailable(), "realm-child", "realm-role-in-group", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
|
||||
assertNames(userRoles.realmLevel().listEffective(), "realm-composite", "realm-child", "realm-role-in-group", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION,
|
||||
Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
|
||||
assertNames(userRoles.clientLevel(clientUuid).listAll(), "client-composite");
|
||||
assertNames(userRoles.clientLevel(clientUuid).listAvailable(), "client-child",
|
||||
"client-role-in-group");
|
||||
assertNames(userRoles.clientLevel(clientUuid).listEffective(), "client-composite", "client-child",
|
||||
"client-role-in-group");
|
||||
|
||||
// Make direct assignments for roles which are already indirectly assigned
|
||||
userRoles.realmLevel().add(Collections.singletonList(realm.roles().get("realm-child").toRepresentation()));
|
||||
userRoles.realmLevel()
|
||||
.add(Collections.singletonList(realm.roles().get("realm-role-in-group").toRepresentation()));
|
||||
userRoles.clientLevel(clientUuid).add(Collections
|
||||
.singletonList(realm.clients().get(clientUuid).roles().get("client-child").toRepresentation()));
|
||||
userRoles.clientLevel(clientUuid).add(Collections
|
||||
.singletonList(realm.clients().get(clientUuid).roles().get("client-role-in-group").toRepresentation()));
|
||||
|
||||
// List realm roles
|
||||
assertNames(userRoles.realmLevel().listAll(), "realm-composite",
|
||||
"realm-child", "realm-role-in-group", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertNames(userRoles.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
|
||||
assertNames(userRoles.realmLevel().listEffective(), "realm-composite", "realm-child", "realm-role-in-group",
|
||||
"offline_access", Constants.AUTHZ_UMA_AUTHORIZATION,
|
||||
Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
|
||||
// List client roles
|
||||
assertNames(userRoles.clientLevel(clientUuid).listAll(), "client-composite", "client-child",
|
||||
"client-role-in-group");
|
||||
assertNames(userRoles.clientLevel(clientUuid).listAvailable());
|
||||
assertNames(userRoles.clientLevel(clientUuid).listEffective(), "client-composite", "client-child",
|
||||
"client-role-in-group");
|
||||
|
||||
// Get mapping representation
|
||||
MappingsRepresentation all = userRoles.getAll();
|
||||
assertNames(all.getRealmMappings(), "realm-composite",
|
||||
"realm-child", "realm-role-in-group", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + managedRealm.getName());
|
||||
assertEquals(1, all.getClientMappings().size());
|
||||
assertNames(all.getClientMappings().get("myclient").getMappings(), "client-composite", "client-child",
|
||||
"client-role-in-group");
|
||||
}
|
||||
|
||||
private RoleRepresentation getRoleByName(String name, List<RoleRepresentation> roles) {
|
||||
for(RoleRepresentation role : roles) {
|
||||
if(role.getName().equalsIgnoreCase(name)) {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private RealmRepresentation addTestEventListener(RealmRepresentation rep) {
|
||||
if (rep.getEventsListeners() == null) {
|
||||
rep.setEventsListeners(new LinkedList<String>());
|
||||
}
|
||||
|
||||
if (!rep.getEventsListeners().contains(TestEventsListenerProviderFactory.PROVIDER_ID)) {
|
||||
rep.getEventsListeners().add(TestEventsListenerProviderFactory.PROVIDER_ID);
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,789 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.userprofile.DefaultAttributes;
|
||||
import org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserSearchTest extends AbstractUserTest {
|
||||
|
||||
@Test
|
||||
public void countUsersByEnabledFilter() {
|
||||
|
||||
// create 2 enabled and 1 disabled user
|
||||
UserRepresentation enabledUser1 = new UserRepresentation();
|
||||
enabledUser1.setUsername("enabled1");
|
||||
enabledUser1.setEmail("enabled1@enabledfilter.com");
|
||||
enabledUser1.setEnabled(true);
|
||||
createUser(enabledUser1);
|
||||
|
||||
UserRepresentation enabledUser2 = new UserRepresentation();
|
||||
enabledUser2.setUsername("enabled2");
|
||||
enabledUser2.setEmail("enabled2@enabledfilter.com");
|
||||
enabledUser2.setEnabled(true);
|
||||
createUser(enabledUser2);
|
||||
|
||||
UserRepresentation disabledUser1 = new UserRepresentation();
|
||||
disabledUser1.setUsername("disabled1");
|
||||
disabledUser1.setEmail("disabled1@enabledfilter.com");
|
||||
disabledUser1.setEnabled(false);
|
||||
createUser(disabledUser1);
|
||||
|
||||
Boolean enabled = true;
|
||||
Boolean disabled = false;
|
||||
|
||||
// count all users with @enabledfilter.com
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, "@enabledfilter.com", null, null, null, null), is(3));
|
||||
|
||||
// count users that are enabled and have username enabled1
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, "@enabledfilter.com", null, "enabled1", enabled, null),is(1));
|
||||
|
||||
// count users that are disabled
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, "@enabledfilter.com", null, null, disabled, null), is(1));
|
||||
|
||||
// count users that are enabled
|
||||
assertThat(managedRealm.admin().users().count(null, null, null, "@enabledfilter.com", null, null, enabled, null), is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByEmail() {
|
||||
createUsers();
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search(null, null, null, "user1@localhost", null, null);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().search(null, null, null, "@localhost", null, null);
|
||||
assertEquals(9, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByEmailExactMatch() {
|
||||
createUsers();
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByEmail("user1@localhost", true);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().search("@localhost", true);
|
||||
assertEquals(0, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByUsername() {
|
||||
createUsers();
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("username1", null, null, null, null, null);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().search("user", null, null, null, null, null);
|
||||
assertEquals(9, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByAttribute() {
|
||||
createUsers();
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("test", "test1");
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(attributes));
|
||||
assertEquals(1, users.size());
|
||||
|
||||
attributes.clear();
|
||||
attributes.put("attr", "common");
|
||||
|
||||
users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(attributes));
|
||||
assertEquals(9, users.size());
|
||||
|
||||
attributes.clear();
|
||||
attributes.put("x", "common");
|
||||
users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(attributes));
|
||||
assertEquals(0, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByMultipleAttributes() {
|
||||
createUsers();
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("username", "user", "test", "test1", "attr", "common", "test1", "test1")));
|
||||
assertThat(users, hasSize(1));
|
||||
|
||||
//custom user attribute should not use wildcard search by default
|
||||
users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("username", "user", "test", "est", "attr", "mm", "test1", "test1")));
|
||||
assertThat(users, hasSize(0));
|
||||
|
||||
//custom user attribute should use wildcard
|
||||
users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("username", "user", "test", "est", "attr", "mm", "test1", "test1")), false);
|
||||
assertThat(users, hasSize(1));
|
||||
|
||||
//with exact=true the user shouldn't be returned
|
||||
users = managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("test", "est", "attr", "mm", "test1", "test1")), Boolean.TRUE);
|
||||
assertThat(users, hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByAttributesWithPagination() {
|
||||
createUsers();
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("attr", "Common");
|
||||
for (int i = 1; i < 10; i++) {
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByAttributes(i - 1, 1, null, false, mapToSearchQuery(attributes));
|
||||
assertEquals(1, users.size());
|
||||
assertTrue(users.get(0).getAttributes().keySet().stream().anyMatch(attributes::containsKey));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeAndReadUserWithLongAttributeValue() {
|
||||
String longValue = RandomStringUtils.random(Integer.parseInt(DefaultAttributes.DEFAULT_MAX_LENGTH_ATTRIBUTES), true, true);
|
||||
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").password("password").name("user1FirstName", "user1LastName")
|
||||
.email("user1@example.com").emailVerified().attribute("attr", longValue).build();
|
||||
String userId = createUser(userRep);
|
||||
|
||||
UserRepresentation user1 = managedRealm.admin().users().get(userId).toRepresentation();
|
||||
|
||||
Assertions.assertNotNull(user1);
|
||||
assertThat(user1.getAttributes().get("attr").get(0), equalTo(longValue));
|
||||
|
||||
UserRepresentation userRep2 = UserConfigBuilder.create()
|
||||
.username("user2").password("password").name("user2FirstName", "user2LastName")
|
||||
.email("user2@example.com").emailVerified().attribute("attr", longValue + "a").build();
|
||||
|
||||
Response response = managedRealm.admin().users().create(userRep2);
|
||||
assertThat(response.getStatus(), equalTo(400));
|
||||
assertThat(response.readEntity(ErrorRepresentation.class).getErrorMessage(), equalTo("error-invalid-length"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByLongAttributes() {
|
||||
// random string with suffix that makes it case-sensitive and distinct
|
||||
String longValue = RandomStringUtils.random(Integer.parseInt(DefaultAttributes.DEFAULT_MAX_LENGTH_ATTRIBUTES) - 1, true, true) + "u";
|
||||
String longValue2 = RandomStringUtils.random(Integer.parseInt(DefaultAttributes.DEFAULT_MAX_LENGTH_ATTRIBUTES) - 1, true, true) + "v";
|
||||
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user1").password("password").name("user1FirstName", "user1LastName")
|
||||
.email("user1@example.com").emailVerified()
|
||||
.attribute("test1", longValue, "v2").attribute("test2", "v2").build();
|
||||
UserRepresentation userRep2 = UserConfigBuilder.create()
|
||||
.username("user2").password("password").name("user2FirstName", "user2LastName")
|
||||
.email("user2@example.com").emailVerified()
|
||||
.attribute("test1", longValue, "v2").attribute("test2", longValue2).build();
|
||||
UserRepresentation userRep3 = UserConfigBuilder.create()
|
||||
.username("user3").password("password").name("user3FirstName", "user3LastName")
|
||||
.email("user3@example.com").emailVerified()
|
||||
.attribute("test2", longValue, "v3").attribute("test4", "v4").build();
|
||||
|
||||
createUser(userRep);
|
||||
createUser(userRep2);
|
||||
createUser(userRep3);
|
||||
|
||||
assertThat(managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
|
||||
containsInAnyOrder("user1", "user2"));
|
||||
assertThat(managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue, "test2", longValue2))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
|
||||
contains("user2"));
|
||||
|
||||
//case-insensitive search
|
||||
assertThat(managedRealm.admin().users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue, "test2", longValue2.toLowerCase(Locale.ENGLISH)))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
|
||||
contains("user2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByUsernameExactMatch() {
|
||||
createUsers();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("username11");
|
||||
|
||||
createUser(user);
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("username1", true);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().searchByUsername("username1", true);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().search("user", true);
|
||||
assertEquals(0, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByFirstNameExact() {
|
||||
createUsers();
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByFirstName("First1", true);
|
||||
assertEquals(1, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByLastNameExact() {
|
||||
createUsers();
|
||||
List<UserRepresentation> users = managedRealm.admin().users().searchByLastName("Last1", true);
|
||||
assertEquals(1, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByFirstNameNullForLastName() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1");
|
||||
user.setFirstName("Erik");
|
||||
user.setRequiredActions(Collections.emptyList());
|
||||
user.setEnabled(true);
|
||||
|
||||
createUser(user);
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("Erik", 0, 50);
|
||||
assertEquals(1, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByLastNameNullForFirstName() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user1");
|
||||
user.setLastName("de Wit");
|
||||
user.setRequiredActions(Collections.emptyList());
|
||||
user.setEnabled(true);
|
||||
|
||||
createUser(user);
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("*wit*", null, null);
|
||||
assertEquals(1, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByEnabled() {
|
||||
String userCommonName = "enabled-disabled-user";
|
||||
|
||||
UserRepresentation user1 = new UserRepresentation();
|
||||
user1.setUsername(userCommonName + "1");
|
||||
user1.setRequiredActions(Collections.emptyList());
|
||||
user1.setEnabled(true);
|
||||
createUser(user1);
|
||||
|
||||
UserRepresentation user2 = new UserRepresentation();
|
||||
user2.setUsername(userCommonName + "2");
|
||||
user2.setRequiredActions(Collections.emptyList());
|
||||
user2.setEnabled(false);
|
||||
createUser(user2);
|
||||
|
||||
List<UserRepresentation> enabledUsers = managedRealm.admin().users().search(null, null, null, null, null, null, true, false);
|
||||
assertEquals(1, enabledUsers.size());
|
||||
|
||||
List<UserRepresentation> enabledUsersWithFilter = managedRealm.admin().users().search(userCommonName, null, null, null, null, null, true, true);
|
||||
assertEquals(1, enabledUsersWithFilter.size());
|
||||
assertEquals(user1.getUsername(), enabledUsersWithFilter.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> disabledUsers = managedRealm.admin().users().search(userCommonName, null, null, null, null, null, false, false);
|
||||
assertEquals(1, disabledUsers.size());
|
||||
assertEquals(user2.getUsername(), disabledUsers.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> allUsers = managedRealm.admin().users().search(userCommonName, null, null, null, 0, 100, null, true);
|
||||
assertEquals(2, allUsers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchWithFilters() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user2");
|
||||
user.setFirstName("First");
|
||||
user.setLastName("Last");
|
||||
user.setEmail("user2@localhost");
|
||||
user.setRequiredActions(Collections.emptyList());
|
||||
user.setEnabled(false);
|
||||
createUser(user);
|
||||
|
||||
List<UserRepresentation> searchFirstNameAndDisabled = managedRealm.admin().users().search(null, "First", null, null, null, null, false, true);
|
||||
assertEquals(1, searchFirstNameAndDisabled.size());
|
||||
assertEquals(user.getUsername(), searchFirstNameAndDisabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchLastNameAndEnabled = managedRealm.admin().users().search(null, null, "Last", null, null, null, true, false);
|
||||
assertEquals(0, searchLastNameAndEnabled.size());
|
||||
|
||||
List<UserRepresentation> searchEmailAndDisabled = managedRealm.admin().users().search(null, null, null, "user2@localhost", 0, 50, false, true);
|
||||
assertEquals(1, searchEmailAndDisabled.size());
|
||||
assertEquals(user.getUsername(), searchEmailAndDisabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchInvalidSizeAndDisabled = managedRealm.admin().users().search(null, null, null, null, 10, 20, null, false);
|
||||
assertEquals(0, searchInvalidSizeAndDisabled.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchWithFilterAndEnabledAttribute() {
|
||||
createUser();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("user3");
|
||||
user.setFirstName("user3First");
|
||||
user.setLastName("user3Last");
|
||||
user.setEmail("user3@localhost");
|
||||
user.setRequiredActions(Collections.emptyList());
|
||||
user.setEnabled(false);
|
||||
createUser(user);
|
||||
|
||||
List<UserRepresentation> searchFilterUserNameAndDisabled = managedRealm.admin().users().search("user3", false, 0, 5);
|
||||
assertEquals(1, searchFilterUserNameAndDisabled.size());
|
||||
assertEquals(user.getUsername(), searchFilterUserNameAndDisabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchFilterMailAndDisabled = managedRealm.admin().users().search("user3@localhost", false, 0, 5);
|
||||
assertEquals(1, searchFilterMailAndDisabled.size());
|
||||
assertEquals(user.getUsername(), searchFilterMailAndDisabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchFilterLastNameAndEnabled = managedRealm.admin().users().search("user3Last", true, 0, 5);
|
||||
assertEquals(0, searchFilterLastNameAndEnabled.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByIdp() {
|
||||
// Add user without IDP
|
||||
createUser();
|
||||
|
||||
// add sample Identity Providers
|
||||
final String identityProviderAlias1 = "identity-provider-alias1";
|
||||
addSampleIdentityProvider(identityProviderAlias1, 0);
|
||||
final String identityProviderAlias2 = "identity-provider-alias2";
|
||||
addSampleIdentityProvider(identityProviderAlias2, 1);
|
||||
|
||||
final String commonIdpUserId = "commonIdpUserId";
|
||||
|
||||
// create first IDP1 User with link
|
||||
final String idp1User1Username = "idp1user1";
|
||||
final String idp1User1KeycloakId = createUser(idp1User1Username, "idp1user1@localhost");
|
||||
final String idp1User1UserId = "idp1user1Id";
|
||||
FederatedIdentityRepresentation link1_1 = new FederatedIdentityRepresentation();
|
||||
link1_1.setUserId(idp1User1UserId);
|
||||
link1_1.setUserName(idp1User1Username);
|
||||
addFederatedIdentity(idp1User1KeycloakId, identityProviderAlias1, link1_1);
|
||||
|
||||
// create second IDP1 User with link
|
||||
final String idp1User2Username = "idp1user2";
|
||||
final String idp1User2KeycloakId = createUser(idp1User2Username, "idp1user2@localhost");
|
||||
FederatedIdentityRepresentation link1_2 = new FederatedIdentityRepresentation();
|
||||
link1_2.setUserId(commonIdpUserId);
|
||||
link1_2.setUserName(idp1User2Username);
|
||||
addFederatedIdentity(idp1User2KeycloakId, identityProviderAlias1, link1_2);
|
||||
|
||||
// create IDP2 user with link
|
||||
final String idp2UserUsername = "idp2user";
|
||||
final String idp2UserKeycloakId = createUser(idp2UserUsername, "idp2user@localhost");
|
||||
FederatedIdentityRepresentation link2 = new FederatedIdentityRepresentation();
|
||||
link2.setUserId(commonIdpUserId);
|
||||
link2.setUserName(idp2UserUsername);
|
||||
addFederatedIdentity(idp2UserKeycloakId, identityProviderAlias2, link2);
|
||||
|
||||
// run search tests
|
||||
List<UserRepresentation> searchForAllUsers =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, null, null, null, null, null, null);
|
||||
assertEquals(4, searchForAllUsers.size());
|
||||
|
||||
List<UserRepresentation> searchByIdpAlias =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, identityProviderAlias1, null, null, null, null,
|
||||
null);
|
||||
assertEquals(2, searchByIdpAlias.size());
|
||||
assertEquals(idp1User1Username, searchByIdpAlias.get(0).getUsername());
|
||||
assertEquals(idp1User2Username, searchByIdpAlias.get(1).getUsername());
|
||||
|
||||
List<UserRepresentation> searchByIdpUserId =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, null, commonIdpUserId, null, null, null, null);
|
||||
assertEquals(2, searchByIdpUserId.size());
|
||||
assertEquals(idp1User2Username, searchByIdpUserId.get(0).getUsername());
|
||||
assertEquals(idp2UserUsername, searchByIdpUserId.get(1).getUsername());
|
||||
|
||||
List<UserRepresentation> searchByIdpAliasAndUserId =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, identityProviderAlias1, idp1User1UserId, null, null,
|
||||
null,
|
||||
null);
|
||||
assertEquals(1, searchByIdpAliasAndUserId.size());
|
||||
assertEquals(idp1User1Username, searchByIdpAliasAndUserId.get(0).getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchByIdpAndEnabled() {
|
||||
// add sample Identity Provider
|
||||
final String identityProviderAlias = "identity-provider-alias";
|
||||
addSampleIdentityProvider(identityProviderAlias, 0);
|
||||
|
||||
// add disabled user with IDP link
|
||||
UserRepresentation disabledUser = new UserRepresentation();
|
||||
final String disabledUsername = "disabled_username";
|
||||
disabledUser.setUsername(disabledUsername);
|
||||
disabledUser.setEmail("disabled@localhost");
|
||||
disabledUser.setEnabled(false);
|
||||
final String disabledUserKeycloakId = createUser(disabledUser);
|
||||
FederatedIdentityRepresentation disabledUserLink = new FederatedIdentityRepresentation();
|
||||
final String disabledUserId = "disabledUserId";
|
||||
disabledUserLink.setUserId(disabledUserId);
|
||||
disabledUserLink.setUserName(disabledUsername);
|
||||
addFederatedIdentity(disabledUserKeycloakId, identityProviderAlias, disabledUserLink);
|
||||
|
||||
// add enabled user with IDP link
|
||||
UserRepresentation enabledUser = new UserRepresentation();
|
||||
final String enabledUsername = "enabled_username";
|
||||
enabledUser.setUsername(enabledUsername);
|
||||
enabledUser.setEmail("enabled@localhost");
|
||||
enabledUser.setEnabled(true);
|
||||
final String enabledUserKeycloakId = createUser(enabledUser);
|
||||
FederatedIdentityRepresentation enabledUserLink = new FederatedIdentityRepresentation();
|
||||
final String enabledUserId = "enabledUserId";
|
||||
enabledUserLink.setUserId(enabledUserId);
|
||||
enabledUserLink.setUserName(enabledUsername);
|
||||
addFederatedIdentity(enabledUserKeycloakId, identityProviderAlias, enabledUserLink);
|
||||
|
||||
// run search tests
|
||||
List<UserRepresentation> searchByIdpAliasAndEnabled =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, identityProviderAlias, null, null, null, true, null);
|
||||
assertEquals(1, searchByIdpAliasAndEnabled.size());
|
||||
assertEquals(enabledUsername, searchByIdpAliasAndEnabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchByIdpAliasAndDisabled =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, identityProviderAlias, null, null, null, false,
|
||||
null);
|
||||
assertEquals(1, searchByIdpAliasAndDisabled.size());
|
||||
assertEquals(disabledUsername, searchByIdpAliasAndDisabled.get(0).getUsername());
|
||||
|
||||
List<UserRepresentation> searchByIdpAliasWithoutEnabledFlag =
|
||||
managedRealm.admin().users().search(null, null, null, null, null, identityProviderAlias, null, null, null, null, null);
|
||||
assertEquals(2, searchByIdpAliasWithoutEnabledFlag.size());
|
||||
assertEquals(disabledUsername, searchByIdpAliasWithoutEnabledFlag.get(0).getUsername());
|
||||
assertEquals(enabledUsername, searchByIdpAliasWithoutEnabledFlag.get(1).getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchById() {
|
||||
List<String> userIds = createUsers();
|
||||
String expectedUserId = userIds.get(0);
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("id:" + expectedUserId, null, null);
|
||||
|
||||
assertEquals(1, users.size());
|
||||
assertEquals(expectedUserId, users.get(0).getId());
|
||||
|
||||
users = managedRealm.admin().users().search("id: " + expectedUserId + " ", null, null);
|
||||
|
||||
assertEquals(1, users.size());
|
||||
assertEquals(expectedUserId, users.get(0).getId());
|
||||
|
||||
// Should allow searching for multiple users
|
||||
String expectedUserId2 = userIds.get(1);
|
||||
List<UserRepresentation> multipleUsers = managedRealm.admin().users().search(String.format("id:%s %s", expectedUserId, expectedUserId2), 0 , 10);;
|
||||
assertThat(multipleUsers, hasSize(2));
|
||||
assertThat(multipleUsers.get(0).getId(), is(expectedUserId));
|
||||
assertThat(multipleUsers.get(1).getId(), is(expectedUserId2));
|
||||
|
||||
// Should take arbitrary amount of spaces in between ids
|
||||
List<UserRepresentation> multipleUsers2 = managedRealm.admin().users().search(String.format("id: %s %s ", expectedUserId, expectedUserId2), 0 , 10);;
|
||||
assertThat(multipleUsers2, hasSize(2));
|
||||
assertThat(multipleUsers2.get(0).getId(), is(expectedUserId));
|
||||
assertThat(multipleUsers2.get(1).getId(), is(expectedUserId2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void infixSearch() {
|
||||
List<String> userIds = createUsers();
|
||||
|
||||
// Username search
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("*1*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("*y*", null, null);
|
||||
assertThat(users.size(), is(0));
|
||||
|
||||
users = managedRealm.admin().users().search("*name*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("**", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
// First/Last name search
|
||||
users = managedRealm.admin().users().search("*first1*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("*last*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
// Email search
|
||||
users = managedRealm.admin().users().search("*@localhost*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("*1@local*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixSearch() {
|
||||
List<String> userIds = createUsers();
|
||||
|
||||
// Username search
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("user", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("user*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("name", null, null);
|
||||
assertThat(users, hasSize(0));
|
||||
|
||||
users = managedRealm.admin().users().search("name*", null, null);
|
||||
assertThat(users, hasSize(0));
|
||||
|
||||
users = managedRealm.admin().users().search("username1", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("username1*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search(null, null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
// First/Last name search
|
||||
users = managedRealm.admin().users().search("first1", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("first1*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("last", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
users = managedRealm.admin().users().search("last*", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
|
||||
// Email search
|
||||
users = managedRealm.admin().users().search("user1@local", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("user1@local*", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circumfixSearch() {
|
||||
createUsers();
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("u*name", null, null);
|
||||
assertThat(users, hasSize(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wildcardSearch() {
|
||||
UserProfileResource upResource = managedRealm.admin().users().userProfile();
|
||||
UPConfig upConfig = upResource.getConfiguration();
|
||||
Map<String, Object> prohibitedCharsOrigCfg = upConfig.getAttribute(UserModel.USERNAME).getValidations().get(UsernameProhibitedCharactersValidator.ID);
|
||||
upConfig.getAttribute(UserModel.USERNAME).getValidations().remove(UsernameProhibitedCharactersValidator.ID);
|
||||
upResource.update(upConfig);
|
||||
adminEvents.clear();
|
||||
|
||||
try {
|
||||
createUser("0user\\\\0", "email0@emal");
|
||||
createUser("1user\\\\", "email1@emal");
|
||||
createUser("2user\\\\%", "email2@emal");
|
||||
createUser("3user\\\\*", "email3@emal");
|
||||
createUser("4user\\\\_", "email4@emal");
|
||||
|
||||
assertThat(managedRealm.admin().users().search("*", null, null), hasSize(5));
|
||||
assertThat(managedRealm.admin().users().search("*user\\", null, null), hasSize(5));
|
||||
assertThat(managedRealm.admin().users().search("\"2user\\\\%\"", null, null), hasSize(1));
|
||||
} finally {
|
||||
upConfig.getAttribute(UserModel.USERNAME).addValidation(UsernameProhibitedCharactersValidator.ID, prohibitedCharsOrigCfg);
|
||||
upResource.update(upConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactSearch() {
|
||||
List<String> userIds = createUsers();
|
||||
|
||||
// Username search
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("\"username1\"", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
users = managedRealm.admin().users().search("\"user\"", null, null);
|
||||
assertThat(users, hasSize(0));
|
||||
|
||||
users = managedRealm.admin().users().search("\"\"", null, null);
|
||||
assertThat(users, hasSize(0));
|
||||
|
||||
// First/Last name search
|
||||
users = managedRealm.admin().users().search("\"first1\"", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
|
||||
// Email search
|
||||
users = managedRealm.admin().users().search("\"user1@localhost\"", null, null);
|
||||
assertThat(users, hasSize(1));
|
||||
assertThat(userIds.get(0), equalTo(users.get(0).getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchWithExactMatch() {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("test_username");
|
||||
user.setFirstName("test_first_name");
|
||||
user.setLastName("test_last_name");
|
||||
user.setEmail("test_email@test.com");
|
||||
user.setEnabled(true);
|
||||
user.setEmailVerified(true);
|
||||
createUser(user);
|
||||
|
||||
UserRepresentation user2 = new UserRepresentation();
|
||||
user2.setUsername("test_username2");
|
||||
user2.setFirstName("test_first_name2");
|
||||
user2.setLastName("test_last_name");
|
||||
user2.setEmail("test_email@test.com2");
|
||||
user2.setEnabled(true);
|
||||
user2.setEmailVerified(true);
|
||||
createUser(user2);
|
||||
|
||||
UserRepresentation user3 = new UserRepresentation();
|
||||
user3.setUsername("test_username3");
|
||||
user3.setFirstName("test_first_name");
|
||||
user3.setLastName("test_last_name3");
|
||||
user3.setEmail("test_email@test.com3");
|
||||
user3.setEnabled(true);
|
||||
user3.setEmailVerified(true);
|
||||
createUser(user3);
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search(
|
||||
null, null, null, "test_email@test.co",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(0, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, null, null, "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(1, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, null, "test_last", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(0, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, null, "test_last_name", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(1, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, "test_first", "test_last_name", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(0, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, "test_first_name", "test_last_name", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(1, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
"test_usernam", "test_first_name", "test_last_name", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(0, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
"test_username", "test_first_name", "test_last_name", "test_email@test.com",
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(1, users.size());
|
||||
|
||||
users = managedRealm.admin().users().search(
|
||||
null, null, "test_last_name", null,
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(2, users.size());
|
||||
users = managedRealm.admin().users().search(
|
||||
null, "test_first_name", null, null,
|
||||
0, 10, null, null, true
|
||||
);
|
||||
assertEquals(2, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countUsersNotServiceAccount() {
|
||||
createUsers();
|
||||
|
||||
Integer count = managedRealm.admin().users().count();
|
||||
assertEquals(9, count.intValue());
|
||||
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
|
||||
client.setClientId("test-client");
|
||||
client.setPublicClient(false);
|
||||
client.setSecret("secret");
|
||||
client.setServiceAccountsEnabled(true);
|
||||
client.setEnabled(true);
|
||||
client.setRedirectUris(Arrays.asList("http://url"));
|
||||
|
||||
String clientId = ApiUtil.getCreatedId(managedRealm.admin().clients().create(client));
|
||||
|
||||
// KEYCLOAK-5660, should not consider service accounts
|
||||
assertEquals(9, managedRealm.admin().users().count().intValue());
|
||||
|
||||
// client cleanup
|
||||
managedRealm.admin().clients().get(clientId).remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchPaginated() {
|
||||
createUsers();
|
||||
|
||||
List<UserRepresentation> users = managedRealm.admin().users().search("username", 0, 1);
|
||||
assertEquals(1, users.size());
|
||||
assertEquals("username1", users.get(0).getUsername());
|
||||
|
||||
users = managedRealm.admin().users().search("username", 5, 2);
|
||||
assertEquals(2, users.size());
|
||||
assertEquals("username6", users.get(0).getUsername());
|
||||
assertEquals("username7", users.get(1).getUsername());
|
||||
|
||||
users = managedRealm.admin().users().search("username", 7, 20);
|
||||
assertEquals(2, users.size());
|
||||
assertEquals("username8", users.get(0).getUsername());
|
||||
assertEquals("username9", users.get(1).getUsername());
|
||||
|
||||
users = managedRealm.admin().users().search("username", 0, 20);
|
||||
assertEquals(9, users.size());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,340 @@
|
||||
package org.keycloak.tests.admin.user;
|
||||
|
||||
import io.restassured.internal.common.assertion.Assertion;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.ClientErrorException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.crypto.hash.Argon2Parameters;
|
||||
import org.keycloak.crypto.hash.Argon2PasswordHashProviderFactory;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class UserUpdateTest extends AbstractUserTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@InjectOAuthClient
|
||||
OAuthClient oauth;
|
||||
|
||||
@Test
|
||||
public void updateUserWithHashedCredentials() {
|
||||
UserRepresentation userRep = UserConfigBuilder.create()
|
||||
.username("user_hashed_creds").name("Hashed", "User").email("user_hashed_creds@localhost").build();
|
||||
|
||||
String userId = createUser(userRep);
|
||||
|
||||
byte[] salt = new byte[]{-69, 85, 87, 99, 26, -107, 125, 99, -77, 30, -111, 118, 108, 100, -117, -56};
|
||||
|
||||
PasswordCredentialModel credentialModel = PasswordCredentialModel.createFromValues("pbkdf2-sha256", salt,
|
||||
27500, "uskEPZWMr83pl2mzNB95SFXfIabe2UH9ClENVx/rrQqOjFEjL2aAOGpWsFNNF3qoll7Qht2mY5KxIDm3Rnve2w==");
|
||||
credentialModel.setCreatedDate(1001L);
|
||||
CredentialRepresentation hashedPassword = ModelToRepresentation.toRepresentation(credentialModel);
|
||||
|
||||
UserRepresentation userRepresentation = new UserRepresentation();
|
||||
userRepresentation.setCredentials(Collections.singletonList(hashedPassword));
|
||||
|
||||
managedRealm.admin().users().get(userId).update(userRepresentation);
|
||||
|
||||
oauth.openLoginForm();
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
loginPage.fillLogin("user_hashed_creds", "admin");
|
||||
loginPage.submit();
|
||||
|
||||
assertTrue(driver.getPageSource().contains("Happy days"));
|
||||
|
||||
AccountHelper.logout(managedRealm.admin(), "user_hashed_creds");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsername() {
|
||||
switchEditUsernameAllowedOn(true);
|
||||
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user11");
|
||||
updateUser(user, userRep);
|
||||
|
||||
userRep = managedRealm.admin().users().get(id).toRepresentation();
|
||||
Assertions.assertEquals("user11", userRep.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithoutUsername() {
|
||||
switchEditUsernameAllowedOn(true);
|
||||
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
|
||||
UserRepresentation rep = new UserRepresentation();
|
||||
rep.setFirstName("Firstname");
|
||||
|
||||
user.update(rep);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep, ResourceType.USER);
|
||||
|
||||
rep = new UserRepresentation();
|
||||
rep.setLastName("Lastname");
|
||||
|
||||
user.update(rep);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep, ResourceType.USER);
|
||||
|
||||
rep = managedRealm.admin().users().get(id).toRepresentation();
|
||||
|
||||
Assertions.assertEquals("user1", rep.getUsername());
|
||||
Assertions.assertEquals("user1@localhost", rep.getEmail());
|
||||
Assertions.assertEquals("Firstname", rep.getFirstName());
|
||||
Assertions.assertEquals("Lastname", rep.getLastName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithEmailAsUsernameEditUsernameDisabled() {
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
assertFalse(rep.isEditUsernameAllowed());
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
Assertions.assertEquals("user1@localhost", userRep.getUsername());
|
||||
|
||||
userRep.setEmail("user11@localhost");
|
||||
updateUser(user, userRep);
|
||||
|
||||
userRep = managedRealm.admin().users().get(id).toRepresentation();
|
||||
Assertions.assertEquals("user11@localhost", userRep.getUsername());
|
||||
Assertions.assertEquals("user11@localhost", userRep.getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithEmailAsUsernameEditUsernameAllowed() {
|
||||
switchRegistrationEmailAsUsername(true);
|
||||
switchEditUsernameAllowedOn(true);
|
||||
|
||||
String id = createUser();
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
Assertions.assertEquals("user1@localhost", userRep.getUsername());
|
||||
|
||||
userRep.setEmail("user11@localhost");
|
||||
updateUser(user, userRep);
|
||||
|
||||
userRep = managedRealm.admin().users().get(id).toRepresentation();
|
||||
Assertions.assertEquals("user11@localhost", userRep.getUsername());
|
||||
Assertions.assertEquals("user11@localhost", userRep.getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithExistingEmail() {
|
||||
final String userId = createUser();
|
||||
assertNotNull(userId);
|
||||
assertNotNull(createUser("user2", "user2@localhost"));
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(userId);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
assertNotNull(userRep);
|
||||
userRep.setEmail("user2@localhost");
|
||||
|
||||
try {
|
||||
updateUser(user, userRep);
|
||||
fail("Expected failure - Email conflict");
|
||||
} catch (ClientErrorException e) {
|
||||
assertNotNull(e.getResponse());
|
||||
assertThat(e.getResponse().getStatus(), is(409));
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("User exists with same email", error.getErrorMessage());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsernameNotPossible() {
|
||||
RealmRepresentation realmRep = managedRealm.admin().toRepresentation();
|
||||
assertFalse(realmRep.isEditUsernameAllowed());
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user11");
|
||||
|
||||
try {
|
||||
updateUser(user, userRep);
|
||||
fail("Should fail because realm does not allow edit username");
|
||||
} catch (BadRequestException expected) {
|
||||
ErrorRepresentation error = expected.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assertions.assertEquals("error-user-attribute-read-only", error.getErrorMessage());
|
||||
}
|
||||
|
||||
userRep = managedRealm.admin().users().get(id).toRepresentation();
|
||||
Assertions.assertEquals("user1", userRep.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsernameAccessingViaOldUsername() {
|
||||
switchEditUsernameAllowedOn(true);
|
||||
createUser();
|
||||
|
||||
try {
|
||||
UserResource user = managedRealm.admin().users().get("user1");
|
||||
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user1");
|
||||
updateUser(user, userRep);
|
||||
|
||||
managedRealm.admin().users().get("user11").toRepresentation();
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
Assertions.assertEquals(404, e.getResponse().getStatus());
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
} 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 = managedRealm.admin().users().get(createdId);
|
||||
userRep = user.toRepresentation();
|
||||
userRep.setUsername("user1");
|
||||
user.update(userRep);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
Assertions.assertEquals(409, e.getResponse().getStatus());
|
||||
|
||||
Assertions.assertNull(adminEvents.poll());
|
||||
} finally {
|
||||
enableBruteForce(false);
|
||||
switchEditUsernameAllowedOn(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithRawCredentials() {
|
||||
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));
|
||||
|
||||
String id = createUser(user);
|
||||
|
||||
PasswordCredentialModel credential = PasswordCredentialModel
|
||||
.createFromCredentialModel(fetchCredentials("user_rawpw"));
|
||||
assertNotNull(credential, "Expecting credential");
|
||||
assertEquals(Argon2PasswordHashProviderFactory.ID, credential.getPasswordCredentialData().getAlgorithm());
|
||||
assertEquals(Argon2Parameters.DEFAULT_ITERATIONS, credential.getPasswordCredentialData().getHashIterations());
|
||||
assertNotEquals("ABCD", credential.getPasswordSecretData().getValue());
|
||||
Assertions.assertEquals(CredentialRepresentation.PASSWORD, credential.getType());
|
||||
|
||||
UserResource userResource = managedRealm.admin().users().get(id);
|
||||
UserRepresentation userRep = userResource.toRepresentation();
|
||||
|
||||
CredentialRepresentation rawPasswordForUpdate = new CredentialRepresentation();
|
||||
rawPasswordForUpdate.setValue("EFGH");
|
||||
rawPasswordForUpdate.setType(CredentialRepresentation.PASSWORD);
|
||||
userRep.setCredentials(Arrays.asList(rawPasswordForUpdate));
|
||||
|
||||
updateUser(userResource, userRep);
|
||||
|
||||
PasswordCredentialModel updatedCredential = PasswordCredentialModel
|
||||
.createFromCredentialModel(fetchCredentials("user_rawpw"));
|
||||
assertNotNull(updatedCredential, "Expecting credential");
|
||||
assertEquals(Argon2PasswordHashProviderFactory.ID, updatedCredential.getPasswordCredentialData().getAlgorithm());
|
||||
assertEquals(Argon2Parameters.DEFAULT_ITERATIONS, updatedCredential.getPasswordCredentialData().getHashIterations());
|
||||
assertNotEquals("EFGH", updatedCredential.getPasswordSecretData().getValue());
|
||||
Assertions.assertEquals(CredentialRepresentation.PASSWORD, updatedCredential.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessUserFromOtherRealm() {
|
||||
RealmRepresentation firstRealm = new RealmRepresentation();
|
||||
|
||||
firstRealm.setRealm("first-realm");
|
||||
|
||||
adminClient.realms().create(firstRealm);
|
||||
|
||||
UserRepresentation firstUser = new UserRepresentation();
|
||||
|
||||
firstUser.setUsername("first");
|
||||
firstUser.setEmail("first@first-realm.org");
|
||||
|
||||
firstUser.setId(ApiUtil.getCreatedId(adminClient.realm(firstRealm.getRealm()).users().create(firstUser)));
|
||||
|
||||
RealmRepresentation secondRealm = new RealmRepresentation();
|
||||
|
||||
secondRealm.setRealm("second-realm");
|
||||
|
||||
adminClient.realms().create(secondRealm);
|
||||
|
||||
adminClient.realm(firstRealm.getRealm()).users().get(firstUser.getId()).update(firstUser);
|
||||
|
||||
try {
|
||||
adminClient.realm(secondRealm.getRealm()).users().get(firstUser.getId()).toRepresentation();
|
||||
fail("Should not have access to firstUser from another realm");
|
||||
} catch (NotFoundException nfe) {
|
||||
// ignore
|
||||
} finally {
|
||||
adminClient.realm(secondRealm.getRealm()).remove();
|
||||
adminClient.realm(firstRealm.getRealm()).remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void enableBruteForce(boolean enable) {
|
||||
RealmRepresentation rep = managedRealm.admin().toRepresentation();
|
||||
managedRealm.cleanup().add(r -> r.update(rep));
|
||||
rep.setBruteForceProtected(enable);
|
||||
managedRealm.admin().update(rep);
|
||||
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
|
||||
}
|
||||
}
|
||||
@ -48,5 +48,13 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-storage</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-storage-private</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerTransaction;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class TestEventsListenerProvider implements EventListenerProvider {
|
||||
|
||||
private static final BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
|
||||
private static final BlockingQueue<AdminEvent> adminEvents = new LinkedBlockingQueue<>();
|
||||
private final EventListenerTransaction tx = new EventListenerTransaction((event, includeRepre) -> adminEvents.add(event), events::add);
|
||||
|
||||
public TestEventsListenerProvider(KeycloakSession session) {
|
||||
session.getTransactionManager().enlistAfterCompletion(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
tx.addEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
tx.addAdminEvent(event, includeRepresentation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public static Event poll() {
|
||||
return events.poll();
|
||||
}
|
||||
|
||||
public static AdminEvent pollAdminEvent() {
|
||||
return adminEvents.poll();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
public static void clearAdminEvents() {
|
||||
adminEvents.clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class TestEventsListenerProviderFactory implements EventListenerProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "event-queue";
|
||||
|
||||
@Override
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new TestEventsListenerProvider(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.federation;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.credential.OTPCredentialModel;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.storage.UserStoragePrivateUtil;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class DummyUserFederationProvider implements UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
UserRegistrationProvider,
|
||||
CredentialInputValidator {
|
||||
|
||||
private final Map<String, UserModel> users;
|
||||
private KeycloakSession session;
|
||||
private ComponentModel component;
|
||||
|
||||
// Hardcoded password of test-user
|
||||
public static final String HARDCODED_PASSWORD = "secret";
|
||||
|
||||
// Hardcoded otp code, which will be always considered valid for the test-user
|
||||
public static final String HARDCODED_OTP = "123456";
|
||||
|
||||
|
||||
|
||||
public DummyUserFederationProvider(KeycloakSession session, ComponentModel component, Map<String, UserModel> users) {
|
||||
this.users = users;
|
||||
this.session = session;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserModel local = UserStoragePrivateUtil.userLocalStorage(session).addUser(realm, username);
|
||||
local.setFederationLink(component.getId());
|
||||
|
||||
users.put(username, local);
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
return users.remove(user.getUsername()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(RealmModel realm, String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(RealmModel realm, String username) {
|
||||
return users.get(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(RealmModel realm, String email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
|
||||
}
|
||||
|
||||
public Set<String> getSupportedCredentialTypes() {
|
||||
return new HashSet<>(Arrays.asList(PasswordCredentialModel.TYPE, OTPCredentialModel.TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return getSupportedCredentialTypes().contains(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!supportsCredentialType(credentialType)) return false;
|
||||
|
||||
if (user.getUsername().equals("test-user")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput credentialInput) {
|
||||
if (user.getUsername().equals("test-user")) {
|
||||
if (PasswordCredentialModel.TYPE.equals(credentialInput.getType())) {
|
||||
return HARDCODED_PASSWORD.equals(credentialInput.getChallengeResponse());
|
||||
} else if (OTPCredentialModel.TYPE.equals(credentialInput.getType())) {
|
||||
return HARDCODED_OTP.equals(credentialInput.getChallengeResponse());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.federation;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.ImportSynchronization;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class DummyUserFederationProviderFactory implements UserStorageProviderFactory<DummyUserFederationProvider>, ImportSynchronization {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DummyUserFederationProviderFactory.class);
|
||||
public static final String PROVIDER_NAME = "dummy";
|
||||
|
||||
private AtomicInteger fullSyncCounter = new AtomicInteger();
|
||||
private AtomicInteger changedSyncCounter = new AtomicInteger();
|
||||
|
||||
private Map<String, UserModel> users = new HashMap<String, UserModel>();
|
||||
|
||||
@Override
|
||||
public DummyUserFederationProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new DummyUserFederationProvider(session, model, users);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property().name("important.config")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
|
||||
logger.info("syncAllUsers invoked");
|
||||
fullSyncCounter.incrementAndGet();
|
||||
return SynchronizationResult.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
|
||||
logger.info("syncChangedUsers invoked");
|
||||
changedSyncCounter.incrementAndGet();
|
||||
return SynchronizationResult.empty();
|
||||
}
|
||||
|
||||
public int getFullSyncCounter() {
|
||||
return fullSyncCounter.get();
|
||||
}
|
||||
|
||||
public int getChangedSyncCounter() {
|
||||
return changedSyncCounter.get();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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.federation;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.models.credential.PasswordUserCredentialModel;
|
||||
import org.keycloak.storage.*;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
|
||||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater,
|
||||
CredentialInputValidator, UserGroupMembershipFederatedStorage.Streams, UserQueryProvider, ImportedUserValidation {
|
||||
|
||||
private static final Logger log = Logger.getLogger(UserMapStorage.class);
|
||||
|
||||
protected final Map<String, String> userPasswords;
|
||||
protected final ConcurrentMap<String, Set<String>> userGroups;
|
||||
protected ComponentModel model;
|
||||
protected KeycloakSession session;
|
||||
protected EditMode editMode;
|
||||
private transient Boolean importEnabled;
|
||||
|
||||
public static final AtomicInteger allocations = new AtomicInteger(0);
|
||||
public static final AtomicInteger closings = new AtomicInteger(0);
|
||||
public static final AtomicInteger realmRemovals = new AtomicInteger(0);
|
||||
public static final AtomicInteger groupRemovals = new AtomicInteger(0);
|
||||
public static final AtomicInteger roleRemovals = new AtomicInteger(0);
|
||||
|
||||
public UserMapStorage(KeycloakSession session, ComponentModel model, Map<String, String> userPasswords, ConcurrentMap<String, Set<String>> userGroups) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.userPasswords = userPasswords;
|
||||
this.userGroups = userGroups;
|
||||
allocations.incrementAndGet();
|
||||
|
||||
String editModeString = model.getConfig().getFirst(LDAPConstants.EDIT_MODE);
|
||||
if (editModeString == null) {
|
||||
this.editMode = EditMode.UNSYNCED;
|
||||
} else {
|
||||
this.editMode = EditMode.valueOf(editModeString);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getUserIdInMap(RealmModel realm, String userId) {
|
||||
return realm.getId() + "/" + userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(RealmModel realm, String id) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
final String username = storageId.getExternalId();
|
||||
if (!userPasswords.containsKey(translateUserName(username))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createUser(realm, username);
|
||||
}
|
||||
|
||||
public Set<String> getUsernames() {
|
||||
return userPasswords.keySet();
|
||||
}
|
||||
|
||||
private UserModel createUser(RealmModel realm, String username) {
|
||||
UserModel user;
|
||||
if (isImportEnabled()) {
|
||||
user = UserStoragePrivateUtil.userLocalStorage(session).addUser(realm, username);
|
||||
user.setEnabled(true);
|
||||
user.setFederationLink(model.getId());
|
||||
} else {
|
||||
user = new AbstractUserAdapterFederatedStorage.Streams(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username.toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String innerUsername) {
|
||||
if (! Objects.equals(innerUsername, username.toLowerCase())) {
|
||||
throw new RuntimeException("Unsupported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveGroup(GroupModel group) {
|
||||
UserMapStorage.this.leaveGroup(realm, getUsername(), group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void joinGroup(GroupModel group) {
|
||||
UserMapStorage.this.joinGroup(realm, getUsername(), group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return model.getId();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return PasswordCredentialModel.TYPE.equals(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (editMode == EditMode.READ_ONLY) {
|
||||
throw new ReadOnlyException("Federated storage is not writable");
|
||||
}
|
||||
if (!(input instanceof UserCredentialModel)) {
|
||||
return false;
|
||||
}
|
||||
if (input.getType().equals(PasswordCredentialModel.TYPE)) {
|
||||
userPasswords.put(translateUserName(user.getUsername()), input.getChallengeResponse());
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
return PasswordCredentialModel.TYPE.equals(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
// Test "instanceof PasswordUserCredentialModel" on purpose. We want to test that the backwards compatibility
|
||||
if (!(input instanceof PasswordUserCredentialModel)) {
|
||||
return false;
|
||||
}
|
||||
if (input.getType().equals(PasswordCredentialModel.TYPE)) {
|
||||
String pw = userPasswords.get(translateUserName(user.getUsername()));
|
||||
|
||||
// Using "getValue" on purpose here, to test that backwards compatibility works as expected
|
||||
return pw != null && pw.equals(((UserCredentialModel) input).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(RealmModel realm, String username) {
|
||||
if (!userPasswords.containsKey(translateUserName(username))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createUser(realm, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(RealmModel realm, String email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
if (editMode == EditMode.READ_ONLY) {
|
||||
throw new ReadOnlyException("Federated storage is not writable");
|
||||
}
|
||||
|
||||
userPasswords.put(translateUserName(username), "");
|
||||
return createUser(realm, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) {
|
||||
log.warnf("User '%s' can't be deleted in LDAP as editMode is '%s'. Deleting user just from Keycloak DB, but he will be re-imported from LDAP again once searched in Keycloak", user.getUsername(), editMode.toString());
|
||||
userPasswords.remove(translateUserName(user.getUsername()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return userPasswords.remove(translateUserName(user.getUsername())) != null;
|
||||
}
|
||||
|
||||
public boolean removeUserByName(String userName) {
|
||||
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) {
|
||||
log.warnf("User '%s' can't be deleted in LDAP as editMode is '%s'. Deleting user just from Keycloak DB, but he will be re-imported from LDAP again once searched in Keycloak", userName, editMode.toString());
|
||||
userPasswords.remove(translateUserName(userName));
|
||||
return true;
|
||||
}
|
||||
|
||||
return userPasswords.remove(translateUserName(userName)) != null;
|
||||
}
|
||||
|
||||
public boolean isImportEnabled() {
|
||||
if (importEnabled == null) {
|
||||
String val = model.getConfig().getFirst(IMPORT_ENABLED);
|
||||
if (val == null) {
|
||||
importEnabled = true;
|
||||
} else {
|
||||
importEnabled = Boolean.valueOf(val);
|
||||
}
|
||||
}
|
||||
return importEnabled;
|
||||
|
||||
}
|
||||
|
||||
public void setImportEnabled(boolean flag) {
|
||||
importEnabled = flag;
|
||||
model.getConfig().putSingle(IMPORT_ENABLED, Boolean.toString(flag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
log.infof("preRemove: realm=%s", realm.getName());
|
||||
realmRemovals.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
log.infof("preRemove: realm=%s, group=%s", realm.getName(), group.getName());
|
||||
groupRemovals.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
log.infof("preRemove: realm=%s, role=%s", realm.getName(), role.getName());
|
||||
roleRemovals.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closings.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
return userPasswords.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, String search) {
|
||||
String tSearch = translateUserName(search);
|
||||
return userPasswords.keySet().stream()
|
||||
.sorted()
|
||||
.filter(userName -> translateUserName(userName).contains(tSearch))
|
||||
.map(userName -> createUser(realm, userName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||
String tSearch = translateUserName(search);
|
||||
Stream<String> userStream = userPasswords.keySet().stream()
|
||||
.sorted()
|
||||
.filter(userName -> translateUserName(userName).contains(search));
|
||||
|
||||
return paginatedStream(userStream, firstResult, maxResults).map(userName -> createUser(realm, userName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params, Integer firstResult, Integer maxResults) {
|
||||
Stream<String> userStream = userPasswords.keySet().stream()
|
||||
.sorted();
|
||||
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case UserModel.USERNAME:
|
||||
case UserModel.SEARCH:
|
||||
if (Boolean.valueOf(params.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
|
||||
userStream = userStream.filter(s -> s.toLowerCase().equals(value.toLowerCase()));
|
||||
} else {
|
||||
userStream = userStream.filter(s -> s.toLowerCase().contains(value.toLowerCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paginatedStream(userStream, firstResult, maxResults).map(userName -> createUser(realm, userName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, Integer firstResult, Integer maxResults) {
|
||||
return getMembershipStream(realm, group, firstResult == null ? -1 : firstResult, maxResults == null ? -1 : maxResults)
|
||||
.map(userName -> createUser(realm, userName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserByUserAttributeStream(RealmModel realm, String attrName, String attrValue) {
|
||||
if (isImportEnabled()) {
|
||||
return UserStoragePrivateUtil.userLocalStorage(session).searchForUserByUserAttributeStream(realm, attrName, attrValue);
|
||||
} else {
|
||||
return UserStorageUtil.userFederatedStorage(session).getUsersByUserAttributeStream(realm, attrName, attrValue)
|
||||
.map(userName -> createUser(realm, userName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<GroupModel> getGroupsStream(RealmModel realm, String userId) {
|
||||
Set<String> set = userGroups.get(getUserIdInMap(realm, userId));
|
||||
if (set == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
return set.stream().map(realm::getGroupById);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void joinGroup(RealmModel realm, String userId, GroupModel group) {
|
||||
Set<String> groups = userGroups.computeIfAbsent(getUserIdInMap(realm, userId), userName -> new ConcurrentSkipListSet());
|
||||
groups.add(group.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveGroup(RealmModel realm, String userId, GroupModel group) {
|
||||
Set<String> set = userGroups.get(getUserIdInMap(realm, userId));
|
||||
if (set != null) {
|
||||
set.remove(group.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getMembershipStream(RealmModel realm, GroupModel group, Integer firstResult, Integer max) {
|
||||
Stream<String> userStream = paginatedStream(userGroups.entrySet().stream(), firstResult, max)
|
||||
.filter(me -> me.getValue().contains(group.getId()))
|
||||
.map(Map.Entry::getKey)
|
||||
.filter(realmUser -> realmUser.startsWith(realm.getId()))
|
||||
.map(realmUser -> realmUser.substring(realmUser.indexOf("/") + 1));
|
||||
|
||||
return userStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel validate(RealmModel realm, UserModel local) {
|
||||
final boolean userExists = userPasswords.containsKey(translateUserName(local.getUsername()));
|
||||
if (! userExists) {
|
||||
userGroups.remove(getUserIdInMap(realm, local.getUsername()));
|
||||
}
|
||||
return userExists ? local : null;
|
||||
}
|
||||
|
||||
private static String translateUserName(String userName) {
|
||||
return userName == null ? null : userName.toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.federation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserMapStorageFactory implements UserStorageProviderFactory<UserMapStorage> {
|
||||
|
||||
|
||||
public static final String PROVIDER_ID = "user-password-map-arq";
|
||||
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty attr = new ProviderConfigProperty("attr", "attr",
|
||||
"This is some attribute",
|
||||
ProviderConfigProperty.STRING_TYPE, null);
|
||||
configProperties.add(attr);
|
||||
}
|
||||
|
||||
private final Map<String, String> userPasswords = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Set<String>> userGroups = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserMapStorage create(KeycloakSession session, ComponentModel model) {
|
||||
return new UserMapStorage(session, model, userPasswords, userGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
userPasswords.clear();
|
||||
userGroups.clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
org.keycloak.testsuite.events.TestEventsListenerProviderFactory
|
||||
@ -0,0 +1,2 @@
|
||||
org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
|
||||
org.keycloak.testsuite.federation.UserMapStorageFactory
|
||||
@ -44,6 +44,11 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.testframework</groupId>
|
||||
<artifactId>keycloak-test-framework-remote-providers</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.tests.utils;
|
||||
|
||||
import jakarta.mail.Address;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.Multipart;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MailUtils {
|
||||
|
||||
private static Pattern mailPattern = Pattern.compile("http[^\\s\"]*");
|
||||
|
||||
public static String getLink(String body) {
|
||||
Matcher matcher = mailPattern.matcher(body);
|
||||
if (matcher.find()) {
|
||||
return matcher.group();
|
||||
}
|
||||
throw new AssertionError("No link found in " + body);
|
||||
}
|
||||
|
||||
public static String getPasswordResetEmailLink(MimeMessage message) throws IOException {
|
||||
return getPasswordResetEmailLink(new EmailBody(message));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message email message
|
||||
* @return first recipient of the email message
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public static String getRecipient(MimeMessage message) throws MessagingException {
|
||||
Address[] recipients = message.getRecipients(MimeMessage.RecipientType.TO);
|
||||
return recipients[0].toString();
|
||||
}
|
||||
|
||||
public static String getPasswordResetEmailLink(EmailBody body) throws IOException {
|
||||
final String textChangePwdUrl = getLink(body.getText());
|
||||
String htmlChangePwdUrl = getLink(body.getHtml());
|
||||
|
||||
assertEquals(htmlChangePwdUrl, textChangePwdUrl);
|
||||
|
||||
return htmlChangePwdUrl;
|
||||
}
|
||||
|
||||
public static EmailBody getBody(MimeMessage message) throws IOException {
|
||||
return new EmailBody(message);
|
||||
}
|
||||
|
||||
public static class EmailBody {
|
||||
|
||||
private String text;
|
||||
private String html;
|
||||
|
||||
private EmailBody(MimeMessage message) throws IOException {
|
||||
try {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
|
||||
String textContentType = multipart.getBodyPart(0).getContentType();
|
||||
|
||||
assertEquals("text/plain; charset=UTF-8", textContentType);
|
||||
|
||||
text = (String) multipart.getBodyPart(0).getContent();
|
||||
|
||||
String htmlContentType = multipart.getBodyPart(1).getContentType();
|
||||
|
||||
assertEquals("text/html; charset=UTF-8", htmlContentType);
|
||||
|
||||
html = (String) multipart.getBodyPart(1).getContent();
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public String getHtml() {
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package org.keycloak.tests.utils.runonserver;
|
||||
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testframework.remote.providers.runonserver.FetchOnServer;
|
||||
import org.keycloak.testframework.remote.providers.runonserver.FetchOnServerWrapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by st on 26.01.17.
|
||||
*/
|
||||
public class RunHelpers {
|
||||
|
||||
public static FetchOnServerWrapper<RealmRepresentation> internalRealm() {
|
||||
return new FetchOnServerWrapper() {
|
||||
|
||||
@Override
|
||||
public FetchOnServer getRunOnServer() {
|
||||
return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session, session.getContext().getRealm(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RealmRepresentation> getResultClass() {
|
||||
return RealmRepresentation.class;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static FetchOnServerWrapper<ComponentRepresentation> internalComponent(String componentId) {
|
||||
return new FetchOnServerWrapper() {
|
||||
|
||||
@Override
|
||||
public FetchOnServer getRunOnServer() {
|
||||
return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session, session.getContext().getRealm().getComponent(componentId), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ComponentRepresentation> getResultClass() {
|
||||
return ComponentRepresentation.class;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static FetchOnServerWrapper<CredentialModel> fetchCredentials(String username) {
|
||||
return new FetchOnServerWrapper() {
|
||||
|
||||
@Override
|
||||
public FetchOnServer getRunOnServer() {
|
||||
return (FetchOnServer) session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserByUsername(realm, username);
|
||||
List<CredentialModel> storedCredentialsByType = user.credentialManager().getStoredCredentialsByTypeStream(CredentialRepresentation.PASSWORD)
|
||||
.collect(Collectors.toList());
|
||||
return storedCredentialsByType.get(0);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getResultClass() {
|
||||
return CredentialModel.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -185,19 +185,6 @@ public class UserBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder federatedLink(String identityProvider, String federatedUserId) {
|
||||
if (rep.getFederatedIdentities() == null) {
|
||||
rep.setFederatedIdentities(new LinkedList<>());
|
||||
}
|
||||
FederatedIdentityRepresentation federatedIdentity = new FederatedIdentityRepresentation();
|
||||
federatedIdentity.setUserId(federatedUserId);
|
||||
federatedIdentity.setUserName(rep.getUsername());
|
||||
federatedIdentity.setIdentityProvider(identityProvider);
|
||||
|
||||
rep.getFederatedIdentities().add(federatedIdentity);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRepresentation build() {
|
||||
return rep;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user