예제 #1
0
class IndividualRequest(_ModerationBase):
    """Resource for moderating a membership change."""
    def __init__(self, mlist, token):
        super().__init__()
        self._mlist = mlist
        self._registrar = ISubscriptionManager(self._mlist)
        self._token = token

    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)
            assert resource is not None, resource
        except LookupError:
            not_found(response)
            return
        okay(response, etag(resource))

    def on_post(self, request, response):
        try:
            validator = Validator(action=enum_validator(Action))
            arguments = validator(request)
        except ValueError as error:
            bad_request(response, str(error))
            return
        action = arguments['action']
        if action in (Action.defer, Action.hold):
            # At least see if the token is in the database.
            pendable = self._pendings.confirm(self._token, expunge=False)
            if pendable is None:
                not_found(response)
            else:
                no_content(response)
        elif action is Action.accept:
            try:
                self._registrar.confirm(self._token)
            except LookupError:
                not_found(response)
            except AlreadySubscribedError:
                conflict(response, 'Already subscribed')
            else:
                no_content(response)
        elif action is Action.discard:
            # At least see if the token is in the database.
            pendable = self._pendings.confirm(self._token, expunge=True)
            if pendable is None:
                not_found(response)
            else:
                no_content(response)
        else:
            assert action is Action.reject, action
            # Like discard but sends a rejection notice to the user.
            pendable = self._pendings.confirm(self._token, expunge=True)
            if pendable is None:
                not_found(response)
            else:
                no_content(response)
                send_rejection(self._mlist, _('Subscription request'),
                               pendable['email'], _('[No reason given]'))
예제 #2
0
 def setUp(self):
     with transaction():
         self._mlist = create_list('*****@*****.**')
         self._registrar = ISubscriptionManager(self._mlist)
         manager = getUtility(IUserManager)
         self._anne = manager.create_address('*****@*****.**',
                                             'Anne Person')
         self._bart = manager.make_user('*****@*****.**', 'Bart Person')
         set_preferred(self._bart)
예제 #3
0
 def setUp(self):
     self._commandq = config.switchboards['command']
     self._runner = make_testable_runner(CommandRunner, 'command')
     with transaction():
         # Register a subscription requiring confirmation.
         self._mlist = create_list('*****@*****.**')
         self._mlist.send_welcome_message = False
         anne = getUtility(IUserManager).create_address('*****@*****.**')
         registrar = ISubscriptionManager(self._mlist)
         self._token, token_owner, member = registrar.register(anne)
예제 #4
0
 def process(self, mlist, msg, msgdata, arguments, results):
     """See `IEmailCommand`."""
     email = msg.sender
     if not email:
         print(_('$self.name: No valid email address found to unsubscribe'),
               file=results)
         return ContinueProcessing.no
     user_manager = getUtility(IUserManager)
     user = user_manager.get_user(email)
     if user is None:
         print(_('No registered user for email address: $email'),
               file=results)
         return ContinueProcessing.no
     # The address that the -leave command was sent from, must be verified.
     # Otherwise you could link a bogus address to anyone's account, and
     # then send a leave command from that address.
     if user_manager.get_address(email).verified_on is None:
         print(_('Invalid or unverified email address: $email'),
               file=results)
         return ContinueProcessing.no
     already_left = msgdata.setdefault('leaves', set())
     for user_address in user.addresses:
         # Only recognize verified addresses.
         if user_address.verified_on is None:
             continue
         member = mlist.members.get_member(user_address.email)
         if member is not None:
             break
     else:
         # There are two possible situations.  Either none of the user's
         # addresses are subscribed to this mailing list, or this command
         # email *already* unsubscribed the user from the mailing list.
         # E.g. if a message was sent to the -leave address and it
         # contained the 'leave' command.  Don't send a bogus response in
         # this case, just ignore subsequent leaves of the same address.
         if email not in already_left:
             print(_('$self.name: $email is not a member of '
                     '$mlist.fqdn_listname'),
                   file=results)
             return ContinueProcessing.no
     if email in already_left:
         return ContinueProcessing.yes
     # Ignore any subsequent 'leave' commands.
     already_left.add(email)
     manager = ISubscriptionManager(mlist)
     token, token_owner, member = manager.unregister(user_address)
     person = formataddr((user.display_name, email))  # noqa
     if member is None:
         print(_('$person left $mlist.fqdn_listname'), file=results)
     else:
         print(_('Confirmation email sent to $person to leave'
                 ' $mlist.fqdn_listname'),
               file=results)
     return ContinueProcessing.yes
예제 #5
0
def add_members(mlist, in_fp, delivery, invite, welcome_msg):
    """Add members to a mailing list."""
    user_manager = getUtility(IUserManager)
    registrar = ISubscriptionManager(mlist)
    email_validator = getUtility(IEmailValidator)
    for line in in_fp:
        # Ignore blank lines and lines that start with a '#'.
        if line.startswith('#') or len(line.strip()) == 0:
            continue
        # Parse the line and ensure that the values are unicodes.
        display_name, email = parseaddr(line)
        # parseaddr can return invalid emails.  E.g. parseaddr('foobar@')
        # returns ('', 'foobar@') in python 3.6.7 and 3.7.1 so check validity.
        if not email_validator.is_valid(email):
            line = line.strip()
            print(_('Cannot parse as valid email address (skipping): $line'),
                  file=sys.stderr)
            continue
        subscriber = get_addr(display_name, email, user_manager)
        # For error messages.
        email = formataddr((display_name, email))
        delivery_status = DeliveryStatus.enabled
        if delivery is None or delivery == 'regular' or delivery == 'disabled':
            delivery_mode = DeliveryMode.regular
            if delivery == 'disabled':
                delivery_status = DeliveryStatus.by_moderator
        elif delivery == 'mime':
            delivery_mode = DeliveryMode.mime_digests
        elif delivery == 'plain':
            delivery_mode = DeliveryMode.plaintext_digests
        elif delivery == 'summary':
            delivery_mode = DeliveryMode.summary_digests
        try:
            member = registrar.register(subscriber,
                                        pre_verified=True,
                                        pre_approved=True,
                                        pre_confirmed=True,
                                        invitation=invite,
                                        send_welcome_message=welcome_msg)[2]
            if member is not None:
                member.preferences.delivery_status = delivery_status
                member.preferences.delivery_mode = delivery_mode
        except AlreadySubscribedError:
            # It's okay if the address is already subscribed, just print a
            # warning and continue.
            print(_('Already subscribed (skipping): $email'), file=sys.stderr)
        except MembershipIsBannedError:
            print(_('Membership is banned (skipping): $email'),
                  file=sys.stderr)
        except SubscriptionPendingError:
            print(_('Subscription already pending (skipping): $email'),
                  file=sys.stderr)
