KEYCLOAK-84 added totp test and better form feedback

This commit is contained in:
vrockai 2013-10-11 12:37:03 +02:00
parent a0808ad224
commit 23f2fb9125
10 changed files with 122 additions and 19 deletions

View file

@ -81,6 +81,10 @@ public class FormServiceImpl implements FormService {
Map<String, Object> attributes = new HashMap<String, Object>();
if (dataBean.getError() != null){
attributes.put("message", new ErrorBean(dataBean.getError(), dataBean.getErrorType()));
}
RealmBean realm = new RealmBean(dataBean.getRealm());
attributes.put("template", new TemplateBean(realm, dataBean.getContextPath()));
@ -145,10 +149,6 @@ public class FormServiceImpl implements FormService {
private class CommandPassword implements Command {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
if (dataBean.getError() != null){
attributes.put("message", new ErrorBean(dataBean.getError(), dataBean.getErrorType()));
}
RealmBean realm = new RealmBean(dataBean.getRealm());
attributes.put("realm", realm);

View file

@ -60,6 +60,7 @@ body {
}
.header.rcue .navbar {
margin-bottom: 0;
z-index:auto;
}
.header.rcue .navbar.primary {
font-size: 13px;

View file

@ -35,7 +35,8 @@
</div>
<#if error?has_content>
<div class="feedback error bottom-left show">
<div class="
error bottom-left show">
<p>
<strong id="loginError">${rb.getString(error.summary)}</strong>
</p>

View file

@ -3,7 +3,7 @@
<html>
<head>
<meta charset="utf-8">
<title>Edit Account</title>
<title>Edit Account - <#nested "title"></title>
<link rel="icon" href="img/favicon.ico">
<!-- Frameworks -->
@ -34,10 +34,14 @@
</head>
<body class="admin-console user ${bodyClass}">
<#if error?has_content>
<!--div class="feedback success show"><p><strong>Success!</strong> Your changes have been saved.</p></div-->
<#if message?has_content>
<div class="feedback-aligner">
<div class="alert alert-danger">${rb.getString(error.summary)}</div>
<#if message.success>
<div class="feedback success show"><p><strong>${rb.getString('successHeader')}</strong> ${rb.getString(message.summary)}</p></div>
</#if>
<#if message.error>
<div class="feedback error show"><p><strong>${rb.getString('errorHeader')}</strong> ${rb.getString(message.summary)}</p></div>
</#if>
</div>
</#if>

View file

@ -1,7 +1,9 @@
<#import "template-main.ftl" as layout>
<@layout.mainLayout active='totp' bodyClass='totp'; section>
<#if section = "header">
<#if section = "title">
Google Authenticator
<#elseif section = "header">
<#if totp.enabled>
<h2>Authenticators</h2>
@ -12,7 +14,6 @@
<#elseif section = "content">
<#if totp.enabled>
<#-- TODO this is only mock page -->
<form>
<fieldset>
<p class="info">You have the following authenticators set up:</p>
@ -21,13 +22,16 @@
<tbody>
<tr>
<td class="provider"><span class="social googleplus">Google</span></td>
<td class="soft">Connected as john@google.com</td>
<td class="action">
<a href="${url.totpRemoveUrl}" class="button">Remove Google</a>
</td>
</tr>
</tbody>
</table>
<p class="info">
If the totp authentication is required by the realm and you remove your configured authenticator,
you will have to reconfigure it immediately or on the next login.
</p>
</fieldset>
</form>

View file

@ -37,6 +37,9 @@ invalidPasswordExisting=Invalid existing password
invalidPasswordConfirm=Password confirmation doesn't match
invalidTotp=Invalid authenticator code
successTotp=Google authenticator configured.
successTotpRemoved=Google authenticator removed.
usernameExists=Username already exists
error=A system error has occured, contact admin

View file

@ -169,7 +169,8 @@ public class AccountService {
public Response processTotpRemove() {
UserModel user = getUserFromAuthManager();
user.setTotp(false);
return Flows.forms(realm, request, uriInfo).setUser(user).forwardToTotp();
return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.ErrorType.SUCCESS)
.setUser(user).forwardToTotp();
}
@Path("totp")
@ -214,7 +215,8 @@ public class AccountService {
if (accessCodeEntry != null) {
return redirectOauth(user, accessCodeEntry);
} else {
return Flows.forms(realm, request, uriInfo).setUser(user).forwardToTotp();
return Flows.forms(realm, request, uriInfo).setError("successTotp").setErrorType(FormFlows.ErrorType.SUCCESS)
.setUser(user).forwardToTotp();
}
}

View file

@ -30,6 +30,8 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountTotpPage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginConfigTotpPage;
@ -54,9 +56,6 @@ public class RequiredActionTotpSetupTest {
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
appRealm.addRequiredCredential(CredentialRepresentation.TOTP);
appRealm.setResetPasswordAllowed(true);
UserModel user = appRealm.getUser("test-user@localhost");
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
}
});
@ -76,6 +75,12 @@ public class RequiredActionTotpSetupTest {
@WebResource
protected LoginConfigTotpPage totpPage;
@WebResource
protected AccountTotpPage accountTotpPage;
@WebResource
protected OAuthClient oauth;
@WebResource
protected RegisterPage registerPage;
@ -101,9 +106,69 @@ public class RequiredActionTotpSetupTest {
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
String totpSecret = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpSecret));
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
oauth.openLogout();
loginPage.open();
loginPage.loginTotp("test-user@localhost", "password", totp.generate(totpSecret));
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
@Test
public void setupTotpRegisteredAfterTotpRemoval() {
// Register new user
loginPage.open();
loginPage.clickRegister();
registerPage.register("firstName2", "lastName2", "email2", "setupTotp2", "password2", "password2");
// Configure totp
totpPage.assertCurrent();
String totpCode = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpCode));
// After totp config, user should be on the app page
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
// Logout
oauth.openLogout();
// Try to login after logout
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Totp is already configured, thus one-time password is needed, login page should be loaded
Assert.assertTrue(loginPage.isCurrent());
Assert.assertFalse(totpPage.isCurrent());
// Login with one-time password
loginPage.loginTotp("setupTotp2", "password2", totp.generate(totpCode));
// Open account page
accountTotpPage.open();
accountTotpPage.assertCurrent();
// Remove google authentificator
accountTotpPage.removeTotp();
// Logout
oauth.openLogout();
// Try to login
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Since the authentificator was removed, it has to be set up again
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
}

View file

@ -41,6 +41,9 @@ public class AccountTotpPage extends Page {
@FindBy(css = "button[type=\"submit\"]")
private WebElement submitButton;
@FindBy(linkText = "Remove Google")
private WebElement removeLink;
public void configure(String totp) {
totpInput.sendKeys(totp);
submitButton.click();
@ -51,11 +54,15 @@ public class AccountTotpPage extends Page {
}
public boolean isCurrent() {
return driver.getPageSource().contains("Google Authenticator Setup");
return driver.getTitle().contains("Edit Account - Google Authenticator");
}
public void open() {
driver.navigate().to(PATH);
}
public void removeTotp() {
removeLink.click();
}
}

View file

@ -41,6 +41,9 @@ public class LoginPage extends Page {
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(id = "totp")
private WebElement totp;
@FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton;
@ -63,6 +66,19 @@ public class LoginPage extends Page {
submitButton.click();
}
public void loginTotp(String username, String password, String code) {
usernameInput.clear();
usernameInput.sendKeys(username);
passwordInput.clear();
passwordInput.sendKeys(password);
totp.clear();
totp.sendKeys(code);
submitButton.click();
}
public String getError() {
return loginErrorMessage != null ? loginErrorMessage.getText() : null;
}