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)))
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)
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)
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)
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)
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))
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))
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)
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)))
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()
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)))
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()
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()
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)
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)))
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))
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))
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)
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)
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]'))
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 _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)))
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]"))
# # 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),
# # 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),
# 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),
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)
# 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),
def test_enum_validator_invalid(self): self.assertRaises(ValueError, enum_validator(Action), 'not-a-thing')
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_blank(self): self.assertEqual(enum_validator(Action, allow_blank=True)(''), None)