예제 #6
0
    def test_confirmation_message(self):
        # Create an address to subscribe.
        address = getUtility(IUserManager).create_address(
            '*****@*****.**', 'Anne Person')
        # Register the address with the list to create a confirmation notice.
        ISubscriptionManager(self._mlist).register(address)
        # Now there's one message in the virgin queue.
        items = get_queue_messages('virgin', expected_count=1)
        message = items[0].msg
        self.assertTrue(str(message['subject']).startswith('confirm'))
        self.assertMultiLineEqual(
            message.get_payload(), """\
Email Address Registration Confirmation

Hello, this is the GNU Mailman server at example.com.

We have received a registration request for the email address

    [email protected]

Before you can start using GNU Mailman at this site, you must first confirm
that this is your email address.  You can do this by replying to this message,
keeping the Subject header intact.

If you do not wish to register this email address, simply disregard this
message.  If you think you are being maliciously subscribed to the list, or
have any other questions, you may contact

    [email protected]
""")
예제 #7
0
class TestUnsubscription(unittest.TestCase):
    """Test unsubscription requests."""

    layer = SMTPLayer

    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._manager = ISubscriptionManager(self._mlist)

    def test_unsubscribe_defer(self):
        # When unsubscriptions must be approved by the moderator, but the
        # moderator defers this decision.
        anne = getUtility(IUserManager).create_address(
            '*****@*****.**', 'Anne Person')
        token, token_owner, member = self._manager.register(
            anne, pre_verified=True, pre_confirmed=True, pre_approved=True)
        self.assertIsNone(token)
        self.assertEqual(member.address.email, '*****@*****.**')
        # Now hold and handle an unsubscription request.
        token = hold_unsubscription(self._mlist, '*****@*****.**')
        handle_unsubscription(self._mlist, token, Action.defer)

    def test_bogus_token(self):
        # Try to handle an unsubscription with a bogus token.
        self.assertRaises(LookupError, self._manager.confirm, None)
예제 #8
0
 def setUp(self):
     self._mlist = create_list('*****@*****.**')
     anne = getUtility(IUserManager).create_address('*****@*****.**',
                                                    'Anne Person')
     self._token, token_owner, member = ISubscriptionManager(
         self._mlist).register(anne)
     self._command = Confirm()
     # Clear the virgin queue.
     get_queue_messages('virgin')
예제 #9
0
 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)
예제 #10
0
 def test_duplicate_pending_subscription(self):
     # Issue #199 - a member's subscription is already pending and they try
     # to subscribe again.
     registrar = ISubscriptionManager(self._mlist)
     with transaction():
         self._mlist.subscription_policy = SubscriptionPolicy.moderate
         anne = self._usermanager.create_address('*****@*****.**')
         token, token_owner, member = registrar.register(
             anne, pre_verified=True, pre_confirmed=True)
         self.assertEqual(token_owner, TokenOwner.moderator)
         self.assertIsNone(member)
     with self.assertRaises(HTTPError) as cm:
         call_api('http://*****:*****@example.com',
             'pre_verified': True,
             'pre_confirmed': True,
             })
     self.assertEqual(cm.exception.code, 409)
     self.assertEqual(cm.exception.reason,
                      'Subscription request already pending')
예제 #11
0
def syncmembers(ctx, in_fp, delivery, welcome_msg, goodbye_msg, admin_notify,
                no_change, listspec):
    """Add and delete mailing list members to match an input file."""
    global email_validator, registrar, user_manager
    mlist = getUtility(IListManager).get(listspec)
    if mlist is None:
        ctx.fail(_('No such list: $listspec'))
    email_validator = getUtility(IEmailValidator)
    registrar = ISubscriptionManager(mlist)
    user_manager = getUtility(IUserManager)
    sync_members(mlist, in_fp, delivery, welcome_msg, goodbye_msg,
                 admin_notify, no_change)
 def process(self, mlist, msg, msgdata, arguments, results):
     """See `IEmailCommand`."""
     # Parse the arguments.
     delivery_mode, address = self._parse_arguments(arguments, results)
     if delivery_mode is ContinueProcessing.no:
         return ContinueProcessing.no
     if not address:
         # RFC 2047 decode the From: header.
         if msg['from'] is None:
             display_name, email = ('', '')
         else:
             decoded_from = str(make_header(decode_header(msg['from'])))
             display_name, email = parseaddr(decoded_from)
     else:
         display_name, email = ('', address)
     # Address could be None or the empty string.
     if not email:
         email = msg.sender
     if not email:
         print(_('$self.name: No valid address found to subscribe'),
               file=results)
         return ContinueProcessing.no
     if isinstance(email, bytes):
         email = email.decode('ascii')
     # Have we already seen one join request from this user during the
     # processing of this email?
     joins = getattr(results, 'joins', set())
     if email in joins:
         # Do not register this join.
         return ContinueProcessing.yes
     joins.add(email)
     results.joins = joins
     person = formataddr((display_name, email))                # noqa: F841
     try:
         subscriber = match_subscriber(email, display_name)
     except InvalidEmailAddressError as e:
         print('Invalid email address: {}'.format(e), file=results)
         return ContinueProcessing.yes
     try:
         ISubscriptionManager(mlist).register(subscriber)
     except (AlreadySubscribedError, InvalidEmailAddressError,
             MembershipIsBannedError) as e:
         print(str(e), file=results)
     except SubscriptionPendingError:
         # SubscriptionPendingError doesn't return an error message.
         listname = mlist.fqdn_listname                        # noqa: F841
         print(_('$person has a pending subscription for $listname'),
               file=results)
     else:
         print(_('Confirmation email sent to $person'), file=results)
     return ContinueProcessing.yes
 def test_nonascii_confirmation_message(self):
     # Add the 'yy' language and set it
     getUtility(ILanguageManager).add('yy', 'utf-8', 'Ylandia')
     self._mlist.preferred_language = 'yy'
     # Create an address to subscribe.
     address = getUtility(IUserManager).create_address(
         '*****@*****.**', 'Anne Person')
     # Register the address with the list to create a confirmation notice.
     ISubscriptionManager(self._mlist).register(address)
     # Now there's one message in the virgin queue.
     items = get_queue_messages('virgin', expected_count=1)
     message = items[0].msg
     self.assertTrue(str(message['subject']).startswith('Your confirm'))
     self.assertMultiLineEqual(
         message.get_payload(decode=True).decode('utf-8'),
         'Wé need your confirmation\n')
