~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
4
"""
5
Custom Password widgets.
6
7
TODO: Consider folding this back into Zope3 -- StuartBishop 20050520
8
"""
9
10
__metaclass__ = type
11
5534.1.1 by Carlos Perello Marin
Updated password widget to use a more clear approach
12
from zope.app.form.browser import PasswordWidget
13
from zope.app.form.browser.interfaces import ITextBrowserWidget
14
from zope.app.form.interfaces import WidgetInputError
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
15
from zope.component import getUtility
5534.1.1 by Carlos Perello Marin
Updated password widget to use a more clear approach
16
from zope.interface import implements
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
17
from zope.schema.interfaces import ValidationError
7849.16.59 by Sidnei da Silva
- Cleanup some more imports
18
7849.16.4 by Sidnei da Silva
- Import ViewPageTemplateFile from z3c.pt everywhere
19
from z3c.ptcompat import ViewPageTemplateFile
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
20
2236 by Canonical.com Patch Queue Manager
[trivial] Remove canonical._
21
from canonical.launchpad import _
11882.2.2 by Jonathan Lange
Clear up a heck of a lot of imports from canonical.launchpad.interfaces.
22
from canonical.launchpad.interfaces.launchpad import IPasswordEncryptor
5534.1.1 by Carlos Perello Marin
Updated password widget to use a more clear approach
23
from canonical.launchpad.webapp.interfaces import IMultiLineWidgetLayout
24
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
25
26
class PasswordMismatch(ValidationError):
2865.1.7 by Stuart Bishop
Improve PasswordChangeWidget validation, stopping a System Error from occuring
27
    __doc__ = _("Passwords do not match.")
28
29
    def __repr__(self):
30
        return repr(self.__doc__)
31
32
33
class RequiredPasswordMissing(ValidationError):
34
    __doc__ = _("You must enter a password.")
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
35
36
    def __repr__(self):
37
        return repr(self.__doc__)
38
39
40
class PasswordChangeWidget(PasswordWidget):
41
    """A password change widget.
42
43
    Text is not echoed to the user, and two text boxes are used to ensure
44
    the password is entered correctly.
45
    """
5534.1.1 by Carlos Perello Marin
Updated password widget to use a more clear approach
46
    implements(ITextBrowserWidget, IMultiLineWidgetLayout)
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
47
    type = 'password change'
5534.1.1 by Carlos Perello Marin
Updated password widget to use a more clear approach
48
    display_label = False
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
49
50
    __call__ = ViewPageTemplateFile('templates/passwordchange.pt')
51
52
    def hasInput(self):
53
        """We always have input if there is an existing value
4785.3.7 by Jeroen Vermeulen
Removed whitespace at ends of lines
54
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
55
        No input indicates unchanged.
56
        """
57
        if PasswordWidget.hasInput(self):
58
            return True
59
60
        # If we don't have input from the user, we lie because we will
61
        # use the existing value.
62
        return bool(self._getCurrentPassword())
63
64
    def _getCurrentPassword(self):
65
        # Yesh... indirection up the wazoo to do something this simple.
66
        # Returns the current password.
67
        return self.context.get(self.context.context) or None
68
69
    def getInputValue(self):
70
        """Ensure both text boxes contain the same value and inherited checks
71
4173.1.5 by Francis J. Lacoste
Upgrade canonical widgets to use IBrowserFormNG.
72
        >>> from canonical.launchpad.webapp.servers import (
73
        ...     LaunchpadTestRequest)
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
74
        >>> from zope.schema import Field
75
        >>> field = Field(__name__='foo', title=u'Foo')
76
77
        The widget will only return a value if both of the text boxes
78
        contain the same value. It returns the value encrypted.
79
4173.1.5 by Francis J. Lacoste
Upgrade canonical widgets to use IBrowserFormNG.
80
        >>> request = LaunchpadTestRequest(form={
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
81
        ...     'field.foo': u'My Password', 'field.foo_dupe': u'My Password'})
82
        >>> widget = PasswordChangeWidget(field, request)
83
        >>> crypted_pw = widget.getInputValue()
84
        >>> encryptor = getUtility(IPasswordEncryptor)
85
        >>> encryptor.validate(u'My Password', crypted_pw)
86
        True
87
4785.3.7 by Jeroen Vermeulen
Removed whitespace at ends of lines
88
        Otherwise it raises the exception required by IInputWidget
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
89
4173.1.5 by Francis J. Lacoste
Upgrade canonical widgets to use IBrowserFormNG.
90
        >>> request = LaunchpadTestRequest(form={
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
91
        ...     'field.foo': u'My Password', 'field.foo_dupe': u'No Match'})
92
        >>> widget = PasswordChangeWidget(field, request)
93
        >>> widget.getInputValue()
94
        Traceback (most recent call last):
95
            [...]
2865.1.9 by Stuart Bishop
Fix doctest
96
        WidgetInputError: ('foo', u'Foo', u'Passwords do not match.')
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
97
        """
4173.1.5 by Francis J. Lacoste
Upgrade canonical widgets to use IBrowserFormNG.
98
        value1 = self.request.form_ng.getOne(self.name, None)
99
        value2 = self.request.form_ng.getOne('%s_dupe' % self.name, None)
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
100
        if value1 != value2:
101
            self._error = WidgetInputError(
102
                    self.context.__name__, self.label, PasswordMismatch()
103
                    )
104
            raise self._error
105
106
        # If the user hasn't entered a password, we use the existing one
2865.1.7 by Stuart Bishop
Improve PasswordChangeWidget validation, stopping a System Error from occuring
107
        # if it is there. If it is not there, we are creating a new password
108
        # and we raise an error
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
109
        if not value1:
2865.1.7 by Stuart Bishop
Improve PasswordChangeWidget validation, stopping a System Error from occuring
110
            current_password = self._getCurrentPassword()
111
            if current_password:
112
                return current_password
113
            else:
114
                self._error = WidgetInputError(
115
                        self.context.__name__, self.label,
116
                        RequiredPasswordMissing()
117
                        )
118
                raise self._error
1786 by Canonical.com Patch Queue Manager
[r=jamesh] Password Change Widget
119
120
        # Do any other validation
121
        value = PasswordWidget.getInputValue(self)
122
        assert value == value1, 'Form system has changed'
123
124
        # If we have matching plaintext, encrypt it and return the password
125
        encryptor = getUtility(IPasswordEncryptor)
126
        return encryptor.encrypt(value)
127