def on_get(self, request, response): """/domains/<domain>/owners""" if self._domain is None: not_found(response) return resource = self._make_collection(request) okay(response, etag(resource))
def on_get(self, request, response): """Return a single domain end-point.""" domain = getUtility(IDomainManager).get(self._domain) if domain is None: not_found(response) else: okay(response, self._resource_as_json(domain))
def on_put(self, request, response): """Set or replace the addresses's user.""" if self._user: self._user.unlink(self._address) # Process post data and check for an existing user. fields = CREATION_FIELDS.copy() fields['user_id'] = int fields['_optional'] = fields['_optional'] + ( 'user_id', 'email', 'is_server_owner') try: validator = Validator(**fields) arguments = validator(request) except ValueError as error: bad_request(response, str(error)) return user_manager = getUtility(IUserManager) if 'user_id' in arguments: raw_uid = arguments['user_id'] user_id = UUID(int=raw_uid) user = user_manager.get_user_by_id(user_id) if user is None: not_found(response, b'No user with ID {}'.format(raw_uid)) return okay(response) else: user = create_user(arguments, request, response) if user is None: return user.link(self._address)
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_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_post(self, request, response): """POST to /addresses Add a new address to the user record. """ if self._user is None: not_found(response) return user_manager = getUtility(IUserManager) validator = Validator(email=str, display_name=str, _optional=('display_name',)) try: address = user_manager.create_address(**validator(request)) except ValueError as error: bad_request(response, str(error)) except InvalidEmailAddressError: bad_request(response, b'Invalid email address') except ExistingAddressError: # Check if the address is not linked to any user, link it to the # current user and return it. Since we're linking to an existing # address, ignore any given display_name attribute. address = user_manager.get_address(validator(request)['email']) if address.user is None: address.user = self._user location = self.path_to('addresses/{}'.format(address.email)) created(response, location) else: bad_request(response, 'Address belongs to other user.') else: # Link the address to the current user and return it. address.user = self._user location = self.path_to('addresses/{}'.format(address.email)) created(response, location)
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 _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_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_put(self, request, response): """Set or replace the addresses's user.""" if self._user: self._user.unlink(self._address) # Process post data and check for an existing user. fields = CREATION_FIELDS.copy() fields['user_id'] = self.api.to_uuid fields['_optional'] = fields['_optional'] + ( 'user_id', 'email', 'is_server_owner') try: validator = Validator(**fields) arguments = validator(request) except ValueError as error: bad_request(response, str(error)) return user_manager = getUtility(IUserManager) if 'user_id' in arguments: user_id = arguments['user_id'] user = user_manager.get_user_by_id(user_id) if user is None: not_found(response, b'No user with ID {}'.format(user_id)) return okay(response) else: user = create_user(self.api, arguments, response) if user is None: return user.link(self._address)
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_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_get(self, request, response): if self._section is None: resource = dict( sections=sorted(section.name for section in config), self_link=self.api.path_to('system/configuration'), ) okay(response, etag(resource)) return missing = object() section = getattr(config, self._section, missing) if section is missing: not_found(response) return # Sections don't have .keys(), .values(), or .items() but we can # iterate over them. resource = {key: section[key] for key in section} # Add a `self_link` attribute to the resource. This is a little ugly # because technically speaking we're mixing namespaces. We can't have # a variable named `self_link` in any section, but also we can't have # `http_etag` either, so unless we want to shove all these values into # a sub dictionary (which we don't), we have to live with it. self_link = self.api.path_to('system/configuration/{}'.format( section.name)) resource['self_link'] = self_link okay(response, etag(resource))
def on_post(self, request, response): """POST to /addresses Add a new address to the user record. """ if self._user is None: not_found(response) return user_manager = getUtility(IUserManager) validator = Validator(email=str, display_name=str, _optional=('display_name', )) try: address = user_manager.create_address(**validator(request)) except ValueError as error: bad_request(response, str(error)) except InvalidEmailAddressError: bad_request(response, b'Invalid email address') except ExistingAddressError: # Check if the address is not linked to any user, link it to the # current user and return it. Since we're linking to an existing # address, ignore any given display_name attribute. address = user_manager.get_address(validator(request)['email']) if address.user is None: address.user = self._user location = self.path_to('addresses/{}'.format(address.email)) created(response, location) else: bad_request(response, 'Address belongs to other user.') else: # Link the address to the current user and return it. address.user = self._user location = self.path_to('addresses/{}'.format(address.email)) 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_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_get(self, request, response): """Get a header match.""" try: header_match = self.header_matches[self._position] except IndexError: not_found(response, "No header match at this position: {}".format(self._position)) else: okay(response, etag(self._resource_as_dict(header_match)))
def test_not_found_with_bytes_body(self): response = FakeResponse() helpers.not_found(response, b'Resource not found') self.assertEqual(response.content_type, 'application/json; charset=UTF-8') self.assertEqual(json.loads(response.body), { 'title': '404 Not Found', 'description': 'Resource not found', })
def on_get(self, request, response): """Get a header match.""" try: header_match = self.header_matches[self._position] except IndexError: not_found(response, 'No header match at this position: {}'.format( self._position)) else: okay(response, etag(self._resource_as_dict(header_match)))
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): """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_get(self, request, response): # Get the pended record associated with this token, if it exists in # the pending table. try: resource = self._resource_as_dict(self._token) except LookupError: not_found(response) return okay(response, etag(resource))
def on_get(self, request, response): template = getUtility(ITemplateManager).raw(self._template, None) if template is None: not_found(response) else: resource = dict(uri=template.uri) resource['self_link'] = self.api.path_to( 'uris/{}'.format(self._template)) okay(response, etag(resource))
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_get(self, request, response): try: request_id = int(self._request_id) except ValueError: not_found(response) return resource = self._make_resource(request_id) if resource is None: not_found(response) else: okay(response, etag(resource))
def on_get(self, request, response): try: request_id = int(self._request_id) except ValueError: bad_request(response) return resource = self._make_resource(request_id) if resource is None: not_found(response) else: okay(response, etag(resource))
def on_get(self, request, response): """Get a banned email.""" if self.ban_manager.is_banned(self._email): resource = dict( email=self._email, self_link=self._location(self._email), ) if self._mlist is not None: resource['list_id'] = self._mlist.list_id okay(response, etag(resource)) else: not_found(response, 'Email is not banned: {}'.format(self._email))
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_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 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 on_get(self, request, response): try: request_id = int(self._request_id) except ValueError: bad_request(response) return resource = self._make_resource(request_id, MEMBERSHIP_CHANGE_REQUESTS) if resource is None: not_found(response) else: # Remove unnecessary keys. del resource['key'] okay(response, etag(resource))
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_get(self, request, response): if self._section is None: resource = dict(sections=sorted(section.name for section in config)) okay(response, etag(resource)) return missing = object() section = getattr(config, self._section, missing) if section is missing: not_found(response) return # Sections don't have .keys(), .values(), or .items() but we can # iterate over them. resource = {key: section[key] for key in section} okay(response, etag(resource))
def on_get(self, request, response): if self._section is None: resource = dict( sections=sorted(section.name for section in config)) okay(response, etag(resource)) return missing = object() section = getattr(config, self._section, missing) if section is missing: not_found(response) return # Sections don't have .keys(), .values(), or .items() but we can # iterate over them. resource = {key: section[key] for key in section} okay(response, etag(resource))
def on_put(self, request, response): """Set a mailing list configuration.""" attribute = self._attribute # The list of required attributes differs between API version. For # backward compatibility, in API 3.0 all of the *_uri attributes are # optional. In API 3.1 none of these are allowed since they are # handled by the template manager API. validators = VALIDATORS.copy() attributes = api_attributes(self.api) if self.api.version_info == (3, 0): validators.update({ attribute: URIAttributeMapper(str) for attribute in TEMPLATE_ATTRIBUTES }) validators['_optional'] = TEMPLATE_ATTRIBUTES.keys() 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 _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 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 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 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_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_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_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_get(self, request, response): # XXX We currently only support .txt and .html files. extension = EXTENSIONS.get(self.content_type) if extension is None: not_found(response) return template = self.template + extension fp = None try: try: path, fp = find(template, self.mlist, self.language) except TemplateNotFoundError: not_found(response) return else: return fp.read() finally: if fp is not None: fp.close()