예제 #14
0
 def process(self, mlist, msg, msgdata, arguments, results):
     """See `IEmailCommand`."""
     # The token must be in the arguments.
     if len(arguments) == 0:
         print(_('No confirmation token found'), file=results)
         return ContinueProcessing.no
     # Make sure we don't try to confirm the same token more than once.
     token = arguments[0]
     tokens = getattr(results, 'confirms', set())
     if token in tokens:
         # Do not try to confirm this one again.
         return ContinueProcessing.no
     tokens.add(token)
     results.confirms = tokens
     try:
         new_token, token_owner, member = ISubscriptionManager(
             mlist).confirm(token)
         if new_token is None:
             assert token_owner is TokenOwner.no_one, token_owner
             # We can't assert anything about member.  It will be None when
             # the workflow we're confirming is a subscription request,
             # and non-None when we're confirming an unsubscription request.
             # This class doesn't know which is happening.
             succeeded = True
         elif token_owner is TokenOwner.moderator:
             # This must have been a confirm-then-moderate (un)subscription.
             assert new_token != token
             # We can't assert anything about member for the above reason.
             succeeded = True
         else:
             assert token_owner is not TokenOwner.no_one, token_owner
             assert member is None, member
             succeeded = False
     except LookupError:
         # The token must not exist in the database.
         succeeded = False
     except MembershipIsBannedError as e:
         print(str(e), file=results)
         return ContinueProcessing.no
     if succeeded:
         print(_('Confirmed'), file=results)
         # After the 'confirm' command, do not process any other commands in
         # the email.
         return ContinueProcessing.no
     print(_('Confirmation token did not match'), file=results)
     return ContinueProcessing.no
 def test_subscription_pending(self):
     # Create an address.
     address = getUtility(IUserManager).create_address(
         '*****@*****.**', 'Anne Person')
     # Pend a subscription.
     self._mlist.subscription_policy = SubscriptionPolicy.confirm
     ISubscriptionManager(self._mlist).register(address)
     with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp:
         print('Anne Person <*****@*****.**>', file=infp)
         result = self._command.invoke(addmembers, (
             infp.name, 'ant.example.com'))
     self.assertEqual(
        result.output,
        'Subscription already pending (skipping): '
        'Anne Person <*****@*****.**>\n'
        )
     self.assertEqual(len(list(self._mlist.members.members)), 0)
 def test_join_pending(self):
     self._mlist.subscription_policy = SubscriptionPolicy.confirm
     # Try to subscribe someone who already has a subscription pending.
     # Anne is a real user, with a validated address, who already has a
     # pending subscription for this mailing list.
     anne = getUtility(IUserManager).create_user('*****@*****.**')
     set_preferred(anne)
     # Initiate a subscription.
     ISubscriptionManager(self._mlist).register(anne)
     # And try to subscribe.
     msg = Message()
     msg['From'] = '*****@*****.**'
     results = Results()
     self._command.process(self._mlist, msg, {}, (), results)
     self.assertEqual(
         str(results).splitlines()[-1],
         '[email protected] has a pending subscription for [email protected]')
class TestUnsubscription(unittest.TestCase):
    """Test unsubscription requests."""

    layer = SMTPLayer

    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._manager = ISubscriptionManager(self._mlist)

    def test_unsubscribe_defer(self):
        # When unsubscriptions must be approved by the moderator, but the
        # moderator defers this decision.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_address('*****@*****.**', 'Anne Person')
        token, token_owner, member = self._manager.register(anne,
                                                            pre_verified=True,
                                                            pre_confirmed=True,
                                                            pre_approved=True)
        self.assertIsNone(token)
        self.assertEqual(member.address.email, '*****@*****.**')
        bart = user_manager.create_user('*****@*****.**', 'Bart User')
        address = set_preferred(bart)
        self._mlist.subscribe(address, MemberRole.moderator)
        # Now hold and handle an unsubscription request.
        token = hold_unsubscription(self._mlist, '*****@*****.**')
        handle_unsubscription(self._mlist, token, Action.defer)
        items = get_queue_messages('virgin', expected_count=2)
        # Find the moderator message.
        for item in items:
            if item.msg['to'] == '*****@*****.**':
                break
        else:
            raise AssertionError('No moderator email found')
        self.assertEqual(item.msgdata['recipients'],
                         {'*****@*****.**'})
        self.assertEqual(
            item.msg['subject'],
            'New unsubscription request from Test by [email protected]')

    def test_bogus_token(self):
        # Try to handle an unsubscription with a bogus token.
        self.assertRaises(LookupError, self._manager.confirm, None)
 def test_join_already_a_member(self):
     # Try to subscribe someone who is already a member.  Anne is a real
     # user, with a validated address, but she is not a member of the
     # mailing list yet.
     anne = getUtility(IUserManager).create_user('*****@*****.**')
     set_preferred(anne)
     # First subscribe anne.
     ISubscriptionManager(self._mlist).register(anne,
                                                pre_verified=True,
                                                pre_confirmed=True,
                                                pre_approved=True)
     # Then initiate a subscription.
     msg = Message()
     msg['From'] = '*****@*****.**'
     results = Results()
     self._command.process(self._mlist, msg, {}, (), results)
     self.assertEqual(
         str(results).splitlines()[-1],
         '[email protected] is already a MemberRole.member of '
         'mailing list [email protected]')
예제 #19
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._mlist2 = create_list('*****@*****.**')
        self._mlist.subscription_policy = SubscriptionPolicy.moderate
        self._mlist.unsubscription_policy = SubscriptionPolicy.moderate
        msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 1

