Merge pull request #4366 from hmlnarik/KEYCLOAK-4694-null
KEYCLOAK-4694
This commit is contained in:
commit
3b5ca2bac0
21 changed files with 297 additions and 22 deletions
|
@ -57,6 +57,8 @@ public interface Constants {
|
|||
String KEY = "key";
|
||||
|
||||
String SKIP_LINK = "skipLink";
|
||||
String TEMPLATE_ATTR_ACTION_URI = "actionUri";
|
||||
String TEMPLATE_ATTR_REQUIRED_ACTIONS = "requiredActions";
|
||||
|
||||
// Prefix for user attributes used in various "context"data maps
|
||||
String USER_ATTRIBUTES_PREFIX = "user.attributes.";
|
||||
|
|
|
@ -22,14 +22,20 @@ import org.keycloak.authentication.RequiredActionProvider;
|
|||
import org.keycloak.authentication.actiontoken.*;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import java.util.Objects;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -64,6 +70,21 @@ public class ExecuteActionsActionTokenHandler extends AbstractActionTokenHander<
|
|||
@Override
|
||||
public Response handleToken(ExecuteActionsActionToken token, ActionTokenContext<ExecuteActionsActionToken> tokenContext) {
|
||||
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
|
||||
final UriInfo uriInfo = tokenContext.getUriInfo();
|
||||
final RealmModel realm = tokenContext.getRealm();
|
||||
final KeycloakSession session = tokenContext.getSession();
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
// Update the authentication session in the token
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.CONFIRM_EXECUTION_OF_ACTIONS)
|
||||
.setAttribute(Constants.TEMPLATE_ATTR_ACTION_URI, confirmUri)
|
||||
.setAttribute(Constants.TEMPLATE_ATTR_REQUIRED_ACTIONS, token.getRequiredActions())
|
||||
.createInfoPage();
|
||||
}
|
||||
|
||||
String redirectUri = RedirectUtils.verifyRedirectUri(tokenContext.getUriInfo(), token.getRedirectUri(),
|
||||
tokenContext.getRealm(), authSession.getClient());
|
||||
|
|
|
@ -30,6 +30,7 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
|||
|
||||
private static final String JSON_FIELD_IDENTITY_PROVIDER_USERNAME = "idpu";
|
||||
private static final String JSON_FIELD_IDENTITY_PROVIDER_ALIAS = "idpa";
|
||||
private static final String JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID = "oasid";
|
||||
|
||||
@JsonProperty(value = JSON_FIELD_IDENTITY_PROVIDER_USERNAME)
|
||||
private String identityProviderUsername;
|
||||
|
@ -37,6 +38,9 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
|||
@JsonProperty(value = JSON_FIELD_IDENTITY_PROVIDER_ALIAS)
|
||||
private String identityProviderAlias;
|
||||
|
||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
||||
private String originalAuthenticationSessionId;
|
||||
|
||||
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String authenticationSessionId,
|
||||
String identityProviderUsername, String identityProviderAlias) {
|
||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, authenticationSessionId);
|
||||
|
@ -62,4 +66,12 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
|||
public void setIdentityProviderAlias(String identityProviderAlias) {
|
||||
this.identityProviderAlias = identityProviderAlias;
|
||||
}
|
||||
|
||||
public String getOriginalAuthenticationSessionId() {
|
||||
return originalAuthenticationSessionId;
|
||||
}
|
||||
|
||||
public void setOriginalAuthenticationSessionId(String originalAuthenticationSessionId) {
|
||||
this.originalAuthenticationSessionId = originalAuthenticationSessionId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,18 @@ import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAut
|
|||
import org.keycloak.events.*;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
import java.util.Collections;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
* Action token handler for verification of e-mail address.
|
||||
|
@ -58,6 +63,9 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
|||
public Response handleToken(IdpVerifyAccountLinkActionToken token, ActionTokenContext<IdpVerifyAccountLinkActionToken> tokenContext) {
|
||||
UserModel user = tokenContext.getAuthenticationSession().getAuthenticatedUser();
|
||||
EventBuilder event = tokenContext.getEvent();
|
||||
final UriInfo uriInfo = tokenContext.getUriInfo();
|
||||
final RealmModel realm = tokenContext.getRealm();
|
||||
final KeycloakSession session = tokenContext.getSession();
|
||||
|
||||
event.event(EventType.IDENTITY_PROVIDER_LINK_ACCOUNT)
|
||||
.detail(Details.EMAIL, user.getEmail())
|
||||
|
@ -65,16 +73,28 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
|||
.detail(Details.IDENTITY_PROVIDER_USERNAME, token.getIdentityProviderUsername())
|
||||
.success();
|
||||
|
||||
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
token.setOriginalAuthenticationSessionId(token.getAuthenticationSessionId());
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.CONFIRM_ACCOUNT_LINKING, token.getIdentityProviderUsername(), token.getIdentityProviderAlias())
|
||||
.setAttribute(Constants.TEMPLATE_ATTR_ACTION_URI, confirmUri)
|
||||
.createInfoPage();
|
||||
}
|
||||
|
||||
// verify user email as we know it is valid as this entry point would never have gotten here.
|
||||
user.setEmailVerified(true);
|
||||
|
||||
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
AuthenticationSessionManager asm = new AuthenticationSessionManager(tokenContext.getSession());
|
||||
asm.removeAuthenticationSession(tokenContext.getRealm(), authSession, true);
|
||||
if (token.getOriginalAuthenticationSessionId() != null) {
|
||||
AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
|
||||
asm.removeAuthenticationSession(realm, authSession, true);
|
||||
|
||||
AuthenticationSessionProvider authSessProvider = tokenContext.getSession().authenticationSessions();
|
||||
authSession = authSessProvider.getAuthenticationSession(tokenContext.getRealm(), token.getAuthenticationSessionId());
|
||||
AuthenticationSessionProvider authSessProvider = session.authenticationSessions();
|
||||
authSession = authSessProvider.getAuthenticationSession(realm, token.getOriginalAuthenticationSessionId());
|
||||
|
||||
if (authSession != null) {
|
||||
authSession.setAuthNote(IdpEmailVerificationAuthenticator.VERIFY_ACCOUNT_IDP_USERNAME, token.getIdentityProviderUsername());
|
||||
|
@ -85,7 +105,7 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
|||
);
|
||||
}
|
||||
|
||||
return tokenContext.getSession().getProvider(LoginFormsProvider.class)
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.IDENTITY_PROVIDER_LINK_SUCCESS, token.getIdentityProviderAlias(), token.getIdentityProviderUsername())
|
||||
.setAttribute(Constants.SKIP_LINK, true)
|
||||
.createInfoPage();
|
||||
|
|
|
@ -29,10 +29,14 @@ public class VerifyEmailActionToken extends DefaultActionToken {
|
|||
public static final String TOKEN_TYPE = "verify-email";
|
||||
|
||||
private static final String JSON_FIELD_EMAIL = "eml";
|
||||
private static final String JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID = "oasid";
|
||||
|
||||
@JsonProperty(value = JSON_FIELD_EMAIL)
|
||||
private String email;
|
||||
|
||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
||||
private String originalAuthenticationSessionId;
|
||||
|
||||
public VerifyEmailActionToken(String userId, int absoluteExpirationInSecs, String authenticationSessionId, String email) {
|
||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, authenticationSessionId);
|
||||
this.email = email;
|
||||
|
@ -48,4 +52,12 @@ public class VerifyEmailActionToken extends DefaultActionToken {
|
|||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getOriginalAuthenticationSessionId() {
|
||||
return originalAuthenticationSessionId;
|
||||
}
|
||||
|
||||
public void setOriginalAuthenticationSessionId(String originalAuthenticationSessionId) {
|
||||
this.originalAuthenticationSessionId = originalAuthenticationSessionId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,20 @@ import org.keycloak.TokenVerifier.Predicate;
|
|||
import org.keycloak.authentication.actiontoken.*;
|
||||
import org.keycloak.events.*;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import java.util.Objects;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
* Action token handler for verification of e-mail address.
|
||||
|
@ -57,13 +63,29 @@ public class VerifyEmailActionTokenHandler extends AbstractActionTokenHander<Ver
|
|||
}
|
||||
|
||||
@Override
|
||||
public Response handleToken(VerifyEmailActionToken token, ActionTokenContext<VerifyEmailActionToken> tokenContext) {
|
||||
public Response handleToken(VerifyEmailActionToken token, ActionTokenContext<VerifyEmailActionToken> tokenContext) {
|
||||
UserModel user = tokenContext.getAuthenticationSession().getAuthenticatedUser();
|
||||
EventBuilder event = tokenContext.getEvent();
|
||||
|
||||
event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail());
|
||||
|
||||
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
|
||||
final UriInfo uriInfo = tokenContext.getUriInfo();
|
||||
final RealmModel realm = tokenContext.getRealm();
|
||||
final KeycloakSession session = tokenContext.getSession();
|
||||
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
// Update the authentication session in the token
|
||||
token.setOriginalAuthenticationSessionId(token.getAuthenticationSessionId());
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.CONFIRM_EMAIL_ADDRESS_VERIFICATION, user.getEmail())
|
||||
.setAttribute(Constants.TEMPLATE_ATTR_ACTION_URI, confirmUri)
|
||||
.createInfoPage();
|
||||
}
|
||||
|
||||
// verify user email as we know it is valid as this entry point would never have gotten here.
|
||||
user.setEmailVerified(true);
|
||||
|
@ -72,9 +94,10 @@ public class VerifyEmailActionTokenHandler extends AbstractActionTokenHander<Ver
|
|||
|
||||
event.success();
|
||||
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
if (token.getOriginalAuthenticationSessionId() != null) {
|
||||
AuthenticationSessionManager asm = new AuthenticationSessionManager(tokenContext.getSession());
|
||||
asm.removeAuthenticationSession(tokenContext.getRealm(), authSession, true);
|
||||
|
||||
return tokenContext.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.EMAIL_VERIFIED)
|
||||
.createInfoPage();
|
||||
|
@ -82,8 +105,8 @@ public class VerifyEmailActionTokenHandler extends AbstractActionTokenHander<Ver
|
|||
|
||||
tokenContext.setEvent(event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN));
|
||||
|
||||
String nextAction = AuthenticationManager.nextRequiredAction(tokenContext.getSession(), authSession, tokenContext.getClientConnection(), tokenContext.getRequest(), tokenContext.getUriInfo(), event);
|
||||
return AuthenticationManager.redirectToRequiredActions(tokenContext.getSession(), tokenContext.getRealm(), authSession, tokenContext.getUriInfo(), nextAction);
|
||||
String nextAction = AuthenticationManager.nextRequiredAction(session, authSession, tokenContext.getClientConnection(), tokenContext.getRequest(), uriInfo, event);
|
||||
return AuthenticationManager.redirectToRequiredActions(session, realm, authSession, uriInfo, nextAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
|
||||
@Override
|
||||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
@ -112,7 +112,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
setRealm(session.getContext().getRealm());
|
||||
setUser(user);
|
||||
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("realmName", realm.getName());
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
|
||||
@Override
|
||||
public void sendConfirmIdentityBrokerLink(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
@ -142,7 +142,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
|
||||
@Override
|
||||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
@ -155,7 +155,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
|
||||
@Override
|
||||
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
|
|
@ -449,7 +449,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
public Response createIdpLinkEmailPage() {
|
||||
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
|
||||
String idpAlias = brokerContext.getIdpConfig().getAlias();
|
||||
idpAlias = ObjectUtil.capitalize(idpAlias);;
|
||||
idpAlias = ObjectUtil.capitalize(idpAlias);
|
||||
setMessage(MessageType.WARNING, Messages.LINK_IDP, idpAlias);
|
||||
|
||||
return createResponse(LoginFormsPages.LOGIN_IDP_LINK_EMAIL);
|
||||
|
|
|
@ -160,6 +160,12 @@ public class Messages {
|
|||
|
||||
public static final String IDENTITY_PROVIDER_LINK_SUCCESS = "identityProviderLinkSuccess";
|
||||
|
||||
public static final String CONFIRM_ACCOUNT_LINKING = "confirmAccountLinking";
|
||||
|
||||
public static final String CONFIRM_EMAIL_ADDRESS_VERIFICATION = "confirmEmailAddressVerification";
|
||||
|
||||
public static final String CONFIRM_EXECUTION_OF_ACTIONS = "confirmExecutionOfActions";
|
||||
|
||||
public static final String STALE_CODE = "staleCodeMessage";
|
||||
|
||||
public static final String STALE_CODE_ACCOUNT = "staleCodeAccountMessage";
|
||||
|
|
|
@ -704,6 +704,7 @@ public class UserResource {
|
|||
String link = builder.build(realm.getName()).toString();
|
||||
|
||||
this.session.getProvider(EmailTemplateProvider.class)
|
||||
.setAttribute(Constants.TEMPLATE_ATTR_REQUIRED_ACTIONS, token.getRequiredActions())
|
||||
.setRealm(realm)
|
||||
.setUser(user)
|
||||
.sendExecuteActions(link, TimeUnit.SECONDS.toMinutes(lifespan));
|
||||
|
|
|
@ -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.testsuite.pages;
|
||||
|
||||
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 String getInfo() {
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getPageSource().contains("kc-info-message") && proceedLink.isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clickProceedLink() {
|
||||
proceedLink.click();
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
|||
import org.keycloak.testsuite.auth.page.AuthRealm;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.ProceedPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
@ -81,6 +82,9 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
|
|||
@Page
|
||||
protected InfoPage infoPage;
|
||||
|
||||
@Page
|
||||
protected ProceedPage proceedPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
|
@ -330,6 +334,8 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
|
|||
|
||||
driver.navigate().to(verificationUrl2.trim());
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
proceedPage.clickProceedLink();
|
||||
infoPage.assertCurrent();
|
||||
assertEquals("Your email address has been verified.", infoPage.getInfo());
|
||||
}
|
||||
|
@ -355,6 +361,9 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
|
|||
driver.manage().deleteAllCookies();
|
||||
|
||||
driver.navigate().to(verificationUrl.trim());
|
||||
proceedPage.assertCurrent();
|
||||
proceedPage.clickProceedLink();
|
||||
infoPage.assertCurrent();
|
||||
|
||||
events.expectRequiredAction(EventType.VERIFY_EMAIL)
|
||||
.user(testUserId)
|
||||
|
|
|
@ -55,6 +55,7 @@ import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
|||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.ProceedPage;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
|
@ -107,6 +108,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
@Page
|
||||
protected InfoPage infoPage;
|
||||
|
||||
@Page
|
||||
protected ProceedPage proceedPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
|
@ -628,6 +632,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
@ -664,6 +671,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
|
||||
|
@ -706,6 +716,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass" + i, "new-pass" + i);
|
||||
|
@ -744,6 +757,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
driver.manage().deleteAllCookies();
|
||||
|
@ -751,6 +767,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
@ -850,6 +869,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
@ -910,6 +932,9 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
|
||||
proceedPage.clickProceedLink();
|
||||
passwordUpdatePage.assertCurrent();
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
@ -981,11 +1006,17 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
driver.navigate().to(link);
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Verify Email"));
|
||||
proceedPage.clickProceedLink();
|
||||
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
|
||||
driver.navigate().to("about:blank");
|
||||
|
||||
driver.navigate().to(link); // It should be possible to use the same action token multiple times
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Verify Email"));
|
||||
proceedPage.clickProceedLink();
|
||||
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.testsuite.pages.InfoPage;
|
|||
import org.keycloak.testsuite.pages.LoginExpiredPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
||||
import org.keycloak.testsuite.pages.ProceedPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
|
@ -52,6 +53,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -345,6 +347,9 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProvi
|
|||
// Go to the same link again
|
||||
driver.navigate().to(linkFromMail.trim());
|
||||
|
||||
proceedPage.assertCurrent();
|
||||
Assert.assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm linking the account"));
|
||||
proceedPage.clickProceedLink();
|
||||
infoPage.assertCurrent();
|
||||
Assert.assertThat(infoPage.getInfo(), startsWith("You successfully verified your email. Please go back to your original browser and continue there with the login."));
|
||||
}
|
||||
|
@ -379,10 +384,14 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProvi
|
|||
|
||||
WebDriver driver2 = webRule2.getDriver();
|
||||
InfoPage infoPage2 = webRule2.getPage(InfoPage.class);
|
||||
ProceedPage proceedPage2 = webRule2.getPage(ProceedPage.class);
|
||||
|
||||
driver2.navigate().to(linkFromMail.trim());
|
||||
|
||||
// authenticated, but not redirected to app. Just seeing info page.
|
||||
proceedPage2.assertCurrent();
|
||||
Assert.assertThat(proceedPage2.getInfo(), Matchers.containsString("Confirm linking the account"));
|
||||
proceedPage2.clickProceedLink();
|
||||
infoPage2.assertCurrent();
|
||||
Assert.assertThat(infoPage2.getInfo(), startsWith("You successfully verified your email. Please go back to your original browser and continue there with the login."));
|
||||
} finally {
|
||||
|
|
|
@ -110,6 +110,9 @@ public abstract class AbstractIdentityProviderTest {
|
|||
@WebResource
|
||||
protected InfoPage infoPage;
|
||||
|
||||
@WebResource
|
||||
protected ProceedPage proceedPage;
|
||||
|
||||
protected KeycloakSession session;
|
||||
|
||||
protected int logoutTimeOffset = 0;
|
||||
|
|
|
@ -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.testsuite.pages;
|
||||
|
||||
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 String getInfo() {
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getPageSource().contains("kc-info-message") && proceedLink.isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clickProceedLink() {
|
||||
proceedLink.click();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
<#assign requiredActionsText>
|
||||
<#if requiredActions??><#list requiredActions><b><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></b></#list><#else></#if>
|
||||
</#assign>
|
||||
<html>
|
||||
<body>
|
||||
${msg("executeActionsBodyHtml",link, linkExpiration, realmName)}
|
||||
${msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText)}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,8 +11,8 @@ passwordResetSubject=Reset password
|
|||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">Link to reset credentials</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsSubject=Update Your Account
|
||||
executeActionsBody=Your administrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your administrator has just requested that you update your {2} account. Click on the link below to start this process.</p><p><a href="{0}">Link to account update</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsBody=Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.</p><p><a href="{0}">Link to account update</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
eventLoginErrorSubject=Login error
|
||||
eventLoginErrorBody=A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.
|
||||
eventLoginErrorBodyHtml=<p>A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.</p>
|
||||
|
@ -25,3 +25,9 @@ eventUpdatePasswordBodyHtml=<p>Your password was changed on {0} from {1}. If thi
|
|||
eventUpdateTotpSubject=Update TOTP
|
||||
eventUpdateTotpBody=TOTP was updated for your account on {0} from {1}. If this was not you, please contact an admin.
|
||||
eventUpdateTotpBodyHtml=<p>TOTP was updated for your account on {0} from {1}. If this was not you, please contact an admin.</p>
|
||||
|
||||
requiredAction.CONFIGURE_TOTP=Configure OTP
|
||||
requiredAction.terms_and_conditions=Terms and Conditions
|
||||
requiredAction.UPDATE_PASSWORD=Update Password
|
||||
requiredAction.UPDATE_PROFILE=Update Profile
|
||||
requiredAction.VERIFY_EMAIL=Verify Email
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
${msg("executeActionsBody",link, linkExpiration, realmName)}
|
||||
<#assign requiredActionsText>
|
||||
<#if requiredActions??><#list requiredActions><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></#list><#else></#if>
|
||||
</#assign>
|
||||
${msg("executeActionsBody",link, linkExpiration, realmName, requiredActionsText)}
|
|
@ -6,11 +6,13 @@
|
|||
${message.summary}
|
||||
<#elseif section = "form">
|
||||
<div id="kc-info-message">
|
||||
<p class="instruction">${message.summary}</p>
|
||||
<p class="instruction">${message.summary}<#if requiredActions??><#list requiredActions>: <b><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></b></#list><#else></#if></p>
|
||||
<#if skipLink??>
|
||||
<#else>
|
||||
<#if pageRedirectUri??>
|
||||
<p><a href="${pageRedirectUri}">${msg("backToApplication")}</a></p>
|
||||
<#elseif actionUri??>
|
||||
<p><a href="${actionUri}">${msg("proceedWithAction")}</a></p>
|
||||
<#elseif client.baseUrl??>
|
||||
<p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
|
||||
</#if>
|
||||
|
|
|
@ -219,6 +219,9 @@ identityProviderNotUniqueMessage=Realm supports multiple identity providers. Cou
|
|||
emailVerifiedMessage=Your email address has been verified.
|
||||
staleEmailVerificationLink=The link you clicked is a old stale link and is no longer valid. Maybe you have already verified your email?
|
||||
identityProviderAlreadyLinkedMessage=Federated identity returned by {0} is already linked to another user.
|
||||
confirmAccountLinking=Confirm linking the account {0} of identity provider {1} with your account.
|
||||
confirmEmailAddressVerification=Confirm validity of e-mail address {0}.
|
||||
confirmExecutionOfActions=Perform the following action(s)
|
||||
|
||||
locale_ca=Catal\u00E0
|
||||
locale_de=Deutsch
|
||||
|
@ -242,5 +245,12 @@ invalidParameterMessage=Invalid parameter\: {0}
|
|||
alreadyLoggedIn=You are already logged in.
|
||||
differentUserAuthenticated=You are already authenticated as different user ''{0}'' in this session. Please logout first.
|
||||
brokerLinkingSessionExpired=Requested broker account linking, but current session is no longer valid.
|
||||
proceedWithAction=» Click here to proceed
|
||||
|
||||
requiredAction.CONFIGURE_TOTP=Configure OTP
|
||||
requiredAction.terms_and_conditions=Terms and Conditions
|
||||
requiredAction.UPDATE_PASSWORD=Update Password
|
||||
requiredAction.UPDATE_PROFILE=Update Profile
|
||||
requiredAction.VERIFY_EMAIL=Verify Email
|
||||
|
||||
p3pPolicy=CP="This is not a P3P policy!"
|
||||
|
|
Loading…
Reference in a new issue