KEYCLOAK-12860 KEYCLOAK-12875 Fix for Account REST Credentials to work with LDAP and social users
This commit is contained in:
parent
876086c846
commit
a76c496c23
15 changed files with 309 additions and 23 deletions
|
@ -2,6 +2,7 @@ package org.keycloak.authentication;
|
|||
|
||||
import org.keycloak.credential.CredentialProvider;
|
||||
import org.keycloak.credential.CredentialTypeMetadata;
|
||||
import org.keycloak.credential.CredentialTypeMetadataContext;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
|
@ -15,7 +16,10 @@ public class AuthenticationSelectionOption {
|
|||
Authenticator authenticator = session.getProvider(Authenticator.class, authExec.getAuthenticator());
|
||||
if (authenticator instanceof CredentialValidator) {
|
||||
CredentialProvider credentialProvider = ((CredentialValidator) authenticator).getCredentialProvider(session);
|
||||
credentialTypeMetadata = credentialProvider.getCredentialTypeMetadata();
|
||||
|
||||
CredentialTypeMetadataContext ctx = CredentialTypeMetadataContext.builder()
|
||||
.build(session);
|
||||
credentialTypeMetadata = credentialProvider.getCredentialTypeMetadata(ctx);
|
||||
} else {
|
||||
credentialTypeMetadata = null;
|
||||
}
|
||||
|
|
|
@ -50,5 +50,5 @@ public interface CredentialProvider<T extends CredentialModel> extends Provider
|
|||
return getCredentialFromModel(models.get(0));
|
||||
}
|
||||
|
||||
CredentialTypeMetadata getCredentialTypeMetadata();
|
||||
CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class CredentialTypeMetadata implements Comparable<CredentialTypeMetadata
|
|||
|
||||
|
||||
public enum Category {
|
||||
PASSWORD("password", 1),
|
||||
BASIC_AUTHENTICATION("basic-authentication", 1),
|
||||
TWO_FACTOR("two-factor", 2),
|
||||
PASSWORDLESS("passwordless", 3);
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2019 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.credential;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class CredentialTypeMetadataContext {
|
||||
|
||||
private UserModel user;
|
||||
|
||||
private CredentialTypeMetadataContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return user, for which we create metadata. Could be null
|
||||
*/
|
||||
public UserModel getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public static CredentialTypeMetadataContext.CredentialTypeMetadataContextBuilder builder() {
|
||||
return new CredentialTypeMetadataContext.CredentialTypeMetadataContextBuilder();
|
||||
}
|
||||
|
||||
// BUILDER
|
||||
|
||||
public static class CredentialTypeMetadataContextBuilder {
|
||||
|
||||
private CredentialTypeMetadataContext instance = new CredentialTypeMetadataContext();
|
||||
|
||||
public CredentialTypeMetadataContext.CredentialTypeMetadataContextBuilder user(UserModel user) {
|
||||
instance.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CredentialTypeMetadataContext build(KeycloakSession session) {
|
||||
// Possible to have null user
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -142,7 +142,7 @@ public class OTPCredentialProvider implements CredentialProvider<OTPCredentialMo
|
|||
}
|
||||
|
||||
@Override
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata() {
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
|
||||
return CredentialTypeMetadata.builder()
|
||||
.type(getType())
|
||||
.category(CredentialTypeMetadata.Category.TWO_FACTOR)
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.keycloak.credential;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.credential.hash.PasswordHashProvider;
|
||||
import org.keycloak.models.ModelException;
|
||||
|
@ -296,14 +295,23 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
|
|||
}
|
||||
|
||||
@Override
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata() {
|
||||
return CredentialTypeMetadata.builder()
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
|
||||
CredentialTypeMetadata.CredentialTypeMetadataBuilder metadataBuilder = CredentialTypeMetadata.builder()
|
||||
.type(getType())
|
||||
.category(CredentialTypeMetadata.Category.PASSWORD)
|
||||
.displayName("password")
|
||||
.category(CredentialTypeMetadata.Category.BASIC_AUTHENTICATION)
|
||||
.displayName("password-display-name")
|
||||
.helpText("password-help-text")
|
||||
.iconCssClass("kcAuthenticatorPasswordClass")
|
||||
.updateAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString())
|
||||
.iconCssClass("kcAuthenticatorPasswordClass");
|
||||
|
||||
// Check if we are creating or updating password
|
||||
UserModel user = metadataContext.getUser();
|
||||
if (user != null && session.userCredentialManager().isConfiguredFor(session.getContext().getRealm(), user, getType())) {
|
||||
metadataBuilder.updateAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
} else {
|
||||
metadataBuilder.createAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
|
||||
}
|
||||
|
||||
return metadataBuilder
|
||||
.removeable(false)
|
||||
.build(session);
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
|
|||
}
|
||||
|
||||
@Override
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata() {
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
|
||||
return CredentialTypeMetadata.builder()
|
||||
.type(getType())
|
||||
.category(CredentialTypeMetadata.Category.TWO_FACTOR)
|
||||
|
|
|
@ -40,7 +40,7 @@ public class WebAuthnPasswordlessCredentialProvider extends WebAuthnCredentialPr
|
|||
}
|
||||
|
||||
@Override
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata() {
|
||||
public CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
|
||||
return CredentialTypeMetadata.builder()
|
||||
.type(getType())
|
||||
.category(CredentialTypeMetadata.Category.PASSWORDLESS)
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.keycloak.authentication.AuthenticatorFactory;
|
|||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.credential.CredentialProvider;
|
||||
import org.keycloak.credential.CredentialTypeMetadata;
|
||||
import org.keycloak.credential.CredentialTypeMetadataContext;
|
||||
import org.keycloak.credential.PasswordCredentialProvider;
|
||||
import org.keycloak.credential.PasswordCredentialProviderFactory;
|
||||
import org.keycloak.credential.UserCredentialStoreManager;
|
||||
|
@ -31,6 +32,7 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -185,13 +187,29 @@ public class AccountCredentialResource {
|
|||
continue;
|
||||
}
|
||||
|
||||
CredentialTypeMetadata metadata = credentialProvider.getCredentialTypeMetadata();
|
||||
CredentialTypeMetadataContext ctx = CredentialTypeMetadataContext.builder()
|
||||
.user(user)
|
||||
.build(session);
|
||||
CredentialTypeMetadata metadata = credentialProvider.getCredentialTypeMetadata(ctx);
|
||||
|
||||
List<CredentialRepresentation> userCredentialModels = filterUserCredentials ? null : models.stream()
|
||||
.filter(credentialModel -> credentialProvider.getType().equals(credentialModel.getType()))
|
||||
.map(ModelToRepresentation::toRepresentation)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (userCredentialModels != null && userCredentialModels.isEmpty() &&
|
||||
session.userCredentialManager().isConfiguredFor(realm, user, credentialProviderType)) {
|
||||
// In case user is federated in the userStorage, he may have credential configured on the userStorage side. We're
|
||||
// creating "dummy" credential representing the credential provided by userStorage
|
||||
CredentialRepresentation credential = new CredentialRepresentation();
|
||||
credential.setId(credentialProviderType + "-id");
|
||||
credential.setType(credentialProviderType);
|
||||
credential.setCreatedDate(-1L);
|
||||
credential.setPriority(0);
|
||||
|
||||
userCredentialModels = Collections.singletonList(credential);
|
||||
}
|
||||
|
||||
CredentialContainer credType = new CredentialContainer(metadata, userCredentialModels);
|
||||
credentialTypes.add(credType);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<resource-root path="integration-arquillian-testsuite-providers-${project.version}.jar"/>
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="com.fasterxml.jackson.core.jackson-core"/>
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="javax.servlet.api"/>
|
||||
|
|
|
@ -20,10 +20,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.WebAuthnPasswordlessAuthenticatorFactory;
|
||||
import org.keycloak.authentication.requiredactions.WebAuthnPasswordlessRegisterFactory;
|
||||
import org.keycloak.authentication.requiredactions.WebAuthnRegister;
|
||||
import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.credential.CredentialTypeMetadata;
|
||||
|
@ -45,7 +45,6 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
|||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
|
||||
|
@ -70,7 +69,6 @@ import static org.junit.Assert.*;
|
|||
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -91,6 +89,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
@Test
|
||||
public void testUpdateProfile() throws IOException {
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
String originalUsername = user.getUsername();
|
||||
String originalFirstName = user.getFirstName();
|
||||
String originalLastName = user.getLastName();
|
||||
String originalEmail = user.getEmail();
|
||||
|
@ -157,6 +156,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
realmRep.setEditUsernameAllowed(true);
|
||||
adminClient.realm("test").update(realmRep);
|
||||
|
||||
user.setUsername(originalUsername);
|
||||
user.setFirstName(originalFirstName);
|
||||
user.setLastName(originalLastName);
|
||||
user.setEmail(originalEmail);
|
||||
|
@ -316,8 +316,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
Assert.assertEquals(4, credentials.size());
|
||||
|
||||
AccountCredentialResource.CredentialContainer password = credentials.get(0);
|
||||
assertCredentialContainerExpected(password, PasswordCredentialModel.TYPE, CredentialTypeMetadata.Category.PASSWORD.toString(),
|
||||
"password", "password-help-text", "kcAuthenticatorPasswordClass",
|
||||
assertCredentialContainerExpected(password, PasswordCredentialModel.TYPE, CredentialTypeMetadata.Category.BASIC_AUTHENTICATION.toString(),
|
||||
"password-display-name", "password-help-text", "kcAuthenticatorPasswordClass",
|
||||
null, UserModel.RequiredAction.UPDATE_PASSWORD.toString(), false, 1);
|
||||
|
||||
CredentialRepresentation password1 = password.getUserCredentials().get(0);
|
||||
|
@ -443,6 +443,32 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCredentialsForUserWithoutPassword() throws IOException {
|
||||
// This is just to call REST to ensure tokenUtil will authenticate user and create the tokens.
|
||||
// We won't be able to authenticate later as user won't have password
|
||||
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
||||
|
||||
// Remove password from the user now
|
||||
UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
|
||||
for (CredentialRepresentation credential : user.credentials()) {
|
||||
if (PasswordCredentialModel.TYPE.equals(credential.getType())) {
|
||||
user.removeCredential(credential.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Get credentials. Ensure user doesn't have password credential and create action is UPDATE_PASSWORD
|
||||
credentials = getCredentials();
|
||||
AccountCredentialResource.CredentialContainer password = credentials.get(0);
|
||||
assertCredentialContainerExpected(password, PasswordCredentialModel.TYPE, CredentialTypeMetadata.Category.BASIC_AUTHENTICATION.toString(),
|
||||
"password-display-name", "password-help-text", "kcAuthenticatorPasswordClass",
|
||||
UserModel.RequiredAction.UPDATE_PASSWORD.toString(), null, false, 0);
|
||||
|
||||
// Re-add the password to the user
|
||||
ApiUtil.resetUserPassword(user, "password", false);
|
||||
|
||||
}
|
||||
|
||||
// Sets new requirement and returns current requirement
|
||||
private AuthenticationExecutionModel.Requirement setExecutionRequirement(String flowAlias, String executionDisplayName, AuthenticationExecutionModel.Requirement newRequirement) {
|
||||
List<AuthenticationExecutionInfoRepresentation> executionInfos = testRealm().flows().getExecutions(flowAlias);
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright 2019 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.ldap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.representations.account.UserRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.util.LDAPRule;
|
||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||
import org.keycloak.testsuite.util.TokenUtil;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@EnableFeature(value = ACCOUNT_API, skipRestart = true)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
||||
|
||||
@Rule
|
||||
public TokenUtil tokenUtil = new TokenUtil("johnkeycloak", "Password1");
|
||||
|
||||
@ClassRule
|
||||
public static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
protected CloseableHttpClient httpClient;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
httpClient = HttpClientBuilder.create().build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LDAPRule getLDAPRule() {
|
||||
return ldapRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterImportTestRealm() {
|
||||
testingClient.server().run(session -> {
|
||||
LDAPTestContext ctx = LDAPTestContext.init(session);
|
||||
RealmModel appRealm = ctx.getRealm();
|
||||
|
||||
// Delete all LDAP users and add some new for testing
|
||||
LDAPTestUtils.removeAllLDAPUsers(ctx.getLdapProvider(), appRealm);
|
||||
|
||||
LDAPObject john = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
||||
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john, "Password1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfile() throws IOException {
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
assertEquals("John", user.getFirstName());
|
||||
assertEquals("Doe", user.getLastName());
|
||||
assertEquals("john@email.org", user.getEmail());
|
||||
assertFalse(user.isEmailVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCredentials() throws IOException {
|
||||
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
||||
|
||||
AccountCredentialResource.CredentialContainer password = credentials.get(0);
|
||||
Assert.assertEquals(PasswordCredentialModel.TYPE, password.getType());
|
||||
Assert.assertEquals(1, password.getUserCredentials().size());
|
||||
CredentialRepresentation userPassword = password.getUserCredentials().get(0);
|
||||
|
||||
// Password won't have createdDate and any metadata set
|
||||
Assert.assertEquals(PasswordCredentialModel.TYPE, userPassword.getType());
|
||||
Assert.assertEquals(userPassword.getCreatedDate(), new Long(-1L));
|
||||
Assert.assertNull(userPassword.getCredentialData());
|
||||
Assert.assertNull(userPassword.getSecretData());
|
||||
}
|
||||
|
||||
private String getAccountUrl(String resource) {
|
||||
return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account" + (resource != null ? "/" + resource : "");
|
||||
}
|
||||
|
||||
// Send REST request to get all credential containers and credentials of current user
|
||||
private List<AccountCredentialResource.CredentialContainer> getCredentials() throws IOException {
|
||||
return SimpleHttp.doGet(getAccountUrl("credentials"), httpClient)
|
||||
.auth(tokenUtil.getToken()).asJson(new TypeReference<List<AccountCredentialResource.CredentialContainer>>() {});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -36,6 +36,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
|
||||
import org.keycloak.testsuite.WebAuthnAssume;
|
||||
import org.keycloak.testsuite.admin.Users;
|
||||
import org.keycloak.testsuite.auth.page.login.OTPSetup;
|
||||
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
|
||||
import org.keycloak.testsuite.pages.webauthn.WebAuthnRegisterPage;
|
||||
|
@ -62,7 +63,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
|||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||
*/
|
||||
public class SigningInTest extends BaseAccountPageTest {
|
||||
public static final String PASSWORD_LABEL = "Password";
|
||||
public static final String PASSWORD_LABEL = "My Password";
|
||||
public static final String WEBAUTHN_FLOW_ID = "75e2390e-f296-49e6-acf8-6d21071d7e10";
|
||||
|
||||
@Page
|
||||
|
@ -148,7 +149,7 @@ public class SigningInTest extends BaseAccountPageTest {
|
|||
|
||||
assertEquals(3, signingInPage.getCategoriesCount());
|
||||
|
||||
assertEquals("Password", signingInPage.getCategoryTitle("password"));
|
||||
assertEquals("Basic Authentication", signingInPage.getCategoryTitle("basic-authentication"));
|
||||
assertEquals("Two-Factor Authentication", signingInPage.getCategoryTitle("two-factor"));
|
||||
assertEquals("Passwordless", signingInPage.getCategoryTitle("passwordless"));
|
||||
|
||||
|
@ -180,8 +181,35 @@ public class SigningInTest extends BaseAccountPageTest {
|
|||
|
||||
assertUserCredential(PASSWORD_LABEL, false, passwordCred);
|
||||
assertNotEquals(previousCreatedAt, passwordCred.getCreatedAt());
|
||||
}
|
||||
|
||||
// TODO KEYCLOAK-12875 try to update/set up password when user has no password configured
|
||||
@Test
|
||||
public void updatePasswordTestForUserWithoutPassword() {
|
||||
// Remove password from the user through admin REST API
|
||||
String passwordId = testUserResource().credentials().get(0).getId();
|
||||
testUserResource().removeCredential(passwordId);
|
||||
|
||||
// Refresh the page
|
||||
refreshPageAndWaitForLoad();
|
||||
|
||||
// Test user doesn't have password set
|
||||
assertTrue(passwordCredentialType.isSetUpLinkVisible());
|
||||
assertFalse(passwordCredentialType.isSetUp());
|
||||
|
||||
// Set password
|
||||
passwordCredentialType.clickSetUpLink();
|
||||
updatePasswordPage.assertCurrent();
|
||||
String originalPassword = Users.getPasswordOf(testUser);
|
||||
updatePasswordPage.updatePasswords(originalPassword, originalPassword);
|
||||
// TODO uncomment this once KEYCLOAK-12852 is resolved
|
||||
// signingInPage.assertCurrent();
|
||||
|
||||
// Credential set-up now
|
||||
assertFalse(passwordCredentialType.isSetUpLinkVisible());
|
||||
assertTrue(passwordCredentialType.isSetUp());
|
||||
SigningInPage.UserCredential passwordCred =
|
||||
passwordCredentialType.getUserCredential(testUserResource().credentials().get(0).getId());
|
||||
assertUserCredential(PASSWORD_LABEL, false, passwordCred);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -342,6 +342,7 @@ saml.post-form.js-disabled=JavaScript is disabled. We strongly recommend to enab
|
|||
#authenticators
|
||||
otp-display-name=Authenticator Application
|
||||
otp-help-text=Enter a verification code from authenticator application.
|
||||
password-display-name=Password
|
||||
password-help-text=Log in by entering your password.
|
||||
auth-username-form-display-name=Username
|
||||
auth-username-form-help-text=Start log in by entering your username
|
||||
|
|
|
@ -68,6 +68,9 @@ notSetUp={0} is not set up.
|
|||
two-factor=Two-Factor Authentication
|
||||
passwordless=Passwordless
|
||||
unknown=Unknown
|
||||
password-display-name=Password
|
||||
password-help-text=Log in by entering your password.
|
||||
password=My Password
|
||||
otp-display-name=Authenticator Application
|
||||
otp-help-text=Enter a verification code from authenticator application.
|
||||
webauthn-display-name=Security Key
|
||||
|
@ -75,7 +78,6 @@ webauthn-help-text=Use your security key to log in.
|
|||
webauthn-passwordless-display-name=Security Key
|
||||
webauthn-passwordless-help-text=Use your security key for passwordless log in.
|
||||
basic-authentication=Basic Authentication
|
||||
basic-auth-help-text=Sign in with username and password.
|
||||
|
||||
# Applications page
|
||||
applicationsPageTitle=Applications
|
||||
|
|
Loading…
Reference in a new issue