~barry/mailman/events-and-web

« back to all changes in this revision

Viewing changes to src/mailman/rest/users.py

  • Committer: Barry Warsaw
  • Date: 2012-09-22 17:35:24 UTC
  • Revision ID: barry@list.org-20120922173524-55hz5ltrdzukupdt
 * You can now PUT and PATCH on user resources to change the user's display
   name or password.  For passwords, you pass in the clear text password and
   Mailman will hash it before storing.

Also:

 * Major refactoring of validators for PUT and PATCH.  Pull the common logic
   out of configuration.py and put it in a PatchValidator class in
   helpers.py.  Also move GetterSetter to helpers.py
 * Add new exception classes RESTError, UnknownPATCHRequestError,
   ReadOnlyPATCHRequestError.  These are used in the PatchValidator.
 * Added Validator.update() which works nicely for PATCH and PUT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
from zope.component import getUtility
33
33
 
34
34
from mailman.config import config
 
35
from mailman.core.errors import (
 
36
    ReadOnlyPATCHRequestError, UnknownPATCHRequestError)
35
37
from mailman.interfaces.address import ExistingAddressError
36
38
from mailman.interfaces.usermanager import IUserManager
37
39
from mailman.rest.addresses import UserAddresses
38
 
from mailman.rest.helpers import CollectionMixin, etag, no_content, path_to
 
40
from mailman.rest.helpers import (
 
41
    CollectionMixin, GetterSetter, PATCH, etag, no_content, path_to)
39
42
from mailman.rest.preferences import Preferences
40
 
from mailman.rest.validator import Validator
 
43
from mailman.rest.validator import PatchValidator, Validator
 
44
 
 
45
 
 
46
# Attributes of a user which can be changed via the REST API.
 
47
class PasswordEncrypterGetterSetter(GetterSetter):
 
48
    def __init__(self):
 
49
        super(PasswordEncrypterGetterSetter, self).__init__(
 
50
            config.password_context.encrypt)
 
51
    def get(self, obj, attribute):
 
52
        assert attribute == 'cleartext_password'
 
53
        super(PasswordEncrypterGetterSetter, self).get(obj, 'password')
 
54
    def put(self, obj, attribute, value):
 
55
        assert attribute == 'cleartext_password'
 
56
        super(PasswordEncrypterGetterSetter, self).put(obj, 'password', value)
 
57
 
 
58
 
 
59
ATTRIBUTES = dict(
 
60
    display_name=GetterSetter(unicode),
 
61
    cleartext_password=PasswordEncrypterGetterSetter(),
 
62
    )
41
63
 
42
64
 
43
65
 
165
187
            self._user.preferences,
166
188
            'users/{0}'.format(self._user.user_id.int))
167
189
        return child, []
 
190
 
 
191
    @PATCH()
 
192
    def patch_update(self, request):
 
193
        """Patch the user's configuration (i.e. partial update)."""
 
194
        if self._user is None:
 
195
            return http.not_found()
 
196
        try:
 
197
            validator = PatchValidator(request, ATTRIBUTES)
 
198
        except UnknownPATCHRequestError as error:
 
199
            return http.bad_request(
 
200
                [], b'Unknown attribute: {0}'.format(error.attribute))
 
201
        except ReadOnlyPATCHRequestError as error:
 
202
            return http.bad_request(
 
203
                [], b'Read-only attribute: {0}'.format(error.attribute))
 
204
        validator.update(self._user, request)
 
205
        return no_content()
 
206
 
 
207
    @resource.PUT()
 
208
    def put_update(self, request):
 
209
        """Put the user's configuration (i.e. full update)."""
 
210
        if self._user is None:
 
211
            return http.not_found()
 
212
        validator = Validator(**ATTRIBUTES)
 
213
        try:
 
214
            validator.update(self._user, request)
 
215
        except UnknownPATCHRequestError as error:
 
216
            return http.bad_request(
 
217
                [], b'Unknown attribute: {0}'.format(error.attribute))
 
218
        except ReadOnlyPATCHRequestError as error:
 
219
            return http.bad_request(
 
220
                [], b'Read-only attribute: {0}'.format(error.attribute))
 
221
        except ValueError as error:
 
222
            return http.bad_request([], str(error))
 
223
        return no_content()