""")
        # Hold this message.
        hold_message(self._mlist, msg, {}, 'Non-member post')
        # And a second one too.
        msg2 = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 2

""")
        hold_message(self._mlist, msg2, {}, 'Some other reason')
        usermanager = getUtility(IUserManager)
        submanager = ISubscriptionManager(self._mlist)
        # Generate held subscription.
        usera = usermanager.make_user('*****@*****.**')
        usera.addresses[0].verified_on = usera.addresses[0].registered_on
        usera.preferred_address = usera.addresses[0]
        submanager.register(usera)
        # Generate a held unsubscription.
        userb = usermanager.make_user('*****@*****.**')
        userb.addresses[0].verified_on = userb.addresses[0].registered_on
        userb.preferred_address = userb.addresses[0]
        submanager.register(userb,
                            pre_verified=True,
                            pre_confirmed=True,
                            pre_approved=True)
        submanager.unregister(userb)
        self._command = CliRunner()
예제 #20
0
 def _confirm(self):
     # There will be two messages in the queue - the confirmation messages,
     # and a reply to Anne notifying her of the status of her command
     # email.  We need to dig the confirmation token out of the Subject
     # header of the latter so that we can confirm the subscription.
     items = get_queue_messages('virgin', sort_on='subject',
                                expected_count=2)
     subject_words = str(items[1].msg['subject']).split()
     self.assertEqual(subject_words[0], 'confirm')
     token = subject_words[1]
     token, token_owner, rmember = ISubscriptionManager(
         self._mlist).confirm(token)
     self.assertIsNone(token)
     self.assertEqual(token_owner, TokenOwner.no_one)
     # Now, make sure that Anne is a member of the list and is receiving
     # digest deliveries.
     members = getUtility(ISubscriptionService).find_members(
         '*****@*****.**')
     self.assertEqual(len(members), 1)
     self.assertEqual(rmember, members[0])
     return rmember
    def test_confirmation_message(self):
        # Create an address to subscribe.
        address = getUtility(IUserManager).create_address(
            '*****@*****.**', 'Anne Person')
        # Register the address with the list to create a confirmation notice.
        ISubscriptionManager(self._mlist).register(address)
        # Now there's one message in the virgin queue.
        items = get_queue_messages('virgin', expected_count=1)
        message = items[0].msg
        self.assertTrue(str(message['subject']).startswith('Your confirm'))
        token = re.sub(r'^.*\+([^+@]*)@.*$', r'\1', str(message['from']))
        self.assertMultiLineEqual(
            message.get_payload(), """\
Email Address Registration Confirmation

Hello, this is the GNU Mailman server at example.com.

We have received a registration request for the email address

    [email protected]

Before you can start using GNU Mailman at this site, you must first confirm
that this is your email address.  You can do this by replying to this message.

Or you should include the following line -- and only the following
line -- in a message to [email protected]:

    confirm {}

Note that simply sending a `reply' to this message should work from
most mail readers.

If you do not wish to register this email address, simply disregard this
message.  If you think you are being maliciously subscribed to the list, or
have any other questions, you may contact

    [email protected]
""".format(token))
예제 #22
0
 def process(self, mlist, msg, msgdata, arguments, results):
     """See `IEmailCommand`."""
     # The token must be in the arguments.
     if len(arguments) == 0:
         print(_('No confirmation token found'), file=results)
         return ContinueProcessing.no
     # Make sure we don't try to confirm the same token more than once.
     token = arguments[0]
     tokens = getattr(results, 'confirms', set())
     if token in tokens:
         # Do not try to confirm this one again.
         return ContinueProcessing.yes
     tokens.add(token)
     results.confirms = tokens
     try:
         new_token, token_owner, member = ISubscriptionManager(
             mlist).confirm(token)
         if new_token is None:
             assert token_owner is TokenOwner.no_one, token_owner
             assert member is not None, member
             succeeded = True
         elif token_owner is TokenOwner.moderator:
             # This must have been a confirm-then-moderator subscription.
             assert new_token != token
             assert member is None, member
             succeeded = True
         else:
             assert token_owner is not TokenOwner.no_one, token_owner
             assert member is None, member
             succeeded = False
     except LookupError:
         # The token must not exist in the database.
         succeeded = False
     if succeeded:
         print(_('Confirmed'), file=results)
         return ContinueProcessing.yes
     print(_('Confirmation token did not match'), file=results)
     return ContinueProcessing.no
예제 #23
0
 def process(self, mlist, msg, msgdata, arguments, results):
     """See `IEmailCommand`."""
     # Parse the arguments.
     delivery_mode = self._parse_arguments(arguments, results)
     if delivery_mode is ContinueProcessing.no:
         return ContinueProcessing.no
     display_name, email = parseaddr(msg['from'])
     # Address could be None or the empty string.
     if not email:
         email = msg.sender
     if not email:
         print(_('$self.name: No valid address found to subscribe'),
               file=results)
         return ContinueProcessing.no
     if isinstance(email, bytes):
         email = email.decode('ascii')
     # Have we already seen one join request from this user during the
     # processing of this email?
     joins = getattr(results, 'joins', set())
     if email in joins:
         # Do not register this join.
         return ContinueProcessing.yes
     joins.add(email)
     results.joins = joins
     person = formataddr((display_name, email))  # noqa: F841
     # Is this person already a member of the list?  Search for all
     # matching memberships.
     members = getUtility(ISubscriptionService).find_members(
         email, mlist.list_id, MemberRole.member)
     if len(members) > 0:
         print(_('$person is already a member'), file=results)
         return ContinueProcessing.yes
     subscriber = match_subscriber(email, display_name)
     ISubscriptionManager(mlist).register(subscriber)
     print(_('Confirmation email sent to $person'), file=results)
     return ContinueProcessing.yes
