KEYCLOAK-7970-KEYCLOAK-7222 Add clientId to action tokens
This commit is contained in:
parent
c5f861a522
commit
d04791243c
12 changed files with 230 additions and 22 deletions
|
@ -43,10 +43,11 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
||||||
|
|
||||||
|
|
||||||
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId,
|
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId,
|
||||||
String identityProviderUsername, String identityProviderAlias) {
|
String identityProviderUsername, String identityProviderAlias, String clientId) {
|
||||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
||||||
this.identityProviderUsername = identityProviderUsername;
|
this.identityProviderUsername = identityProviderUsername;
|
||||||
this.identityProviderAlias = identityProviderAlias;
|
this.identityProviderAlias = identityProviderAlias;
|
||||||
|
this.issuedFor = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdpVerifyAccountLinkActionToken() {
|
private IdpVerifyAccountLinkActionToken() {
|
||||||
|
|
|
@ -27,8 +27,9 @@ public class ResetCredentialsActionToken extends DefaultActionToken {
|
||||||
|
|
||||||
public static final String TOKEN_TYPE = "reset-credentials";
|
public static final String TOKEN_TYPE = "reset-credentials";
|
||||||
|
|
||||||
public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId) {
|
public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String clientId) {
|
||||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
||||||
|
this.issuedFor = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResetCredentialsActionToken() {
|
private ResetCredentialsActionToken() {
|
||||||
|
|
|
@ -37,9 +37,10 @@ public class VerifyEmailActionToken extends DefaultActionToken {
|
||||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
||||||
private String originalAuthenticationSessionId;
|
private String originalAuthenticationSessionId;
|
||||||
|
|
||||||
public VerifyEmailActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String email) {
|
public VerifyEmailActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String email, String clientId) {
|
||||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
|
||||||
this.email = email;
|
this.email = email;
|
||||||
|
this.issuedFor = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VerifyEmailActionToken() {
|
private VerifyEmailActionToken() {
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
|
||||||
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
|
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
|
||||||
IdpVerifyAccountLinkActionToken token = new IdpVerifyAccountLinkActionToken(
|
IdpVerifyAccountLinkActionToken token = new IdpVerifyAccountLinkActionToken(
|
||||||
existingUser.getId(), absoluteExpirationInSecs, authSessionEncodedId,
|
existingUser.getId(), absoluteExpirationInSecs, authSessionEncodedId,
|
||||||
brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias()
|
brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias(), authSession.getClient().getClientId()
|
||||||
);
|
);
|
||||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
|
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
|
||||||
authSession.getClient().getClientId(), authSession.getTabId());
|
authSession.getClient().getClientId(), authSession.getTabId());
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
|
||||||
|
|
||||||
// We send the secret in the email in a link as a query param.
|
// We send the secret in the email in a link as a query param.
|
||||||
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authenticationSession).getEncodedId();
|
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authenticationSession).getEncodedId();
|
||||||
ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId);
|
ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, authenticationSession.getClient().getClientId());
|
||||||
String link = UriBuilder
|
String link = UriBuilder
|
||||||
.fromUri(context.getActionTokenUrl(token.serialize(context.getSession(), context.getRealm(), context.getUriInfo())))
|
.fromUri(context.getActionTokenUrl(token.serialize(context.getSession(), context.getRealm(), context.getUriInfo())))
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -143,7 +143,7 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
||||||
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
|
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
|
||||||
|
|
||||||
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
|
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
|
||||||
VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, user.getEmail());
|
VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, user.getEmail(), authSession.getClient().getClientId());
|
||||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
|
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
|
||||||
authSession.getClient().getClientId(), authSession.getTabId());
|
authSession.getClient().getClientId(), authSession.getTabId());
|
||||||
String link = builder.build(realm.getName()).toString();
|
String link = builder.build(realm.getName()).toString();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.testsuite.actions;
|
package org.keycloak.testsuite.actions;
|
||||||
|
|
||||||
import org.jboss.arquillian.drone.api.annotation.Drone;
|
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
|
import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -29,6 +30,7 @@ import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.EventRepresentation;
|
import org.keycloak.representations.idm.EventRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -43,9 +45,11 @@ import org.keycloak.testsuite.pages.InfoPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.RegisterPage;
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
||||||
|
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||||
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.MailUtils;
|
import org.keycloak.testsuite.util.MailUtils;
|
||||||
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.SecondBrowser;
|
import org.keycloak.testsuite.util.SecondBrowser;
|
||||||
import org.keycloak.testsuite.util.UserActionTokenBuilder;
|
import org.keycloak.testsuite.util.UserActionTokenBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
@ -384,9 +388,7 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
|
||||||
events.expectRequiredAction(EventType.VERIFY_EMAIL)
|
events.expectRequiredAction(EventType.VERIFY_EMAIL)
|
||||||
.user(testUserId)
|
.user(testUserId)
|
||||||
.detail(Details.CODE_ID, Matchers.not(Matchers.is(mailCodeId)))
|
.detail(Details.CODE_ID, Matchers.not(Matchers.is(mailCodeId)))
|
||||||
.client(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID) // as authentication sessions are browser-specific,
|
.client(oauth.getClientId()) // the "test-app" client specified in loginPage.open() is expected
|
||||||
// the client and redirect_uri is unrelated to
|
|
||||||
// the "test-app" specified in loginPage.open()
|
|
||||||
.detail(Details.REDIRECT_URI, Matchers.any(String.class))
|
.detail(Details.REDIRECT_URI, Matchers.any(String.class))
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
|
|
||||||
|
@ -629,6 +631,39 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyEmailNewBrowserSessionPreserveClient() throws IOException, MessagingException {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
|
||||||
|
verifyEmailPage.assertCurrent();
|
||||||
|
|
||||||
|
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getLastReceivedMessage();
|
||||||
|
|
||||||
|
String verificationUrl = getPasswordResetEmailLink(message);
|
||||||
|
|
||||||
|
// open link in the second browser without the session
|
||||||
|
driver2.navigate().to(verificationUrl.trim());
|
||||||
|
|
||||||
|
// follow the link
|
||||||
|
final WebElement proceedLink = driver2.findElement(By.linkText("» Click here to proceed"));
|
||||||
|
assertThat(proceedLink, Matchers.notNullValue());
|
||||||
|
|
||||||
|
// check if the initial client is preserved
|
||||||
|
String link = proceedLink.getAttribute("href");
|
||||||
|
assertThat(link, Matchers.containsString("client_id=test-app"));
|
||||||
|
proceedLink.click();
|
||||||
|
|
||||||
|
// confirmation in the second browser
|
||||||
|
assertThat(driver2.getPageSource(), Matchers.containsString("kc-info-message"));
|
||||||
|
assertThat(driver2.getPageSource(), Matchers.containsString("Your email address has been verified."));
|
||||||
|
|
||||||
|
final WebElement backToApplicationLink = driver2.findElement(By.linkText("« Back to Application"));
|
||||||
|
assertThat(backToApplicationLink, Matchers.notNullValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void verifyEmailDuringAuthFlow() throws IOException, MessagingException {
|
public void verifyEmailDuringAuthFlow() throws IOException, MessagingException {
|
||||||
try (Closeable u = new UserAttributeUpdater(testRealm().users().get(testUserId))
|
try (Closeable u = new UserAttributeUpdater(testRealm().users().get(testUserId))
|
||||||
|
|
|
@ -19,9 +19,11 @@ package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
@ -36,6 +38,7 @@ import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
||||||
import org.openqa.selenium.TimeoutException;
|
import org.openqa.selenium.TimeoutException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
|
import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
|
||||||
|
@ -142,6 +145,22 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
||||||
return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/account/password";
|
return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/account/password";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the login page for an existing client in provided realm
|
||||||
|
* @param realmName Name of the realm
|
||||||
|
* @param clientId ClientId of a client. Client has to exists in the realm.
|
||||||
|
* @return Login URL
|
||||||
|
*/
|
||||||
|
protected String getLoginUrl(String realmName, String clientId) {
|
||||||
|
List<ClientRepresentation> clients = adminClient.realm(realmName).clients().findByClientId(clientId);
|
||||||
|
|
||||||
|
assertThat(clients, Matchers.is(Matchers.not(Matchers.empty())));
|
||||||
|
|
||||||
|
String redirectURI = clients.get(0).getBaseUrl();
|
||||||
|
|
||||||
|
return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" +
|
||||||
|
clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=openid";
|
||||||
|
}
|
||||||
|
|
||||||
protected void logoutFromRealm(String realm) {
|
protected void logoutFromRealm(String realm) {
|
||||||
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
|
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.keycloak.testsuite.broker;
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -16,6 +19,7 @@ import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.pages.ConsentPage;
|
import org.keycloak.testsuite.pages.ConsentPage;
|
||||||
import org.keycloak.testsuite.util.*;
|
import org.keycloak.testsuite.util.*;
|
||||||
|
|
||||||
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.TimeoutException;
|
import org.openqa.selenium.TimeoutException;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -27,11 +31,14 @@ import java.util.stream.Collectors;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
|
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
|
||||||
|
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
|
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
|
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
|
||||||
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
|
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
@ -49,6 +56,10 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||||
|
|
||||||
protected IdentityProviderResource identityProviderResource;
|
protected IdentityProviderResource identityProviderResource;
|
||||||
|
|
||||||
|
@Drone
|
||||||
|
@SecondBrowser
|
||||||
|
protected WebDriver driver2;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeBrokerTest() {
|
public void beforeBrokerTest() {
|
||||||
log.debug("creating user for realm " + bc.providerRealmName());
|
log.debug("creating user for realm " + bc.providerRealmName());
|
||||||
|
@ -181,15 +192,10 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||||
MailServer.createEmailAccount(USER_EMAIL, "password");
|
MailServer.createEmailAccount(USER_EMAIL, "password");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//configure smpt server in the realm
|
configureSMPTServer();
|
||||||
RealmRepresentation master = adminClient.realm(bc.consumerRealmName()).toRepresentation();
|
|
||||||
master.setSmtpServer(suiteContext.getSmtpServer());
|
|
||||||
adminClient.realm(bc.consumerRealmName()).update(master);
|
|
||||||
|
|
||||||
//create user on consumer's site who should be linked later
|
//create user on consumer's site who should be linked later
|
||||||
UserRepresentation newUser = UserBuilder.create().username("consumer").email(USER_EMAIL).enabled(true).build();
|
String linkedUserId = createUser("consumer");
|
||||||
String userId = createUserWithAdminClient(adminClient.realm(bc.consumerRealmName()), newUser);
|
|
||||||
resetUserPassword(adminClient.realm(bc.consumerRealmName()).users().get(userId), "password", false);
|
|
||||||
|
|
||||||
//test
|
//test
|
||||||
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
|
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
|
||||||
|
@ -228,8 +234,74 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||||
assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
|
assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
|
||||||
|
|
||||||
//test if the user has verified email
|
//test if the user has verified email
|
||||||
assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(userId).toRepresentation().isEmailVerified());
|
assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(linkedUserId).toRepresentation().isEmailVerified());
|
||||||
} finally {
|
} finally {
|
||||||
|
removeUserByUsername(adminClient.realm(bc.consumerRealmName()), "consumer");
|
||||||
|
// stop mail server
|
||||||
|
MailServer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyEmailInNewBrowserWithPreserveClient() {
|
||||||
|
//start mail server
|
||||||
|
MailServer.start();
|
||||||
|
MailServer.createEmailAccount(USER_EMAIL, "password");
|
||||||
|
|
||||||
|
try {
|
||||||
|
configureSMPTServer();
|
||||||
|
|
||||||
|
//create user on consumer's site who should be linked later
|
||||||
|
String linkedUserId = createUser("consumer");
|
||||||
|
|
||||||
|
driver.navigate().to(getLoginUrl(bc.consumerRealmName(), "broker-app"));
|
||||||
|
|
||||||
|
log.debug("Clicking social " + bc.getIDPAlias());
|
||||||
|
accountLoginPage.clickSocial(bc.getIDPAlias());
|
||||||
|
|
||||||
|
waitForPage(driver, "log in to", true);
|
||||||
|
|
||||||
|
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||||
|
|
||||||
|
log.debug("Logging in");
|
||||||
|
accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
|
||||||
|
Assert.assertTrue(updateAccountInformationPage.isCurrent());
|
||||||
|
Assert.assertTrue("We must be on correct realm right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||||
|
|
||||||
|
log.debug("Updating info on updateAccount page");
|
||||||
|
updateAccountInformationPage.updateAccountInformation("Firstname", "Lastname");
|
||||||
|
|
||||||
|
//link account by email
|
||||||
|
waitForPage(driver, "account already exists", false);
|
||||||
|
idpConfirmLinkPage.clickLinkAccount();
|
||||||
|
|
||||||
|
String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, USER_EMAIL,
|
||||||
|
"Someone wants to link your ", false);
|
||||||
|
|
||||||
|
log.info("navigating to url from email in second browser: " + url);
|
||||||
|
|
||||||
|
// navigate to url in the second browser
|
||||||
|
driver2.navigate().to(url);
|
||||||
|
|
||||||
|
final WebElement proceedLink = driver2.findElement(By.linkText("» Click here to proceed"));
|
||||||
|
MatcherAssert.assertThat(proceedLink, Matchers.notNullValue());
|
||||||
|
|
||||||
|
// check if the initial client is preserved
|
||||||
|
String link = proceedLink.getAttribute("href");
|
||||||
|
MatcherAssert.assertThat(link, Matchers.containsString("client_id=broker-app"));
|
||||||
|
proceedLink.click();
|
||||||
|
|
||||||
|
assertThat(driver2.getPageSource(), Matchers.containsString("You successfully verified your email. Please go back to your original browser and continue there with the login."));
|
||||||
|
|
||||||
|
//test if the user has verified email
|
||||||
|
assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(linkedUserId).toRepresentation().isEmailVerified());
|
||||||
|
} finally {
|
||||||
|
removeUserByUsername(adminClient.realm(bc.consumerRealmName()), "consumer");
|
||||||
// stop mail server
|
// stop mail server
|
||||||
MailServer.stop();
|
MailServer.stop();
|
||||||
}
|
}
|
||||||
|
@ -431,4 +503,17 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||||
String link = errorPage.getBackToApplicationLink();
|
String link = errorPage.getBackToApplicationLink();
|
||||||
Assert.assertTrue(link.endsWith("/auth/realms/consumer/account"));
|
Assert.assertTrue(link.endsWith("/auth/realms/consumer/account"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureSMPTServer() {
|
||||||
|
RealmRepresentation master = adminClient.realm(bc.consumerRealmName()).toRepresentation();
|
||||||
|
master.setSmtpServer(suiteContext.getSmtpServer());
|
||||||
|
adminClient.realm(bc.consumerRealmName()).update(master);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createUser(String username) {
|
||||||
|
UserRepresentation newUser = UserBuilder.create().username(username).email(USER_EMAIL).enabled(true).build();
|
||||||
|
String userId = createUserWithAdminClient(adminClient.realm(bc.consumerRealmName()), newUser);
|
||||||
|
resetUserPassword(adminClient.realm(bc.consumerRealmName()).users().get(userId), "password", false);
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,20 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClientRepresentation> createConsumerClients(SuiteContext suiteContext) {
|
public List<ClientRepresentation> createConsumerClients(SuiteContext suiteContext) {
|
||||||
return null;
|
ClientRepresentation client = new ClientRepresentation();
|
||||||
|
client.setId("broker-app");
|
||||||
|
client.setClientId("broker-app");
|
||||||
|
client.setName("broker-app");
|
||||||
|
client.setSecret("broker-app-secret");
|
||||||
|
client.setEnabled(true);
|
||||||
|
|
||||||
|
client.setRedirectUris(Collections.singletonList(getAuthRoot(suiteContext) +
|
||||||
|
"/auth/*"));
|
||||||
|
|
||||||
|
client.setBaseUrl(getAuthRoot(suiteContext) +
|
||||||
|
"/auth/realms/" + REALM_CONS_NAME + "/app");
|
||||||
|
|
||||||
|
return Collections.singletonList(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -166,6 +166,15 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
||||||
.addRedirectUri("http://localhost:8080/sales-post/*")
|
.addRedirectUri("http://localhost:8080/sales-post/*")
|
||||||
.attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE)
|
.attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE)
|
||||||
.attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE)
|
.attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE)
|
||||||
|
.build(),
|
||||||
|
ClientBuilder.create()
|
||||||
|
.id("broker-app")
|
||||||
|
.clientId("broker-app")
|
||||||
|
.name("broker-app")
|
||||||
|
.secret("broker-app-secret")
|
||||||
|
.enabled(true)
|
||||||
|
.addRedirectUri(getAuthRoot(suiteContext) + "/auth/*")
|
||||||
|
.baseUrl(getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/app")
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.forms;
|
package org.keycloak.testsuite.forms;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken;
|
import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
@ -40,9 +42,11 @@ import org.keycloak.testsuite.pages.LoginPasswordResetPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||||
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
||||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||||
|
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.MailUtils;
|
import org.keycloak.testsuite.util.MailUtils;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.util.SecondBrowser;
|
||||||
import org.keycloak.testsuite.util.UserActionTokenBuilder;
|
import org.keycloak.testsuite.util.UserActionTokenBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
|
@ -57,7 +61,11 @@ import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,6 +76,10 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
@Drone
|
||||||
|
@SecondBrowser
|
||||||
|
protected WebDriver driver2;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
}
|
}
|
||||||
|
@ -182,7 +194,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
String changePasswordUrl = resetPassword("login-test");
|
String changePasswordUrl = resetPassword("login-test");
|
||||||
events.clear();
|
events.clear();
|
||||||
|
|
||||||
assertSecondPasswordResetFails(changePasswordUrl, null); // KC_RESTART doesn't exists, it was deleted after first successful reset-password flow was finished
|
assertSecondPasswordResetFails(changePasswordUrl, oauth.getClientId()); // KC_RESTART doesn't exists, it was deleted after first successful reset-password flow was finished
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -194,7 +206,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
driver.navigate().to(resetUri); // This is necessary to delete KC_RESTART cookie that is restricted to /auth/realms/test path
|
driver.navigate().to(resetUri); // This is necessary to delete KC_RESTART cookie that is restricted to /auth/realms/test path
|
||||||
driver.manage().deleteAllCookies();
|
driver.manage().deleteAllCookies();
|
||||||
|
|
||||||
assertSecondPasswordResetFails(changePasswordUrl, null);
|
assertSecondPasswordResetFails(changePasswordUrl, oauth.getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assertSecondPasswordResetFails(String changePasswordUrl, String clientId) {
|
public void assertSecondPasswordResetFails(String changePasswordUrl, String clientId) {
|
||||||
|
@ -204,7 +216,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
assertEquals("Action expired. Please continue with login now.", errorPage.getError());
|
assertEquals("Action expired. Please continue with login now.", errorPage.getError());
|
||||||
|
|
||||||
events.expect(EventType.RESET_PASSWORD)
|
events.expect(EventType.RESET_PASSWORD)
|
||||||
.client("account")
|
.client(clientId)
|
||||||
.session((String) null)
|
.session((String) null)
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.error(Errors.EXPIRED_CODE)
|
.error(Errors.EXPIRED_CODE)
|
||||||
|
@ -1012,4 +1024,36 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordLinkNewBrowserSessionPreserveClient() throws IOException, MessagingException {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.resetPassword();
|
||||||
|
|
||||||
|
resetPasswordPage.assertCurrent();
|
||||||
|
|
||||||
|
resetPasswordPage.changePassword("login-test");
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
assertEquals("You should receive an email shortly with further instructions.", loginPage.getSuccessMessage());
|
||||||
|
|
||||||
|
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||||
|
|
||||||
|
String changePasswordUrl = MailUtils.getPasswordResetEmailLink(message);
|
||||||
|
|
||||||
|
driver2.navigate().to(changePasswordUrl.trim());
|
||||||
|
|
||||||
|
final WebElement newPassword = driver2.findElement(By.id("password-new"));
|
||||||
|
newPassword.sendKeys("resetPassword");
|
||||||
|
final WebElement confirmPassword = driver2.findElement(By.id("password-confirm"));
|
||||||
|
confirmPassword.sendKeys("resetPassword");
|
||||||
|
final WebElement submit = driver2.findElement(By.cssSelector("input[type=\"submit\"]"));
|
||||||
|
submit.click();
|
||||||
|
|
||||||
|
assertThat(driver2.getCurrentUrl(), Matchers.containsString("client_id=test-app"));
|
||||||
|
|
||||||
|
assertThat(driver2.getPageSource(), Matchers.containsString("Your account has been updated."));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue