Example #1
0
 def create(self, request):
     """Create a new member."""
     service = getUtility(ISubscriptionService)
     try:
         validator = Validator(
             list_id=unicode,
             subscriber=subscriber_validator,
             display_name=unicode,
             delivery_mode=enum_validator(DeliveryMode),
             role=enum_validator(MemberRole),
             _optional=('delivery_mode', 'display_name', 'role'))
         member = service.join(**validator(request))
     except AlreadySubscribedError:
         return http.conflict([], b'Member already subscribed')
     except NoSuchListError:
         return http.bad_request([], b'No such list')
     except InvalidEmailAddressError:
         return http.bad_request([], b'Invalid email address')
     except ValueError as error:
         return http.bad_request([], str(error))
     # The member_id are UUIDs.  We need to use the integer equivalent in
     # the URL.
     member_id = member.member_id.int
     location = path_to('members/{0}'.format(member_id))
     # Include no extra headers or body.
     return http.created(location, [], None)
    def on_get(self, request, response):
        """/lists/listname/requests"""
        validator = Validator(
            token_owner=enum_validator(TokenOwner),
            request_type=enum_validator(PendType),
            page=int,
            count=int,
            _optional=['token_owner', 'page', 'count', 'request_type'],
            )

        try:
            data = validator(request)
        except ValueError as error:
            bad_request(response, str(error))
        else:
            data.pop('page', None)
            data.pop('count', None)
            token_owner = data.pop('token_owner', None)
            pend_type = data.pop('request_type', PendType.subscription)
            pendings = getUtility(IPendings).find(
                mlist=self._mlist,
                pend_type=pend_type.name,
                token_owner=token_owner)
            resource = _SubscriptionRequestsFound(
                [token for token, pendable in pendings])
            okay(response, etag(resource._make_collection(request)))
Example #3
0
 def on_post(self, request, response):
     """Create a new member."""
     service = getUtility(ISubscriptionService)
     try:
         validator = Validator(list_id=str,
                               subscriber=subscriber_validator,
                               display_name=str,
                               delivery_mode=enum_validator(DeliveryMode),
                               role=enum_validator(MemberRole),
                               _optional=('delivery_mode', 'display_name',
                                          'role'))
         member = service.join(**validator(request))
     except AlreadySubscribedError:
         conflict(response, b'Member already subscribed')
     except NoSuchListError:
         bad_request(response, b'No such list')
     except InvalidEmailAddressError:
         bad_request(response, b'Invalid email address')
     except ValueError as error:
         bad_request(response, str(error))
     else:
         # The member_id are UUIDs.  We need to use the integer equivalent
         # in the URL.
         member_id = member.member_id.int
         location = path_to('members/{0}'.format(member_id))
         created(response, location)
Example #4
0
    def on_patch(self, request, response):
        """Patch the membership.

        This is how subscription changes are done.
        """
        if self._member is None:
            not_found(response)
            return
        try:
            values = Validator(
                address=str,
                delivery_mode=enum_validator(DeliveryMode),
                moderation_action=enum_validator(Action, allow_blank=True),
                _optional=('address', 'delivery_mode', 'moderation_action'),
            )(request)
        except ValueError as error:
            bad_request(response, str(error))
            return
        if 'address' in values:
            email = values['address']
            address = getUtility(IUserManager).get_address(email)
            if address is None:
                bad_request(response, b'Address not registered')
                return
            try:
                self._member.address = address
            except (MembershipError, UnverifiedAddressError) as error:
                bad_request(response, str(error))
                return
        if 'delivery_mode' in values:
            self._member.preferences.delivery_mode = values['delivery_mode']
        if 'moderation_action' in values:
            self._member.moderation_action = values['moderation_action']
        no_content(response)
Example #5
0
    def on_patch(self, request, response):
        """Patch the membership.

        This is how subscription changes are done.
        """
        if self._member is None:
            not_found(response)
            return
        try:
            values = Validator(
                address=str,
                delivery_mode=enum_validator(DeliveryMode),
                moderation_action=enum_validator(Action),
                _optional=('address', 'delivery_mode', 'moderation_action'),
                )(request)
        except ValueError as error:
            bad_request(response, str(error))
            return
        if 'address' in values:
            email = values['address']
            address = getUtility(IUserManager).get_address(email)
            if address is None:
                bad_request(response, b'Address not registered')
                return
            try:
                self._member.address = address
            except (MembershipError, UnverifiedAddressError) as error:
                bad_request(response, str(error))
                return
        if 'delivery_mode' in values:
            self._member.preferences.delivery_mode = values['delivery_mode']
        if 'moderation_action' in values:
            self._member.moderation_action = values['moderation_action']
        no_content(response)
