Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-08-13T16:51:15+02:00
Author: Jure Cerjak (jcerjak) <jcerjak@termitnjak.si>
Commit: https://github.com/plone/plone.app.users/commit/fd8a29812598df320637a03588cf0c7599d2a9e7

fetch security settings from the registry instead of portal properties

Files changed:
M plone/app/users/browser/account.py
M plone/app/users/browser/register.py
M plone/app/users/browser/userdatapanel.py
M plone/app/users/tests/base.py
M plone/app/users/tests/duplicate_email.rst
M plone/app/users/tests/email_login.rst
M plone/app/users/tests/flexible_user_registration.rst
M plone/app/users/tests/userdata.rst

diff --git a/plone/app/users/browser/account.py b/plone/app/users/browser/account.py
index f5274ef..4fc7343 100644
--- a/plone/app/users/browser/account.py
+++ b/plone/app/users/browser/account.py
@@ -3,6 +3,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import safe_unicode
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -12,9 +13,11 @@
 from plone.app.users.utils import notifyWidgetActionExecutionError
 from plone.autoform.form import AutoExtensibleForm
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import form
 from zope.component import getMultiAdapter
+from zope.component import getUtility
 from zope.event import notify
 from zope.interface import implements
 
@@ -123,8 +126,10 @@ def validate_email(self, action, data):
         ]
         if 'email' not in error_keys:
             registration = getToolByName(context, 'portal_registration')
-            properties = getToolByName(context, 'portal_properties')
-            if properties.site_properties.getProperty('use_email_as_login'):
+            registry = getUtility(IRegistry)
+            security_settings = registry.forInterface(
+                ISecuritySchema, prefix="plone")
+            if security_settings.use_email_as_login:
                 err_str = ''
                 try:
                     id_allowed = registration.isMemberIdAllowed(data['email'])
diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index 040f323..952988c 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -4,6 +4,7 @@
 from Products.CMFCore.permissions import ManagePortal
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import normalizeString
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -19,6 +20,7 @@
 from plone.autoform.interfaces import OMITTED_KEY
 from plone.autoform.interfaces import ORDER_KEY
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import field
 from z3c.form import form
@@ -51,6 +53,11 @@ class BaseRegistrationForm(AutoExtensibleForm, form.Form):
     # this attribute indicates if user was successfully registered
     _finishedRegister = False
 
+    def _get_security_settings(self):
+        """Return security settings from the registry."""
+        registry = getUtility(IRegistry)
+        return registry.forInterface(ISecuritySchema, prefix="plone")
+
     def render(self):
         if self._finishedRegister:
             return self.context.unrestrictedTraverse('registered')()
@@ -63,7 +70,8 @@ def updateFields(self):
         """
         portal_props = getToolByName(self.context, 'portal_properties')
         props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
+        use_email_as_login = settings.use_email_as_login
 
         # Ensure all listed fields are in the schema
         registration_fields = [f for f in props.getProperty(
@@ -159,7 +167,7 @@ def generate_user_id(self, data):
         1. We query a utility, so integrators can register a hook to
            generate a user id using their own logic.
 
-        2. If use_uuid_as_userid is set in the site_properties, we
+        2. If use_uuid_as_userid is set in the registry, we
            generate a uuid.
 
         3. If a username is given and we do not use email as login,
@@ -198,9 +206,8 @@ def generate_user_id(self, data):
                 data['user_id'] = userid
                 return userid
 
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        if props.getProperty('use_uuid_as_userid'):
+        settings = self._get_security_settings()
+        if settings.use_uuid_as_userid:
             userid = uuid_userid_generator()
             data['user_id'] = userid
             return userid
@@ -209,7 +216,7 @@ def generate_user_id(self, data):
         userid = data.get('username')
         if userid:
             # If we are not using email as login, then this user name is fine.
-            if not props.getProperty('use_email_as_login'):
+            if not settings.use_email_as_login:
                 data['user_id'] = userid
                 return userid
 
@@ -285,11 +292,9 @@ def generate_login_name(self, data):
         login_name = data.get('username')
         login_name = pas.applyTransform(login_name)
         data['login_name'] = login_name
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
         # If we are not using email as login, then this user name is fine.
-        if not use_email_as_login:
+        if not settings.use_email_as_login:
             return login_name
 
         # We use email as login.
@@ -319,9 +324,6 @@ def validate_registration(self, action, data):
         form_field_names = [f for f in self.fields]
 
         portal = getUtility(ISiteRoot)
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
 
         # passwords should match
         if 'password' in form_field_names:
@@ -349,7 +351,8 @@ def validate_registration(self, action, data):
                         notifyWidgetActionExecutionError(action,
                                                          'password', err_str)
 
-        if use_email_as_login:
+        settings = self._get_security_settings()
+        if settings.use_email_as_login:
             username_field = 'email'
         else:
             username_field = 'username'
@@ -486,7 +489,8 @@ def handle_join_success(self, data):
         # set member properties
         self.applyProperties(user_id, data)
 
-        if data.get('mail_me') or (portal.validate_email and
+        settings = self._get_security_settings()
+        if data.get('mail_me') or (not settings.enable_user_pwd_choice and
                                    not data.get('password')):
             # We want to validate the email address (users cannot
             # select their own passwords on the register form) or the
@@ -588,9 +592,10 @@ def showForm(self):
         ctrlOverview = getMultiAdapter((portal, self.request),
                                        name='overview-controlpanel')
 
+        settings = self._get_security_settings()
         # hide form iff mailhost_warning == True and validate_email == True
         return not (ctrlOverview.mailhost_warning() and
-                    portal.getProperty('validate_email', True))
+                    not settings.enable_user_pwd_choice)
 
     def updateFields(self):
         if not self.showForm:
@@ -602,8 +607,8 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # Can the user actually set his/her own password?
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # No? Remove the password fields.
             defaultFields = defaultFields.omit('password', 'password_ctl')
         else:
@@ -622,8 +627,8 @@ def updateWidgets(self):
             return
 
         super(RegistrationForm, self).updateWidgets()
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # Show a message indicating that a password reset link
             # will be mailed to the user.
             widget = self.widgets['mail_me']
@@ -661,7 +666,8 @@ def updateFields(self):
             # will check that at least one of the options is chosen.
             defaultFields['password'].field.required = False
             defaultFields['password_ctl'].field.required = False
-            if portal.getProperty('validate_email', True):
+            settings = self._get_security_settings()
+            if not settings.enable_user_pwd_choice:
                 defaultFields['mail_me'].field.default = True
             else:
                 defaultFields['mail_me'].field.default = False
diff --git a/plone/app/users/browser/userdatapanel.py b/plone/app/users/browser/userdatapanel.py
index e0adc6b..675d25f 100644
--- a/plone/app/users/browser/userdatapanel.py
+++ b/plone/app/users/browser/userdatapanel.py
@@ -2,6 +2,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import set_own_login_name
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.PlonePAS.tools.membership import default_portrait
@@ -9,6 +10,8 @@
 from plone.app.users.browser.account import AccountPanelSchemaAdapter
 from plone.app.users.schema import IUserDataSchema
 from plone.namedfile.file import NamedBlobImage
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 
 class UserDataPanelAdapter(AccountPanelSchemaAdapter):
@@ -43,8 +46,10 @@ def get_email(self):
         return self._getProperty('email')
 
     def set_email(self, value):
-        pp = getToolByName(self.context, 'portal_properties')
-        if pp.site_properties.getProperty('use_email_as_login'):
+        registry = getUtility(IRegistry)
+        security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+        if security_settings.use_email_as_login:
             mt = getToolByName(self.context, 'portal_membership')
             if self.context.getId() == mt.getAuthenticatedMember().getId():
                 set_own_login_name(self.context, value)
diff --git a/plone/app/users/tests/base.py b/plone/app/users/tests/base.py
index e7b7271..92049e1 100644
--- a/plone/app/users/tests/base.py
+++ b/plone/app/users/tests/base.py
@@ -8,6 +8,7 @@
 from AccessControl.SecurityInfo import ClassSecurityInfo
 from Acquisition import aq_base
 from Products.CMFCore.interfaces import ISiteRoot
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.tests.utils import MockMailHost
 from Products.MailHost.interfaces import IMailHost
 from Products.PlonePAS.Extensions.Install import activatePluginInterfaces
@@ -15,6 +16,7 @@
 from Products.PluggableAuthService.interfaces.plugins import IValidationPlugin
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
+from plone.registry.interfaces import IRegistry
 from OFS.Cache import Cacheable
 from zope.component import getSiteManager
 from zope.component import getUtility
@@ -37,6 +39,9 @@ def afterSetUp(self):
         self.portal._original_MailHost = self.portal.MailHost
         self.portal.MailHost = mailhost = MockMailHost('MailHost')
         self.membership = self.portal.portal_membership
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
         sm = getSiteManager(context=self.portal)
         sm.unregisterUtility(provided=IMailHost)
         sm.registerUtility(mailhost, provided=IMailHost)
diff --git a/plone/app/users/tests/duplicate_email.rst b/plone/app/users/tests/duplicate_email.rst
index 80084e5..36d1e0e 100644
--- a/plone/app/users/tests/duplicate_email.rst
+++ b/plone/app/users/tests/duplicate_email.rst
@@ -5,8 +5,7 @@ When email address is used as login name, duplicates are not allowed.
 
 Use email addresses as login name:
 
-    >>> ptool = self.portal.portal_properties
-    >>> ptool.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
 
 Create a new user one:
 
@@ -26,7 +25,7 @@ Login as user two:
     >>> self.browser.open('http://nohost/plone/')
     >>> self.browser.getLink('Log in').click()
 
-    >>> self.browser.getControl('E-mail').value = 'usertwo@example.com'
+    >>> self.browser.getControl('Login Name').value = 'usertwo@example.com'
     >>> self.browser.getControl('Password').value = 'secret'
     >>> self.browser.getControl('Log in').click()
     >>> 'Login failed' in self.browser.contents
diff --git a/plone/app/users/tests/email_login.rst b/plone/app/users/tests/email_login.rst
index 9f32103..b9cd8c8 100644
--- a/plone/app/users/tests/email_login.rst
+++ b/plone/app/users/tests/email_login.rst
@@ -46,9 +46,10 @@ Testing email address as login name
     >>> 'Failed to create your account' in browser.contents
     False
 
-    We can login immediately.
-    >>> 'Click the button to log in immediately.' in browser.contents
-    True
+    We can now login.
+    >>> browser.getLink('Log in').click()
+    >>> browser.getControl('Login Name').value = 'bob-jones+test@example.com'
+    >>> browser.getControl('Password').value = 'secret'
     >>> browser.getControl('Log in').click()
     >>> 'You are now logged in' in browser.contents
     True
@@ -59,10 +60,9 @@ Testing email address as login name
     True
     >>> browser.getLink(url='http://nohost/plone/logout').click()
 
-    We login as manager. The login form now has a different label for
-    the login name.
+    We login as manager.
     >>> browser.open('http://nohost/plone/login_form')
-    >>> browser.getControl('E-mail').value = portal_owner
+    >>> browser.getControl('Login Name').value = portal_owner
     >>> browser.getControl('Password').value = default_password
     >>> browser.getControl('Log in').click()
 
diff --git a/plone/app/users/tests/flexible_user_registration.rst b/plone/app/users/tests/flexible_user_registration.rst
index 89a7d9b..c015cd1 100644
--- a/plone/app/users/tests/flexible_user_registration.rst
+++ b/plone/app/users/tests/flexible_user_registration.rst
@@ -125,7 +125,7 @@ get all required fields on registration form.
 
 Check render register form in 'Use Email As Login' mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> portal.portal_properties.site_properties._updateProperty('user_registration_fields', ['username'])
     >>> browser.open('http://nohost/plone/@@register')
     >>> 'Registration form' in browser.contents
@@ -136,12 +136,10 @@ Check render register form in 'Use Email As Login' mode.
     >>> browser.getControl('Password').value = 'testpassword'
     >>> browser.getControl('Confirm password').value = 'testpassword'
     >>> browser.getControl('Register').click()
-    >>> browser.contents
-    '...Welcome!...You have been registered...'
 
 Revert email mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False
 
 Check register form with portrait field.
 
diff --git a/plone/app/users/tests/userdata.rst b/plone/app/users/tests/userdata.rst
index cfc0518..de9b40d 100644
--- a/plone/app/users/tests/userdata.rst
+++ b/plone/app/users/tests/userdata.rst
@@ -210,7 +210,7 @@ Modifying user data in email mode
 
 Let's switch to using Email as Login Name
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> self.browser.open("http://nohost/plone/" + view_name)
 
 Update our email and see if login name was synced:
@@ -237,4 +237,4 @@ should fail with validation errors.
 
 Revert back from email mode
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-08-16T21:59:03+02:00
Author: Jure Cerjak (jcerjak) <jcerjak@termitnjak.si>
Commit: https://github.com/plone/plone.app.users/commit/981420c33ba2a50c1ed6396ba54c8279edd88dd6

add more tests for generating a user id and user name

Files changed:
M plone/app/users/tests/test_loginname_generator.py
M plone/app/users/tests/test_new_user.py
M plone/app/users/tests/test_userid_generator.py

diff --git a/plone/app/users/tests/test_loginname_generator.py b/plone/app/users/tests/test_loginname_generator.py
index 0b3ea51..2884bdd 100644
--- a/plone/app/users/tests/test_loginname_generator.py
+++ b/plone/app/users/tests/test_loginname_generator.py
@@ -1,30 +1,68 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import ILoginNameGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateLoginName(BaseTestCase):
-    def test_generate_user_id_simplistic(self):
+
+    def afterSetUp(self):
+        super(TestGenerateLoginName, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom login name generator overrides the default
+        behavior.
+        """
+        sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': 'me me me', 'username': 'frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
+
+        self.assertEqual(form.generate_login_name(data), 'me me me')
+        self.assertEqual(data.get('login_name'), 'me me me')
+
+    def test_custom_generator_empty(self):
+        """Test that the username is used if a custom login name generator
+        returns an empty value.
+        """
         sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': '', 'username': 'Frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
 
