diff options
| author | Barry Warsaw | 2012-09-22 17:13:13 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2012-09-22 17:13:13 -0400 |
| commit | 12b9839a5e7f1e9fda477c5e40ed190e08292da7 (patch) | |
| tree | 838f38fd2a6d571da443a6c0335c17044ff86371 | |
| parent | 7e97811156ad3fbf9daafc253a2c473b05e07542 (diff) | |
| download | mailman-12b9839a5e7f1e9fda477c5e40ed190e08292da7.tar.gz mailman-12b9839a5e7f1e9fda477c5e40ed190e08292da7.tar.zst mailman-12b9839a5e7f1e9fda477c5e40ed190e08292da7.zip | |
| -rw-r--r-- | src/mailman/docs/NEWS.rst | 5 | ||||
| -rw-r--r-- | src/mailman/rest/addresses.py | 41 | ||||
| -rw-r--r-- | src/mailman/rest/docs/addresses.rst | 42 | ||||
| -rw-r--r-- | src/mailman/rest/tests/test_addresses.py | 74 |
4 files changed, 159 insertions, 3 deletions
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst index 50eeb0fda..8fec45957 100644 --- a/src/mailman/docs/NEWS.rst +++ b/src/mailman/docs/NEWS.rst @@ -23,6 +23,11 @@ REST * 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. + * You can now verify and unverify an email address through the REST API. + POST to .../addresses/<email>/verify and .../addresses/<email>/unverify + respectively. The POST data is ignored. It is not an error to verify or + unverify an address more than once, but verifying an already verified + address does not change its `.verified_on` date. (LP: #1054730) 3.0 beta 2 -- "Freeze" diff --git a/src/mailman/rest/addresses.py b/src/mailman/rest/addresses.py index 2e81cb030..1e043c2c7 100644 --- a/src/mailman/rest/addresses.py +++ b/src/mailman/rest/addresses.py @@ -31,10 +31,11 @@ from operator import attrgetter from restish import http, resource from zope.component import getUtility -from mailman.rest.helpers import CollectionMixin, etag, path_to +from mailman.rest.helpers import CollectionMixin, etag, no_content, path_to from mailman.rest.members import MemberCollection from mailman.rest.preferences import Preferences from mailman.interfaces.usermanager import IUserManager +from mailman.utilities.datetime import now @@ -76,6 +77,24 @@ class AllAddresses(_AddressBase): +class _VerifyResource(resource.Resource): + """A helper resource for verify/unverify POSTS.""" + + def __init__(self, address, action): + self._address = address + self._action = action + assert action in ('verify', 'unverify') + + @resource.POST() + def verify(self, request): + # We don't care about the POST data, just do the action. + if self._action == 'verify' and self._address.verified_on is None: + self._address.verified_on = now() + elif self._action == 'unverify': + self._address.verified_on = None + return no_content() + + class AnAddress(_AddressBase): """An address.""" @@ -115,6 +134,26 @@ class AnAddress(_AddressBase): 'addresses/{0}'.format(self._address.email)) return child, [] + @resource.child() + def verify(self, request, segments): + """/addresses/<email>/verify""" + if len(segments) != 0: + return http.bad_request() + if self._address is None: + return http.not_found() + child = _VerifyResource(self._address, 'verify') + return child, [] + + @resource.child() + def unverify(self, request, segments): + """/addresses/<email>/verify""" + if len(segments) != 0: + return http.bad_request() + if self._address is None: + return http.not_found() + child = _VerifyResource(self._address, 'unverify') + return child, [] + class UserAddresses(_AddressBase): diff --git a/src/mailman/rest/docs/addresses.rst b/src/mailman/rest/docs/addresses.rst index cb9242d2b..f05b6b9b2 100644 --- a/src/mailman/rest/docs/addresses.rst +++ b/src/mailman/rest/docs/addresses.rst @@ -90,7 +90,6 @@ Verifying When the address gets verified, this attribute is available in the REST representation. -:: >>> from mailman.utilities.datetime import now >>> anne.verified_on = now() @@ -103,6 +102,47 @@ representation. self_link: http://localhost:9001/3.0/addresses/anne@example.com verified_on: 2005-08-01T07:49:23 +Addresses can also be verified through the REST API, by POSTing to the +'verify' sub-resource. The POST data is ignored. + + >>> dump_json('http://localhost:9001/3.0/addresses/' + ... 'cris@example.com/verify', {}) + content-length: 0 + date: ... + server: ... + status: 204 + +Now Cris's address is verified. + + >>> dump_json('http://localhost:9001/3.0/addresses/cris@example.com') + display_name: Cris Person + email: cris@example.com + http_etag: "..." + original_email: cris@example.com + registered_on: 2005-08-01T07:49:23 + self_link: http://localhost:9001/3.0/addresses/cris@example.com + verified_on: 2005-08-01T07:49:23 + +If you should ever need to 'unverify' an address, POST to the 'unverify' +sub-resource. Again, the POST data is ignored. + + >>> dump_json('http://localhost:9001/3.0/addresses/' + ... 'cris@example.com/unverify', {}) + content-length: 0 + date: ... + server: ... + status: 204 + +Now Cris's address is unverified. + + >>> dump_json('http://localhost:9001/3.0/addresses/cris@example.com') + display_name: Cris Person + email: cris@example.com + http_etag: "..." + original_email: cris@example.com + registered_on: 2005-08-01T07:49:23 + self_link: http://localhost:9001/3.0/addresses/cris@example.com + User addresses ============== diff --git a/src/mailman/rest/tests/test_addresses.py b/src/mailman/rest/tests/test_addresses.py index 385b83912..01ce710b2 100644 --- a/src/mailman/rest/tests/test_addresses.py +++ b/src/mailman/rest/tests/test_addresses.py @@ -28,11 +28,14 @@ __all__ = [ import unittest from urllib2 import HTTPError +from zope.component import getUtility from mailman.app.lifecycle import create_list from mailman.database.transaction import transaction +from mailman.interfaces.usermanager import IUserManager from mailman.testing.helpers import call_api from mailman.testing.layers import RESTLayer +from mailman.utilities.datetime import now @@ -52,4 +55,73 @@ class TestAddresses(unittest.TestCase): except HTTPError as exc: self.assertEqual(exc.code, 404) else: - raise AssertionError('Expected HTTPError') + raise AssertionError('Expected HTTPError 404') + + def test_verify_a_missing_address(self): + # POSTing to the 'verify' sub-resource returns a 404. + try: + call_api('http://localhost:9001/3.0/addresses/' + 'nobody@example.com/verify', {}) + except HTTPError as exc: + self.assertEqual(exc.code, 404) + else: + raise AssertionError('Expected HTTPError 404') + + def test_unverify_a_missing_address(self): + # POSTing to the 'unverify' sub-resource returns a 404. + try: + call_api('http://localhost:9001/3.0/addresses/' + 'nobody@example.com/unverify', {}) + except HTTPError as exc: + self.assertEqual(exc.code, 404) + else: + raise AssertionError('Expected HTTPError 404') + + def test_verify_already_verified(self): + # It's okay to verify an already verified; it just doesn't change the + # value. + verified_on = now() + with transaction(): + anne = getUtility(IUserManager).create_address('anne@example.com') + anne.verified_on = verified_on + response, content = call_api( + 'http://localhost:9001/3.0/addresses/anne@example.com/verify', {}) + self.assertEqual(content['status'], '204') + self.assertEqual(anne.verified_on, verified_on) + + def test_unverify_already_unverified(self): + # It's okay to unverify an already unverified; it just doesn't change + # the value. + with transaction(): + anne = getUtility(IUserManager).create_address('anne@example.com') + self.assertEqual(anne.verified_on, None) + response, content = call_api( + 'http://localhost:9001/3.0/addresses/anne@example.com/unverify', {}) + self.assertEqual(content['status'], '204') + self.assertEqual(anne.verified_on, None) + + def test_verify_bad_request(self): + # Too many segments after /verify. + with transaction(): + anne = getUtility(IUserManager).create_address('anne@example.com') + self.assertEqual(anne.verified_on, None) + try: + call_api('http://localhost:9001/3.0/addresses/' + 'anne@example.com/verify/foo', {}) + except HTTPError as exc: + self.assertEqual(exc.code, 400) + else: + raise AssertionError('Expected HTTPError 400') + + def test_unverify_bad_request(self): + # Too many segments after /verify. + with transaction(): + anne = getUtility(IUserManager).create_address('anne@example.com') + self.assertEqual(anne.verified_on, None) + try: + call_api('http://localhost:9001/3.0/addresses/' + 'anne@example.com/unverify/foo', {}) + except HTTPError as exc: + self.assertEqual(exc.code, 400) + else: + raise AssertionError('Expected HTTPError 400') |
