Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jcerjak plip10359 security controlpanel #329

Merged
merged 5 commits into from
Dec 14, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Products/CMFPlone/controlpanel/bbb/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<adapter factory=".mail.MailControlPanelAdapter" />
<adapter factory=".navigation.NavigationControlPanelAdapter" />
<adapter factory=".search.SearchControlPanelAdapter" />
<adapter factory=".security.SecurityControlPanelAdapter" />
<adapter factory=".site.SiteControlPanelAdapter" />
<adapter factory=".markup.MarkupControlPanelAdapter" />

Expand Down
78 changes: 78 additions & 0 deletions Products/CMFPlone/controlpanel/bbb/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.siteroot import IPloneSiteRoot
from Products.CMFPlone.interfaces import ISecuritySchema
from plone.registry.interfaces import IRegistry
from zope.component import adapts
from zope.component import getUtility
from zope.interface import implements
from zope.site.hooks import getSite


class SecurityControlPanelAdapter(object):

adapts(IPloneSiteRoot)
implements(ISecuritySchema)

def __init__(self, context):
self.portal = getSite()
self.pmembership = getToolByName(context, 'portal_membership')
registry = getUtility(IRegistry)
self.settings = registry.forInterface(
ISecuritySchema, prefix="plone")

def get_enable_self_reg(self):
return self.settings.enable_self_reg

def set_enable_self_reg(self, value):
# additional processing in the event handler
self.settings.enable_self_reg = value

enable_self_reg = property(get_enable_self_reg, set_enable_self_reg)

def get_enable_user_pwd_choice(self):
return self.settings.enable_user_pwd_choice

def set_enable_user_pwd_choice(self, value):
self.settings.enable_user_pwd_choice = value

enable_user_pwd_choice = property(get_enable_user_pwd_choice,
set_enable_user_pwd_choice)

def get_enable_user_folders(self):
return self.settings.enable_user_folders

def set_enable_user_folders(self, value):
# additional processing in the event handler
self.settings.enable_user_folders = value

enable_user_folders = property(get_enable_user_folders,
set_enable_user_folders)

def get_allow_anon_views_about(self):
return self.settings.allow_anon_views_about

def set_allow_anon_views_about(self, value):
self.settings.allow_anon_views_about = value

allow_anon_views_about = property(get_allow_anon_views_about,
set_allow_anon_views_about)

def get_use_email_as_login(self):
return self.settings.use_email_as_login

def set_use_email_as_login(self, value):
# additional processing in the event handler
self.settings.use_email_as_login = value

use_email_as_login = property(get_use_email_as_login,
set_use_email_as_login)

def get_use_uuid_as_userid(self):
return self.settings.use_uuid_as_userid

def set_use_uuid_as_userid(self, value):
self.settings.use_uuid_as_userid = value

use_uuid_as_userid = property(get_use_uuid_as_userid,
set_use_uuid_as_userid)
17 changes: 17 additions & 0 deletions Products/CMFPlone/controlpanel/browser/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@
permission="plone.app.controlpanel.Search"
/>

<!-- Security Control Panel -->
<browser:page
name="security-controlpanel"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
class=".security.SecurityControlPanel"
permission="plone.app.controlpanel.Security"
/>

<!-- Security Control Panel - EMail Login -->
<browser:page
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
name="migrate-to-emaillogin"
class=".security.EmailLogin"
template="emaillogin.pt"
permission="cmf.ManagePortal"
/>

<!-- Site Control Panel -->
<browser:page
name="site-controlpanel"
Expand Down
67 changes: 67 additions & 0 deletions Products/CMFPlone/controlpanel/browser/emaillogin.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
metal:use-macro="context/prefs_main_template/macros/master"
i18n:domain="plone">

<body>

<metal:main metal:fill-slot="prefs_configlet_content">
<h1 class="documentFirstHeading"
i18n:translate="heading_find_duplicate_login_names">
Find duplicate login names
</h1>

<p i18n:translate="help_duplicate_login_names">
Switching the email login setting in the
<a i18n:name="link"
tal:attributes="href string:${context/portal_url}/@@security-controlpanel"
i18n:translate="">Security settings</a>
on or off automatically changes the login name for existing users.
This may fail when there are duplicates.
On this page you can search for duplicates.
</p>