Example #6
0
 def on_post(self, request, response):
     """Create a new member."""
     service = getUtility(ISubscriptionService)
     try:
         validator = Validator(
             list_id=unicode,
             subscriber=subscriber_validator,
             display_name=unicode,
             delivery_mode=enum_validator(DeliveryMode),
             role=enum_validator(MemberRole),
             _optional=('delivery_mode', 'display_name', 'role'))
         member = service.join(**validator(request))
     except AlreadySubscribedError:
         conflict(response, b'Member already subscribed')
     except NoSuchListError:
         bad_request(response, b'No such list')
     except InvalidEmailAddressError:
         bad_request(response, b'Invalid email address')
     except ValueError as error:
         bad_request(response, str(error))
     else:
         # The member_id are UUIDs.  We need to use the integer equivalent
         # in the URL.
         member_id = member.member_id.int
         location = path_to('members/{0}'.format(member_id))
         created(response, location)
Example #7
0
 def _find(self, request, response):
     """Find a member"""
     service = getUtility(ISubscriptionService)
     validator = Validator(
         list_id=str,
         subscriber=str,
         role=enum_validator(MemberRole),
         # Allow pagination.
         page=int,
         count=int,
         fields=list_of_strings_validator,
         _optional=('list_id', 'subscriber', 'role', 'page', 'count',
                    'fields'))
     try:
         data = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
     else:
         # Remove any optional pagination query elements; they will be
         # handled later.
         data.pop('page', None)
         data.pop('count', None)
         fields = data.pop('fields', None)
         members = service.find_members(**data)
         resource = _FoundMembers(members, self.api)
         try:
             collection = resource._make_collection(request, fields)
         except ValueError as ex:
             bad_request(response, str(ex))
             return
         okay(response, etag(collection))
Example #8
0
    def patch_membership(self, request):
        """Patch the membership.

        This is how subscription changes are done.
        """
        if self._member is None:
            return http.not_found()
        try:
            values = Validator(
                address=unicode,
                delivery_mode=enum_validator(DeliveryMode),
                _optional=('address', 'delivery_mode'))(request)
        except ValueError as error:
            return http.bad_request([], str(error))
        if 'address' in values:
            email = values['address']
            address = getUtility(IUserManager).get_address(email)
            if address is None:
                return http.bad_request([], b'Address not registered')
            try:
                self._member.address = address
            except (MembershipError, UnverifiedAddressError) as error:
                return http.bad_request([], str(error))
        if 'delivery_mode' in values:
            self._member.preferences.delivery_mode = values['delivery_mode']
        return no_content()
    def _find(self, request, response):
        validator = Validator(
            header=str,
            action=enum_validator(Action),
            tag=str,
            _optional=('action', 'tag', 'header')
           )
        try:
            data = validator(request)
        except ValueError as error:
            bad_request(response, str(error))
            return

        # Remove any optional pagination elements.
        action = data.pop('action', None)
        if action is not None:
            data['chain'] = action.name
        service = IHeaderMatchList(self._mlist)
        self.header_matches = list(service.filter(**data))

        # Return 404 if no values were found.
        if len(self.header_matches) == 0:
            return not_found(
                response,
                'Cound not find any HeaderMatch for provided search options.')

        resource = self._make_collection(request)
        okay(response, etag(resource))
Example #10
0
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         bad_request(response)
         return
     results = requests.get_request(request_id)
     if results is None:
         not_found(response)
         return
     key, data = results
     try:
         request_type = RequestType[data['_request_type']]
     except ValueError:
         bad_request(response)
         return
     if request_type is RequestType.subscription:
         handle_subscription(self._mlist, request_id, **arguments)
     elif request_type is RequestType.unsubscription:
         handle_unsubscription(self._mlist, request_id, **arguments)
     else:
         bad_request(response)
         return
     no_content(response)
Example #11
0
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         bad_request(response)
         return
     results = requests.get_request(request_id)
     if results is None:
         not_found(response)
         return
     key, data = results
     try:
         request_type = RequestType[data['_request_type']]
     except ValueError:
         bad_request(response)
         return
     if request_type is RequestType.subscription:
         handle_subscription(self._mlist, request_id, **arguments)
     elif request_type is RequestType.unsubscription:
         handle_unsubscription(self._mlist, request_id, **arguments)
     else:
         bad_request(response)
         return
     no_content(response)
 def patch_put(self, request, response, is_optional):
     """Update the header match."""
     try:
         header_match = self.header_matches[self._position]
     except IndexError:
         not_found(response, "No header match at this position: {}".format(self._position))
         return
     kws = dict(header=lowercase, pattern=str, position=int, action=enum_validator(Action))
     if is_optional:
         # For a PATCH, all attributes are optional.
         kws["_optional"] = kws.keys()
     else:
         # For a PUT, position can remain unchanged and action can be None.
         kws["_optional"] = ("action", "position")
     validator = Validator(**kws)
     try:
         arguments = validator(request)
         action = arguments.pop("action", None)
         if action is not None:
             arguments["chain"] = action.name
         for key, value in arguments.items():
             setattr(header_match, key, value)
     except ValueError as error:
         bad_request(response, str(error))
         return
     else:
         no_content(response)
