Barry Warsaw pushed to branch master at mailman / Mailman

Commits:
811f10df by Barry Warsaw at 2015-09-20T14:56:47Z
Document an attribute.

Add a doctest for the `owners` top-level resource.

- - - - -
b2a05024 by Barry Warsaw at 2015-09-20T14:56:47Z
Start of owners top level resource.

- - - - -
50757a93 by Barry Warsaw at 2015-09-20T15:43:28Z
Add server_owners attribute to the user manager.

- - - - -
77b8745f by Barry Warsaw at 2015-09-20T15:54:06Z
Implement the REST API for <api>/owners.

- - - - -
6060ae33 by Barry Warsaw at 2015-09-20T15:59:11Z
Add another code-path test.

- - - - -
4188b533 by Barry Warsaw at 2015-09-20T17:23:54Z
Add NEWS.

- - - - -
61877ba9 by Barry Warsaw at 2015-09-22T23:35:47Z
Document an attribute.

Add a doctest for the `owners` top-level resource.

- - - - -
8dfa62d6 by Barry Warsaw at 2015-09-22T23:35:47Z
Start of owners top level resource.

- - - - -
f27103bf by Barry Warsaw at 2015-09-22T23:35:47Z
Add server_owners attribute to the user manager.

- - - - -
3b90cb8e by Barry Warsaw at 2015-09-22T23:35:47Z
Implement the REST API for <api>/owners.

- - - - -
f6e09955 by Barry Warsaw at 2015-09-22T23:35:47Z
Add another code-path test.

- - - - -
2f6a9c1f by Barry Warsaw at 2015-09-22T23:35:47Z
Add NEWS.

- - - - -
d68b6a21 by Barry Warsaw at 2015-09-23T16:25:15Z
Merge branch 'issue135' of gitlab.com:warsaw/mailman into issue135

- - - - -
c8811695 by Barry Warsaw at 2015-09-23T16:27:20Z
Document an attribute.

Add a doctest for the `owners` top-level resource.

- - - - -
4f1fba02 by Barry Warsaw at 2015-09-23T16:27:20Z
Start of owners top level resource.

- - - - -
dfc1cff7 by Barry Warsaw at 2015-09-23T16:27:20Z
Add server_owners attribute to the user manager.

- - - - -
c8779a10 by Barry Warsaw at 2015-09-23T16:27:20Z
Implement the REST API for <api>/owners.

- - - - -
b0a9d9e3 by Barry Warsaw at 2015-09-23T16:27:20Z
Add another code-path test.

- - - - -
afb59427 by Barry Warsaw at 2015-09-23T16:27:20Z
Add NEWS.

- - - - -
3023f73c by Barry Warsaw at 2015-09-23T16:30:37Z
Merge branch 'issue135' of gitlab.com:warsaw/mailman into issue135