예제 #24
0
class TestSubscriptionModeration(unittest.TestCase):
    layer = RESTLayer
    maxDiff = None

    def setUp(self):
        with transaction():
            self._mlist = create_list('*****@*****.**')
            self._registrar = ISubscriptionManager(self._mlist)
            manager = getUtility(IUserManager)
            self._anne = manager.create_address('*****@*****.**',
                                                'Anne Person')
            self._bart = manager.make_user('*****@*****.**', 'Bart Person')
            set_preferred(self._bart)

    def test_no_such_list(self):
        # Try to get the requests of a nonexistent list.
        with self.assertRaises(HTTPError) as cm:
            call_api('http://*****:*****@example.com/'
                     'requests')
        self.assertEqual(cm.exception.code, 404)

    def test_no_such_subscription_token(self):
        # Bad request when the token is not in the database.
        with self.assertRaises(HTTPError) as cm:
            call_api('http://*****:*****@example.com/'
                     'requests/missing')
        self.assertEqual(cm.exception.code, 404)

    def test_bad_subscription_action(self):
        # POSTing to a held message with a bad action.
        token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        # Let's try to handle her request, but with a bogus action.
        url = 'http://*****:*****@example.com/requests/{}'
        with self.assertRaises(HTTPError) as cm:
            call_api(url.format(token), dict(action='bogus', ))
        self.assertEqual(cm.exception.code, 400)
        self.assertEqual(cm.exception.msg, 'Cannot convert parameters: action')

    def test_list_held_requests(self):
        # We can view all the held requests.
        with transaction():
            token_1, token_owner, member = self._registrar.register(self._anne)
            # Anne's subscription request got held.
            self.assertIsNotNone(token_1)
            self.assertIsNone(member)
            token_2, token_owner, member = self._registrar.register(self._bart)
            self.assertIsNotNone(token_2)
            self.assertIsNone(member)
        json, response = call_api(
            'http://*****:*****@example.com/requests')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(json['total_size'], 2)
        tokens = set(entry['token'] for entry in json['entries'])
        self.assertEqual(tokens, {token_1, token_2})
        emails = set(entry['email'] for entry in json['entries'])
        self.assertEqual(emails, {'*****@*****.**', '*****@*****.**'})

    def test_view_malformed_held_message(self):
        # Opening a bad (i.e. bad structure) email and holding it.
        email_path = resource_filename('mailman.rest.tests.data',
                                       'bad_email.eml')
        with open(email_path, 'rb') as fp:
            msg = message_from_binary_file(fp)
        msg.sender = '*****@*****.**'
        with transaction():
            hold_message(self._mlist, msg)
        # Now trying to access held messages from REST API should not give
        # 500 server error if one of the messages can't be parsed properly.
        json, response = call_api(
            'http://*****:*****@example.com/held')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(json['entries']), 1)
        self.assertEqual(json['entries'][0]['msg'],
                         'This message is defective')

    def test_individual_request(self):
        # We can view an individual request.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
            # Anne's subscription request got held.
            self.assertIsNotNone(token)
            self.assertIsNone(member)
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(json['token'], token)
        self.assertEqual(json['token_owner'], token_owner.name)
        self.assertEqual(json['email'], '*****@*****.**')

    def test_accept(self):
        # POST to the request to accept it.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), dict(action='accept', ))
        self.assertEqual(response.status_code, 204)
        # Anne is a member.
        self.assertEqual(
            self._mlist.members.get_member('*****@*****.**').address,
            self._anne)
        # The request URL no longer exists.
        with self.assertRaises(HTTPError) as cm:
            call_api(url.format(token), dict(action='accept', ))
        self.assertEqual(cm.exception.code, 404)

    def test_accept_already_subscribed(self):
        # POST to a subscription request, but the user is already subscribed.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
            # Make Anne already a member.
            self._mlist.subscribe(self._anne)
        # Accept the pending subscription, which raises an error.
        url = 'http://*****:*****@example.com'
                '/requests/bogus', dict(action='accept'))
        self.assertEqual(cm.exception.code, 404)

    def test_accept_by_moderator_clears_request_queue(self):
        # After accepting a message held for moderator approval, there are no
        # more requests to handle.
        #
        # We start with nothing in the queue.
        json, response = call_api(
            'http://*****:*****@example.com/requests')
        self.assertEqual(json['total_size'], 0)
        # Anne tries to subscribe to a list that only requests moderator
        # approval.
        with transaction():
            self._mlist.subscription_policy = SubscriptionPolicy.moderate
            token, token_owner, member = self._registrar.register(
                self._anne, pre_verified=True, pre_confirmed=True)
        # There's now one request in the queue, and it's waiting on moderator
        # approval.
        json, response = call_api(
            'http://*****:*****@example.com/requests')
        self.assertEqual(json['total_size'], 1)
        entry = json['entries'][0]
        self.assertEqual(entry['token_owner'], 'moderator')
        self.assertEqual(entry['email'], '*****@*****.**')
        # The moderator approves the request.
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), {'action': 'accept'})
        self.assertEqual(response.status_code, 204)
        # And now the request queue is empty.
        json, response = call_api(
            'http://*****:*****@example.com/requests')
        self.assertEqual(json['total_size'], 0)

    def test_discard(self):
        # POST to the request to discard it.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), dict(action='discard', ))
        self.assertEqual(response.status_code, 204)
        # Anne is not a member.
        self.assertIsNone(self._mlist.members.get_member('*****@*****.**'))
        # The request URL no longer exists.
        with self.assertRaises(HTTPError) as cm:
            call_api(url.format(token), dict(action='discard', ))
        self.assertEqual(cm.exception.code, 404)

    def test_defer(self):
        # Defer the decision for some other moderator.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), dict(action='defer', ))
        self.assertEqual(response.status_code, 204)
        # Anne is not a member.
        self.assertIsNone(self._mlist.members.get_member('*****@*****.**'))
        # The request URL still exists.
        json, response = call_api(url.format(token), dict(action='defer', ))
        self.assertEqual(response.status_code, 204)
        # And now we can accept it.
        json, response = call_api(url.format(token), dict(action='accept', ))
        self.assertEqual(response.status_code, 204)
        # Anne is a member.
        self.assertEqual(
            self._mlist.members.get_member('*****@*****.**').address,
            self._anne)
        # The request URL no longer exists.
        with self.assertRaises(HTTPError) as cm:
            call_api(url.format(token), dict(action='accept', ))
        self.assertEqual(cm.exception.code, 404)

    def test_defer_bad_token(self):
        # Try to accept a request with a bogus token.
        with self.assertRaises(HTTPError) as cm:
            call_api(
                'http://*****:*****@example.com'
                '/requests/bogus', dict(action='defer'))
        self.assertEqual(cm.exception.code, 404)

    def test_reject(self):
        # POST to the request to reject it.  This leaves a bounce message in
        # the virgin queue.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        # Clear out the virgin queue, which currently contains the
        # confirmation message sent to Anne.
        get_queue_messages('virgin')
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), dict(action='reject', ))
        self.assertEqual(response.status_code, 204)
        # Anne is not a member.
        self.assertIsNone(self._mlist.members.get_member('*****@*****.**'))
        # The request URL no longer exists.
        with self.assertRaises(HTTPError) as cm:
            call_api(url.format(token), dict(action='reject', ))
        self.assertEqual(cm.exception.code, 404)
        # And the rejection message to Anne is now in the virgin queue.
        items = get_queue_messages('virgin')
        self.assertEqual(len(items), 1)
        message = items[0].msg
        self.assertEqual(message['From'], '*****@*****.**')
        self.assertEqual(message['To'], '*****@*****.**')
        self.assertEqual(message['Subject'],
                         'Request to mailing list "Ant" rejected')

    def test_reject_bad_token(self):
        # Try to accept a request with a bogus token.
        with self.assertRaises(HTTPError) as cm:
            call_api(
                'http://*****:*****@example.com'
                '/requests/bogus', dict(action='reject'))
        self.assertEqual(cm.exception.code, 404)

    def test_hold_keeps_holding(self):
        # POST to the request to continue holding it.
        with transaction():
            token, token_owner, member = self._registrar.register(self._anne)
        # Anne's subscription request got held.
        self.assertIsNone(member)
        # Clear out the virgin queue, which currently contains the
        # confirmation message sent to Anne.
        get_queue_messages('virgin')
        url = 'http://*****:*****@example.com/requests/{}'
        json, response = call_api(url.format(token), dict(action='hold', ))
        self.assertEqual(response.status_code, 204)
        # Anne is not a member.
        self.assertIsNone(self._mlist.members.get_member('*****@*****.**'))
        # The request URL still exists.
        json, response = call_api(url.format(token), dict(action='defer', ))
        self.assertEqual(response.status_code, 204)

    def test_subscribe_other_role_with_no_preferred_address(self):
        with transaction():
            cate = getUtility(IUserManager).create_user('*****@*****.**')
        with self.assertRaises(HTTPError) as cm:
            call_api(
                'http://*****:*****@example.com')
        with self.assertRaises(HTTPError) as cm:
            call_api(
                'http://*****:*****@example.com',
                    'role': 'moderator',
                })
        self.assertEqual(cm.exception.code, 400)
        self.assertEqual(cm.exception.reason, 'Membership is banned')
