diff --git a/organize/README.txt b/organize/README.txt
index 31761d7..d085f2a 100644
--- a/organize/README.txt
+++ b/organize/README.txt
@@ -224,6 +224,26 @@ Now we can also retrieve it from the authentication utility:
u'Tom Sawyer'
+Change Password
+---------------
+
+ >>> data = {'oldPassword': u'tiger',
+ ... 'password': u'lion',
+ ... 'passwordConfirm': u'lion'}
+
+ >>> request = TestRequest()
+
+We need a principal for testing the login stuff:
+
+ >>> from zope.app.authentication.principalfolder import InternalPrincipal
+ >>> principal = InternalPrincipal('scott', 'tiger', 'Scotty')
+ >>> request.setPrincipal(principal)
+
+ >>> from loops.organize.browser import PasswordChange
+ >>> pwcView = PasswordChange(menu, request, testing=True)
+ >>> pwcView.changePassword(data)
+
+
Fin de partie
=============
diff --git a/organize/browser.py b/organize/browser.py
index e8de0a3..8c3d45b 100644
--- a/organize/browser.py
+++ b/organize/browser.py
@@ -25,6 +25,7 @@ $Id$
from zope import interface, component
from zope.app import zapi
+from zope.app.authentication.principalfolder import InternalPrincipal
from zope.app.form.browser.textwidgets import PasswordWidget as BasePasswordWidget
from zope.app.form.interfaces import WidgetInputError
from zope.app.pagetemplate import ViewPageTemplateFile
@@ -39,8 +40,9 @@ from loops.browser.concept import ConceptView
from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView
from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager
-from loops.organize.interfaces import IMemberRegistration
+from loops.organize.interfaces import IMemberRegistration, IPasswordChange
from loops.organize.party import getPersonForUser
+from loops.organize.util import getInternalPrincipal
import loops.browser.util
_ = MessageFactory('zope')
@@ -73,9 +75,26 @@ class PasswordWidget(BasePasswordWidget):
return value
+class OldPasswordWidget(BasePasswordWidget):
+
+ def getInputValue(self):
+ value = super(OldPasswordWidget, self).getInputValue()
+ if value:
+ principal = self.request.principal
+ if not isinstance(principal, InternalPrincipal):
+ principal = getInternalPrincipal(principal.id)
+ if not principal.checkPassword(value):
+ v = _(u'Your old password was not entered correctly.')
+ self._error = WidgetInputError(
+ self.context.__name__, self.label, v)
+ raise self._error
+ return value
+
+
class MemberRegistration(NodeView, Form):
form_fields = FormFields(IMemberRegistration).omit('age')
+ form_fields['password'].custom_widget = PasswordWidget
template = loops.browser.util.dataform
label = _(u'Member Registration')
@@ -92,10 +111,11 @@ class MemberRegistration(NodeView, Form):
def item(self):
return self
- def xupdate(self):
+ def update(self):
# see cybertools.browser.view.GenericView.update()
NodeView.update(self)
Form.update(self)
+ return True
@action(_(u'Register'))
def handle_register_action(self, action, data):
@@ -117,3 +137,53 @@ class MemberRegistration(NodeView, Form):
% (self.url, login, message))
return person
+
+class PasswordChange(NodeView, Form):
+
+ form_fields = FormFields(IPasswordChange).select(
+ 'oldPassword', 'password', 'passwordConfirm')
+ form_fields['oldPassword'].custom_widget = OldPasswordWidget
+ form_fields['password'].custom_widget = PasswordWidget
+ template = loops.browser.util.dataform
+ label = _(u'Change Password')
+
+ def __init__(self, context, request, testing=False):
+ super(PasswordChange, self).__init__(context, request)
+ if not testing:
+ self.setUpWidgets()
+
+ @Lazy
+ def macro(self):
+ return self.template.macros['content']
+
+ @Lazy
+ def item(self):
+ return self
+
+ def update(self):
+ # see cybertools.browser.view.GenericView.update()
+ NodeView.update(self)
+ Form.update(self)
+ return True
+
+ @action(_(u'Change Password'))
+ def handle_change_password_action(self, action, data):
+ self.changePassword(data)
+
+ def changePassword(self, data=None):
+ form = data or self.request.form
+ oldPw = form.get('oldPassword')
+ pw = form.get('password')
+ if form.get('passwordConfirm') != pw:
+ raise ValueError(u'Password and password confirmation do not match.')
+ regMan = IMemberRegistrationManager(self.context.getLoopsRoot())
+ principal = self.request.principal
+ result = regMan.changePassword(principal, oldPw, pw)
+ if not result:
+ raise ValueError(u'Your old password was not entered correctly.')
+ message = _(u'Your password has been changed')
+ self.request.response.redirect('%s?message=%s'
+ % (self.url, message))
+ #self.request.response.redirect('%s/logout.html?message=%s'
+ # % (self.url, message))
+
diff --git a/organize/configure.zcml b/organize/configure.zcml
index 91331a2..b4054e9 100644
--- a/organize/configure.zcml
+++ b/organize/configure.zcml
@@ -84,13 +84,12 @@
permission="zope.Public"
/>
-
+
diff --git a/organize/member.py b/organize/member.py
index 785bb71..60835cf 100644
--- a/organize/member.py
+++ b/organize/member.py
@@ -22,15 +22,15 @@ Member registration adapter(s).
$Id$
"""
-from zope.app import zapi
from zope import interface, component, schema
+from zope.app.component import queryNextUtility
from zope.component import adapts
from zope.interface import implements
from zope.app.authentication.interfaces import IPluggableAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.authentication.principalfolder import InternalPrincipal
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
-from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
from zope.event import notify
from zope.i18nmessageid import MessageFactory
from zope.cachedescriptors.property import Lazy
@@ -39,9 +39,8 @@ from cybertools.typology.interfaces import IType
from loops.interfaces import ILoops
from loops.concept import Concept
from loops.organize.interfaces import IMemberRegistrationManager
-from loops.organize.util import getPrincipalFolder, authPluginId
-
-_ = MessageFactory('zope')
+from loops.organize.util import getPrincipalFolder, authPluginId, getInternalPrincipal
+from loops.util import _
class MemberRegistrationManager(object):
@@ -56,7 +55,6 @@ class MemberRegistrationManager(object):
# step 1: create an internal principal in the loops principal folder:
pFolder = getPrincipalFolder(self.context)
title = firstName and ' '.join((firstName, lastName)) or lastName
- # TODO: care for password encryption:
principal = InternalPrincipal(userId, password, title)
pFolder[userId] = principal
# step 2: create a corresponding person concept:
@@ -80,6 +78,11 @@ class MemberRegistrationManager(object):
notify(ObjectModifiedEvent(person))
return personAdapter
- def changePassword(self, oldPw, newPw):
- pass
+ def changePassword(self, principal, oldPw, newPw):
+ if not isinstance(principal, InternalPrincipal):
+ principal = getInternalPrincipal(principal.id)
+ if not principal.checkPassword(oldPw):
+ return False
+ principal.setPassword(newPw)
+ return True
diff --git a/organize/util.py b/organize/util.py
index 70a90cc..6bf8fc4 100644
--- a/organize/util.py
+++ b/organize/util.py
@@ -22,7 +22,6 @@ Utilities for the loops.organize package.
$Id$
"""
-from zope.app import zapi
from zope import interface, component, schema
from zope.app.authentication.interfaces import IPluggableAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin
@@ -32,16 +31,37 @@ authPluginId = 'loops'
def getPrincipalFolder(context=None):
- pau = zapi.getUtility(IAuthentication, context=context)
- if not IPluggableAuthentication.providedBy(pau):
- raise ValueError(u'There is no pluggable authentication '
- 'utility available.')
- if not authPluginId in pau.authenticatorPlugins:
- raise ValueError(u'There is no loops authenticator '
- 'plugin available.')
- #return component.queryUtility(IAuthenticatorPlugin, authPluginId,
- # context=pau)
- for name, plugin in pau.getAuthenticatorPlugins():
- if name == authPluginId:
- return plugin
+ pau = component.getUtility(IAuthentication, context=context)
+ if not IPluggableAuthentication.providedBy(pau):
+ raise ValueError(u'There is no pluggable authentication '
+ 'utility available.')
+ if not authPluginId in pau.authenticatorPlugins:
+ raise ValueError(u'There is no loops authenticator '
+ 'plugin available.')
+ for name, plugin in pau.getAuthenticatorPlugins():
+ if name == authPluginId:
+ return plugin
+
+def getInternalPrincipal(id, context=None):
+ pau = component.getUtility(IAuthentication, context=context)
+ if not IPluggableAuthentication.providedBy(pau):
+ raise ValueError(u'There is no pluggable authentication '
+ 'utility available.')
+ if not id.startswith(pau.prefix):
+ next = queryNextUtility(pau, IAuthentication)
+ if next is None:
+ raise PrincipalLookupError(id)
+ return next.getPrincipal(id)
+ id = id[len(pau.prefix):]
+ for name, authplugin in pau.getAuthenticatorPlugins():
+ if not id.startswith(authplugin.prefix):
+ continue
+ principal = authplugin.get(id[len(authplugin.prefix):])
+ if principal is None:
+ continue
+ return principal
+ next = queryNextUtility(pau, IAuthentication)
+ if next is not None:
+ return next.getPrincipal(pau.prefix + id)
+ raise PrincipalLookupError(id)