keycloak-memoizeit

Merge pull request #67 from vrockai/KEYCLOAK-84 KEYCLOAK-84

10/15/2013 6:26:27 AM

Details

diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java
index fdb11fe..cb82aaa 100644
--- a/forms/src/main/java/org/keycloak/forms/UrlBean.java
+++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java
@@ -120,6 +120,10 @@ public class UrlBean {
         return Urls.accountTotpPage(baseURI, realm.getId()).toString();
     }
 
+    public String getTotpRemoveUrl() {
+        return Urls.accountTotpRemove(baseURI, realm.getId()).toString();
+    }
+
     public String getEmailVerificationUrl() {
         return Urls.accountEmailVerification(baseURI, realm.getId()).toString();
     }
diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
index 1cd60fa..27c2df1 100644
--- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
+++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
@@ -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);
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css
index c3b1bd7..04e3340 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css
@@ -60,6 +60,7 @@ body {
 }
 .header.rcue .navbar {
   margin-bottom: 0;
+  z-index:auto;
 }
 .header.rcue .navbar.primary {
   font-size: 13px;
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl
index e456284..253ab75 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl
@@ -25,7 +25,7 @@
             <!-- <p>Forgot <a href="#">Username</a> or <a href="#">Password</a>?</p> -->
         </div>
 
-        <input type="submit" value="Log In" />
+        <input class="btn-primary" type="submit" value="Log In" />
     </form>
 
     <#elseif section = "info">
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
index d8cca1f..ac7e496 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl
@@ -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>
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-main.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-main.ftl
index 3cc7975..7cf9f71 100644
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-main.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-main.ftl
@@ -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>
 
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
index 63de6ef..5f4d6dc 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
@@ -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,11 +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="user-totp-setup.html" class="button">Remove Google</a></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>
 
diff --git a/forms/src/main/resources/org/keycloak/forms/messages.properties b/forms/src/main/resources/org/keycloak/forms/messages.properties
index 92722f9..67b737f 100644
--- a/forms/src/main/resources/org/keycloak/forms/messages.properties
+++ b/forms/src/main/resources/org/keycloak/forms/messages.properties
@@ -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
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index b78d9ed..2874971 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -164,6 +164,15 @@ public class AccountService {
         return accessCodeEntry;
     }
 
+    @Path("totp-remove")
+    @GET
+    public Response processTotpRemove() {
+        UserModel user = getUserFromAuthManager();
+        user.setTotp(false);
+        return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.ErrorType.SUCCESS)
+                .setUser(user).forwardToTotp();
+    }
+
     @Path("totp")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@@ -206,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();
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index 1da6a61..b6a86a2 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -55,6 +55,10 @@ public class Urls {
         return accountBase(baseUri).path(AccountService.class, "totpPage").build(realmId);
     }
 
+    public static URI accountTotpRemove(URI baseUri, String realmId) {
+        return accountBase(baseUri).path(AccountService.class, "processTotpRemove").build(realmId);
+    }
+
     public static URI accountEmailVerification(URI baseUri, String realmId) {
         return accountBase(baseUri).path(AccountService.class, "emailVerification").build(realmId);
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
index 916513d..bb70d97 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
@@ -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);
         }
 
     });
@@ -77,6 +76,12 @@ public class RequiredActionTotpSetupTest {
     protected LoginConfigTotpPage totpPage;
 
     @WebResource
+    protected AccountTotpPage accountTotpPage;
+
+    @WebResource
+    protected OAuthClient oauth;
+
+    @WebResource
     protected RegisterPage registerPage;
 
     protected TimeBasedOTP totp = new TimeBasedOTP();
@@ -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());
+
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java
index 32cfb80..11cc502 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java
@@ -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();
+    }
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index 1d9c949..d099690 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -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;
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java
index cb56000..d38e2ac 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java
@@ -35,7 +35,7 @@ public abstract class Page {
 
     public void assertCurrent() {
         String name = getClass().getSimpleName();
-        Assert.assertTrue("Exptected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")",
+        Assert.assertTrue("Expected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")",
                 isCurrent());
     }