예제 #25
0
 def setUp(self):
     self._mlist = create_list('*****@*****.**')
     self._registrar = ISubscriptionManager(self._mlist)
     self._pendings = getUtility(IPendings)
     self._anne = getUtility(IUserManager).create_address(
         '*****@*****.**')
예제 #26
0
class TestRegistrar(unittest.TestCase):
    """Test registration."""

    layer = ConfigLayer

    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._registrar = ISubscriptionManager(self._mlist)
        self._pendings = getUtility(IPendings)
        self._anne = getUtility(IUserManager).create_address(
            '*****@*****.**')

    def test_initial_conditions(self):
        # Registering a subscription request provides a unique token associated
        # with a pendable, and the owner of the token.
        self.assertEqual(self._pendings.count, 0)
        token, token_owner, member = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(member)
        self.assertEqual(self._pendings.count, 1)
        record = self._pendings.confirm(token, expunge=False)
        self.assertEqual(record['list_id'], self._mlist.list_id)
        self.assertEqual(record['email'], '*****@*****.**')

    def test_subscribe(self):
        # Registering a subscription request where no confirmation or
        # moderation steps are needed, leaves us with no token or owner, since
        # there's nothing more to do.
        self._mlist.subscription_policy = SubscriptionPolicy.open
        self._anne.verified_on = now()
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNone(token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)
        # There's nothing to confirm.
        record = self._pendings.confirm(token, expunge=False)
        self.assertIsNone(record)

    def test_no_such_token(self):
        # Given a token which is not in the database, a LookupError is raised.
        self._registrar.register(self._anne)
        self.assertRaises(LookupError, self._registrar.confirm, 'not-a-token')

    def test_confirm_because_verify(self):
        # We have a subscription request which requires the user to confirm
        # (because she does not have a verified address), but not the moderator
        # to approve.  Running the workflow gives us a token.  Confirming the
        # token subscribes the user.
        self._mlist.subscription_policy = SubscriptionPolicy.open
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now confirm the subscription.
        token, token_owner, rmember = self._registrar.confirm(token)
        self.assertIsNone(token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)

    def test_confirm_because_confirm(self):
        # We have a subscription request which requires the user to confirm
        # (because of list policy), but not the moderator to approve.  Running
        # the workflow gives us a token.  Confirming the token subscribes the
        # user.
        self._mlist.subscription_policy = SubscriptionPolicy.confirm
        self._anne.verified_on = now()
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now confirm the subscription.
        token, token_owner, rmember = self._registrar.confirm(token)
        self.assertIsNone(token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)

    def test_confirm_because_moderation(self):
        # We have a subscription request which requires the moderator to
        # approve.  Running the workflow gives us a token.  Confirming the
        # token subscribes the user.
        self._mlist.subscription_policy = SubscriptionPolicy.moderate
        self._anne.verified_on = now()
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.moderator)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now confirm the subscription.
        token, token_owner, rmember = self._registrar.confirm(token)
        self.assertIsNone(token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)

    def test_confirm_because_confirm_then_moderation(self):
        # We have a subscription request which requires the user to confirm
        # (because she does not have a verified address) and the moderator to
        # approve.  Running the workflow gives us a token.  Confirming the
        # token runs the workflow a little farther, but still gives us a
        # token.  Confirming again subscribes the user.
        self._mlist.subscription_policy = (
            SubscriptionPolicy.confirm_then_moderate)
        self._anne.verified_on = now()
        # Runs until subscription confirmation.
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now confirm the subscription, and wait for the moderator to approve
        # the subscription.  She is still not subscribed.
        new_token, token_owner, rmember = self._registrar.confirm(token)
        # The new token, used for the moderator to approve the message, is not
        # the same as the old token.
        self.assertNotEqual(new_token, token)
        self.assertIsNotNone(new_token)
        self.assertEqual(token_owner, TokenOwner.moderator)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Confirm once more, this time as the moderator approving the
        # subscription.  Now she's a member.
        token, token_owner, rmember = self._registrar.confirm(new_token)
        self.assertIsNone(token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)

    def test_confirm_then_moderate_with_different_tokens(self):
        # Ensure that the confirmation token the user sees when they have to
        # confirm their subscription is different than the token the moderator
        # sees when they approve the subscription.  This prevents the user
        # from using a replay attack to subvert moderator approval.
        self._mlist.subscription_policy = (
            SubscriptionPolicy.confirm_then_moderate)
        self._anne.verified_on = now()
        # Runs until subscription confirmation.
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now confirm the subscription, and wait for the moderator to approve
        # the subscription.  She is still not subscribed.
        new_token, token_owner, rmember = self._registrar.confirm(token)
        # The status is not true because the user has not yet been subscribed
        # to the mailing list.
        self.assertIsNotNone(new_token)
        self.assertEqual(token_owner, TokenOwner.moderator)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # The new token is different than the old token.
        self.assertNotEqual(token, new_token)
        # Trying to confirm with the old token does not work.
        self.assertRaises(LookupError, self._registrar.confirm, token)
        # Confirm once more, this time with the new token, as the moderator
        # approving the subscription.  Now she's a member.
        done_token, token_owner, rmember = self._registrar.confirm(new_token)
        # The token is None, signifying that the member has been subscribed.
        self.assertIsNone(done_token)
        self.assertEqual(token_owner, TokenOwner.no_one)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertEqual(rmember, member)
        self.assertEqual(member.address, self._anne)

    def test_discard_waiting_for_confirmation(self):
        # While waiting for a user to confirm their subscription, we discard
        # the workflow.
        self._mlist.subscription_policy = SubscriptionPolicy.confirm
        self._anne.verified_on = now()
        # Runs until subscription confirmation.
        token, token_owner, rmember = self._registrar.register(self._anne)
        self.assertIsNotNone(token)
        self.assertEqual(token_owner, TokenOwner.subscriber)
        self.assertIsNone(rmember)
        member = self._mlist.regular_members.get_member('*****@*****.**')
        self.assertIsNone(member)
        # Now discard the subscription request.
        self._registrar.discard(token)
        # Trying to confirm the token now results in an exception.
        self.assertRaises(LookupError, self._registrar.confirm, token)

    def test_admin_notify_mchanges(self):
        # When a user gets subscribed via the subscription policy workflow,
        # the list administrators get an email notification.
        self._mlist.subscription_policy = SubscriptionPolicy.open
        self._mlist.admin_notify_mchanges = True
        self._mlist.send_welcome_message = False
        token, token_owner, member = self._registrar.register(
            self._anne, pre_verified=True)
        # Anne is now a member.
        self.assertEqual(member.address.email, '*****@*****.**')
        # And there's a notification email waiting for Bart.
        items = get_queue_messages('virgin', expected_count=1)
        message = items[0].msg
        self.assertEqual(message['To'], '*****@*****.**')
        self.assertEqual(message['Subject'], 'Ant subscription notification')
        self.assertEqual(
            message.get_payload(), """\
[email protected] has been successfully subscribed to Ant.
""")

    def test_no_admin_notify_mchanges(self):
        # Even when a user gets subscribed via the subscription policy
        # workflow, the list administrators won't get an email notification if
        # they don't want one.
        self._mlist.subscription_policy = SubscriptionPolicy.open
        self._mlist.admin_notify_mchanges = False
        self._mlist.send_welcome_message = False
        # Bart is an administrator of the mailing list.
        bart = getUtility(IUserManager).create_address('*****@*****.**',
                                                       'Bart Person')
        self._mlist.subscribe(bart, MemberRole.owner)
        token, token_owner, member = self._registrar.register(
            self._anne, pre_verified=True)
        # Anne is now a member.
        self.assertEqual(member.address.email, '*****@*****.**')
        # There's no notification email waiting for Bart.
        get_queue_messages('virgin', expected_count=0)
