You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by mg...@apache.org on 2015/01/03 17:52:07 UTC

isis git commit: ISIS-987 Provide some sort of mechanism to allow users to self-register for an Isis application.

Repository: isis
Updated Branches:
  refs/heads/ISIS-987 790470c80 -> 06dc69a5e


ISIS-987 Provide some sort of mechanism to allow users to self-register for an Isis application.

Implement password reset functionality.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/06dc69a5
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/06dc69a5
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/06dc69a5

Branch: refs/heads/ISIS-987
Commit: 06dc69a5eae6628dab45aa9682c0d1b4e0864e28
Parents: 790470c
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Sat Jan 3 18:51:29 2015 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Sat Jan 3 18:51:29 2015 +0200

----------------------------------------------------------------------
 .../wicket/ui/pages/login/IsisSignInPanel.java  |  30 ++--
 .../signup/AccountConfirmationMap.java          | 153 -------------------
 .../signup/PasswordResetEmailPanel.java         |   6 +-
 .../signup/PasswordResetPage.java               |  21 ++-
 .../signup/PasswordResetPage.properties         |   2 +
 .../signup/PasswordResetPanel.java              |  78 +++++-----
 .../wicket/ui/pages/register/RegisterPanel.html |   2 +-
 .../wicket/ui/pages/register/RegisterPanel.java |  51 ++++---
 .../pages/signup/EmailAvailableValidator.java   |  28 ++--
 .../ui/pages/signup/RegistrationFormPanel.java  |   2 +-
 .../userreg/UserRegistrationService.java        |   2 +-
 .../runtime/system/context/IsisContext.java     |  46 +++++-
 12 files changed, 167 insertions(+), 254 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/login/IsisSignInPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/login/IsisSignInPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/login/IsisSignInPanel.java
index 71120fe..e8b657f 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/login/IsisSignInPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/login/IsisSignInPanel.java
@@ -28,7 +28,6 @@ import org.apache.wicket.authroles.authentication.panel.SignInPanel;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
 import org.apache.isis.applib.services.userreg.UserRegistrationService;
 import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 import org.apache.isis.viewer.wicket.model.models.PageType;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
 
@@ -75,13 +74,15 @@ public class IsisSignInPanel extends SignInPanel {
     }
 
     private void addPasswdResetLink(String id) {
-        // TODO ISIS-987 is this the correct way to check for the service availability here ? Open a session, etc.
-        IsisContext.openSession(new InitialisationSession());
-        Class<? extends Page> passwdResetPageClass = pageClassRegistry.getPageClass(PageType.PASSWORD_RESET);
-        BookmarkablePageLink<Void> passwdResetLink = new BookmarkablePageLink<>(id, passwdResetPageClass);
-        final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
-        passwdResetLink.setVisibilityAllowed(true);//userRegistrationService != null);
-        IsisContext.closeSession();
+        Class < ?extends Page> passwdResetPageClass = pageClassRegistry.getPageClass(PageType.PASSWORD_RESET);
+        final BookmarkablePageLink<Void> passwdResetLink = new BookmarkablePageLink<>(id, passwdResetPageClass);
+        IsisContext.doInSession(new Runnable() {
+            @Override
+            public void run() {
+                final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
+                passwdResetLink.setVisibilityAllowed(userRegistrationService != null);
+            }
+        });
         getSignInForm().addOrReplace(passwdResetLink);
     }
 