Example #13
0
 def _find(self, request, response):
     validator = Validator(
         subscriber=subscriber_validator(self.api),
         role=enum_validator(MemberRole),
         # Allow pagination.
         page=int,
         count=int,
         _optional=('role', 'page', 'count'))
     try:
         data = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     else:
         # Remove any optional pagination query elements.
         data.pop('page', None)
         data.pop('count', None)
         service = getUtility(ISubscriptionService)
         # Get all membership records for given subscriber.
         memberships = service.find_members(**data)
         # Get all the lists from from the membership records.
         lists = [
             getUtility(IListManager).get_by_list_id(member.list_id)
             for member in memberships
         ]
         # If there are no matching lists, return a 404.
         if not len(lists):
             return not_found(response)
         resource = _ListOfLists(lists, self.api)
         okay(response, etag(resource._make_collection(request)))
Example #14
0
 def moderate(self, request):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         return http.bad_request([], str(error))
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         return http.bad_request()
     results = requests.get_request(request_id)
     if results is None:
         return http.not_found()
     key, data = results
     try:
         request_type = RequestType(data['_request_type'])
     except ValueError:
         return http.bad_request()
     if request_type is RequestType.subscription:
         handle_subscription(self._mlist, request_id, **arguments)
     elif request_type is RequestType.unsubscription:
         handle_unsubscription(self._mlist, request_id, **arguments)
     else:
         return http.bad_request()
     return no_content()
Example #15
0
 def _patch_put(self, request, response, is_optional):
     """Update the header match."""
     try:
         header_match = self.header_matches[self._position]
     except IndexError:
         not_found(
             response,
             'No header match at this position: {}'.format(self._position))
         return
     kws = dict(
         header=lowercase,
         pattern=str,
         position=int,
         action=enum_validator(Action),
     )
     if is_optional:
         # For a PATCH, all attributes are optional.
         kws['_optional'] = kws.keys()
     else:
         # For a PUT, position can remain unchanged and action can be None.
         kws['_optional'] = ('action', 'position')
     validator = Validator(**kws)
     try:
         arguments = validator(request)
         action = arguments.pop('action', None)
         if action is not None:
             arguments['chain'] = action.name
         for key, value in arguments.items():
             setattr(header_match, key, value)
     except ValueError as error:
         bad_request(response, str(error))
         return
     else:
         no_content(response)
 def on_get(self, request, response):
     validator = Validator(
         token_owner=enum_validator(TokenOwner),
         request_type=enum_validator(PendType),
         _optional=['token_owner', 'request_type'])
     try:
         data = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
     else:
         token_owner = data.pop('token_owner', None)
         pend_type = data.pop('request_type', PendType.subscription)
         count = getUtility(IPendings).count(
             mlist=self._mlist,
             pend_type=pend_type.name,
             token_owner=token_owner)
         okay(response, etag(dict(count=count)))
Example #17
0
 def patch_put(self, request, is_optional):
     if self._parent is None:
         return http.not_found()
     kws = dict(
         acknowledge_posts=GetterSetter(as_boolean),
         delivery_mode=GetterSetter(enum_validator(DeliveryMode)),
         delivery_status=GetterSetter(enum_validator(DeliveryStatus)),
         preferred_language=GetterSetter(language_validator),
         receive_list_copy=GetterSetter(as_boolean),
         receive_own_postings=GetterSetter(as_boolean),
         )
     if is_optional:
         # For a PUT, all attributes are optional.
         kws['_optional'] = kws.keys()
     try:
         Validator(**kws).update(self._parent, request)
     except ValueError as error:
         return http.bad_request([], str(error))
     return no_content()
Example #18
0
 def patch_put(self, request, is_optional):
     if self._parent is None:
         return http.not_found()
     kws = dict(
         acknowledge_posts=as_boolean,
         delivery_mode=enum_validator(DeliveryMode),
         delivery_status=enum_validator(DeliveryStatus),
         preferred_language=language_validator,
         receive_list_copy=as_boolean,
         receive_own_postings=as_boolean,
         )
     if is_optional:
         # For a PUT, all attributes are optional.
         kws['_optional'] = kws.keys()
     try:
         values = Validator(**kws)(request)
     except ValueError as error:
         return http.bad_request([], str(error))
     for key, value in values.items():
         setattr(self._parent, key, value)
     return no_content()
Example #19
0
 def patch_put(self, request, response, is_optional):
     if self._parent is None:
         not_found(response)
         return
     kws = dict(
         acknowledge_posts=GetterSetter(as_boolean),
         hide_address=GetterSetter(as_boolean),
         delivery_mode=GetterSetter(enum_validator(DeliveryMode)),
         delivery_status=GetterSetter(enum_validator(DeliveryStatus)),
         preferred_language=GetterSetter(language_validator),
         receive_list_copy=GetterSetter(as_boolean),
         receive_own_postings=GetterSetter(as_boolean),
         )
     if is_optional:
         # For a PUT, all attributes are optional.
         kws['_optional'] = kws.keys()
     try:
         Validator(**kws).update(self._parent, request)
     except ValueError as error:
         bad_request(response, str(error))
     else:
         no_content(response)
Example #20
0
 def on_post(self, request, response):
     """Find a member"""
     service = getUtility(ISubscriptionService)
     validator = Validator(list_id=str,
                           subscriber=str,
                           role=enum_validator(MemberRole),
                           _optional=('list_id', 'subscriber', 'role'))
     try:
         members = service.find_members(**validator(request))
     except ValueError as error:
         bad_request(response, str(error))
     else:
         resource = _FoundMembers(members, self.api_version)
         okay(response, etag(resource._make_collection(request)))