예제 #27
0
파일: members.py 프로젝트: aisworld/mailman
 def on_post(self, request, response):
     """Create a new member."""
     try:
         validator = Validator(list_id=str,
                               subscriber=subscriber_validator(self.api),
                               display_name=str,
                               delivery_mode=enum_validator(DeliveryMode),
                               role=enum_validator(MemberRole),
                               pre_verified=bool,
                               pre_confirmed=bool,
                               pre_approved=bool,
                               _optional=('delivery_mode', 'display_name',
                                          'role', 'pre_verified',
                                          'pre_confirmed', 'pre_approved'))
         arguments = validator(request)
     except ValueError as error:
         bad_request(response, str(error))
         return
     # Dig the mailing list out of the arguments.
     list_id = arguments.pop('list_id')
     mlist = getUtility(IListManager).get_by_list_id(list_id)
     if mlist is None:
         bad_request(response, b'No such list')
         return
     # Figure out what kind of subscriber is being registered.  Either it's
     # a user via their preferred email address or it's an explicit address.
     # If it's a UUID, then it must be associated with an existing user.
     subscriber = arguments.pop('subscriber')
     user_manager = getUtility(IUserManager)
     # We use the display name if there is one.
     display_name = arguments.pop('display_name', '')
     if isinstance(subscriber, UUID):
         user = user_manager.get_user_by_id(subscriber)
         if user is None:
             bad_request(response, b'No such user')
             return
         subscriber = user
     else:
         # This must be an email address.  See if there's an existing
         # address object associated with this email.
         address = user_manager.get_address(subscriber)
         if address is None:
             # Create a new address, which of course will not be validated.
             address = user_manager.create_address(subscriber, display_name)
         subscriber = address
     # What role are we subscribing?  Regular members go through the
     # subscription policy workflow while owners, moderators, and
     # nonmembers go through the legacy API for now.
     role = arguments.pop('role', MemberRole.member)
     if role is MemberRole.member:
         # Get the pre_ flags for the subscription workflow.
         pre_verified = arguments.pop('pre_verified', False)
         pre_confirmed = arguments.pop('pre_confirmed', False)
         pre_approved = arguments.pop('pre_approved', False)
         # Now we can run the registration process until either the
         # subscriber is subscribed, or the workflow is paused for
         # verification, confirmation, or approval.
         registrar = ISubscriptionManager(mlist)
         try:
             token, token_owner, member = registrar.register(
                 subscriber,
                 pre_verified=pre_verified,
                 pre_confirmed=pre_confirmed,
                 pre_approved=pre_approved)
         except AlreadySubscribedError:
             conflict(response, b'Member already subscribed')
             return
         except MissingPreferredAddressError:
             bad_request(response, b'User has no preferred address')
             return
         except MembershipIsBannedError:
             bad_request(response, b'Membership is banned')
             return
         except SubscriptionPendingError:
             conflict(response, b'Subscription request already pending')
             return
         if token is None:
             assert token_owner is TokenOwner.no_one, token_owner
             # The subscription completed.  Let's get the resulting member
             # and return the location to the new member.  Member ids are
             # UUIDs and need to be converted to URLs because JSON doesn't
             # directly support UUIDs.
             member_id = self.api.from_uuid(member.member_id)
             location = self.api.path_to('members/{}'.format(member_id))
             created(response, location)
             return
         # The member could not be directly subscribed because there are
         # some out-of-band steps that need to be completed.  E.g. the user
         # must confirm their subscription or the moderator must approve
         # it.  In this case, an HTTP 202 Accepted is exactly the code that
         # we should use, and we'll return both the confirmation token and
         # the "token owner" so the client knows who should confirm it.
         assert token is not None, token
         assert token_owner is not TokenOwner.no_one, token_owner
         assert member is None, member
         content = dict(token=token, token_owner=token_owner.name)
         accepted(response, etag(content))
         return
     # 2015-04-15 BAW: We're subscribing some role other than a regular
     # member.  Use the legacy API for this for now.
     assert role in (MemberRole.owner, MemberRole.moderator,
                     MemberRole.nonmember)
     # 2015-04-15 BAW: We're limited to using an email address with this
     # legacy API, so if the subscriber is a user, the user must have a
     # preferred address, which we'll use, even though it will subscribe
     # the explicit address.  It is an error if the user does not have a
     # preferred address.
     #
     # If the subscriber is an address object, just use that.
     if IUser.providedBy(subscriber):
         if subscriber.preferred_address is None:
             bad_request(response, b'User without preferred address')
             return
         email = subscriber.preferred_address.email
     else:
         assert IAddress.providedBy(subscriber)
         email = subscriber.email
     delivery_mode = arguments.pop('delivery_mode', DeliveryMode.regular)
     record = RequestRecord(email, display_name, delivery_mode)
     try:
         member = add_member(mlist, record, role)
     except MembershipIsBannedError:
         bad_request(response, b'Membership is banned')
         return
     except AlreadySubscribedError:
         bad_request(
             response,
             '{} is already an {} of {}'.format(email, role.name,
                                                mlist.fqdn_listname))
         return
     # The subscription completed.  Let's get the resulting member
     # and return the location to the new member.  Member ids are
     # UUIDs and need to be converted to URLs because JSON doesn't
     # directly support UUIDs.
     member_id = self.api.from_uuid(member.member_id)
     location = self.api.path_to('members/{}'.format(member_id))
     created(response, location)