-        # Without a function, return username
-        self.assertEqual(
-            self.generateLoginName(dict(username='frank')),
-            'frank'
-        )
-
-        # Generator overrides this behavior
-        sm.registerUtility(lambda data: data['useme'],
-                           provided=ILoginNameGenerator)
-        self.assertEqual(
-            self.generateLoginName(dict(useme='me me me', username='frank')),
-            'me me me'
-        )
-
-    def generateLoginName(self, data):
-        """Generate login name, optionally registering function first"""
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_disabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting disabled.
+        """
         form = BaseRegistrationForm(self.portal, {})
-        return form.generate_login_name(data)
+        data = {'username': 'Frank'}
+        self.security_settings.use_email_as_login = False
+
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_enabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting enabled.
+        """
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'username': 'Frank', 'email': 'Frank@Test.com'}
+        self.security_settings.use_email_as_login = True
+
+        self.assertEqual(form.generate_login_name(data), 'frank@test.com')
+        self.assertEqual(data.get('login_name'), 'frank@test.com')
diff --git a/plone/app/users/tests/test_new_user.py b/plone/app/users/tests/test_new_user.py
index efc5e3e..d4397f3 100644
--- a/plone/app/users/tests/test_new_user.py
+++ b/plone/app/users/tests/test_new_user.py
@@ -1,7 +1,12 @@
 # -*- coding: utf-8 -*-
 from hashlib import sha1 as sha
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.tests.base import BaseTestCase
+from plone.app.users.utils import uuid_userid_generator
 from plone.protect import authenticator as auth
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 import hmac
 
@@ -32,3 +37,129 @@ def test_new_user_as_site_administrator(self):
             'Site Administrator' in
             self.portal.acl_users.getUserById('newuser').getRoles()
         )
+
+
+class TestGenerateUserIdLoginName(BaseTestCase):
+    """Test if the user id and user name are properly generated based on the
+    security settings.
+    """
+
+    def afterSetUp(self):
+        super(TestGenerateUserIdLoginName, self).afterSetUp()
+        self.portal_url = self.portal.absolute_url()
+        self.portal.acl_users._doAddUser(
+            'siteadmin', 'secret', ['Site Administrator'], []
+        )
+        self.browser.handleErrors = False
+        self.browser.addHeader('Authorization', 'Basic siteadmin:secret')
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_uuid_disabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # user id should be set the same as user name
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(user.getId(), 'newie')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_disabled_email_as_login_enabled_no_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'newuser@example.com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # Since full name is not provided, the user id is set based on the
+        # e-mail, the same as the user name.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_disabled_email_as_login_enabled_has_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # User id should be set based on the full name, user name should be
+        # set based on the e-mail.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_enabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_enabled_email_as_login_enabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id, user name should be based on
+        # the e-mail
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
diff --git a/plone/app/users/tests/test_userid_generator.py b/plone/app/users/tests/test_userid_generator.py
index 4d72d55..ee9ea78 100644
--- a/plone/app/users/tests/test_userid_generator.py
+++ b/plone/app/users/tests/test_userid_generator.py
@@ -1,15 +1,49 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import IUserIdGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
 from plone.app.users.utils import uuid_userid_generator
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateUserId(BaseTestCase):
 
