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_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 _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_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 _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 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 _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_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_get(self, request, response): """Get all the archiver statuses.""" archiver_set = IListArchiverSet(self._mlist) resource = { archiver.name: archiver.is_enabled for archiver in archiver_set.archivers } okay(response, etag(resource))
def on_get(self, request, response): """/<api>/system/versions""" resource = dict( mailman_version=system.mailman_version, python_version=system.python_version, self_link=path_to('system/versions'), ) okay(response, etag(resource))
def on_get(self, request, response): """/<api>/system""" resource = dict( mailman_version=system.mailman_version, python_version=system.python_version, self_link=path_to('system'), ) okay(response, etag(resource))
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 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_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): """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_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 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 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): """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_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_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 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 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 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 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 on_get(self, request, response): """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: bad_request( response, b'Unknown attribute: {}'.format(self._attribute)) return else: attribute = self._attribute value = ATTRIBUTES[attribute].get(self._mlist, attribute) resource[attribute] = value okay(response, etag(resource))
def on_get(self, request, response): resource = dict() for attr in PREFERENCES: # Handle this one specially. if attr == 'preferred_language': continue value = getattr(self._parent, attr, None) if value is not None: resource[attr] = value # Add the preferred language, if it's not missing. preferred_language = self._parent.preferred_language if preferred_language is not None: resource['preferred_language'] = preferred_language.code # Add the self link. resource['self_link'] = path_to( '{0}/preferences'.format(self._base_url)) okay(response, etag(resource))
def on_delete(self, request, response): """Delete the members of the named mailing list.""" status = {} try: validator = Validator(emails=list_of_strings_validator) arguments = validator(request) except ValueError as error: bad_request(response, str(error)) return emails = arguments.pop('emails') success, fail = getUtility(ISubscriptionService).unsubscribe_members( self._mlist.list_id, emails) # There should be no email in both sets. assert success.isdisjoint(fail), (success, fail) status.update({email: True for email in success}) status.update({email: False for email in fail}) okay(response, etag(status))
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 on_get(self, request, response): resource = dict() for attr in PREFERENCES: # Handle this one specially. if attr == 'preferred_language': continue value = getattr(self._parent, attr, None) if value is not None: resource[attr] = value # Add the preferred language, if it's not missing. preferred_language = self._parent.preferred_language if preferred_language is not None: resource['preferred_language'] = preferred_language.code # Add the self link. resource['self_link'] = self.api.path_to( '{}/preferences'.format(self._base_url)) okay(response, etag(resource))
def lists(self, request, segments): """/<api>/lists /<api>/lists/<list> /<api>/lists/<list>/... """ if len(segments) == 0: return AllLists() elif len(segments) == 1 and segments[0] == 'styles': manager = getUtility(IStyleManager) style_names = sorted(style.name for style in manager.styles) resource = dict(style_names=style_names, default=config.styles.default) return http.ok([], etag(resource)) else: # list-id is preferred, but for backward compatibility, # fqdn_listname is also accepted. list_identifier = segments.pop(0) return AList(list_identifier), segments
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 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_get(self, request, response): """Get a mailing list configuration.""" resource = {} if self._attribute is None: # This is a requst for all the mailing list's configuration # variables. Return all readable attributes. for attribute in ATTRIBUTES: value = ATTRIBUTES[attribute].get(self._mlist, attribute) resource[attribute] = value elif self._attribute not in ATTRIBUTES: # This is a request for a specific, nonexistent attribute. not_found( response, 'Unknown attribute: {}'.format(self._attribute)) return else: # This is a request for a specific attribute. attribute = self._attribute value = ATTRIBUTES[attribute].get(self._mlist, attribute) resource[attribute] = value okay(response, etag(resource))
def on_get(self, request, response): """/members""" validator = Validator( fields=list_of_strings_validator, count=int, page=int, _optional=['fields', 'count', 'page'], ) try: data = validator(request) except ValueError as ex: bad_request(response, str(ex)) return fields = data.get('fields', None) try: resource = self._make_collection(request, fields) except ValueError as ex: bad_request(response, str(ex)) return okay(response, etag(resource))
def on_get(self, request, response): """Get a mailing list configuration.""" resource = {} attributes = api_attributes(self.api) if self._attribute is None: # This is a request for all the mailing list's configuration # variables. Return all readable attributes. for attribute, getter in attributes.items(): value = getter.get(self._mlist, attribute) resource[attribute] = value elif self._attribute in attributes: # This is a request for a specific attribute. value = attributes[self._attribute].get(self._mlist, self._attribute) resource[self._attribute] = value else: # This is a request for a specific, nonexistent attribute. not_found(response, 'Unknown attribute: {}'.format(self._attribute)) return okay(response, etag(resource))
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_get(self, request, response): """/domains/<domain>/lists""" resource = self._make_collection(request) okay(response, etag(resource))
def on_get(self, request, response): resource = self._make_collection(request) resource['self_link'] = self.api.path_to( '{}/uris'.format(self._prefix)) okay(response, etag(resource))
def on_get(self, request, response): """/lists/listname/requests""" resource = self._make_collection(request) okay(response, etag(resource))
def on_get(self, request, response): resource = dict(chains=sorted(config.chains)) okay(response, etag(resource))
def requests(self, request): """/lists/listname/requests""" # `request` is a restish.http.Request object. resource = self._make_collection(request) return http.ok([], etag(resource))
def collection(self, request): """/users""" resource = self._make_collection(request) return http.ok([], etag(resource))
def on_get(self, request, response): """/addresses""" assert self._user is not None okay(response, etag(self._make_collection(request)))
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 on_get(self, request, response): """roster/[members|owners|moderators]""" resource = self._make_collection(request) okay(response, etag(resource))
def on_get(self, request, response): resource = dict( next_digest_number=self._mlist.next_digest_number, volume=self._mlist.volume, ) okay(response, etag(resource))
def on_get(self, request, response): resource = dict(pipelines=sorted(config.pipelines)) okay(response, etag(resource))
def on_get(self, request, response): okay(response, etag(self._resource))
def on_get(self, request, response): """/members""" resource = self._make_collection(request) okay(response, etag(resource))
def on_get(self, request, response): """/owners""" resource = self._make_collection(request) okay(response, etag(resource))