def on_put(self, request, response): """Set a mailing list configuration.""" attribute = self._attribute if attribute is None: validator = Validator(**VALIDATORS) try: validator.update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) return elif attribute not in ATTRIBUTES: bad_request(response, b'Unknown attribute: {}'.format(attribute)) return elif ATTRIBUTES[attribute].decoder is None: bad_request( response, b'Read-only attribute: {}'.format(attribute)) return else: validator = Validator(**{attribute: VALIDATORS[attribute]}) try: validator.update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) return no_content(response)
def on_delete(self, request, response): """Delete the named mailing list.""" if self._mlist is None: not_found(response) else: remove_list(self._mlist) no_content(response)
def on_delete(self, request, response): """Delete the named user, all her memberships, and addresses.""" if self._user is None: not_found(response) return self._user.unlink(self._address) no_content(response)
def on_delete(self, request, response): """Remove an email from the ban list.""" if self.ban_manager.is_banned(self._email): self.ban_manager.unban(self._email) no_content(response) else: not_found(response, 'Email is not banned: {}'.format(self._email))
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): # We don't care about the POST data, just do the action. if self._action == 'verify' and self._address.verified_on is None: self._address.verified_on = now() elif self._action == 'unverify': self._address.verified_on = None 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 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 on_delete(self, request, response): """Remove a header match.""" try: del self.header_matches[self._position] except IndexError: not_found(response, "No header match at this position: {}".format(self._position)) else: no_content(response)
def on_delete(self, request, response): """Delete the domain.""" try: getUtility(IDomainManager).remove(self._domain) except KeyError: # The domain does not exist. not_found(response) else: no_content(response)
def on_delete(self, request, response): """Delete the named user and all associated resources.""" if self._user is None: not_found(response) return for member in self._user.memberships.members: member.unsubscribe() user_manager = getUtility(IUserManager) user_manager.delete_user(self._user) no_content(response)
def on_delete(self, request, response): """Delete the named user, all her memberships, and addresses.""" if self._user is None: not_found(response) return for member in self._user.memberships.members: member.unsubscribe() user_manager = getUtility(IUserManager) for address in self._user.addresses: user_manager.delete_address(address) user_manager.delete_user(self._user) no_content(response)
def on_delete(self, request, response): """Delete the queue file.""" switchboard = config.switchboards.get(self._name) if switchboard is None: not_found(response, 'No such queue: {}'.format(self._name)) return try: switchboard.dequeue(self._filebase) except FileNotFoundError: not_found(response, 'No such queue file: {}'.format(self._filebase)) else: no_content(response)
def patch_put(self, request, response, is_optional): archiver_set = IListArchiverSet(self._mlist) kws = {archiver.name: ArchiverGetterSetter(self._mlist) for archiver in archiver_set.archivers} if is_optional: # For a PATCH, all attributes are optional. kws['_optional'] = kws.keys() try: Validator(**kws).update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) else: no_content(response)
def on_delete(self, request, response): """Delete the member (i.e. unsubscribe).""" # Leaving a list is a bit different than deleting a moderator or # owner. Handle the former case first. For now too, we will not send # an admin or user notification. if self._member is None: not_found(response) return mlist = getUtility(IListManager).get_by_list_id(self._member.list_id) if self._member.role is MemberRole.member: delete_member(mlist, self._member.address.email, False, False) else: self._member.unsubscribe() no_content(response)
def on_patch(self, request, response): """Patch the user's configuration (i.e. partial update).""" if self._user is None: not_found(response) return try: validator = PatchValidator(request, ATTRIBUTES) except UnknownPATCHRequestError as error: bad_request( response, b'Unknown attribute: {0}'.format(error.attribute)) except ReadOnlyPATCHRequestError as error: bad_request( response, b'Read-only attribute: {0}'.format(error.attribute)) else: validator.update(self._user, request) no_content(response)
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 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 on_delete(self, request, response): """Delete the named user and all associated resources.""" if self._user is None: not_found(response) return for member in self._user.memberships.members: member.unsubscribe() user_manager = getUtility(IUserManager) # SQLAlchemy is susceptable to delete-elements-while-iterating bugs so # first figure out all the addresses we want to delete, then in a # separate pass, delete those addresses. (See LP: #1419519) delete = list(self._user.addresses) for address in delete: user_manager.delete_address(address) user_manager.delete_user(self._user) no_content(response)
def verify(self, request): # We don't care about the POST data, just do the action. if self._action == 'verify' and self._address.verified_on is None: self._address.verified_on = now() elif self._action == 'unverify': self._address.verified_on = None return no_content()
def delete(self, request): """Delete the domain.""" try: getUtility(IDomainManager).remove(self._domain) except KeyError: # The domain does not exist. return http.not_found() return no_content()
def on_patch(self, request, response): """Patch the configuration (i.e. partial update).""" if self._attribute is None: # We're PATCHing one or more of the attributes on the list's # configuration resource, so all the writable attributes are valid # candidates for updating. converters = ATTRIBUTES else: # We're PATCHing a specific list configuration attribute # sub-resource. Because the request data must be a dictionary, we # restrict it to containing only a single key, which must match # the attribute name. First, check for any extra attributes in # the request. keys = [key for key, value in request.params.items()] if len(keys) > 1: bad_request(response, 'Expected 1 attribute, got {}'.format( len(keys))) return converter = ATTRIBUTES.get(self._attribute) if converter is None: # This is the case where the URL points to a nonexisting list # configuration attribute sub-resource. not_found(response, 'Unknown attribute: {}'.format( self._attribute)) return converters = {self._attribute: converter} try: validator = PatchValidator(request, converters) except UnknownPATCHRequestError as error: # This is the case where the URL points to the list's entire # configuration resource, but the request dictionary contains a # nonexistent attribute. bad_request( response, 'Unknown attribute: {}'.format(error.attribute)) return except ReadOnlyPATCHRequestError as error: bad_request( response, 'Read-only attribute: {}'.format(error.attribute)) return try: validator.update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) else: no_content(response)
def on_post(self, request, response): # We do not want to encrypt the plaintext password given in the POST # data. That would hash the password, but we need to have the # plaintext in order to pass into passlib. validator = Validator(cleartext_password=GetterSetter(str)) try: values = validator(request) except ValueError as error: bad_request(response, str(error)) return is_valid, new_hash = config.password_context.verify( values['cleartext_password'], self._user.password) if is_valid: if new_hash is not None: self._user.password = new_hash no_content(response) else: forbidden(response)
def on_put(self, request, response): """Put the user's configuration (i.e. full update).""" if self._user is None: not_found(response) return validator = Validator(**ATTRIBUTES) try: validator.update(self._user, request) except UnknownPATCHRequestError as error: bad_request( response, b'Unknown attribute: {0}'.format(error.attribute)) except ReadOnlyPATCHRequestError as error: bad_request( response, b'Read-only attribute: {0}'.format(error.attribute)) except ValueError as error: bad_request(response, str(error)) else: no_content(response)
def on_patch(self, request, response): """Patch the configuration (i.e. partial update).""" try: validator = PatchValidator(request, ATTRIBUTES) except UnknownPATCHRequestError as error: bad_request( response, b'Unknown attribute: {}'.format(error.attribute)) return except ReadOnlyPATCHRequestError as error: bad_request( response, b'Read-only attribute: {}'.format(error.attribute)) return try: validator.update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) else: no_content(response)
def on_patch(self, request, response): """Patch the user's configuration (i.e. partial update).""" if self._user is None: not_found(response) return try: validator = PatchValidator(request, ATTRIBUTES) except UnknownPATCHRequestError as error: bad_request( response, 'Unknown attribute: {0}'.format(error.attribute).encode()) except ReadOnlyPATCHRequestError as error: bad_request( response, 'Read-only attribute: {0}'.format(error.attribute).encode()) else: validator.update(self._user, request) no_content(response)
def patch_put(self, request, response, is_optional): domain = getUtility(IDomainManager).get(self._domain) if domain is None: not_found(response) kws = dict( alias_domain=GetterSetter(str), description=GetterSetter(str), owner=ListOfDomainOwners(list_of_strings_validator), ) if is_optional: # For a PATCH, all attributes are optional. kws['_optional'] = kws.keys() try: Validator(**kws).update(domain, request) except ValueError as error: bad_request(response, str(error)) else: no_content(response)
def on_delete(self, request, response): """Delete the member (i.e. unsubscribe).""" # Leaving a list is a bit different than deleting a moderator or # owner. Handle the former case first. For now too, we will not send # an admin or user notification. if self._member is None: not_found(response) return mlist = getUtility(IListManager).get_by_list_id(self._member.list_id) if self._member.role is MemberRole.member: try: delete_member(mlist, self._member.address.email, False, False) except NotAMemberError: not_found(response) return else: self._member.unsubscribe() 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: 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_delete(self, request, response): """Delete the member (i.e. unsubscribe).""" # Leaving a list is a bit different than deleting a moderator or # owner. Handle the former case first. For now too, we will not send # an admin or user notification. if self._member is None: not_found(response) return mlist = getUtility(IListManager).get_by_list_id(self._member.list_id) if self._member.role is MemberRole.member: try: values = Validator( pre_confirmed=as_boolean, pre_approved=as_boolean, _optional=('pre_confirmed', 'pre_approved'), )(request) except ValueError as error: bad_request(response, str(error)) return manager = ISubscriptionManager(mlist) # XXX(maxking): For backwards compat, we are going to keep # pre-confirmed to be "True" by defualt instead of "False", that it # should be. Any, un-authenticated requests should manually specify # that it is *not* confirmed by the user. if 'pre_confirmed' in values: pre_confirmed = values.get('pre_confirmed') else: pre_confirmed = True token, token_owner, member = manager.unregister( self._member.address, pre_approved=values.get('pre_approved'), pre_confirmed=pre_confirmed) if member is None: assert token is None assert token_owner is TokenOwner.no_one no_content(response) else: assert token is not None content = dict(token=token, token_owner=token_owner.name) accepted(response, etag(content)) else: self._member.unsubscribe() no_content(response)
def on_delete(self, request, response): if self._attribute is None: bad_request(response, 'Cannot delete the list configuration itself') return if self._attribute not in VALIDATORS: bad_request(response, 'Read-only attribute: {}'.format(self._attribute)) return # This kind of sucks because it doesn't scale if the list of attributes # which can be deleted grows. So if we get too many we'll have to use # a lookup table. For now, this is good enough. if self._attribute != 'acceptable_aliases': bad_request( response, 'Attribute cannot be DELETEd: {}'.format(self._attribute)) return IAcceptableAliasSet(self._mlist).clear() 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): """POST to /domains/<domain>/owners """ if self._domain is None: not_found(response) return validator = Validator( owner=ListOfDomainOwners(list_of_strings_validator)) try: validator.update(self._domain, request) except ValueError as error: bad_request(response, str(error)) return return no_content(response)
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 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 _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=GetterSetter(regexp_validator), position=int, action=enum_validator(Action, allow_blank=True), tag=lowercase, ) if is_optional: # For a PATCH, all attributes are optional. kws['_optional'] = kws.keys() else: # For a PUT, position can remain unchanged; tag and action can be # None. kws['_optional'] = ('action', 'position', 'tag') validator = Validator(**kws) try: arguments = validator(request) missing = object() action = arguments.pop('action', missing) if action is not missing and action is not None: arguments['chain'] = action.name elif action is not missing and action is None: arguments['chain'] = action 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 patch_update(self, request): """Patch the user's configuration (i.e. partial update).""" if self._user is None: return http.not_found() try: validator = PatchValidator(request, ATTRIBUTES) except UnknownPATCHRequestError as error: return http.bad_request( [], b'Unknown attribute: {0}'.format(error.attribute)) except ReadOnlyPATCHRequestError as error: return http.bad_request( [], b'Read-only attribute: {0}'.format(error.attribute)) validator.update(self._user, request) return no_content()
def _patch_put(self, request, response, is_optional): kws = {uri: str for uri in self.URIs} optionals = ['username', 'password'] if is_optional: optionals.extend(self.URIs) # When PATCHing or PUTing all uris, a single optional # username/password applies to them all. kws['username'] = str kws['password'] = str kws['_optional'] = optionals try: arguments = Validator(**kws)(request) except ValueError as error: bad_request(response, str(error)) return username = arguments.pop('username', None) password = arguments.pop('password', None) if not username and not password: # Normalize arguments. set_kws = {} elif username and password: # It's fine if both are specified. set_kws = dict(username=username, password=password) else: bad_request(response, 'Specify both username and password, or neither') return manager = getUtility(ITemplateManager) for key, value in arguments.items(): if len(value) == 0: # The empty string is equivalent to DELETE. Yeah, this isn't # very RESTful, but practicality beats purity. manager.delete(key, self._raw_context) else: manager.set(key, self._raw_context, value, **set_kws) no_content(response)
def on_put(self, request, response): """Set a mailing list configuration.""" attribute = self._attribute if attribute is None: # This is a request to update all the list's writable # configuration variables. All must be provided in the request. validator = Validator(**VALIDATORS) try: validator.update(self._mlist, request) except ValueError as error: # Unlike the case where we're PUTting to a specific # configuration sub-resource, if we're PUTting to the list's # entire configuration, but the request has a bogus attribute, # the entire request is considered bad. We can also get here # if one of the attributes is read-only. The error will # contain sufficient details, so just return it as the reason. bad_request(response, str(error)) return elif attribute not in ATTRIBUTES: # Here we're PUTting to a specific resource, but that attribute is # bogus so the URL is considered pointing to a missing resource. not_found(response, 'Unknown attribute: {}'.format(attribute)) return elif ATTRIBUTES[attribute].decoder is None: bad_request( response, 'Read-only attribute: {}'.format(attribute)) return else: # We're PUTting to a specific configuration sub-resource. validator = Validator(**{attribute: VALIDATORS[attribute]}) try: validator.update(self._mlist, request) except ValueError as error: bad_request(response, str(error)) return no_content(response)
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_delete(self, request, response): """DELETE to /domains/<domain>/owners""" if self._domain is None: not_found(response) try: # No arguments. Validator()(request) except ValueError as error: bad_request(response, str(error)) return owner_email = [ owner.addresses[0].email for owner in self._domain.owners ] for email in owner_email: self._domain.remove_owner(email) return no_content(response)
def on_delete(self, request, response): if self._resource_path != 'uids/orphans': not_found(response) return UID.cull_orphans() no_content(response)
def on_delete(self, request, response): manager = getUtility(ITemplateManager) for uri in self.URIs: manager.delete(uri, self._raw_context) no_content(response)
def on_delete(self, request, response): if self._address is None: not_found(response) else: del self._user.preferred_address no_content(response)
def on_delete(self, request, response): self._plugin.number = 0 no_content(response)
def on_delete(self, request, response): """Delete all header matches for this mailing list.""" self.header_matches.clear() no_content(response)
def on_delete(self, request, response): if self._address is None: not_found(response) else: getUtility(IUserManager).delete_address(self._address) no_content(response)
def on_delete(self, request, response): """Delete all preferences.""" for attr in PREFERENCES: if hasattr(self._parent, attr): setattr(self._parent, attr, None) no_content(response)