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 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 create(self, request): """Create a new user.""" try: validator = Validator(email=unicode, display_name=unicode, password=unicode, _optional=('display_name', 'password')) arguments = validator(request) except ValueError as error: return http.bad_request([], str(error)) # We can't pass the 'password' argument to the user creation method, # so strip that out (if it exists), then create the user, adding the # password after the fact if successful. password = arguments.pop('password', None) try: user = getUtility(IUserManager).create_user(**arguments) except ExistingAddressError as error: return http.bad_request([], b'Address already exists {0}'.format( error.email)) if password is None: # This will have to be reset since it cannot be retrieved. password = generate(int(config.passwords.password_length)) scheme = lookup(config.passwords.password_scheme.upper()) user.password = make_secret(password, scheme) location = path_to('users/{0}'.format(user.user_id.int)) return http.created(location, [], None)
def test_bad_request(self): r = http.bad_request() assert r.status.startswith('400') assert r.headers['Content-Type'] == 'text/plain' assert '400 Bad Request' in r.body r = http.bad_request([('Content-Type', 'text/html')], '<p>400 Bad Request</p>') assert r.status.startswith('400') assert r.headers['Content-Type'] == 'text/html' assert r.body == '<p>400 Bad Request</p>' exc = http.BadRequestError() r = exc.make_response() assert r.status.startswith('400')
def test_bad_request(self): r = http.bad_request() assert r.status.startswith("400") assert r.headers["Content-Type"] == "text/plain" assert "400 Bad Request" in r.body r = http.bad_request([("Content-Type", "text/html")], "<p>400 Bad Request</p>") assert r.status.startswith("400") assert r.headers["Content-Type"] == "text/html" assert r.body == "<p>400 Bad Request</p>" exc = http.BadRequestError() r = exc.make_response() assert r.status.startswith("400")
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_configuration(self, request): """Patch the configuration (i.e. partial update).""" 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)) try: validator.update(self._mlist, request) except ValueError as error: return http.bad_request([], str(error)) return no_content()
def system(self, request, segments): """/<api>/system""" if len(segments) == 0: resource = dict( mailman_version=system.mailman_version, python_version=system.python_version, self_link=path_to('system'), ) elif len(segments) > 1: return http.bad_request() elif segments[0] == 'preferences': return ReadOnlyPreferences(system_preferences, 'system'), [] else: return http.bad_request() return http.ok([], etag(resource))
def locate_resource(self, request): """ Locate the resource at the path in request URL by traversing the resource hierarchy. """ # Calculate the path segments relative to the application, # special-casing requests for the the root segment (because we already # have a reference to the root resource). try: segments = url.split_path(request.environ['PATH_INFO']) except UnicodeDecodeError: return http.bad_request() if segments == ['']: segments = [] # Recurse into the resource hierarchy until we run out of segments or # find a Response. resource = self.root while segments and not isinstance(resource, http.Response): resource_child = getattr(resource, 'resource_child', None) # No resource_child method? 404. if resource_child is None: raise http.NotFoundError() result = resource_child(request, segments) # No result returned? 404. if result is None: raise http.NotFoundError() # Either a (resource, remaining segments) tuple or an object to # forward the lookup to is acceptable. if isinstance(result, tuple): resource, segments = result else: resource = result return resource
def create(self, request): """Create a new mailing list.""" try: validator = Validator(fqdn_listname=unicode) mlist = create_list(**validator(request)) except ListAlreadyExistsError: return http.bad_request([], b'Mailing list exists') except BadDomainSpecificationError as error: return http.bad_request([], b'Domain does not exist {0}'.format( error.domain)) except ValueError as error: return http.bad_request([], str(error)) # wsgiref wants headers to be bytes, not unicodes. location = path_to('lists/{0}'.format(mlist.fqdn_listname)) # Include no extra headers or body. return http.created(location, [], None)
def memberships(self, request, segments): """/addresses/<email>/memberships""" if len(segments) != 0: return http.bad_request() if self._address is None: return http.not_found() return AddressMemberships(self._address)
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 put_update(self, request): """Put the user's configuration (i.e. full update).""" if self._user is None: return http.not_found() validator = Validator(**ATTRIBUTES) try: validator.update(self._user, request) 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)) except ValueError as error: return http.bad_request([], str(error)) return no_content()
def unverify(self, request, segments): """/addresses/<email>/verify""" if len(segments) != 0: return http.bad_request() if self._address is None: return http.not_found() child = _VerifyResource(self._address, 'unverify') return child, []
def patch_configuration(self, request): """Patch the configuration (i.e. partial update).""" # Validate only the partial subset of attributes given in the request. validationators = {} for attribute in request.PATCH: if attribute not in ATTRIBUTES: return http.bad_request([], b"Unknown attribute: {0}".format(attribute)) elif ATTRIBUTES[attribute].decoder is None: return http.bad_request([], b"Read-only attribute: {0}".format(attribute)) else: validationators[attribute] = VALIDATORS[attribute] validator = Validator(**validationators) try: self._set_writable_attributes(validator, request) except ValueError as error: return http.bad_request([], str(error)) return no_content()
def create(self, request): """Create a new domain.""" domain_manager = getUtility(IDomainManager) try: validator = Validator(mail_host=unicode, description=unicode, base_url=unicode, contact_address=unicode, _optional=('description', 'base_url', 'contact_address')) domain = domain_manager.add(**validator(request)) except BadDomainSpecificationError: return http.bad_request([], b'Domain exists') except ValueError as error: return http.bad_request([], str(error)) location = path_to('domains/{0}'.format(domain.mail_host)) # Include no extra headers or body. return http.created(location, [], None)
def details(self, request): try: request_id = int(self._request_id) except ValueError: return http.bad_request() resource = self._make_resource(request_id) if resource is None: return http.not_found() return http.ok([], etag(resource))
def lists(self, request, segments): """/domains/<domain>/lists""" if len(segments) == 0: domain = getUtility(IDomainManager).get(self._domain) if domain is None: return http.not_found() return ListsForDomain(domain) else: return http.bad_request()
def wrapper(self, request, *args, **kwargs): try: count = int(request.GET['count']) page = int(request.GET['page']) if count < 0 or page < 0: return http.bad_request([], b'Invalid parameters') # Wrong parameter types or no GET attribute in request object. except (AttributeError, ValueError, TypeError): return http.bad_request([], b'Invalid parameters') # No count/page params. except KeyError: count = page = None result = method(self, request, *args, **kwargs) if count is None and page is None: return result list_start = int((page - 1) * count) list_end = int(page * count) return result[list_start:list_end]
def preferences(self, request, segments): """/members/<id>/preferences""" if len(segments) != 0: return http.bad_request() if self._member is None: return http.not_found() child = Preferences( self._member.preferences, 'members/{0}'.format(self._member.member_id.int)) return child, []
def preferences(self, request, segments): """/addresses/<email>/preferences""" if len(segments) != 0: return http.bad_request() if self._user is None: return http.not_found() child = Preferences( self._user.preferences, 'users/{0}'.format(self._user.user_id.int)) return child, []
def put_configuration(self, request): """Set a mailing list configuration.""" attribute = self._attribute if attribute is None: validator = Validator(**VALIDATORS) try: self._set_writable_attributes(validator, request) except ValueError as error: return http.bad_request([], str(error)) elif attribute not in ATTRIBUTES: return http.bad_request([], b"Unknown attribute: {0}".format(attribute)) elif ATTRIBUTES[attribute].decoder is None: return http.bad_request([], b"Read-only attribute: {0}".format(attribute)) else: validator = Validator(**{attribute: VALIDATORS[attribute]}) try: self._set_writable_attributes(validator, request) except ValueError as error: return http.bad_request([], str(error)) return no_content()
def details(self, request): try: request_id = int(self._request_id) except ValueError: return http.bad_request() resource = self._make_resource(request_id, MEMBERSHIP_CHANGE_REQUESTS) if resource is None: return http.not_found() # Remove unnecessary keys. del resource['key'] return http.ok([], etag(resource))
def patch_put(self, request, is_optional): archiver_set = IListArchiverSet(self._mlist) kws = {archiver.name: ArchiverGetterSetter(self._mlist) for archiver in archiver_set.archivers} if is_optional: # For a PUT, all attributes are optional. kws['_optional'] = kws.keys() try: Validator(**kws).update(self._mlist, request) except ValueError as error: return http.bad_request([], str(error)) return no_content()
def get_configuration(self, request): """Get a mailing list configuration.""" resource = {} if self._attribute is None: # Return all readable attributes. for attribute in ATTRIBUTES: value = ATTRIBUTES[attribute].get(self._mlist, attribute) resource[attribute] = value elif self._attribute not in ATTRIBUTES: return http.bad_request([], b"Unknown attribute: {0}".format(self._attribute)) else: attribute = self._attribute value = ATTRIBUTES[attribute].get(self._mlist, attribute) resource[attribute] = value return http.ok([], etag(resource))
def create(self, request): """POST to /addresses Add a new address to the user record. """ if self._user is None: return http.not_found() user_manager = getUtility(IUserManager) validator = Validator(email=unicode, display_name=unicode, _optional=('display_name',)) try: address = user_manager.create_address(**validator(request)) except ValueError as error: return http.bad_request([], str(error)) except InvalidEmailAddressError: return http.bad_request([], b'Invalid email address') except ExistingAddressError: return http.bad_request([], b'Address already exists') else: # Link the address to the current user and return it. address.user = self._user location = path_to('addresses/{0}'.format(address.email)) return http.created(location, [], None)
def details(self, request): 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() key, data = results msg = getUtility(IMessageStore).get_message_by_id(key) resource = dict( key=key, data=data, msg=msg.as_string(), id=request_id, ) return http.ok([], etag(resource))
def login(self, request, segments): """Log the user in, sort of, by verifying a given password.""" if self._user is None: return http.not_found() # 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(unicode)) try: values = validator(request) except ValueError as error: return http.bad_request([], str(error)) 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 return no_content() return http.forbidden()
def templates(self, request, segments): """/<api>/templates/<fqdn_listname>/<template>/[<language>] Use content negotiation to request language and suffix (content-type). """ if len(segments) == 3: fqdn_listname, template, language = segments elif len(segments) == 2: fqdn_listname, template = segments language = 'en' else: return http.bad_request() mlist = getUtility(IListManager).get(fqdn_listname) if mlist is None: return http.not_found() # XXX dig out content-type from request content_type = None return TemplateFinder( fqdn_listname, template, language, content_type)