예제 #28
0
 def __init__(self, mlist, token):
     super().__init__()
     self._mlist = mlist
     self._registrar = ISubscriptionManager(self._mlist)
     self._token = token
예제 #29
0
    def test_confirm_then_moderate_workflow(self):
        # Issue #114 describes a problem when confirming the moderation email.
        self._mlist.subscription_policy = (
            SubscriptionPolicy.confirm_then_moderate)
        bart = getUtility(IUserManager).create_address('*****@*****.**',
                                                       'Bart Person')
        # Clear any previously queued confirmation messages.
        get_queue_messages('virgin')
        self._token, token_owner, member = ISubscriptionManager(
            self._mlist).register(bart)
        # There should now be one email message in the virgin queue, i.e. the
        # confirmation message sent to Bart.
        items = get_queue_messages('virgin', expected_count=1)
        msg = items[0].msg
        # Confirmations come first, so this one goes to the subscriber.
        self.assertEqual(msg['to'], '*****@*****.**')
        confirm, token = str(msg['subject']).split()
        self.assertEqual(confirm, 'confirm')
        self.assertEqual(token, self._token)
        # Craft a confirmation response with the expected tokens.
        user_response = Message()
        user_response['From'] = '*****@*****.**'
        user_response['To'] = 'test-confirm+{}@example.com'.format(token)
        user_response['Subject'] = 'Re: confirm {}'.format(token)
        user_response.set_payload('')
        # Process the message through the command runner.
        config.switchboards['command'].enqueue(user_response,
                                               listid='test.example.com')
        make_testable_runner(CommandRunner, 'command').run()
        # There are now two messages in the virgin queue.  One is going to the
        # subscriber containing the results of their confirmation message, and
        # the other is to the moderators informing them that they need to
        # handle the moderation queue.
        items = get_queue_messages('virgin', expected_count=2)
        if items[0].msg['to'] == '*****@*****.**':
            results = items[0].msg
            moderator_msg = items[1].msg
        else:
            results = items[1].msg
            moderator_msg = items[0].msg
        # Check the moderator message first.
        self.assertEqual(moderator_msg['to'], '*****@*****.**')
        self.assertEqual(
            moderator_msg['subject'],
            'New subscription request to Test from [email protected]')
        lines = moderator_msg.get_payload().splitlines()
        self.assertEqual(lines[-2].strip(),
                         'For:  Bart Person <*****@*****.**>')
        self.assertEqual(lines[-1].strip(), 'List: [email protected]')
        # Now check the results message.
        self.assertEqual(str(results['subject']),
                         'The results of your email commands')
        self.assertMultiLineEqual(
            results.get_payload(), """\
The results of your email command are provided below.

- Original message details:
From: [email protected]
Subject: Re: confirm {}
Date: n/a
Message-ID: n/a

- Results:
Confirmed

- Done.
""".format(token))
예제 #30
0
 def setUp(self):
     self._mlist = create_list('*****@*****.**')
     anne = subscribe(self._mlist, 'Anne', email='*****@*****.**')
     self._token, token_owner, member = ISubscriptionManager(
         self._mlist).unregister(anne.address)