@@ -90,12 +91,15 @@ public class IsisSignInPanel extends SignInPanel {
     }
 
     protected void addSignUpLink(String id) {
-        // TODO ISIS-987 is this the correct way to check for the service availability here ? Open a session, etc.
-        IsisContext.openSession(new InitialisationSession());
         Class<? extends Page> signUpPageClass = pageClassRegistry.getPageClass(PageType.SIGN_UP);
-        BookmarkablePageLink<Void> signUpLink = new BookmarkablePageLink<>(id, signUpPageClass);
-        final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
-        signUpLink.setVisibilityAllowed(true);//userRegistrationService != null);
+        final BookmarkablePageLink<Void> signUpLink = new BookmarkablePageLink<>(id, signUpPageClass);
+        IsisContext.doInSession(new Runnable() {
+            @Override
+            public void run() {
+                final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
+                signUpLink.setVisibilityAllowed(userRegistrationService != null);
+            }
+        });
         IsisContext.closeSession();
 
         getSignInForm().addOrReplace(signUpLink);

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/AccountConfirmationMap.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/AccountConfirmationMap.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/AccountConfirmationMap.java
deleted file mode 100644
index 72dfd64..0000000
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/AccountConfirmationMap.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.apache.isis.viewer.wicket.ui.pages.password_reset.signup;
-
-import java.util.Map;
-import org.apache.wicket.MetaDataKey;
-import org.apache.wicket.util.collections.MostRecentlyUsedMap;
-import org.apache.wicket.util.time.Duration;
-import org.apache.wicket.util.time.Time;
-
-/**
- * A map that contains the emails to be verified. It has a constraint on the maximum entries that it
- * can contain, and a constraint on the duration of time an entry is considered valid/non-expired
- */
-public class AccountConfirmationMap extends MostRecentlyUsedMap<String, Object>
-{
-	private static final long serialVersionUID = 1L;
-
-	public static final MetaDataKey<AccountConfirmationMap> KEY = new MetaDataKey<AccountConfirmationMap>() {
-	};
-
-	/**
-	 * The actual object that is stored as a value of the map. It wraps the email and
-	 * assigns it a creation time.
-	 */
-	private static class Value
-	{
-		/** the original email to store */
-		private String email;
-
-		/** the time when this email is stored */
-		private Time creationTime;
-	}
-
-	/**
-	 * The duration of time before a {@link Value} is considered as expired
-	 */
-	private final Duration lifetime;
-
-	/**
-	 * Construct.
-	 *
-	 * @param maxEntries
-	 *            how much entries this map can contain
-	 * @param lifetime
-	 *            the duration of time to keep an entry in the map before considering it expired
-	 */
-	public AccountConfirmationMap(int maxEntries, Duration lifetime)
-	{
-		super(maxEntries);
-
-		this.lifetime = lifetime;
-	}
-
-	@Override
-	protected synchronized boolean removeEldestEntry(Map.Entry<String, Object> eldest)
-	{
-		boolean removed = super.removeEldestEntry(eldest);
-		if (removed == false)
-		{
-			Value value = (Value)eldest.getValue();
-			if (value != null)
-			{
-				Duration elapsedTime = Time.now().subtract(value.creationTime);
-				if (lifetime.lessThanOrEqual(elapsedTime))
-				{
-					removedValue = value.email;
-					removed = true;
-				}
-			}
-		}
-		return removed;
-	}
-
-	@Override
-	public String put(String key, Object email)
-	{
-		if (!(email instanceof String))
-		{
-			throw new IllegalArgumentException(AccountConfirmationMap.class.getSimpleName() +
-				" can store only instances of " + String.class.getSimpleName() + ": " + email);
-		}
-
-		Value value = new Value();
-		value.creationTime = Time.now();
-		value.email = (String)email;
-
-		Value oldValue;
-		synchronized (this)
-		{
-			oldValue = (Value)super.put(key, value);
-		}
-
-		return oldValue != null ? oldValue.email : null;
-	}
-
-	@Override
-	public String get(Object key)
-	{
-		String result = null;
-		Value value;
-		synchronized (this)
-		{
-			value = (Value)super.get(key);
-		}
-		if (value != null)
-		{
-			Duration elapsedTime = Time.now().subtract(value.creationTime);
-			if (lifetime.greaterThan(elapsedTime))
-			{
-				result = value.email;
-			}
-			else
-			{
-				// expired, remove it
-				remove(key);
-			}
-		}
-		return result;
-	}
-
-	@Override
-	public String remove(Object key)
-	{
-		Value removedValue;
-		synchronized (this)
-		{
-			removedValue = (Value)super.remove(key);
-		}
-
-		return removedValue != null ? removedValue.email : null;
-	}
-
-	@Override
-	public void putAll(Map<? extends String, ?> m)
-	{
-		throw new UnsupportedOperationException();
-	}
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetEmailPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetEmailPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetEmailPanel.java
index bec5ed7..ba2384d 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetEmailPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetEmailPanel.java
@@ -38,10 +38,8 @@ import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.validation.validator.EmailAddressValidator;
 import org.apache.isis.applib.services.email.EmailNotificationService;
 import org.apache.isis.applib.services.email.events.EmailRegistrationEvent;
-import org.apache.isis.applib.services.userreg.UserRegistrationService;
-import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
+import org.apache.isis.viewer.wicket.ui.pages.signup.AccountConfirmationMap;
 import org.apache.isis.viewer.wicket.ui.pages.signup.EmailAvailableValidator;
 
 /**
@@ -66,7 +64,7 @@ public class PasswordResetEmailPanel extends Panel {
         final RequiredTextField<String> emailField = new RequiredTextField<>("email", Model.of(""));
         emailField.setLabel(new ResourceModel("emailLabel"));
         emailField.add(EmailAddressValidator.getInstance());
-        emailField.add(EmailAvailableValidator.DOESNT_EXIST);
+        emailField.add(EmailAvailableValidator.EXISTS);
 
         FormGroup formGroup = new FormGroup("formGroup", emailField);
         form.add(formGroup);

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.java
index d13779b..899c85f 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.java
@@ -21,14 +21,11 @@ package org.apache.isis.viewer.wicket.ui.pages.password_reset.signup;
 
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.string.StringValue;
-import org.apache.isis.applib.services.userreg.UserRegistrationService;
-import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionModel;
 import org.apache.isis.viewer.wicket.ui.pages.AccountManagementPageAbstract;
 
 /**
- * Boilerplate, pick up our HTML and CSS.
+ * A page used for resetting the password of an user.
  */
 public class PasswordResetPage extends AccountManagementPageAbstract {
     
@@ -62,12 +59,28 @@ public class PasswordResetPage extends AccountManagementPageAbstract {
         }
     }
 
+    /**
+     * Shows a panel with password reset form fields.
+     *
+     * @param id The component id
+     * @param uuid A unique id used to identify the email of the user whose password will be reset
+     * @return A panel with "password reset" form fields
+     */
     protected PasswordResetPanel addPasswordResetPanel(String id, String uuid) {
         final PasswordResetPanel passwordResetPanel = new PasswordResetPanel(id, uuid);
         addOrReplace(passwordResetPanel);
         return passwordResetPanel;
     }
 
+    /**
+     * Shows a panel where where the user has to provide her email address.
+     * An email with unique url will be sent to this email address. Once clicked
+     * {@link #addPasswordResetPanel(String, String)} will be used to actually
+     * change the password
+     *
+     * @param id The component id
+     * @return A panel with "send email for password reset" functionality
+     */
     protected PasswordResetEmailPanel addPasswordResetEmailPanel(String id) {
         final PasswordResetEmailPanel passwordResetEmailPanel = new PasswordResetEmailPanel(id);
         addOrReplace(passwordResetEmailPanel);

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.properties
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.properties b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.properties
index c43b8cc..aac8aea 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.properties
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPage.properties
@@ -27,3 +27,5 @@ confirmPasswordPlaceholder=Type the same password again
 passwordResetSubmitLabel=Submit
 emailSentMessage=An automatic email has been sent to '${email}' for verification.
 noSuchUserByEmail=There is no account with this email
+passwordChangeSuccessful=The password has been changed successfully. You can <a href="${signInUrl}">login</a> now.
+passwordChangeUnsuccessful=There was a problem while updating the password. Please try again.

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPanel.java
index c668e86..2388dfe 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/password_reset/signup/PasswordResetPanel.java
@@ -19,28 +19,27 @@
 
 package org.apache.isis.viewer.wicket.ui.pages.password_reset.signup;
 
+import de.agilecoders.wicket.core.markup.html.bootstrap.common.INotificationMessage;
+import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationMessage;
 import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.UUID;
+import java.util.concurrent.Callable;
 import javax.inject.Inject;
+import org.apache.wicket.Page;
 import org.apache.wicket.markup.html.form.Button;
 import org.apache.wicket.markup.html.form.PasswordTextField;
-import org.apache.wicket.markup.html.form.RequiredTextField;
 import org.apache.wicket.markup.html.form.StatelessForm;
 import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
 import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.request.Url;
-import org.apache.wicket.request.UrlRenderer;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.validation.validator.EmailAddressValidator;
-import org.apache.isis.applib.services.email.EmailNotificationService;
-import org.apache.isis.applib.services.email.events.EmailRegistrationEvent;
-import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
+import org.apache.isis.applib.services.userreg.UserRegistrationService;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.models.PageType;
+import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
+import org.apache.isis.viewer.wicket.ui.pages.signup.AccountConfirmationMap;
 
 /**
  * A panel with a form for creation of new users
@@ -50,11 +49,10 @@ public class PasswordResetPanel extends Panel {
     /**
      * Constructor
      *
-     * @param id
-     *            the component id
-     * @param uuid
+     * @param id The component id
+     * @param uuid The unique id to identify the user's email
      */
-    public PasswordResetPanel(final String id, String uuid) {
+    public PasswordResetPanel(final String id, final String uuid) {
         super(id);
 
         addOrReplace(new NotificationPanel("feedback"));
@@ -77,16 +75,24 @@ public class PasswordResetPanel extends Panel {
             public void onSubmit() {
                 super.onSubmit();
 
-                String email = confirmPasswordField.getModelObject();
-                String confirmationUrl = createUrl(email);
+                final String password = confirmPasswordField.getModelObject();
 
-                boolean emailSent = emailService.send(new EmailRegistrationEvent(email, confirmationUrl));
-                if (emailSent) {
-                    Map<String, String> map = new HashMap<>();
-                    map.put("email", email);
-                    IModel<Map<String, String>> model = Model.ofMap(map);
-                    String emailSentMessage = getString("emailSentMessage", model);
-                    success(emailSentMessage);
+                AccountConfirmationMap accountConfirmationMap = getApplication().getMetaData(AccountConfirmationMap.KEY);
+                final String email = accountConfirmationMap.remove(uuid);
+
+                Boolean passwordUpdated = IsisContext.doInSession(new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws Exception {
+
+                        UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
+                        return userRegistrationService.updatePasswordByEmail(email, password);
+                    }
+                });
+
+                if (passwordUpdated) {
+                    success(createPasswordChangeSuccessfulMessage());
+                } else {
+                    error(getString("passwordChangeUnsuccessful"));
                 }
             }
         };
@@ -94,23 +100,17 @@ public class PasswordResetPanel extends Panel {
         form.add(signUpButton);
     }
 
-    private String createUrl(String email) {
-        String uuid = UUID.randomUUID().toString();
-        uuid = uuid.replace("-", "");
-
-        // TODO mgrigorov: either improve the API or use a DB table for this
-        AccountConfirmationMap accountConfirmationMap = getApplication().getMetaData(AccountConfirmationMap.KEY);
-        accountConfirmationMap.put(uuid, email);
-
-        PageParameters parameters = new PageParameters();
-        parameters.set(0, uuid);
-        CharSequence relativeUrl = urlFor(PasswordResetPage.class, parameters);
-        UrlRenderer urlRenderer = getRequestCycle().getUrlRenderer();
-        String fullUrl = urlRenderer.renderFullUrl(Url.parse(relativeUrl));
-
-        return fullUrl;
+    private INotificationMessage createPasswordChangeSuccessfulMessage() {
+        Class<? extends Page> signInPage = pageClassRegistry.getPageClass(PageType.SIGN_IN);
+        CharSequence signInUrl = urlFor(signInPage, null);
+        Map<String, CharSequence> map = new HashMap<>();
+        map.put("signInUrl", signInUrl);
+        String passwordChangeSuccessful = getString("passwordChangeSuccessful", Model.ofMap(map));
+        NotificationMessage message = new NotificationMessage(Model.of(passwordChangeSuccessful));
+        message.escapeModelStrings(false);
+        return message;
     }
 
     @Inject
-    private EmailNotificationService emailService;
+    private PageClassRegistry pageClassRegistry;
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.html
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.html b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.html
index b8c976d..27b6f3f 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.html
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.html
@@ -26,7 +26,7 @@
 
                 <div class="form-group">
                     <label wicket:for="email"><wicket:message key="emailLabel"/></label>
-                    <input type="text" class="form-control" wicket:id="email" readonly/>
+                    <input type="text" class="form-control" wicket:id="email" readonly tabindex="-1"/>
                 </div>
 
                 <button type="submit" class="btn btn-primary btn-sm"><wicket:message key="registerButtonLabel"/></button>

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.java
index 58d784f..bc5f56d 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/register/RegisterPanel.java
@@ -14,7 +14,6 @@ import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.isis.applib.services.userreg.UserRegistrationService;
 import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 import org.apache.isis.core.runtime.system.transaction.TransactionalClosure;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.isis.viewer.wicket.ui.pages.signup.AccountConfirmationMap;
@@ -113,36 +112,38 @@ public class RegisterPanel extends Panel {
         @Override
         public final void onSubmit()
         {
-            IsisContext.openSession(new InitialisationSession());
-
             final Registree registree = getModelObject();
 
-            // TODO: need to add a link on the login page to register.
-            // TODO: however, if there is no UserRegistrationService domain service available, then should suppress the link to get to the registration page.
-            final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
-
-            IsisContext.getTransactionManager().executeWithinTransaction(new TransactionalClosure() {
-                @Override
-                public void preExecute() {
-                }
-
-                @Override
-                public void execute() {
-                    userRegistrationService.registerUser(registree.getUsername(), registree.getPassword(), registree.getEmail());
-                    removeAccountConfirmation();
-                }
-
-                @Override
-                public void onSuccess() {
-                }
-
+            IsisContext.doInSession(new Runnable() {
                 @Override
-                public void onFailure() {
+                public void run() {
+
+                    // TODO: need to add a link on the login page to register.
+                    // TODO: however, if there is no UserRegistrationService domain service available, then should suppress the link to get to the registration page.
+                    final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
+
+                    IsisContext.getTransactionManager().executeWithinTransaction(new TransactionalClosure() {
+                        @Override
+                        public void preExecute() {
+                        }
+
+                        @Override
+                        public void execute() {
+                            userRegistrationService.registerUser(registree.getUsername(), registree.getPassword(), registree.getEmail());
+                            removeAccountConfirmation();
+                        }
+
+                        @Override
+                        public void onSuccess() {
+                        }
+
+                        @Override
+                        public void onFailure() {
+                        }
+                    });
                 }
             });
 
-            IsisContext.closeSession();
-
             // TODO: more error handling here... eg what if the username is already in use.
             signIn(registree.getUsername(), registree.getPassword());
             setResponsePage(getApplication().getHomePage());

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/EmailAvailableValidator.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/EmailAvailableValidator.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/EmailAvailableValidator.java
index 12deb66..19198f9 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/EmailAvailableValidator.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/EmailAvailableValidator.java
@@ -5,15 +5,14 @@ import org.apache.wicket.validation.IValidator;
 import org.apache.wicket.validation.ValidationError;
 import org.apache.isis.applib.services.userreg.UserRegistrationService;
 import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 
 /**
- * Validates that an email is not already in use by another user
+ * Validates that an email is or is not already in use by another user
  */
 public class EmailAvailableValidator implements IValidator<String> {
 
-    public static final EmailAvailableValidator EXISTS = new EmailAvailableValidator(true, "emailIsNotAvailable");
-    public static final EmailAvailableValidator DOESNT_EXIST = new EmailAvailableValidator(false, "noSuchUserByEmail");
+    public static final EmailAvailableValidator EXISTS = new EmailAvailableValidator(true, "noSuchUserByEmail");
+    public static final EmailAvailableValidator DOESNT_EXIST = new EmailAvailableValidator(false, "emailIsNotAvailable");
 
     private final boolean emailExists;
     private final String resourceKey;
@@ -24,13 +23,18 @@ public class EmailAvailableValidator implements IValidator<String> {
     }
 
     @Override
-    public void validate(IValidatable<String> validatable) {
-        IsisContext.openSession(new InitialisationSession());
-        final UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
-        String email = validatable.getValue();
-        if (userRegistrationService.emailExists(email) == emailExists) {
-            validatable.error(new ValidationError().addKey(resourceKey));
-        }
-        IsisContext.closeSession();
+    public void validate(final IValidatable<String> validatable) {
+        IsisContext.doInSession(new Runnable() {
+            @Override
+            public void run() {
+                UserRegistrationService userRegistrationService = IsisContext.getPersistenceSession().getServicesInjector().lookupService(UserRegistrationService.class);
+                String email = validatable.getValue();
+                boolean emailExists1 = userRegistrationService.emailExists(email);
+                if (emailExists1 != emailExists) {
+                    validatable.error(new ValidationError().addKey(resourceKey));
+                }
+            }
+        });
+
     }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/RegistrationFormPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/RegistrationFormPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/RegistrationFormPanel.java
index fd14c8e..d13cc24 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/RegistrationFormPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/signup/RegistrationFormPanel.java
@@ -62,7 +62,7 @@ public class RegistrationFormPanel extends Panel {
         final RequiredTextField<String> emailField = new RequiredTextField<>("email", Model.of(""));
         emailField.setLabel(new ResourceModel("emailLabel"));
         emailField.add(EmailAddressValidator.getInstance());
-        emailField.add(EmailAvailableValidator.EXISTS);
+        emailField.add(EmailAvailableValidator.DOESNT_EXIST);
 
         FormGroup formGroup = new FormGroup("formGroup", emailField);
         form.add(formGroup);

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/core/applib/src/main/java/org/apache/isis/applib/services/userreg/UserRegistrationService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/UserRegistrationService.java b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/UserRegistrationService.java
index 5639d4f..91850e6 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/UserRegistrationService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/UserRegistrationService.java
@@ -29,5 +29,5 @@ public interface UserRegistrationService {
     boolean emailExists(String emailAddress);
 
     @Programmatic
-    void updatePasswordByEmail(String emailAddress, String password);
+    boolean updatePasswordByEmail(String emailAddress, String password);
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/06dc69a5/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
index 6c5652f..a18047f 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
@@ -20,6 +20,7 @@
 package org.apache.isis.core.runtime.system.context;
 
 import java.util.List;
+import java.util.concurrent.Callable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.isis.applib.profiles.Localization;
@@ -37,6 +38,7 @@ import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
 import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
 import org.apache.isis.core.runtime.authentication.AuthenticationManager;
 import org.apache.isis.core.runtime.system.DeploymentType;
+import org.apache.isis.core.runtime.system.internal.InitialisationSession;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
 import org.apache.isis.core.runtime.system.session.IsisSession;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
@@ -510,5 +512,47 @@ public abstract class IsisContext implements DebuggableWithTitle {
         debug.appendln("context ", this);
     }
 
-
+    /**
+     * A template method that executes a piece of code in a session.
+     * If there is an open session then it is reused, otherwise a temporary one
+     * is created.
+     *
+     * @param runnable The piece of code to run.
+     */
+    public static void doInSession(final Runnable runnable) {
+        doInSession(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                runnable.run();
+                return null;
+            }
+        });
+    }
+
+    /**
+     * A template method that executes a piece of code in a session.
+     * If there is an open session then it is reused, otherwise a temporary one
+     * is created.
+     *
+     * @param callable The piece of code to run.
+     * @return The result of the code execution.
+     */
+    public static <R> R doInSession(Callable<R> callable) {
+        boolean noSession = !inSession();
+        try {
+            if (noSession) {
+                openSession(new InitialisationSession());
+            }
+
+            return callable.call();
+        } catch (Exception x) {
+            throw new RuntimeException(
+                String.format("An error occurred while executing code in %s session", noSession ? "a temporary" : "a"),
+                x);
+        } finally {
+            if (noSession) {
+                closeSession();
+            }
+        }
+    }
 }