Example #21
0
 def on_post(self, request, response):
     """Find a member"""
     service = getUtility(ISubscriptionService)
     validator = Validator(
         list_id=unicode,
         subscriber=unicode,
         role=enum_validator(MemberRole),
         _optional=('list_id', 'subscriber', 'role'))
     try:
         members = service.find_members(**validator(request))
     except ValueError as error:
         bad_request(response, str(error))
     else:
         resource = _FoundMembers(members)._make_collection(request)
         okay(response, etag(resource))
Example #22
0
 def find(self, request):
     """Find a member"""
     service = getUtility(ISubscriptionService)
     validator = Validator(
         list_id=unicode,
         subscriber=unicode,
         role=enum_validator(MemberRole),
         _optional=('list_id', 'subscriber', 'role'))
     members = service.find_members(**validator(request))
     # We can't just return the _FoundMembers instance, because
     # CollectionMixins have only a GET method, which is incompatible with
     # this POSTed resource.  IOW, without doing this here, restish would
     # throw a 405 Method Not Allowed.
     resource = _FoundMembers(members)._make_collection(request)
     return http.ok([], etag(resource))
Example #23
0
 def moderate(self, request):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         return http.bad_request([], str(error))
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         return http.bad_request()
     results = requests.get_request(request_id, RequestType.held_message)
     if results is None:
         return http.not_found()
     handle_message(self._mlist, request_id, **arguments)
     return no_content()
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action),
                               reason=str,
                               _optional=('reason',))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     action = arguments['action']
     if action in (Action.defer, Action.hold):
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=False)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     elif action is Action.accept:
         try:
             self._registrar.confirm(self._token)
         except LookupError:
             not_found(response)
         except AlreadySubscribedError:
             conflict(response, 'Already subscribed')
         else:
             no_content(response)
     elif action is Action.discard:
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     else:
         assert action is Action.reject, action
         # Like discard but sends a rejection notice to the user.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
             reason = arguments.get('reason', _('[No reason given]'))
             send_rejection(
                 self._mlist, _('Subscription request'),
                 pendable['email'], reason)
 def on_post(self, request, response):
     """Add a header match."""
     validator = Validator(header=str, pattern=str, action=enum_validator(Action), _optional=("action",))
     try:
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     action = arguments.pop("action", None)
     if action is not None:
         arguments["chain"] = action.name
     try:
         self.header_matches.append(**arguments)
     except ValueError:
         bad_request(response, b"This header match already exists")
     else:
         header_match = self.header_matches[-1]
         created(response, self._location(header_match.position))
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         not_found(response)
         return
     results = requests.get_request(request_id, RequestType.held_message)
     if results is None:
         not_found(response)
     else:
         handle_message(self._mlist, request_id, **arguments)
         no_content(response)
Example #27
0
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     requests = IListRequests(self._mlist)
     try:
         request_id = int(self._request_id)
     except ValueError:
         bad_request(response)
         return
     results = requests.get_request(request_id, RequestType.held_message)
     if results is None:
         not_found(response)
     else:
         handle_message(self._mlist, request_id, **arguments)
         no_content(response)
Example #28
0
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     action = arguments['action']
     if action in (Action.defer, Action.hold):
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=False)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     elif action is Action.accept:
         try:
             self._registrar.confirm(self._token)
         except LookupError:
             not_found(response)
         except AlreadySubscribedError:
             conflict(response, 'Already subscribed')
         else:
             no_content(response)
     elif action is Action.discard:
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     else:
         assert action is Action.reject, action
         # Like discard but sends a rejection notice to the user.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
             send_rejection(
                 self._mlist, _('Subscription request'),
                 pendable['email'],
                 _('[No reason given]'))
Example #29
0
 def on_post(self, request, response):
     """Add a header match."""
     validator = Validator(header=str,
                           pattern=str,
                           action=enum_validator(Action),
                           _optional=('action', ))
     try:
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     action = arguments.pop('action', None)
     if action is not None:
         arguments['chain'] = action.name
     try:
         self.header_matches.append(**arguments)
     except ValueError:
         bad_request(response, b'This header match already exists')
     else:
         header_match = self.header_matches[-1]
         created(response, self._location(header_match.position))
Example #30
0
 def _find(self, request, response):
     """Find a member"""
     service = getUtility(ISubscriptionService)
     validator = Validator(
         list_id=str,
         subscriber=str,
         role=enum_validator(MemberRole),
         # Allow pagination.
         page=int,
         count=int,
         _optional=('list_id', 'subscriber', 'role', 'page', 'count'))
     try:
         data = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
     else:
         # Remove any optional pagination query elements; they will be
         # handled later.
         data.pop('page', None)
         data.pop('count', None)
         members = service.find_members(**data)
         resource = _FoundMembers(members, self.api)
         okay(response, etag(resource._make_collection(request)))