<div tal:condition="request/form/submitted|nothing">
<div tal:condition="view/duplicates">
<p i18n:translate="msg_login_duplicates_found">
The following login names would be used by more than one account:
</p>
<ul>
<ol tal:repeat="dup view/duplicates">
<span tal:content="python:dup[0]" />:
<span tal:repeat="account python:dup[1]" tal:content="account" />
</ol>
</ul>
</div>
<div tal:condition="not:view/duplicates">
<p i18n:translate="msg_no_login_duplicates_found">
No login names found that are used by more than one account.
</p>
</div>
</div>

<form action=""
name="emaillogin-migrate"
method="post"
class="enableUnloadProtection enableAutoFocus">
<div class="formControls">
<input type="hidden" name="submitted" value="submitted" id="submitted" />
<input class="context"
type="submit"
name="check_email"
value="Check for duplicate emails"
i18n:attributes="value label_check_duplicate_emails" />
<br />
<input class="context"
type="submit"
name="check_userid"
value="Check for duplicate lower case user ids"
i18n:attributes="value label_check_duplicate_user_ids" />
</div>
</form>

</metal:main>
</body>
</html>
127 changes: 127 additions & 0 deletions Products/CMFPlone/controlpanel/browser/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from Acquisition import aq_inner
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone import PloneMessageFactory as _
from Products.CMFPlone.controlpanel.utils import migrate_to_email_login
from Products.CMFPlone.controlpanel.utils import migrate_from_email_login
from Products.CMFPlone.interfaces import ISecuritySchema
from Products.Five.browser import BrowserView
from collections import defaultdict
from plone.app.registry.browser import controlpanel

import logging

logger = logging.getLogger('Products.CMFPlone')


class SecurityControlPanelForm(controlpanel.RegistryEditForm):

id = "SecurityControlPanel"
label = _(u"Security settings")
schema = ISecuritySchema
schema_prefix = "plone"


class SecurityControlPanel(controlpanel.ControlPanelFormWrapper):
form = SecurityControlPanelForm


class EmailLogin(BrowserView):
"""View to help in migrating to or from using email as login.

We used to change the login name of existing users here, but that
is now done by checking or unchecking the option in the security
control panel. Here you can only search for duplicates.
"""

duplicates = []

def __call__(self):
if self.request.form.get('check_email'):
self.duplicates = self.check_email()
elif self.request.form.get('check_userid'):
self.duplicates = self.check_userid()
return self.index()

@property
def _email_list(self):
context = aq_inner(self.context)
pas = getToolByName(context, 'acl_users')
emails = defaultdict(list)
orig_transform = pas.login_transform
try:
if not orig_transform:
# Temporarily set this to lower, as that will happen
# when turning emaillogin on.
pas.login_transform = 'lower'
for user in pas.getUsers():
if user is None:
# Created in the ZMI?
continue
email = user.getProperty('email', '')
if email:
email = pas.applyTransform(email)
else:
logger.warn("User %s has no email address.",
user.getUserId())
# Add the normal login name anyway.
email = pas.applyTransform(user.getUserName())
emails[email].append(user.getUserId())
finally:
pas.login_transform = orig_transform
return emails

def check_email(self):
duplicates = []
for email, userids in self._email_list.items():
if len(userids) > 1:
logger.warn("Duplicate accounts for email address %s: %r",
email, userids)
duplicates.append((email, userids))

return duplicates

@property
def _userid_list(self):
# user ids are unique, but their lowercase version might not
# be unique.
context = aq_inner(self.context)
pas = getToolByName(context, 'acl_users')
userids = defaultdict(list)
orig_transform = pas.login_transform
try:
if not orig_transform:
# Temporarily set this to lower, as that will happen
# when turning emaillogin on.
pas.login_transform = 'lower'
for user in pas.getUsers():
if user is None:
continue
login_name = pas.applyTransform(user.getUserName())
userids[login_name].append(user.getUserId())
finally:
pas.login_transform = orig_transform
return userids

def check_userid(self):
duplicates = []
for login_name, userids in self._userid_list.items():
if len(userids) > 1:
logger.warn("Duplicate accounts for lower case user id "
"%s: %r", login_name, userids)
duplicates.append((login_name, userids))

return duplicates

def switch_to_email(self):
# This is not used and is only here for backwards
# compatibility. It avoids a test failure in
# Products.CMFPlone.
# XXX: check if this can be removed
migrate_to_email_login(self.context)

def switch_to_userid(self):
# This is not used and is only here for backwards
# compatibility. It avoids a test failure in
# Products.CMFPlone.
# XXX: check if this can be removed
migrate_from_email_login(self.context)
2 changes: 2 additions & 0 deletions Products/CMFPlone/controlpanel/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
<include package=".bbb" />
<include package=".browser" />

<include file="events.zcml" />

</configure>
Loading