-    def test_standard_generate_user_id(self):
+    def afterSetUp(self):
+        super(TestGenerateUserId, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom user id generator overrides the default
+        behavior.
+        """
+
+        def one_generator(data):
+            return 'one'
+
+        sm = getSiteManager(context=self.portal)
+        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        form = BaseRegistrationForm(self.portal, {})
+
+        data = {}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+        data = {'username': 'joe',
+                'fullname': 'Joe User',
+                'email': 'joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+    def test_default(self):
+        """Test generating user_id with no custom user id generator and
+        the default security settings.
+        """
         form = BaseRegistrationForm(self.portal, {})
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
@@ -29,51 +63,46 @@ def test_standard_generate_user_id(self):
         self.assertEqual(data.get('user_id'), 'joe-user')
 
         # With no fullname, we take the email.
-        data = {'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
-        self.assertEqual(data.get('user_id'), 'joe@example.org')
-
-    def test_generate_user_id_simplistic(self):
-        # Test a simplistic user id generator.
-        def one_generator(data):
-            return 'one'
-
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        data = {'email': 'Joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'Joe@example.org')
+        self.assertEqual(data.get('user_id'), 'Joe@example.org')
+
+    def test_use_email_as_login_has_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is provided.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
+        data = {'fullname': 'Joe User',
                 'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
-
-    def test_generate_user_id_email(self):
-        # It is easy to force the email as user id.
-        def email_getter(data):
-            return data.get('email')
+        self.assertEqual(form.generate_user_id(data), 'joe-user')
+        self.assertEqual(data.get('user_id'), 'joe-user')
 
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(email_getter, provided=IUserIdGenerator)
+    def test_use_email_as_login_no_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
         self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
+        data = {'email': 'joe@example.org'}
         self.assertEqual(form.generate_user_id(data), 'joe@example.org')
         self.assertEqual(data.get('user_id'), 'joe@example.org')
 
-    def test_generate_user_id_with_uuid(self):
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(uuid_userid_generator, provided=IUserIdGenerator)
+    def test_use_uuid_as_userid_enabled(self):
+        """Test generating a user id if the use_uuid_as_userid setting is
+        enabled.
+        """
+        self.security_settings.use_uuid_as_userid = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
@@ -94,38 +123,3 @@ def test_generate_user_id_with_uuid(self):
         # call to the uuid generator should be unique.
         self.assertNotEqual(form.generate_user_id(data),
                             form.generate_user_id(data))
-
-
-class TestGenerateUUIDUserId(BaseTestCase):
-
-    def afterSetUp(self):
-        super(TestGenerateUUIDUserId, self).afterSetUp()
-        # If use_uuid_as_userid is set in the site_properties, we
-        # generate a uuid.
-        self.ptool = ptool = getattr(self.portal, 'portal_properties')
-        if not ptool.site_properties.hasProperty('use_uuid_as_userid'):
-            ptool.site_properties.manage_addProperty(
-                'use_uuid_as_userid', False, 'boolean'
-            )  # Try to add it.
-        ptool.site_properties.manage_changeProperties(
-            use_uuid_as_userid=True
-        )  # Change it.
-        ptool.site_properties.getProperty('use_uuid_as_userid')
-
-    def test_generate_uuid_user_id(self):
-        self.assertTrue(
-            self.ptool.site_properties.getProperty('use_uuid_as_userid')
-        )
-        form = BaseRegistrationForm(self.portal, {})
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
-        user_id = form.generate_user_id(data)
-        self.assertNotEqual(user_id, 'joe')
-        self.assertEqual(data.get('user_id'), user_id)
-        self.assertEqual(len(user_id), len(uuid_userid_generator()))
-
-        # Calling it twice should give a different result, as every
-        # call to the uuid generator should be unique.
-        self.assertNotEqual(form.generate_user_id(data),
-                            form.generate_user_id(data))


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-08-16T22:52:17+02:00
Author: Jure Cerjak (jcerjak) <jcerjak@termitnjak.si>
Commit: https://github.com/plone/plone.app.users/commit/af659c1151a883d0e7aee22c873fa9804fcad859

apply a transform on the email when generating a user id

This fixes the issue when adding a new user with an uppercase email.
https://github.com/plone/Products.CMFPlone/issues/261

Files changed:
M plone/app/users/browser/register.py
M plone/app/users/tests/test_new_user.py
M plone/app/users/tests/test_userid_generator.py

diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index 952988c..1b4724b 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -222,7 +222,9 @@ def generate_user_id(self, data):
 
         # First get a default value that we can return if we cannot
         # find anything better.
-        default = data.get('username') or data.get('email') or ''
+        pas = getToolByName(self.context, 'acl_users')
+        email = pas.applyTransform(data.get('email'))
+        default = data.get('username') or email or ''
         data['user_id'] = default
         fullname = data.get('fullname')
         if not fullname:
diff --git a/plone/app/users/tests/test_new_user.py b/plone/app/users/tests/test_new_user.py
index d4397f3..aa0f232 100644
--- a/plone/app/users/tests/test_new_user.py
+++ b/plone/app/users/tests/test_new_user.py
@@ -96,6 +96,25 @@ def test_uuid_disabled_email_as_login_enabled_no_full_name(self):
         self.assertEquals(user.getId(), 'newuser@example.com')
         self.assertEquals(user.getUserName(), 'newuser@example.com')
 
+    def test_uuid_disabled_email_as_login_enabled_no_full_name_uppercase(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # the user id is set based on the e-mail, which should be lowercased
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
     def test_uuid_disabled_email_as_login_enabled_has_full_name(self):
         self.security_settings.use_uuid_as_userid = False
         self.security_settings.use_email_as_login = True
diff --git a/plone/app/users/tests/test_userid_generator.py b/plone/app/users/tests/test_userid_generator.py
index ee9ea78..a7fa4c1 100644
--- a/plone/app/users/tests/test_userid_generator.py
+++ b/plone/app/users/tests/test_userid_generator.py
@@ -98,6 +98,21 @@ def test_use_email_as_login_no_fullname(self):
         self.assertEqual(form.generate_user_id(data), 'joe@example.org')
         self.assertEqual(data.get('user_id'), 'joe@example.org')
 
+    def test_use_email_as_login_no_fullname_uppercase_email(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided, with an uppercase e-mail.
+        """
+        self.security_settings.use_email_as_login = True
+        form = BaseRegistrationForm(self.portal, {})
+
+        data = {}
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
+
+        data = {'email': 'Joe@Example.org'}
+        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
+        self.assertEqual(data.get('user_id'), 'joe@example.org')
+
     def test_use_uuid_as_userid_enabled(self):
         """Test generating a user id if the use_uuid_as_userid setting is
         enabled.


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-11-02T12:06:50Z
Author: Jure Cerjak (jcerjak) <jcerjak@termitnjak.si>
Commit: https://github.com/plone/plone.app.users/commit/d6947e4b0bb1ea4557126c60827a05e15ffa8e61

Merge branch 'master' into plip10359-z3cform

Conflicts:
	plone/app/users/browser/register.py

Files changed:
M CHANGES.rst
M plone/app/users/browser/account-configlet.pt
M plone/app/users/browser/account-panel.pt
M plone/app/users/browser/memberregistration.pt
M plone/app/users/browser/membersearch_form.pt
M plone/app/users/browser/newuser_form.pt
M plone/app/users/browser/personalpreferences.py
M plone/app/users/browser/register.py
M plone/app/users/browser/register_form.pt
M plone/app/users/schema.py
M plone/app/users/tests/test_member_search.py
M setup.py

diff --git a/CHANGES.rst b/CHANGES.rst
index 9bedf4f..495ba04 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,7 +1,13 @@
 CHANGES
 =======
 
-2.0.4 (2014-06-03)
+2.1.1 (unreleased)
+------------------
+
+- Nothing changed yet.
+
+
+2.1.0 (2014-10-23)
 ------------------
 
 - Check the permission for the Object tab on the AccountPanelForm as configured
@@ -39,6 +45,9 @@ CHANGES
 2.0 (2014-03-02)
 ----------------
 
+- Refactor the member-search form to a browser view, using z3c.form.
+  [pabo3000]
+
 - Have a soft dependency on plone.app.event and include the timezone field only
   then in the schema, if plone.app.event is available.
   [thet]
diff --git a/plone/app/users/browser/account-configlet.pt b/plone/app/users/browser/account-configlet.pt
index c053bb3..f03df64 100644
--- a/plone/app/users/browser/account-configlet.pt
+++ b/plone/app/users/browser/account-configlet.pt
@@ -36,7 +36,7 @@
         <div class="contentActions">&nbsp;</div>
     </div>
 
-    <div id="content">
+    <article id="content">
 
     <div metal:use-macro="context/global_statusmessage/macros/portal_message">
       Portal status message
@@ -57,7 +57,7 @@
       </metal:block>
     </div>
 
-    </div>
+    </article>
 
 </div>
 </body>
diff --git a/plone/app/users/browser/account-panel.pt b/plone/app/users/browser/account-panel.pt
index 631c62b..45a5043 100644
--- a/plone/app/users/browser/account-panel.pt
+++ b/plone/app/users/browser/account-panel.pt
@@ -37,13 +37,13 @@
             Portal status message
         </div>
 
-        <div id="content">
+        <article id="content">
             <h1 class="documentFirstHeading" tal:content="view/label | nothing" />
             <div id="content-core">
                 <metal:block use-macro="context/@@ploneform-macros/titlelessform">
                 </metal:block>
             </div>
-        </div>
+        </article>
 
     </metal:block>
 
diff --git a/plone/app/users/browser/memberregistration.pt b/plone/app/users/browser/memberregistration.pt
index bfff0af..df1fd12 100644
--- a/plone/app/users/browser/memberregistration.pt
+++ b/plone/app/users/browser/memberregistration.pt
@@ -42,7 +42,7 @@
         Portal status message
       </div>
 
-      <div id="content">
+      <article id="content">
 
           <h1 class="documentFirstHeading"
               i18n:translate="heading_usergroup_settings">User/Groups Settings</h1>
@@ -51,7 +51,7 @@
                 <metal:block use-macro="context/@@ploneform-macros/titlelessform">
                 </metal:block>
           </div>
-      </div>
+      </article>
 </metal:main>
 </body>
 </html>
diff --git a/plone/app/users/browser/membersearch_form.pt b/plone/app/users/browser/membersearch_form.pt
index 99878cf..89acbbe 100644
--- a/plone/app/users/browser/membersearch_form.pt
+++ b/plone/app/users/browser/membersearch_form.pt
@@ -8,16 +8,16 @@
 <body>
     <metal:body fill-slot="body">
 
-        <div id="content" tal:condition="not: view/submitted">
+        <article id="content" tal:condition="not: view/submitted">
             <h1 class="documentFirstHeading"
                 tal:content="view/label | nothing" />
             <div id="content-core">
                 <metal:block use-macro="context/@@ploneform-macros/titlelessform" />
             </div>
-        </div>
+        </article>
 
         <tal:block condition="view/submitted">
-          <div id="content"
+          <article id="content"
                tal:define="listing_allowed python: checkPermission('List portal members', here);
                            results python:listing_allowed and view.results;
                            Batch python:modules['Products.CMFPlone'].Batch;
@@ -78,7 +78,7 @@
                     <p><strong i18n:translate="description_no_results_found">No results were found.</strong></p>
                 </div>
             </div>
-          </div>
+          </article>
         </tal:block>
 
 
diff --git a/plone/app/users/browser/newuser_form.pt b/plone/app/users/browser/newuser_form.pt
index 918f0c0..66ce0dd 100644
--- a/plone/app/users/browser/newuser_form.pt
+++ b/plone/app/users/browser/newuser_form.pt
@@ -14,14 +14,14 @@
 <body>
     <metal:body fill-slot="body">
 
-        <div id="content">
+        <article id="content">
             <h1 class="documentFirstHeading"
                 tal:content="view/label | nothing" />
             <div id="content-core">
                 <metal:block use-macro="context/@@ploneform-macros/titlelessform">
                 </metal:block>
             </div>
-        </div>
+        </article>
 
     </metal:body>
 </body>
diff --git a/plone/app/users/browser/personalpreferences.py b/plone/app/users/browser/personalpreferences.py
index 059b529..1a9f2d1 100644
--- a/plone/app/users/browser/personalpreferences.py
+++ b/plone/app/users/browser/personalpreferences.py
@@ -84,7 +84,7 @@ class IPersonalPreferences(Interface):
         timezone = Choice(
             title=_(u'label_timezone', default=u'Time zone'),
             description=_(u'help_timezone', default=u'Your time zone'),
-            vocabulary='plone.app.event.AvailableTimezones',
+            vocabulary='plone.app.vocabularies.Timezones',
             required=False,
         )
 
diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index 1b4724b..59bb522 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -595,7 +595,8 @@ def showForm(self):
                                        name='overview-controlpanel')
 
         settings = self._get_security_settings()
-        # hide form iff mailhost_warning == True and validate_email == True
+        # hide form if mailhost_warning == True and validate_email == True
+
         return not (ctrlOverview.mailhost_warning() and
                     not settings.enable_user_pwd_choice)
 
diff --git a/plone/app/users/browser/register_form.pt b/plone/app/users/browser/register_form.pt
index c3e2df9..08a5254 100644
--- a/plone/app/users/browser/register_form.pt
+++ b/plone/app/users/browser/register_form.pt
@@ -14,7 +14,7 @@
 <body>
     <metal:body fill-slot="body">
 
-        <div id="content">
+        <article id="content">
             <tal:mailhost tal:condition="view/showForm">
             <h1 class="documentFirstHeading"
                 tal:content="view/label | nothing" />
@@ -45,7 +45,7 @@
                 </div>
 
             </tal:nomailhost>
-        </div>
+        </article>
 
     </metal:body>
 </body>
diff --git a/plone/app/users/schema.py b/plone/app/users/schema.py
index 5f6e375..4b1a3c3 100644
--- a/plone/app/users/schema.py
+++ b/plone/app/users/schema.py
@@ -74,7 +74,7 @@ class IUserDataSchema(Interface):
         description=_(
             u'help_portrait',
             default=u'To add or change the portrait: click the "Browse" '
-                    u'button; select a picture of yourself.  Recommended '
+                    u'button; select a picture of yourself. Recommended '
                     u'image size is 75 pixels wide by 100 pixels tall.'
         ),
         required=False)
diff --git a/plone/app/users/tests/test_member_search.py b/plone/app/users/tests/test_member_search.py
index b2f3f2d..c594a2c 100644
--- a/plone/app/users/tests/test_member_search.py
+++ b/plone/app/users/tests/test_member_search.py
@@ -16,4 +16,4 @@ def test_extract_criteria_from_request(self):
 
         result = extractCriteriaFromRequest(data)
 
-        self.assertEqual(result, {"something": u'any form value'})
+        self.assertEqual(result, {'something': u'any form value'})
diff --git a/setup.py b/setup.py
index 3111050..217564a 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 from setuptools import find_packages
 from setuptools import setup
 
-version = '2.1.dev0'
+version = '2.1.1.dev0'
 
 long_description = '{0}\n{1}'.format(open('README.rst').read(),
                                      open('CHANGES.rst').read())


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-11-16T18:46:14+01:00
Author: Jure Cerjak (jcerjak) <jcerjak@termitnjak.si>
Commit: https://github.com/plone/plone.app.users/commit/923c3bff6e1bd6a9fcbbcfeb2c51927a1cb99d07

minor docstrings update in register.py

Property "validate_email" was replaced by the
"enable_user_pwd_choice" setting in the registry. #216

Files changed:
M plone/app/users/browser/register.py

diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index 59bb522..bb238ea 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -595,8 +595,8 @@ def showForm(self):
                                        name='overview-controlpanel')
 
         settings = self._get_security_settings()
-        # hide form if mailhost_warning == True and validate_email == True
-
+        # hide form if mailhost_warning == True and
+        # enable_user_pwd_choice == False
         return not (ctrlOverview.mailhost_warning() and
                     not settings.enable_user_pwd_choice)
 
@@ -657,7 +657,7 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # The mail_me field needs special handling depending on the
-        # validate_email property and on the correctness of the mail
+        # enable_user_pwd_choice setting and on the correctness of the mail
         # settings.
         portal = getUtility(ISiteRoot)
         ctrlOverview = getMultiAdapter((portal, self.request),


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-12-14T15:15:07+01:00
Author: Timo Stollenwerk (tisto) <tisto@plone.org>
Commit: https://github.com/plone/plone.app.users/commit/165588d2f27e37c2bcb7c432bc60450e7e2782f8

Merge branch 'plip10359-z3cform' of https://github.com/jcerjak/plone.app.users into jcerjak-plip10359-z3cform

Conflicts:
	plone/app/users/tests/base.py

Files changed:
M plone/app/users/browser/account.py
M plone/app/users/browser/register.py
M plone/app/users/browser/userdatapanel.py
M plone/app/users/tests/base.py
M plone/app/users/tests/duplicate_email.rst
M plone/app/users/tests/email_login.rst
M plone/app/users/tests/flexible_user_registration.rst
M plone/app/users/tests/test_loginname_generator.py
M plone/app/users/tests/test_new_user.py
M plone/app/users/tests/test_userid_generator.py
M plone/app/users/tests/userdata.rst

diff --git a/plone/app/users/browser/account.py b/plone/app/users/browser/account.py
index f5274ef..4fc7343 100644
--- a/plone/app/users/browser/account.py
+++ b/plone/app/users/browser/account.py
@@ -3,6 +3,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import safe_unicode
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -12,9 +13,11 @@
 from plone.app.users.utils import notifyWidgetActionExecutionError
 from plone.autoform.form import AutoExtensibleForm
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import form
 from zope.component import getMultiAdapter
+from zope.component import getUtility
 from zope.event import notify
 from zope.interface import implements
 
@@ -123,8 +126,10 @@ def validate_email(self, action, data):
         ]
         if 'email' not in error_keys:
             registration = getToolByName(context, 'portal_registration')
-            properties = getToolByName(context, 'portal_properties')
-            if properties.site_properties.getProperty('use_email_as_login'):
+            registry = getUtility(IRegistry)
+            security_settings = registry.forInterface(
+                ISecuritySchema, prefix="plone")
+            if security_settings.use_email_as_login:
                 err_str = ''
                 try:
                     id_allowed = registration.isMemberIdAllowed(data['email'])
diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index bed5edd..bb238ea 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -4,6 +4,7 @@
 from Products.CMFCore.permissions import ManagePortal
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import normalizeString
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -19,6 +20,7 @@
 from plone.autoform.interfaces import OMITTED_KEY
 from plone.autoform.interfaces import ORDER_KEY
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import field
 from z3c.form import form
@@ -51,6 +53,11 @@ class BaseRegistrationForm(AutoExtensibleForm, form.Form):
     # this attribute indicates if user was successfully registered
     _finishedRegister = False
 
+    def _get_security_settings(self):
+        """Return security settings from the registry."""
+        registry = getUtility(IRegistry)
+        return registry.forInterface(ISecuritySchema, prefix="plone")
+
     def render(self):
         if self._finishedRegister:
             return self.context.unrestrictedTraverse('registered')()
@@ -63,7 +70,8 @@ def updateFields(self):
         """
         portal_props = getToolByName(self.context, 'portal_properties')
         props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
+        use_email_as_login = settings.use_email_as_login
 
         # Ensure all listed fields are in the schema
         registration_fields = [f for f in props.getProperty(
@@ -159,7 +167,7 @@ def generate_user_id(self, data):
         1. We query a utility, so integrators can register a hook to
            generate a user id using their own logic.
 
-        2. If use_uuid_as_userid is set in the site_properties, we
+        2. If use_uuid_as_userid is set in the registry, we
            generate a uuid.
 
         3. If a username is given and we do not use email as login,
@@ -198,9 +206,8 @@ def generate_user_id(self, data):
                 data['user_id'] = userid
                 return userid
 
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        if props.getProperty('use_uuid_as_userid'):
+        settings = self._get_security_settings()
+        if settings.use_uuid_as_userid:
             userid = uuid_userid_generator()
             data['user_id'] = userid
             return userid
@@ -209,13 +216,15 @@ def generate_user_id(self, data):
         userid = data.get('username')
         if userid:
             # If we are not using email as login, then this user name is fine.
-            if not props.getProperty('use_email_as_login'):
+            if not settings.use_email_as_login:
                 data['user_id'] = userid
                 return userid
 
         # First get a default value that we can return if we cannot
         # find anything better.
-        default = data.get('username') or data.get('email') or ''
+        pas = getToolByName(self.context, 'acl_users')
+        email = pas.applyTransform(data.get('email'))
+        default = data.get('username') or email or ''
         data['user_id'] = default
         fullname = data.get('fullname')
         if not fullname:
@@ -285,11 +294,9 @@ def generate_login_name(self, data):
         login_name = data.get('username')
         login_name = pas.applyTransform(login_name)
         data['login_name'] = login_name
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
         # If we are not using email as login, then this user name is fine.
-        if not use_email_as_login:
+        if not settings.use_email_as_login:
             return login_name
 
         # We use email as login.
@@ -319,9 +326,6 @@ def validate_registration(self, action, data):
         form_field_names = [f for f in self.fields]
 
         portal = getUtility(ISiteRoot)
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
 
         # passwords should match
         if 'password' in form_field_names:
@@ -349,7 +353,8 @@ def validate_registration(self, action, data):
                         notifyWidgetActionExecutionError(action,
                                                          'password', err_str)
 
-        if use_email_as_login:
+        settings = self._get_security_settings()
+        if settings.use_email_as_login:
             username_field = 'email'
         else:
             username_field = 'username'
@@ -486,7 +491,8 @@ def handle_join_success(self, data):
         # set member properties
         self.applyProperties(user_id, data)
 
-        if data.get('mail_me') or (portal.validate_email and
+        settings = self._get_security_settings()
+        if data.get('mail_me') or (not settings.enable_user_pwd_choice and
                                    not data.get('password')):
             # We want to validate the email address (users cannot
             # select their own passwords on the register form) or the
@@ -588,9 +594,11 @@ def showForm(self):
         ctrlOverview = getMultiAdapter((portal, self.request),
                                        name='overview-controlpanel')
 
-        # hide form if mailhost_warning == True and validate_email == True
+        settings = self._get_security_settings()
+        # hide form if mailhost_warning == True and
+        # enable_user_pwd_choice == False
         return not (ctrlOverview.mailhost_warning() and
-                    portal.getProperty('validate_email', True))
+                    not settings.enable_user_pwd_choice)
 
     def updateFields(self):
         if not self.showForm:
@@ -602,8 +610,8 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # Can the user actually set his/her own password?
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # No? Remove the password fields.
             defaultFields = defaultFields.omit('password', 'password_ctl')
         else:
@@ -622,8 +630,8 @@ def updateWidgets(self):
             return
 
         super(RegistrationForm, self).updateWidgets()
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # Show a message indicating that a password reset link
             # will be mailed to the user.
             widget = self.widgets['mail_me']
@@ -649,7 +657,7 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # The mail_me field needs special handling depending on the
-        # validate_email property and on the correctness of the mail
+        # enable_user_pwd_choice setting and on the correctness of the mail
         # settings.
         portal = getUtility(ISiteRoot)
         ctrlOverview = getMultiAdapter((portal, self.request),
@@ -661,7 +669,8 @@ def updateFields(self):
             # will check that at least one of the options is chosen.
             defaultFields['password'].field.required = False
             defaultFields['password_ctl'].field.required = False
-            if portal.getProperty('validate_email', True):
+            settings = self._get_security_settings()
+            if not settings.enable_user_pwd_choice:
                 defaultFields['mail_me'].field.default = True
             else:
                 defaultFields['mail_me'].field.default = False
diff --git a/plone/app/users/browser/userdatapanel.py b/plone/app/users/browser/userdatapanel.py
index e0adc6b..675d25f 100644
--- a/plone/app/users/browser/userdatapanel.py
+++ b/plone/app/users/browser/userdatapanel.py
@@ -2,6 +2,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import set_own_login_name
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.PlonePAS.tools.membership import default_portrait
@@ -9,6 +10,8 @@
 from plone.app.users.browser.account import AccountPanelSchemaAdapter
 from plone.app.users.schema import IUserDataSchema
 from plone.namedfile.file import NamedBlobImage
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 
 class UserDataPanelAdapter(AccountPanelSchemaAdapter):
@@ -43,8 +46,10 @@ def get_email(self):
         return self._getProperty('email')
 
     def set_email(self, value):
-        pp = getToolByName(self.context, 'portal_properties')
-        if pp.site_properties.getProperty('use_email_as_login'):
+        registry = getUtility(IRegistry)
+        security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+        if security_settings.use_email_as_login:
             mt = getToolByName(self.context, 'portal_membership')
             if self.context.getId() == mt.getAuthenticatedMember().getId():
                 set_own_login_name(self.context, value)
diff --git a/plone/app/users/tests/base.py b/plone/app/users/tests/base.py
index e26b284..607d5b6 100644
--- a/plone/app/users/tests/base.py
+++ b/plone/app/users/tests/base.py
@@ -9,6 +9,7 @@
 from Acquisition import aq_base
 from Products.CMFCore.interfaces import ISiteRoot
 from Products.CMFPlone.interfaces.controlpanel import IMailSchema
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.tests.utils import MockMailHost
 from Products.MailHost.interfaces import IMailHost
 from Products.PlonePAS.Extensions.Install import activatePluginInterfaces
@@ -16,6 +17,7 @@
 from Products.PluggableAuthService.interfaces.plugins import IValidationPlugin
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
+from plone.registry.interfaces import IRegistry
 from OFS.Cache import Cacheable
 from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
@@ -39,6 +41,9 @@ def afterSetUp(self):
         self.portal._original_MailHost = self.portal.MailHost
         self.portal.MailHost = mailhost = MockMailHost('MailHost')
         self.membership = self.portal.portal_membership
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
         sm = getSiteManager(context=self.portal)
         sm.unregisterUtility(provided=IMailHost)
         sm.registerUtility(mailhost, provided=IMailHost)
diff --git a/plone/app/users/tests/duplicate_email.rst b/plone/app/users/tests/duplicate_email.rst
index 80084e5..36d1e0e 100644
--- a/plone/app/users/tests/duplicate_email.rst
+++ b/plone/app/users/tests/duplicate_email.rst
@@ -5,8 +5,7 @@ When email address is used as login name, duplicates are not allowed.
 
 Use email addresses as login name:
 
-    >>> ptool = self.portal.portal_properties
-    >>> ptool.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
 
 Create a new user one:
 
@@ -26,7 +25,7 @@ Login as user two:
     >>> self.browser.open('http://nohost/plone/')
     >>> self.browser.getLink('Log in').click()
 
-    >>> self.browser.getControl('E-mail').value = 'usertwo@example.com'
+    >>> self.browser.getControl('Login Name').value = 'usertwo@example.com'
     >>> self.browser.getControl('Password').value = 'secret'
     >>> self.browser.getControl('Log in').click()
     >>> 'Login failed' in self.browser.contents
diff --git a/plone/app/users/tests/email_login.rst b/plone/app/users/tests/email_login.rst
index 9f32103..b9cd8c8 100644
--- a/plone/app/users/tests/email_login.rst
+++ b/plone/app/users/tests/email_login.rst
@@ -46,9 +46,10 @@ Testing email address as login name
     >>> 'Failed to create your account' in browser.contents
     False
 
-    We can login immediately.
-    >>> 'Click the button to log in immediately.' in browser.contents
-    True
+    We can now login.
+    >>> browser.getLink('Log in').click()
+    >>> browser.getControl('Login Name').value = 'bob-jones+test@example.com'
+    >>> browser.getControl('Password').value = 'secret'
     >>> browser.getControl('Log in').click()
     >>> 'You are now logged in' in browser.contents
     True
@@ -59,10 +60,9 @@ Testing email address as login name
     True
     >>> browser.getLink(url='http://nohost/plone/logout').click()
 
-    We login as manager. The login form now has a different label for
-    the login name.
+    We login as manager.
     >>> browser.open('http://nohost/plone/login_form')
-    >>> browser.getControl('E-mail').value = portal_owner
+    >>> browser.getControl('Login Name').value = portal_owner
     >>> browser.getControl('Password').value = default_password
     >>> browser.getControl('Log in').click()
 
diff --git a/plone/app/users/tests/flexible_user_registration.rst b/plone/app/users/tests/flexible_user_registration.rst
index 89a7d9b..c015cd1 100644
--- a/plone/app/users/tests/flexible_user_registration.rst
+++ b/plone/app/users/tests/flexible_user_registration.rst
@@ -125,7 +125,7 @@ get all required fields on registration form.
 
 Check render register form in 'Use Email As Login' mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> portal.portal_properties.site_properties._updateProperty('user_registration_fields', ['username'])
     >>> browser.open('http://nohost/plone/@@register')
     >>> 'Registration form' in browser.contents
@@ -136,12 +136,10 @@ Check render register form in 'Use Email As Login' mode.
     >>> browser.getControl('Password').value = 'testpassword'
     >>> browser.getControl('Confirm password').value = 'testpassword'
     >>> browser.getControl('Register').click()
-    >>> browser.contents
-    '...Welcome!...You have been registered...'
 
 Revert email mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False
 
 Check register form with portrait field.
 
diff --git a/plone/app/users/tests/test_loginname_generator.py b/plone/app/users/tests/test_loginname_generator.py
index 0b3ea51..2884bdd 100644
--- a/plone/app/users/tests/test_loginname_generator.py
+++ b/plone/app/users/tests/test_loginname_generator.py
@@ -1,30 +1,68 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import ILoginNameGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateLoginName(BaseTestCase):
-    def test_generate_user_id_simplistic(self):
+
+    def afterSetUp(self):
+        super(TestGenerateLoginName, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom login name generator overrides the default
+        behavior.
+        """
+        sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': 'me me me', 'username': 'frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
+
+        self.assertEqual(form.generate_login_name(data), 'me me me')
+        self.assertEqual(data.get('login_name'), 'me me me')
+
+    def test_custom_generator_empty(self):
+        """Test that the username is used if a custom login name generator
+        returns an empty value.
+        """
         sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': '', 'username': 'Frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
 
-        # Without a function, return username
-        self.assertEqual(
-            self.generateLoginName(dict(username='frank')),
-            'frank'
-        )
-
-        # Generator overrides this behavior
-        sm.registerUtility(lambda data: data['useme'],
-                           provided=ILoginNameGenerator)
-        self.assertEqual(
-            self.generateLoginName(dict(useme='me me me', username='frank')),
-            'me me me'
-        )
-
-    def generateLoginName(self, data):
-        """Generate login name, optionally registering function first"""
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_disabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting disabled.
+        """
         form = BaseRegistrationForm(self.portal, {})
-        return form.generate_login_name(data)
+        data = {'username': 'Frank'}
+        self.security_settings.use_email_as_login = False
+
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_enabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting enabled.
+        """
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'username': 'Frank', 'email': 'Frank@Test.com'}
+        self.security_settings.use_email_as_login = True
+
+        self.assertEqual(form.generate_login_name(data), 'frank@test.com')
+        self.assertEqual(data.get('login_name'), 'frank@test.com')
diff --git a/plone/app/users/tests/test_new_user.py b/plone/app/users/tests/test_new_user.py
index efc5e3e..aa0f232 100644
--- a/plone/app/users/tests/test_new_user.py
+++ b/plone/app/users/tests/test_new_user.py
@@ -1,7 +1,12 @@
 # -*- coding: utf-8 -*-
 from hashlib import sha1 as sha
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.tests.base import BaseTestCase
+from plone.app.users.utils import uuid_userid_generator
 from plone.protect import authenticator as auth
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 import hmac
 
@@ -32,3 +37,148 @@ def test_new_user_as_site_administrator(self):
             'Site Administrator' in
             self.portal.acl_users.getUserById('newuser').getRoles()
         )
+
+
+class TestGenerateUserIdLoginName(BaseTestCase):
+    """Test if the user id and user name are properly generated based on the
+    security settings.
+    """
+
+    def afterSetUp(self):
+        super(TestGenerateUserIdLoginName, self).afterSetUp()
+        self.portal_url = self.portal.absolute_url()
+        self.portal.acl_users._doAddUser(
+            'siteadmin', 'secret', ['Site Administrator'], []
+        )
+        self.browser.handleErrors = False
+        self.browser.addHeader('Authorization', 'Basic siteadmin:secret')
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_uuid_disabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # user id should be set the same as user name
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(user.getId(), 'newie')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_disabled_email_as_login_enabled_no_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'newuser@example.com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # Since full name is not provided, the user id is set based on the
+        # e-mail, the same as the user name.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_disabled_email_as_login_enabled_no_full_name_uppercase(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # the user id is set based on the e-mail, which should be lowercased
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_disabled_email_as_login_enabled_has_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # User id should be set based on the full name, user name should be
+        # set based on the e-mail.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_enabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_enabled_email_as_login_enabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id, user name should be based on
+        # the e-mail
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
diff --git a/plone/app/users/tests/test_userid_generator.py b/plone/app/users/tests/test_userid_generator.py
index 4d72d55..a7fa4c1 100644
--- a/plone/app/users/tests/test_userid_generator.py
+++ b/plone/app/users/tests/test_userid_generator.py
@@ -1,15 +1,49 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import IUserIdGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
 from plone.app.users.utils import uuid_userid_generator
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateUserId(BaseTestCase):
 
-    def test_standard_generate_user_id(self):
+    def afterSetUp(self):
+        super(TestGenerateUserId, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom user id generator overrides the default
+        behavior.
+        """
+
+        def one_generator(data):
+            return 'one'
+
+        sm = getSiteManager(context=self.portal)
+        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        form = BaseRegistrationForm(self.portal, {})
+
+        data = {}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+        data = {'username': 'joe',
+                'fullname': 'Joe User',
+                'email': 'joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+    def test_default(self):
+        """Test generating user_id with no custom user id generator and
+        the default security settings.
+        """
         form = BaseRegistrationForm(self.portal, {})
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
@@ -29,51 +63,61 @@ def test_standard_generate_user_id(self):
         self.assertEqual(data.get('user_id'), 'joe-user')
 
         # With no fullname, we take the email.
-        data = {'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
-        self.assertEqual(data.get('user_id'), 'joe@example.org')
-
-    def test_generate_user_id_simplistic(self):
-        # Test a simplistic user id generator.
-        def one_generator(data):
-            return 'one'
-
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        data = {'email': 'Joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'Joe@example.org')
+        self.assertEqual(data.get('user_id'), 'Joe@example.org')
+
+    def test_use_email_as_login_has_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is provided.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
+        data = {'fullname': 'Joe User',
                 'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
+        self.assertEqual(form.generate_user_id(data), 'joe-user')
+        self.assertEqual(data.get('user_id'), 'joe-user')
 
-    def test_generate_user_id_email(self):
-        # It is easy to force the email as user id.
-        def email_getter(data):
-            return data.get('email')
+    def test_use_email_as_login_no_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided.
+        """
+        self.security_settings.use_email_as_login = True
+        form = BaseRegistrationForm(self.portal, {})
 
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(email_getter, provided=IUserIdGenerator)
+        data = {}
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
+
+        data = {'email': 'joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
+        self.assertEqual(data.get('user_id'), 'joe@example.org')
+
+    def test_use_email_as_login_no_fullname_uppercase_email(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided, with an uppercase e-mail.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
         self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
+        data = {'email': 'Joe@Example.org'}
         self.assertEqual(form.generate_user_id(data), 'joe@example.org')
         self.assertEqual(data.get('user_id'), 'joe@example.org')
 
-    def test_generate_user_id_with_uuid(self):
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(uuid_userid_generator, provided=IUserIdGenerator)
+    def test_use_uuid_as_userid_enabled(self):
+        """Test generating a user id if the use_uuid_as_userid setting is
+        enabled.
+        """
+        self.security_settings.use_uuid_as_userid = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
@@ -94,38 +138,3 @@ def test_generate_user_id_with_uuid(self):
         # call to the uuid generator should be unique.
         self.assertNotEqual(form.generate_user_id(data),
                             form.generate_user_id(data))
-
-
-class TestGenerateUUIDUserId(BaseTestCase):
-
-    def afterSetUp(self):
-        super(TestGenerateUUIDUserId, self).afterSetUp()
-        # If use_uuid_as_userid is set in the site_properties, we
-        # generate a uuid.
-        self.ptool = ptool = getattr(self.portal, 'portal_properties')
-        if not ptool.site_properties.hasProperty('use_uuid_as_userid'):
-            ptool.site_properties.manage_addProperty(
-                'use_uuid_as_userid', False, 'boolean'
-            )  # Try to add it.
-        ptool.site_properties.manage_changeProperties(
-            use_uuid_as_userid=True
-        )  # Change it.
-        ptool.site_properties.getProperty('use_uuid_as_userid')
-
-    def test_generate_uuid_user_id(self):
-        self.assertTrue(
-            self.ptool.site_properties.getProperty('use_uuid_as_userid')
-        )
-        form = BaseRegistrationForm(self.portal, {})
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
-        user_id = form.generate_user_id(data)
-        self.assertNotEqual(user_id, 'joe')
-        self.assertEqual(data.get('user_id'), user_id)
-        self.assertEqual(len(user_id), len(uuid_userid_generator()))
-
-        # Calling it twice should give a different result, as every
-        # call to the uuid generator should be unique.
-        self.assertNotEqual(form.generate_user_id(data),
-                            form.generate_user_id(data))
diff --git a/plone/app/users/tests/userdata.rst b/plone/app/users/tests/userdata.rst
index cfc0518..de9b40d 100644
--- a/plone/app/users/tests/userdata.rst
+++ b/plone/app/users/tests/userdata.rst
@@ -210,7 +210,7 @@ Modifying user data in email mode
 
 Let's switch to using Email as Login Name
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> self.browser.open("http://nohost/plone/" + view_name)
 
 Update our email and see if login name was synced:
@@ -237,4 +237,4 @@ should fail with validation errors.
 
 Revert back from email mode
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False


Repository: plone.app.users
Branch: refs/heads/master
Date: 2014-12-14T19:24:17+01:00
Author: Timo Stollenwerk (tisto) <tisto@plone.org>
Commit: https://github.com/plone/plone.app.users/commit/4962d80a6be76c40706fa3b329a5379a187ba66a

Merge pull request #28 from plone/jcerjak-plip10359-z3cform

Jcerjak plip10359 z3cform

Files changed:
M plone/app/users/browser/account.py
M plone/app/users/browser/register.py
M plone/app/users/browser/userdatapanel.py
M plone/app/users/tests/base.py
M plone/app/users/tests/duplicate_email.rst
M plone/app/users/tests/email_login.rst
M plone/app/users/tests/flexible_user_registration.rst
M plone/app/users/tests/test_loginname_generator.py
M plone/app/users/tests/test_new_user.py
M plone/app/users/tests/test_userid_generator.py
M plone/app/users/tests/userdata.rst

diff --git a/plone/app/users/browser/account.py b/plone/app/users/browser/account.py
index f5274ef..4fc7343 100644
--- a/plone/app/users/browser/account.py
+++ b/plone/app/users/browser/account.py
@@ -3,6 +3,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import safe_unicode
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -12,9 +13,11 @@
 from plone.app.users.utils import notifyWidgetActionExecutionError
 from plone.autoform.form import AutoExtensibleForm
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import form
 from zope.component import getMultiAdapter
+from zope.component import getUtility
 from zope.event import notify
 from zope.interface import implements
 
@@ -123,8 +126,10 @@ def validate_email(self, action, data):
         ]
         if 'email' not in error_keys:
             registration = getToolByName(context, 'portal_registration')
-            properties = getToolByName(context, 'portal_properties')
-            if properties.site_properties.getProperty('use_email_as_login'):
+            registry = getUtility(IRegistry)
+            security_settings = registry.forInterface(
+                ISecuritySchema, prefix="plone")
+            if security_settings.use_email_as_login:
                 err_str = ''
                 try:
                     id_allowed = registration.isMemberIdAllowed(data['email'])
diff --git a/plone/app/users/browser/register.py b/plone/app/users/browser/register.py
index bed5edd..bb238ea 100644
--- a/plone/app/users/browser/register.py
+++ b/plone/app/users/browser/register.py
@@ -4,6 +4,7 @@
 from Products.CMFCore.permissions import ManagePortal
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import normalizeString
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.statusmessages.interfaces import IStatusMessage
@@ -19,6 +20,7 @@
 from plone.autoform.interfaces import OMITTED_KEY
 from plone.autoform.interfaces import ORDER_KEY
 from plone.protect import CheckAuthenticator
+from plone.registry.interfaces import IRegistry
 from z3c.form import button
 from z3c.form import field
 from z3c.form import form
@@ -51,6 +53,11 @@ class BaseRegistrationForm(AutoExtensibleForm, form.Form):
     # this attribute indicates if user was successfully registered
     _finishedRegister = False
 
+    def _get_security_settings(self):
+        """Return security settings from the registry."""
+        registry = getUtility(IRegistry)
+        return registry.forInterface(ISecuritySchema, prefix="plone")
+
     def render(self):
         if self._finishedRegister:
             return self.context.unrestrictedTraverse('registered')()
@@ -63,7 +70,8 @@ def updateFields(self):
         """
         portal_props = getToolByName(self.context, 'portal_properties')
         props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
+        use_email_as_login = settings.use_email_as_login
 
         # Ensure all listed fields are in the schema
         registration_fields = [f for f in props.getProperty(
@@ -159,7 +167,7 @@ def generate_user_id(self, data):
         1. We query a utility, so integrators can register a hook to
            generate a user id using their own logic.
 
-        2. If use_uuid_as_userid is set in the site_properties, we
+        2. If use_uuid_as_userid is set in the registry, we
            generate a uuid.
 
         3. If a username is given and we do not use email as login,
@@ -198,9 +206,8 @@ def generate_user_id(self, data):
                 data['user_id'] = userid
                 return userid
 
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        if props.getProperty('use_uuid_as_userid'):
+        settings = self._get_security_settings()
+        if settings.use_uuid_as_userid:
             userid = uuid_userid_generator()
             data['user_id'] = userid
             return userid
@@ -209,13 +216,15 @@ def generate_user_id(self, data):
         userid = data.get('username')
         if userid:
             # If we are not using email as login, then this user name is fine.
-            if not props.getProperty('use_email_as_login'):
+            if not settings.use_email_as_login:
                 data['user_id'] = userid
                 return userid
 
         # First get a default value that we can return if we cannot
         # find anything better.
-        default = data.get('username') or data.get('email') or ''
+        pas = getToolByName(self.context, 'acl_users')
+        email = pas.applyTransform(data.get('email'))
+        default = data.get('username') or email or ''
         data['user_id'] = default
         fullname = data.get('fullname')
         if not fullname:
@@ -285,11 +294,9 @@ def generate_login_name(self, data):
         login_name = data.get('username')
         login_name = pas.applyTransform(login_name)
         data['login_name'] = login_name
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
+        settings = self._get_security_settings()
         # If we are not using email as login, then this user name is fine.
-        if not use_email_as_login:
+        if not settings.use_email_as_login:
             return login_name
 
         # We use email as login.
@@ -319,9 +326,6 @@ def validate_registration(self, action, data):
         form_field_names = [f for f in self.fields]
 
         portal = getUtility(ISiteRoot)
-        portal_props = getToolByName(self.context, 'portal_properties')
-        props = portal_props.site_properties
-        use_email_as_login = props.getProperty('use_email_as_login')
 
         # passwords should match
         if 'password' in form_field_names:
@@ -349,7 +353,8 @@ def validate_registration(self, action, data):
                         notifyWidgetActionExecutionError(action,
                                                          'password', err_str)
 
-        if use_email_as_login:
+        settings = self._get_security_settings()
+        if settings.use_email_as_login:
             username_field = 'email'
         else:
             username_field = 'username'
@@ -486,7 +491,8 @@ def handle_join_success(self, data):
         # set member properties
         self.applyProperties(user_id, data)
 
-        if data.get('mail_me') or (portal.validate_email and
+        settings = self._get_security_settings()
+        if data.get('mail_me') or (not settings.enable_user_pwd_choice and
                                    not data.get('password')):
             # We want to validate the email address (users cannot
             # select their own passwords on the register form) or the
@@ -588,9 +594,11 @@ def showForm(self):
         ctrlOverview = getMultiAdapter((portal, self.request),
                                        name='overview-controlpanel')
 
-        # hide form if mailhost_warning == True and validate_email == True
+        settings = self._get_security_settings()
+        # hide form if mailhost_warning == True and
+        # enable_user_pwd_choice == False
         return not (ctrlOverview.mailhost_warning() and
-                    portal.getProperty('validate_email', True))
+                    not settings.enable_user_pwd_choice)
 
     def updateFields(self):
         if not self.showForm:
@@ -602,8 +610,8 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # Can the user actually set his/her own password?
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # No? Remove the password fields.
             defaultFields = defaultFields.omit('password', 'password_ctl')
         else:
@@ -622,8 +630,8 @@ def updateWidgets(self):
             return
 
         super(RegistrationForm, self).updateWidgets()
-        portal = getUtility(ISiteRoot)
-        if portal.getProperty('validate_email', True):
+        settings = self._get_security_settings()
+        if not settings.enable_user_pwd_choice:
             # Show a message indicating that a password reset link
             # will be mailed to the user.
             widget = self.widgets['mail_me']
@@ -649,7 +657,7 @@ def updateFields(self):
         defaultFields = field.Fields(self.fields)
 
         # The mail_me field needs special handling depending on the
-        # validate_email property and on the correctness of the mail
+        # enable_user_pwd_choice setting and on the correctness of the mail
         # settings.
         portal = getUtility(ISiteRoot)
         ctrlOverview = getMultiAdapter((portal, self.request),
@@ -661,7 +669,8 @@ def updateFields(self):
             # will check that at least one of the options is chosen.
             defaultFields['password'].field.required = False
             defaultFields['password_ctl'].field.required = False
-            if portal.getProperty('validate_email', True):
+            settings = self._get_security_settings()
+            if not settings.enable_user_pwd_choice:
                 defaultFields['mail_me'].field.default = True
             else:
                 defaultFields['mail_me'].field.default = False
diff --git a/plone/app/users/browser/userdatapanel.py b/plone/app/users/browser/userdatapanel.py
index e0adc6b..675d25f 100644
--- a/plone/app/users/browser/userdatapanel.py
+++ b/plone/app/users/browser/userdatapanel.py
@@ -2,6 +2,7 @@
 from Acquisition import aq_inner
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone import PloneMessageFactory as _
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.utils import set_own_login_name
 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from Products.PlonePAS.tools.membership import default_portrait
@@ -9,6 +10,8 @@
 from plone.app.users.browser.account import AccountPanelSchemaAdapter
 from plone.app.users.schema import IUserDataSchema
 from plone.namedfile.file import NamedBlobImage
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 
 class UserDataPanelAdapter(AccountPanelSchemaAdapter):
@@ -43,8 +46,10 @@ def get_email(self):
         return self._getProperty('email')
 
     def set_email(self, value):
-        pp = getToolByName(self.context, 'portal_properties')
-        if pp.site_properties.getProperty('use_email_as_login'):
+        registry = getUtility(IRegistry)
+        security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+        if security_settings.use_email_as_login:
             mt = getToolByName(self.context, 'portal_membership')
             if self.context.getId() == mt.getAuthenticatedMember().getId():
                 set_own_login_name(self.context, value)
diff --git a/plone/app/users/tests/base.py b/plone/app/users/tests/base.py
index e26b284..607d5b6 100644
--- a/plone/app/users/tests/base.py
+++ b/plone/app/users/tests/base.py
@@ -9,6 +9,7 @@
 from Acquisition import aq_base
 from Products.CMFCore.interfaces import ISiteRoot
 from Products.CMFPlone.interfaces.controlpanel import IMailSchema
+from Products.CMFPlone.interfaces import ISecuritySchema
 from Products.CMFPlone.tests.utils import MockMailHost
 from Products.MailHost.interfaces import IMailHost
 from Products.PlonePAS.Extensions.Install import activatePluginInterfaces
@@ -16,6 +17,7 @@
 from Products.PluggableAuthService.interfaces.plugins import IValidationPlugin
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
+from plone.registry.interfaces import IRegistry
 from OFS.Cache import Cacheable
 from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
@@ -39,6 +41,9 @@ def afterSetUp(self):
         self.portal._original_MailHost = self.portal.MailHost
         self.portal.MailHost = mailhost = MockMailHost('MailHost')
         self.membership = self.portal.portal_membership
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
         sm = getSiteManager(context=self.portal)
         sm.unregisterUtility(provided=IMailHost)
         sm.registerUtility(mailhost, provided=IMailHost)
diff --git a/plone/app/users/tests/duplicate_email.rst b/plone/app/users/tests/duplicate_email.rst
index 80084e5..36d1e0e 100644
--- a/plone/app/users/tests/duplicate_email.rst
+++ b/plone/app/users/tests/duplicate_email.rst
@@ -5,8 +5,7 @@ When email address is used as login name, duplicates are not allowed.
 
 Use email addresses as login name:
 
-    >>> ptool = self.portal.portal_properties
-    >>> ptool.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
 
 Create a new user one:
 
@@ -26,7 +25,7 @@ Login as user two:
     >>> self.browser.open('http://nohost/plone/')
     >>> self.browser.getLink('Log in').click()
 
-    >>> self.browser.getControl('E-mail').value = 'usertwo@example.com'
+    >>> self.browser.getControl('Login Name').value = 'usertwo@example.com'
     >>> self.browser.getControl('Password').value = 'secret'
     >>> self.browser.getControl('Log in').click()
     >>> 'Login failed' in self.browser.contents
diff --git a/plone/app/users/tests/email_login.rst b/plone/app/users/tests/email_login.rst
index 9f32103..b9cd8c8 100644
--- a/plone/app/users/tests/email_login.rst
+++ b/plone/app/users/tests/email_login.rst
@@ -46,9 +46,10 @@ Testing email address as login name
     >>> 'Failed to create your account' in browser.contents
     False
 
-    We can login immediately.
-    >>> 'Click the button to log in immediately.' in browser.contents
-    True
+    We can now login.
+    >>> browser.getLink('Log in').click()
+    >>> browser.getControl('Login Name').value = 'bob-jones+test@example.com'
+    >>> browser.getControl('Password').value = 'secret'
     >>> browser.getControl('Log in').click()
     >>> 'You are now logged in' in browser.contents
     True
@@ -59,10 +60,9 @@ Testing email address as login name
     True
     >>> browser.getLink(url='http://nohost/plone/logout').click()
 
-    We login as manager. The login form now has a different label for
-    the login name.
+    We login as manager.
     >>> browser.open('http://nohost/plone/login_form')
-    >>> browser.getControl('E-mail').value = portal_owner
+    >>> browser.getControl('Login Name').value = portal_owner
     >>> browser.getControl('Password').value = default_password
     >>> browser.getControl('Log in').click()
 
diff --git a/plone/app/users/tests/flexible_user_registration.rst b/plone/app/users/tests/flexible_user_registration.rst
index 89a7d9b..c015cd1 100644
--- a/plone/app/users/tests/flexible_user_registration.rst
+++ b/plone/app/users/tests/flexible_user_registration.rst
@@ -125,7 +125,7 @@ get all required fields on registration form.
 
 Check render register form in 'Use Email As Login' mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> portal.portal_properties.site_properties._updateProperty('user_registration_fields', ['username'])
     >>> browser.open('http://nohost/plone/@@register')
     >>> 'Registration form' in browser.contents
@@ -136,12 +136,10 @@ Check render register form in 'Use Email As Login' mode.
     >>> browser.getControl('Password').value = 'testpassword'
     >>> browser.getControl('Confirm password').value = 'testpassword'
     >>> browser.getControl('Register').click()
-    >>> browser.contents
-    '...Welcome!...You have been registered...'
 
 Revert email mode.
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False
 
 Check register form with portrait field.
 
diff --git a/plone/app/users/tests/test_loginname_generator.py b/plone/app/users/tests/test_loginname_generator.py
index 0b3ea51..2884bdd 100644
--- a/plone/app/users/tests/test_loginname_generator.py
+++ b/plone/app/users/tests/test_loginname_generator.py
@@ -1,30 +1,68 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import ILoginNameGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateLoginName(BaseTestCase):
-    def test_generate_user_id_simplistic(self):
+
+    def afterSetUp(self):
+        super(TestGenerateLoginName, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom login name generator overrides the default
+        behavior.
+        """
+        sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': 'me me me', 'username': 'frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
+
+        self.assertEqual(form.generate_login_name(data), 'me me me')
+        self.assertEqual(data.get('login_name'), 'me me me')
+
+    def test_custom_generator_empty(self):
+        """Test that the username is used if a custom login name generator
+        returns an empty value.
+        """
         sm = getSiteManager(context=self.portal)
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'useme': '', 'username': 'Frank'}
+
+        sm.registerUtility(
+            lambda data: data['useme'], provided=ILoginNameGenerator)
 
-        # Without a function, return username
-        self.assertEqual(
-            self.generateLoginName(dict(username='frank')),
-            'frank'
-        )
-
-        # Generator overrides this behavior
-        sm.registerUtility(lambda data: data['useme'],
-                           provided=ILoginNameGenerator)
-        self.assertEqual(
-            self.generateLoginName(dict(useme='me me me', username='frank')),
-            'me me me'
-        )
-
-    def generateLoginName(self, data):
-        """Generate login name, optionally registering function first"""
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_disabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting disabled.
+        """
         form = BaseRegistrationForm(self.portal, {})
-        return form.generate_login_name(data)
+        data = {'username': 'Frank'}
+        self.security_settings.use_email_as_login = False
+
+        self.assertEqual(form.generate_login_name(data), 'Frank')
+        self.assertEqual(data.get('login_name'), 'Frank')
+
+    def test_use_email_as_login_enabled(self):
+        """Test generating user_id with no custom login name generator and
+        with the use_email_as_login security setting enabled.
+        """
+        form = BaseRegistrationForm(self.portal, {})
+        data = {'username': 'Frank', 'email': 'Frank@Test.com'}
+        self.security_settings.use_email_as_login = True
+
+        self.assertEqual(form.generate_login_name(data), 'frank@test.com')
+        self.assertEqual(data.get('login_name'), 'frank@test.com')
diff --git a/plone/app/users/tests/test_new_user.py b/plone/app/users/tests/test_new_user.py
index efc5e3e..aa0f232 100644
--- a/plone/app/users/tests/test_new_user.py
+++ b/plone/app/users/tests/test_new_user.py
@@ -1,7 +1,12 @@
 # -*- coding: utf-8 -*-
 from hashlib import sha1 as sha
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.tests.base import BaseTestCase
+from plone.app.users.utils import uuid_userid_generator
 from plone.protect import authenticator as auth
+from plone.registry.interfaces import IRegistry
+from zope.component import getUtility
 
 import hmac
 
@@ -32,3 +37,148 @@ def test_new_user_as_site_administrator(self):
             'Site Administrator' in
             self.portal.acl_users.getUserById('newuser').getRoles()
         )
+
+
+class TestGenerateUserIdLoginName(BaseTestCase):
+    """Test if the user id and user name are properly generated based on the
+    security settings.
+    """
+
+    def afterSetUp(self):
+        super(TestGenerateUserIdLoginName, self).afterSetUp()
+        self.portal_url = self.portal.absolute_url()
+        self.portal.acl_users._doAddUser(
+            'siteadmin', 'secret', ['Site Administrator'], []
+        )
+        self.browser.handleErrors = False
+        self.browser.addHeader('Authorization', 'Basic siteadmin:secret')
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_uuid_disabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # user id should be set the same as user name
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(user.getId(), 'newie')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_disabled_email_as_login_enabled_no_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'newuser@example.com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # Since full name is not provided, the user id is set based on the
+        # e-mail, the same as the user name.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_disabled_email_as_login_enabled_no_full_name_uppercase(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # the user id is set based on the e-mail, which should be lowercased
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'newuser@example.com')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_disabled_email_as_login_enabled_has_full_name(self):
+        self.security_settings.use_uuid_as_userid = False
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # User id should be set based on the full name, user name should be
+        # set based on the e-mail.
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@Example.Com')
+        self.assertEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
+
+    def test_uuid_enabled_email_as_login_disabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = False
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('User Name').value = 'newie'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newie')), 1)
+        user = pas.getUser('newie')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newie')
+
+    def test_uuid_enabled_email_as_login_enabled(self):
+        self.security_settings.use_uuid_as_userid = True
+        self.security_settings.use_email_as_login = True
+
+        # create a user
+        self.browser.open('http://nohost/plone/@@new-user')
+        self.browser.getControl('Full Name').value = 'New User'
+        self.browser.getControl('E-mail').value = 'NewUser@Example.Com'
+        self.browser.getControl('Password').value = 'foobar'
+        self.browser.getControl('Confirm password').value = 'foobar'
+        self.browser.getControl('Register').click()
+
+        # uuid should be used for the user id, user name should be based on
+        # the e-mail
+        pas = getToolByName(self.portal, 'acl_users')
+        self.assertEquals(len(pas.searchUsers(name='newuser@example.com')), 1)
+        self.assertEquals(len(pas.searchUsers(name='NewUser@Example.Com')), 1)
+        user = pas.getUser('newuser@example.com')
+        self.assertEquals(len(user.getId()), len(uuid_userid_generator()))
+        self.assertNotEquals(user.getId(), 'newuser@example.com')
+        self.assertNotEquals(user.getId(), 'newie')
+        self.assertNotEquals(user.getId(), 'new-user')
+        self.assertEquals(user.getUserName(), 'newuser@example.com')
diff --git a/plone/app/users/tests/test_userid_generator.py b/plone/app/users/tests/test_userid_generator.py
index 4d72d55..a7fa4c1 100644
--- a/plone/app/users/tests/test_userid_generator.py
+++ b/plone/app/users/tests/test_userid_generator.py
@@ -1,15 +1,49 @@
 # -*- coding: utf-8 -*-
 # Note: test setup somehow fails when only tests from this file are run.
+from Products.CMFPlone.interfaces import ISecuritySchema
 from plone.app.users.browser.interfaces import IUserIdGenerator
 from plone.app.users.browser.register import BaseRegistrationForm
 from plone.app.users.tests.base import BaseTestCase
 from plone.app.users.utils import uuid_userid_generator
+from plone.registry.interfaces import IRegistry
 from zope.component import getSiteManager
+from zope.component import getUtility
 
 
 class TestGenerateUserId(BaseTestCase):
 
-    def test_standard_generate_user_id(self):
+    def afterSetUp(self):
+        super(TestGenerateUserId, self).afterSetUp()
+        registry = getUtility(IRegistry)
+        self.security_settings = registry.forInterface(
+            ISecuritySchema, prefix="plone")
+
+    def test_custom_generator(self):
+        """Test if a custom user id generator overrides the default
+        behavior.
+        """
+
+        def one_generator(data):
+            return 'one'
+
+        sm = getSiteManager(context=self.portal)
+        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        form = BaseRegistrationForm(self.portal, {})
+
+        data = {}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+        data = {'username': 'joe',
+                'fullname': 'Joe User',
+                'email': 'joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'one')
+        self.assertEqual(data.get('user_id'), 'one')
+
+    def test_default(self):
+        """Test generating user_id with no custom user id generator and
+        the default security settings.
+        """
         form = BaseRegistrationForm(self.portal, {})
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
@@ -29,51 +63,61 @@ def test_standard_generate_user_id(self):
         self.assertEqual(data.get('user_id'), 'joe-user')
 
         # With no fullname, we take the email.
-        data = {'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
-        self.assertEqual(data.get('user_id'), 'joe@example.org')
-
-    def test_generate_user_id_simplistic(self):
-        # Test a simplistic user id generator.
-        def one_generator(data):
-            return 'one'
-
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(one_generator, provided=IUserIdGenerator)
+        data = {'email': 'Joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'Joe@example.org')
+        self.assertEqual(data.get('user_id'), 'Joe@example.org')
+
+    def test_use_email_as_login_has_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is provided.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
+        data = {'fullname': 'Joe User',
                 'email': 'joe@example.org'}
-        self.assertEqual(form.generate_user_id(data), 'one')
-        self.assertEqual(data.get('user_id'), 'one')
+        self.assertEqual(form.generate_user_id(data), 'joe-user')
+        self.assertEqual(data.get('user_id'), 'joe-user')
 
-    def test_generate_user_id_email(self):
-        # It is easy to force the email as user id.
-        def email_getter(data):
-            return data.get('email')
+    def test_use_email_as_login_no_fullname(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided.
+        """
+        self.security_settings.use_email_as_login = True
+        form = BaseRegistrationForm(self.portal, {})
 
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(email_getter, provided=IUserIdGenerator)
+        data = {}
+        self.assertEqual(form.generate_user_id(data), '')
+        self.assertEqual(data.get('user_id'), '')
+
+        data = {'email': 'joe@example.org'}
+        self.assertEqual(form.generate_user_id(data), 'joe@example.org')
+        self.assertEqual(data.get('user_id'), 'joe@example.org')
+
+    def test_use_email_as_login_no_fullname_uppercase_email(self):
+        """"Test generating a user id if the use_email_as_login setting is
+        enabled and full name is not provided, with an uppercase e-mail.
+        """
+        self.security_settings.use_email_as_login = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
         self.assertEqual(form.generate_user_id(data), '')
         self.assertEqual(data.get('user_id'), '')
 
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
+        data = {'email': 'Joe@Example.org'}
         self.assertEqual(form.generate_user_id(data), 'joe@example.org')
         self.assertEqual(data.get('user_id'), 'joe@example.org')
 
-    def test_generate_user_id_with_uuid(self):
-        sm = getSiteManager(context=self.portal)
-        sm.registerUtility(uuid_userid_generator, provided=IUserIdGenerator)
+    def test_use_uuid_as_userid_enabled(self):
+        """Test generating a user id if the use_uuid_as_userid setting is
+        enabled.
+        """
+        self.security_settings.use_uuid_as_userid = True
         form = BaseRegistrationForm(self.portal, {})
 
         data = {}
@@ -94,38 +138,3 @@ def test_generate_user_id_with_uuid(self):
         # call to the uuid generator should be unique.
         self.assertNotEqual(form.generate_user_id(data),
                             form.generate_user_id(data))
-
-
-class TestGenerateUUIDUserId(BaseTestCase):
-
-    def afterSetUp(self):
-        super(TestGenerateUUIDUserId, self).afterSetUp()
-        # If use_uuid_as_userid is set in the site_properties, we
-        # generate a uuid.
-        self.ptool = ptool = getattr(self.portal, 'portal_properties')
-        if not ptool.site_properties.hasProperty('use_uuid_as_userid'):
-            ptool.site_properties.manage_addProperty(
-                'use_uuid_as_userid', False, 'boolean'
-            )  # Try to add it.
-        ptool.site_properties.manage_changeProperties(
-            use_uuid_as_userid=True
-        )  # Change it.
-        ptool.site_properties.getProperty('use_uuid_as_userid')
-
-    def test_generate_uuid_user_id(self):
-        self.assertTrue(
-            self.ptool.site_properties.getProperty('use_uuid_as_userid')
-        )
-        form = BaseRegistrationForm(self.portal, {})
-        data = {'username': 'joe',
-                'fullname': 'Joe User',
-                'email': 'joe@example.org'}
-        user_id = form.generate_user_id(data)
-        self.assertNotEqual(user_id, 'joe')
-        self.assertEqual(data.get('user_id'), user_id)
-        self.assertEqual(len(user_id), len(uuid_userid_generator()))
-
-        # Calling it twice should give a different result, as every
-        # call to the uuid generator should be unique.
-        self.assertNotEqual(form.generate_user_id(data),
-                            form.generate_user_id(data))
diff --git a/plone/app/users/tests/userdata.rst b/plone/app/users/tests/userdata.rst
index cfc0518..de9b40d 100644
--- a/plone/app/users/tests/userdata.rst
+++ b/plone/app/users/tests/userdata.rst
@@ -210,7 +210,7 @@ Modifying user data in email mode
 
 Let's switch to using Email as Login Name
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', True)
+    >>> self.security_settings.use_email_as_login = True
     >>> self.browser.open("http://nohost/plone/" + view_name)
 
 Update our email and see if login name was synced:
@@ -237,4 +237,4 @@ should fail with validation errors.
 
 Revert back from email mode
 
-    >>> portal.portal_properties.site_properties._updateProperty('use_email_as_login', False)
+    >>> self.security_settings.use_email_as_login = False