- - - - -
6c75191a by Barry Warsaw at 2015-09-23T20:45:41Z
A new top-level resource `<api>/owners` can be used to get the list of 
server
owners as `IUser`s.  (Closes #135)

- - - - -


9 changed files:

- src/mailman/docs/NEWS.rst
- src/mailman/interfaces/user.py
- src/mailman/interfaces/usermanager.py
- src/mailman/model/docs/usermanager.rst
- src/mailman/model/usermanager.py
- + src/mailman/rest/docs/owners.rst
- src/mailman/rest/root.py
- + src/mailman/rest/tests/test_owners.py
- src/mailman/rest/users.py


Changes:

=====================================
src/mailman/docs/NEWS.rst
=====================================
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -78,6 +78,8 @@ REST
    Bompard.
  * The REST API incorrectly parsed `is_server_owner` values when given
    explicitly in the POST that creates a user.  (Closes #136)
+ * A new top-level resource `<api>/owners` can be used to get the list of
+   server owners as `IUser`s.  (Closes #135)
  * By POSTing to a user resource with an existing unlinked address, you can
    link the address to the user.  Given by Abhilash Raj.
 


=====================================
src/mailman/interfaces/user.py
=====================================
--- a/src/mailman/interfaces/user.py
+++ b/src/mailman/interfaces/user.py
@@ -70,6 +70,9 @@ class IUser(Interface):
     memberships = Attribute(
         """A roster of this user's memberships.""")
 
+    is_server_owner = Attribute(
+        """Boolean flag indicating whether the user is a server owner.""")
+
     def register(email, display_name=None):
         """Register the given email address and link it to this user.
 


=====================================
src/mailman/interfaces/usermanager.py
=====================================
--- a/src/mailman/interfaces/usermanager.py
+++ b/src/mailman/interfaces/usermanager.py
@@ -126,3 +126,6 @@ class IUserManager(Interface):
 
     members = Attribute(
         """An iterator of all the `IMembers` in the database.""")
+
+    server_owners = Attribute(
+        """An iterator over all the `IUsers` who are server owners.""")


=====================================
src/mailman/model/docs/usermanager.rst
=====================================
--- a/src/mailman/model/docs/usermanager.rst
+++ b/src/mailman/model/docs/usermanager.rst
@@ -201,3 +201,47 @@ The user has a single unverified address object.
     >>> for address in cris.addresses:
     ...     print(repr(address))
     <Address: Cris Person <c...@example.com> [not verified] at ...>
+
+
+Server owners
+=============
+
+Some users are designated as *server owners*.  At first there are no server
+owners.
+
+    >>> len(list(user_manager.server_owners))
+    0
+
+Dan is made a server owner.
+
+    >>> user_4.is_server_owner = True
+    >>> owners = list(user_manager.server_owners)
+    >>> len(owners)
+    1
+    >>> owners[0]
+    <User "Dan Person" (...) at ...>
+
+Now Ben and Claire are also made server owners.
+
+    >>> user_2.is_server_owner = True
+    >>> user_3.is_server_owner = True
+    >>> owners = list(user_manager.server_owners)
+    >>> len(owners)
+    3
+    >>> from operator import attrgetter
+    >>> for user in sorted(owners, key=attrgetter('display_name')):
+    ...     print(user)
+    <User "Ben Person" (...) at ...>
+    <User "Claire Person" (...) at ...>
+    <User "Dan Person" (...) at ...>
+
+Clair retires as a server owner.
+
+    >>> user_3.is_server_owner = False
+    >>> owners = list(user_manager.server_owners)
+    >>> len(owners)
+    2
+    >>> for user in sorted(owners, key=attrgetter('display_name')):
+    ...     print(user)
+    <User "Ben Person" (...) at ...>
+    <User "Dan Person" (...) at ...>


=====================================
src/mailman/model/usermanager.py
=====================================
--- a/src/mailman/model/usermanager.py
+++ b/src/mailman/model/usermanager.py
@@ -141,4 +141,11 @@ class UserManager:
     def members(self, store):
         """See `IUserManager."""
         for member in store.query(Member).all():
-                yield member
+            yield member
+
+    @property
+    @dbconnection
+    def server_owners(self, store):
+        """ See `IUserManager."""
+        users = store.query(User).filter_by(is_server_owner=True)
+        yield from users


=====================================
src/mailman/rest/docs/owners.rst
=====================================
--- /dev/null
+++ b/src/mailman/rest/docs/owners.rst
@@ -0,0 +1,89 @@
+===============
+ Server owners
+===============
+
+Certain users can be designated as *server owners*.  This role has no direct
+function in the core, but it can be used by clients of the REST API to
+determine additional permissions.  For example, Postorius might allow server
+owners to create new domains.
+
+Initially, there are no server owners.
+
+    >>> dump_json('http://localhost:9001/3.0/owners')
+    http_etag: "..."
+    start: 0
+    total_size: 0
+
+When new users are created in the core, they do not become server owners by
+default.
+
+    >>> from zope.component import getUtility
+    >>> from mailman.interfaces.usermanager import IUserManager
+    >>> user_manager = getUtility(IUserManager)
+    >>> anne = user_manager.create_user('a...@example.com', 'Anne Person')
+    >>> transaction.commit()
+    >>> dump_json('http://localhost:9001/3.0/owners')
+    http_etag: "..."
+    start: 0
+    total_size: 0
+
+Anne's server owner flag is set.
+
+    >>> anne.is_server_owner = True
+    >>> transaction.commit()
+
+And now we can find her user record.
+
+    >>> dump_json('http://localhost:9001/3.0/owners')
+    entry 0:
+        created_on: 2005-08-01T07:49:23
+        display_name: Anne Person
+        http_etag: "..."
+        is_server_owner: True
+        self_link: http://localhost:9001/3.0/users/1
+        user_id: 1
+    http_etag: "..."
+    start: 0
+    total_size: 1
+
+Bart and Cate are also users, but not server owners.
+
+    >>> bart = user_manager.create_user('b...@example.com', 'Bart Person')
+    >>> cate = user_manager.create_user('c...@example.com', 'Cate Person')
+    >>> transaction.commit()
+    >>> dump_json('http://localhost:9001/3.0/owners')
+    entry 0:
+        created_on: 2005-08-01T07:49:23
+        display_name: Anne Person
+        http_etag: "..."
+        is_server_owner: True
+        self_link: http://localhost:9001/3.0/users/1
+        user_id: 1
+    http_etag: "..."
+    start: 0
+    total_size: 1
+
+Anne retires as a server owner, with Bart and Cate taking over.
+
+    >>> anne.is_server_owner = False
+    >>> bart.is_server_owner = True
+    >>> cate.is_server_owner = True
+    >>> transaction.commit()
+    >>> dump_json('http://localhost:9001/3.0/owners')
+    entry 0:
+        created_on: 2005-08-01T07:49:23
+        display_name: Bart Person
+        http_etag: "..."
+        is_server_owner: True
+        self_link: http://localhost:9001/3.0/users/2
+        user_id: 2
+    entry 1:
+        created_on: 2005-08-01T07:49:23
+        display_name: Cate Person
+        http_etag: "..."
+        is_server_owner: True
+        self_link: http://localhost:9001/3.0/users/3
+        user_id: 3
+    http_etag: "..."
+    start: 0
+    total_size: 2


=====================================
src/mailman/rest/root.py
=====================================
--- a/src/mailman/rest/root.py
+++ b/src/mailman/rest/root.py
@@ -39,7 +39,7 @@ from mailman.rest.members import AMember, AllMembers, 
FindMembers
 from mailman.rest.preferences import ReadOnlyPreferences
 from mailman.rest.queues import AQueue, AQueueFile, AllQueues
 from mailman.rest.templates import TemplateFinder
-from mailman.rest.users import AUser, AllUsers
+from mailman.rest.users import AUser, AllUsers, ServerOwners
 from zope.component import getUtility
 
 
@@ -234,6 +234,14 @@ class TopLevel:
             return AUser(user_id), segments
 
     @child()
+    def owners(self, request, segments):
+        """/<api>/owners"""
+        if len(segments) != 0:
+            return BadRequest(), []
+        else:
+            return ServerOwners(), segments
+
+    @child()
     def templates(self, request, segments):
         """/<api>/templates/<fqdn_listname>/<template>/[<language>]
 


=====================================
src/mailman/rest/tests/test_owners.py
=====================================
--- /dev/null
+++ b/src/mailman/rest/tests/test_owners.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2015 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Additional tests for the top-level owners resource."""
+
+__all__ = [
+    'TestOwners',
+    ]
+
+
+import unittest
+
+from mailman.testing.helpers import call_api
+from mailman.testing.layers import RESTLayer
+from urllib.error import HTTPError
+
+
+
+class TestOwners(unittest.TestCase):
+    layer = RESTLayer
+
+    def test_bogus_trailing_path(self):
+        # Nothing is allowed after the top-level /owners resource.
+        with self.assertRaises(HTTPError) as cm:
+            call_api('http://localhost:9001/3.0/owners/anne')
+        self.assertEqual(cm.exception.code, 400)


=====================================
src/mailman/rest/users.py
=====================================
--- a/src/mailman/rest/users.py
+++ b/src/mailman/rest/users.py
@@ -464,3 +464,18 @@ class OwnersForDomain(_UserBase):
     def _get_collection(self, request):
         """See `CollectionMixin`."""
         return list(self._domain.owners)
+
+
+
+class ServerOwners(_UserBase):
+    """All server owners."""
+
+    def on_get(self, request, response):
+        """/owners"""
+        resource = self._make_collection(request)
+        okay(response, etag(resource))
+
+    @paginate
+    def _get_collection(self, request):
+        """See `CollectionMixin`."""
+        return list(getUtility(IUserManager).server_owners)



View it on GitLab: 
https://gitlab.com/mailman/mailman/compare/4e92faced9c8b215420971164fd5e1de97db7a46...6c75191a230474d51505ba3269f14b9093b35863
_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to