Example #31
0
 def on_post(self, request, response):
     try:
         validator = Validator(action=enum_validator(Action))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     action = arguments["action"]
     if action is Action.defer:
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=False)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     elif action is Action.accept:
         try:
             self._registrar.confirm(self._token)
         except LookupError:
             not_found(response)
         else:
             no_content(response)
     elif action is Action.discard:
         # At least see if the token is in the database.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
     elif action is Action.reject:
         # Like discard but sends a rejection notice to the user.
         pendable = self._pendings.confirm(self._token, expunge=True)
         if pendable is None:
             not_found(response)
         else:
             no_content(response)
             send_rejection(self._mlist, _("Subscription request"), pendable["email"], _("[No reason given]"))
Example #32
0
#
# The decoder must either return the internal value or raise a ValueError if
# the conversion failed (e.g. trying to turn 'Nope' into a boolean).
#
# Many internal value types can be automatically JSON encoded, but see
# mailman.rest.helpers.ExtendedEncoder for specializations of certain types
# (e.g. datetimes, timedeltas, enums).

ATTRIBUTES = dict(
    acceptable_aliases=AcceptableAliases(list_of_unicode),
    admin_immed_notify=GetterSetter(as_boolean),
    admin_notify_mchanges=GetterSetter(as_boolean),
    administrivia=GetterSetter(as_boolean),
    advertised=GetterSetter(as_boolean),
    anonymous_list=GetterSetter(as_boolean),
    autorespond_owner=GetterSetter(enum_validator(ResponseAction)),
    autorespond_postings=GetterSetter(enum_validator(ResponseAction)),
    autorespond_requests=GetterSetter(enum_validator(ResponseAction)),
    autoresponse_grace_period=GetterSetter(as_timedelta),
    autoresponse_owner_text=GetterSetter(unicode),
    autoresponse_postings_text=GetterSetter(unicode),
    autoresponse_request_text=GetterSetter(unicode),
    archive_policy=GetterSetter(enum_validator(ArchivePolicy)),
    bounces_address=GetterSetter(None),
    collapse_alternatives=GetterSetter(as_boolean),
    convert_html_to_plaintext=GetterSetter(as_boolean),
    created_at=GetterSetter(None),
    default_member_action=GetterSetter(enum_validator(Action)),
    default_nonmember_action=GetterSetter(enum_validator(Action)),
    description=GetterSetter(unicode),
    digest_last_sent_at=GetterSetter(None),
Example #33
0
#
# The decoder must either return the internal value or raise a ValueError if
# the conversion failed (e.g. trying to turn 'Nope' into a boolean).
#
# Many internal value types can be automatically JSON encoded, but see
# mailman.rest.helpers.ExtendedEncoder for specializations of certain types
# (e.g. datetimes, timedeltas, enums).

ATTRIBUTES = dict(
    acceptable_aliases=AcceptableAliases(list_of_strings_validator),
    admin_immed_notify=GetterSetter(as_boolean),
    admin_notify_mchanges=GetterSetter(as_boolean),
    administrivia=GetterSetter(as_boolean),
    advertised=GetterSetter(as_boolean),
    anonymous_list=GetterSetter(as_boolean),
    autorespond_owner=GetterSetter(enum_validator(ResponseAction)),
    autorespond_postings=GetterSetter(enum_validator(ResponseAction)),
    autorespond_requests=GetterSetter(enum_validator(ResponseAction)),
    autoresponse_grace_period=GetterSetter(as_timedelta),
    autoresponse_owner_text=GetterSetter(str),
    autoresponse_postings_text=GetterSetter(str),
    autoresponse_request_text=GetterSetter(str),
    archive_policy=GetterSetter(enum_validator(ArchivePolicy)),
    bounces_address=GetterSetter(None),
    collapse_alternatives=GetterSetter(as_boolean),
    convert_html_to_plaintext=GetterSetter(as_boolean),
    created_at=GetterSetter(None),
    default_member_action=GetterSetter(enum_validator(Action)),
    default_nonmember_action=GetterSetter(enum_validator(Action)),
    description=GetterSetter(str),
    digest_last_sent_at=GetterSetter(None),
Example #34
0
# the conversion failed (e.g. trying to turn 'Nope' into a boolean).
#
# Many internal value types can be automatically JSON encoded, but see
# mailman.rest.helpers.ExtendedEncoder for specializations of certain types
# (e.g. datetimes, timedeltas, enums).

ATTRIBUTES = dict(
    acceptable_aliases=AcceptableAliases(list_of_strings_validator),
    accept_these_nonmembers=GetterSetter(list_of_strings_validator),
    admin_immed_notify=GetterSetter(as_boolean),
    admin_notify_mchanges=GetterSetter(as_boolean),
    administrivia=GetterSetter(as_boolean),
    advertised=GetterSetter(as_boolean),
    allow_list_posts=GetterSetter(as_boolean),
    anonymous_list=GetterSetter(as_boolean),
    archive_policy=GetterSetter(enum_validator(ArchivePolicy)),
    archive_rendering_mode=GetterSetter(enum_validator(ArchiveRenderingMode)),
    autorespond_owner=GetterSetter(enum_validator(ResponseAction)),
    autorespond_postings=GetterSetter(enum_validator(ResponseAction)),
    autorespond_requests=GetterSetter(enum_validator(ResponseAction)),
    autoresponse_grace_period=GetterSetter(as_timedelta),
    autoresponse_owner_text=GetterSetter(str),
    autoresponse_postings_text=GetterSetter(str),
    autoresponse_request_text=GetterSetter(str),
    bounces_address=GetterSetter(None),
    bounce_info_stale_after=GetterSetter(as_timedelta),
    bounce_notify_owner_on_disable=GetterSetter(as_boolean),
    bounce_notify_owner_on_removal=GetterSetter(as_boolean),
    bounce_score_threshold=GetterSetter(integer_ge_zero_validator),
    bounce_you_are_disabled_warnings=GetterSetter(integer_ge_zero_validator),
    bounce_you_are_disabled_warnings_interval=GetterSetter(as_timedelta),
Example #35
0
 def on_post(self, request, response):
     """Create a new member."""
     try:
         validator = Validator(list_id=str,
                               subscriber=subscriber_validator(self.api),
                               display_name=str,
                               delivery_mode=enum_validator(DeliveryMode),
                               role=enum_validator(MemberRole),
                               pre_verified=bool,
                               pre_confirmed=bool,
                               pre_approved=bool,
                               _optional=('delivery_mode', 'display_name',
                                          'role', 'pre_verified',
                                          'pre_confirmed', 'pre_approved'))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     # Dig the mailing list out of the arguments.
     list_id = arguments.pop('list_id')
     mlist = getUtility(IListManager).get_by_list_id(list_id)
     if mlist is None:
         bad_request(response, b'No such list')
         return
     # Figure out what kind of subscriber is being registered.  Either it's
     # a user via their preferred email address or it's an explicit address.
     # If it's a UUID, then it must be associated with an existing user.
     subscriber = arguments.pop('subscriber')
     user_manager = getUtility(IUserManager)
     # We use the display name if there is one.
     display_name = arguments.pop('display_name', '')
     if isinstance(subscriber, UUID):
         user = user_manager.get_user_by_id(subscriber)
         if user is None:
             bad_request(response, b'No such user')
             return
         subscriber = user
     else:
         # This must be an email address.  See if there's an existing
         # address object associated with this email.
         address = user_manager.get_address(subscriber)
         if address is None:
             # Create a new address, which of course will not be validated.
             address = user_manager.create_address(subscriber, display_name)
         subscriber = address
     # What role are we subscribing?  Regular members go through the
     # subscription policy workflow while owners, moderators, and
     # nonmembers go through the legacy API for now.
     role = arguments.pop('role', MemberRole.member)
     if role is MemberRole.member:
         # Get the pre_ flags for the subscription workflow.
         pre_verified = arguments.pop('pre_verified', False)
         pre_confirmed = arguments.pop('pre_confirmed', False)
         pre_approved = arguments.pop('pre_approved', False)
         # Now we can run the registration process until either the
         # subscriber is subscribed, or the workflow is paused for
         # verification, confirmation, or approval.
         registrar = ISubscriptionManager(mlist)
         try:
             token, token_owner, member = registrar.register(
                 subscriber,
                 pre_verified=pre_verified,
                 pre_confirmed=pre_confirmed,
                 pre_approved=pre_approved)
         except AlreadySubscribedError:
             conflict(response, b'Member already subscribed')
             return
         except MissingPreferredAddressError:
             bad_request(response, b'User has no preferred address')
             return
         except MembershipIsBannedError:
             bad_request(response, b'Membership is banned')
             return
         except SubscriptionPendingError:
             conflict(response, b'Subscription request already pending')
             return
         if token is None:
             assert token_owner is TokenOwner.no_one, token_owner
             # The subscription completed.  Let's get the resulting member
             # and return the location to the new member.  Member ids are
             # UUIDs and need to be converted to URLs because JSON doesn't
             # directly support UUIDs.
             member_id = self.api.from_uuid(member.member_id)
             location = self.api.path_to('members/{}'.format(member_id))
             created(response, location)
             return
         # The member could not be directly subscribed because there are
         # some out-of-band steps that need to be completed.  E.g. the user
         # must confirm their subscription or the moderator must approve
         # it.  In this case, an HTTP 202 Accepted is exactly the code that
         # we should use, and we'll return both the confirmation token and
         # the "token owner" so the client knows who should confirm it.
         assert token is not None, token
         assert token_owner is not TokenOwner.no_one, token_owner
         assert member is None, member
         content = dict(token=token, token_owner=token_owner.name)
         accepted(response, etag(content))
         return
     # 2015-04-15 BAW: We're subscribing some role other than a regular
     # member.  Use the legacy API for this for now.
     assert role in (MemberRole.owner, MemberRole.moderator,
                     MemberRole.nonmember)
     # 2015-04-15 BAW: We're limited to using an email address with this
     # legacy API, so if the subscriber is a user, the user must have a
     # preferred address, which we'll use, even though it will subscribe
     # the explicit address.  It is an error if the user does not have a
     # preferred address.
     #
     # If the subscriber is an address object, just use that.
     if IUser.providedBy(subscriber):
         if subscriber.preferred_address is None:
             bad_request(response, b'User without preferred address')
             return
         email = subscriber.preferred_address.email
     else:
         assert IAddress.providedBy(subscriber)
         email = subscriber.email
     delivery_mode = arguments.pop('delivery_mode', DeliveryMode.regular)
     record = RequestRecord(email, display_name, delivery_mode)
     try:
         member = add_member(mlist, record, role)
     except MembershipIsBannedError:
         bad_request(response, b'Membership is banned')
         return
     except AlreadySubscribedError:
         bad_request(
             response,
             '{} is already an {} of {}'.format(email, role.name,
                                                mlist.fqdn_listname))
         return
     # The subscription completed.  Let's get the resulting member
     # and return the location to the new member.  Member ids are
     # UUIDs and need to be converted to URLs because JSON doesn't
     # directly support UUIDs.
     member_id = self.api.from_uuid(member.member_id)
     location = self.api.path_to('members/{}'.format(member_id))
     created(response, location)
Example #36
0
# The decoder must either return the internal value or raise a ValueError if
# the conversion failed (e.g. trying to turn 'Nope' into a boolean).
#
# Many internal value types can be automatically JSON encoded, but see
# mailman.rest.helpers.ExtendedEncoder for specializations of certain types
# (e.g. datetimes, timedeltas, enums).

ATTRIBUTES = dict(
    acceptable_aliases=AcceptableAliases(list_of_strings_validator),
    admin_immed_notify=GetterSetter(as_boolean),
    admin_notify_mchanges=GetterSetter(as_boolean),
    administrivia=GetterSetter(as_boolean),
    advertised=GetterSetter(as_boolean),
    allow_list_posts=GetterSetter(as_boolean),
    anonymous_list=GetterSetter(as_boolean),
    archive_policy=GetterSetter(enum_validator(ArchivePolicy)),
    autorespond_owner=GetterSetter(enum_validator(ResponseAction)),
    autorespond_postings=GetterSetter(enum_validator(ResponseAction)),
    autorespond_requests=GetterSetter(enum_validator(ResponseAction)),
    autoresponse_grace_period=GetterSetter(as_timedelta),
    autoresponse_owner_text=GetterSetter(str),
    autoresponse_postings_text=GetterSetter(str),
    autoresponse_request_text=GetterSetter(str),
    bounces_address=GetterSetter(None),
    collapse_alternatives=GetterSetter(as_boolean),
    convert_html_to_plaintext=GetterSetter(as_boolean),
    created_at=GetterSetter(None),
    default_member_action=GetterSetter(enum_validator(Action)),
    default_nonmember_action=GetterSetter(enum_validator(Action)),
    description=GetterSetter(no_newlines_validator),
    display_name=GetterSetter(str),
Example #37
0
#
# The decoder must either return the internal value or raise a ValueError if
# the conversion failed (e.g. trying to turn 'Nope' into a boolean).
#
# Many internal value types can be automatically JSON encoded, but see
# mailman.rest.helpers.ExtendedEncoder for specializations of certain types
# (e.g. datetimes, timedeltas, enums).

ATTRIBUTES = dict(
    acceptable_aliases=AcceptableAliases(list_of_strings_validator),
    admin_immed_notify=GetterSetter(as_boolean),
    admin_notify_mchanges=GetterSetter(as_boolean),
    administrivia=GetterSetter(as_boolean),
    advertised=GetterSetter(as_boolean),
    anonymous_list=GetterSetter(as_boolean),
    autorespond_owner=GetterSetter(enum_validator(ResponseAction)),
    autorespond_postings=GetterSetter(enum_validator(ResponseAction)),
    autorespond_requests=GetterSetter(enum_validator(ResponseAction)),
    autoresponse_grace_period=GetterSetter(as_timedelta),
    autoresponse_owner_text=GetterSetter(str),
    autoresponse_postings_text=GetterSetter(str),
    autoresponse_request_text=GetterSetter(str),
    archive_policy=GetterSetter(enum_validator(ArchivePolicy)),
    bounces_address=GetterSetter(None),
    collapse_alternatives=GetterSetter(as_boolean),
    convert_html_to_plaintext=GetterSetter(as_boolean),
    created_at=GetterSetter(None),
    default_member_action=GetterSetter(enum_validator(Action)),
    default_nonmember_action=GetterSetter(enum_validator(Action)),
    description=GetterSetter(str),
    digest_last_sent_at=GetterSetter(None),
 def test_enum_validator_invalid(self):
     self.assertRaises(ValueError, enum_validator(Action), 'not-a-thing')
Example #39
0
 def on_post(self, request, response):
     """Create a new member."""
     try:
         validator = Validator(
             list_id=str,
             subscriber=subscriber_validator(self.api),
             display_name=str,
             delivery_mode=enum_validator(DeliveryMode),
             role=enum_validator(MemberRole),
             pre_verified=bool,
             pre_confirmed=bool,
             pre_approved=bool,
             _optional=('delivery_mode', 'display_name', 'role',
                        'pre_verified', 'pre_confirmed', 'pre_approved'))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     # Dig the mailing list out of the arguments.
     list_id = arguments.pop('list_id')
     mlist = getUtility(IListManager).get_by_list_id(list_id)
     if mlist is None:
         bad_request(response, b'No such list')
         return
     # Figure out what kind of subscriber is being registered.  Either it's
     # a user via their preferred email address or it's an explicit address.
     # If it's a UUID, then it must be associated with an existing user.
     subscriber = arguments.pop('subscriber')
     user_manager = getUtility(IUserManager)
     # We use the display name if there is one.
     display_name = arguments.pop('display_name', '')
     if isinstance(subscriber, UUID):
         user = user_manager.get_user_by_id(subscriber)
         if user is None:
             bad_request(response, b'No such user')
             return
         subscriber = user
     else:
         # This must be an email address.  See if there's an existing
         # address object associated with this email.
         address = user_manager.get_address(subscriber)
         if address is None:
             # Create a new address, which of course will not be validated.
             address = user_manager.create_address(
                 subscriber, display_name)
         subscriber = address
     # What role are we subscribing?  Regular members go through the
     # subscription policy workflow while owners, moderators, and
     # nonmembers go through the legacy API for now.
     role = arguments.pop('role', MemberRole.member)
     if role is MemberRole.member:
         # Get the pre_ flags for the subscription workflow.
         pre_verified = arguments.pop('pre_verified', False)
         pre_confirmed = arguments.pop('pre_confirmed', False)
         pre_approved = arguments.pop('pre_approved', False)
         # Now we can run the registration process until either the
         # subscriber is subscribed, or the workflow is paused for
         # verification, confirmation, or approval.
         registrar = IRegistrar(mlist)
         try:
             token, token_owner, member = registrar.register(
                 subscriber,
                 pre_verified=pre_verified,
                 pre_confirmed=pre_confirmed,
                 pre_approved=pre_approved)
         except AlreadySubscribedError:
             conflict(response, b'Member already subscribed')
             return
         except MissingPreferredAddressError:
             bad_request(response, b'User has no preferred address')
             return
         except MembershipIsBannedError:
             bad_request(response, b'Membership is banned')
             return
         except SubscriptionPendingError:
             conflict(response, b'Subscription request already pending')
             return
         if token is None:
             assert token_owner is TokenOwner.no_one, token_owner
             # The subscription completed.  Let's get the resulting member
             # and return the location to the new member.  Member ids are
             # UUIDs and need to be converted to URLs because JSON doesn't
             # directly support UUIDs.
             member_id = self.api.from_uuid(member.member_id)
             location = self.api.path_to('members/{}'.format(member_id))
             created(response, location)
             return
         # The member could not be directly subscribed because there are
         # some out-of-band steps that need to be completed.  E.g. the user
         # must confirm their subscription or the moderator must approve
         # it.  In this case, an HTTP 202 Accepted is exactly the code that
         # we should use, and we'll return both the confirmation token and
         # the "token owner" so the client knows who should confirm it.
         assert token is not None, token
         assert token_owner is not TokenOwner.no_one, token_owner
         assert member is None, member
         content = dict(token=token, token_owner=token_owner.name)
         accepted(response, etag(content))
         return
     # 2015-04-15 BAW: We're subscribing some role other than a regular
     # member.  Use the legacy API for this for now.
     assert role in (MemberRole.owner,
                     MemberRole.moderator,
                     MemberRole.nonmember)
     # 2015-04-15 BAW: We're limited to using an email address with this
     # legacy API, so if the subscriber is a user, the user must have a
     # preferred address, which we'll use, even though it will subscribe
     # the explicit address.  It is an error if the user does not have a
     # preferred address.
     #
     # If the subscriber is an address object, just use that.
     if IUser.providedBy(subscriber):
         if subscriber.preferred_address is None:
             bad_request(response, b'User without preferred address')
             return
         email = subscriber.preferred_address.email
     else:
         assert IAddress.providedBy(subscriber)
         email = subscriber.email
     delivery_mode = arguments.pop('delivery_mode', DeliveryMode.regular)
     record = RequestRecord(email, display_name, delivery_mode)
     try:
         member = add_member(mlist, record, role)
     except MembershipIsBannedError:
         bad_request(response, b'Membership is banned')
         return
     except AlreadySubscribedError:
         bad_request(response,
                     '{} is already an {} of {}'.format(
                         email, role.name, mlist.fqdn_listname))
         return
     # The subscription completed.  Let's get the resulting member
     # and return the location to the new member.  Member ids are
     # UUIDs and need to be converted to URLs because JSON doesn't
     # directly support UUIDs.
     member_id = self.api.from_uuid(member.member_id)
     location = self.api.path_to('members/{}'.format(member_id))
     created(response, location)
 def test_enum_validator_valid(self):
     self.assertEqual(enum_validator(Action)('hold'), Action.hold)
 def test_enum_validator_valid(self):
     self.assertEqual(enum_validator(Action)('hold'), Action.hold)
 def test_enum_validator_invalid(self):
     self.assertRaises(ValueError,
                       enum_validator(Action), 'not-a-thing')
 def test_enum_validator_blank(self):
     self.assertEqual(enum_validator(Action, allow_blank=True)(''), None)
 def test_enum_validator_blank(self):
     self.assertEqual(enum_validator(Action, allow_blank=True)(''), None)