コード例 #1
0
ファイル: moderation.py プロジェクト: aswinpj/Mailman
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     ban_manager = IBanManager(mlist)
     user_manager = getUtility(IUserManager)
     # The NonmemberModeration rule misses unconditionally if any of the
     # senders are banned.
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             return False
     # Every sender email must be a member or nonmember directly.  If it is
     # neither, make the email a nonmembers.
     for sender in msg.senders:
         if (mlist.members.get_member(sender) is None
                 and mlist.nonmembers.get_member(sender) is None):   # noqa
             # The email must already be registered, since this happens in
             # the incoming runner itself.
             address = user_manager.get_address(sender)
             assert address is not None, (
                 'Posting address is not registered: {}'.format(sender))
             mlist.subscribe(address, MemberRole.nonmember)
     # Check to see if any of the sender emails is already a member.  If
     # so, then this rule misses.
     member = _find_sender_member(mlist, msg)
     if member is not None:
         return False
     # Do nonmember moderation check.
     for sender in msg.senders:
         nonmember = mlist.nonmembers.get_member(sender)
         assert nonmember is not None, (
             "sender didn't get subscribed as a nonmember".format(sender))
         # Check the '*_these_nonmembers' properties first.  XXX These are
         # legacy attributes from MM2.1; their database type is 'pickle' and
         # they should eventually get replaced.
         for action in ('accept', 'hold', 'reject', 'discard'):
             legacy_attribute_name = '{}_these_nonmembers'.format(action)
             checklist = getattr(mlist, legacy_attribute_name)
             for addr in checklist:
                 if ((addr.startswith('^') and re.match(addr, sender))
                         or addr == sender):     # noqa
                     # The reason will get translated at the point of use.
                     reason = 'The sender is in the nonmember {} list'
                     _record_action(msgdata, action, sender,
                                    reason.format(action))
                     return True
         action = (mlist.default_nonmember_action
                   if nonmember.moderation_action is None
                   else nonmember.moderation_action)
         if action is Action.defer:
             # The regular moderation rules apply.
             return False
         elif action is not None:
             # We must stringify the moderation action so that it can be
             # stored in the pending request table.
             #
             # The reason will get translated at the point of use.
             reason = 'The message is not from a list member'
             _record_action(msgdata, action.name, sender, reason)
             return True
     # The sender must be a member, so this rule does not match.
     return False
コード例 #2
0
ファイル: moderation.py プロジェクト: aswinpj/Mailman
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     # The MemberModeration rule misses unconditionally if any of the
     # senders are banned.
     ban_manager = IBanManager(mlist)
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             return False
     member = _find_sender_member(mlist, msg)
     if member is None:
         return False
     action = (mlist.default_member_action
               if member.moderation_action is None
               else member.moderation_action)
     if action is Action.defer:
         # The regular moderation rules apply.
         return False
     elif action is not None:
         # We must stringify the moderation action so that it can be
         # stored in the pending request table.
         msgdata['moderation_action'] = action.name
         msgdata['moderation_sender'] = sender
         msgdata.setdefault('moderation_reasons', []).append(
             # This will get translated at the point of use.
             'The message comes from a moderated member')
         return True
     # The sender is not a member so this rule does not match.
     return False
コード例 #3
0
ファイル: banned_address.py プロジェクト: aisworld/mailman
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     ban_manager = IBanManager(mlist)
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             return True
     return False
コード例 #4
0
ファイル: test_bans.py プロジェクト: aswinpj/Mailman
 def test_delete_list_does_not_delete_global_bans(self):
     # Global bans are not deleted when the list is deleted.
     global_ban_manager = IBanManager(None)
     global_ban_manager.ban('*****@*****.**')
     getUtility(IListManager).delete(self._mlist)
     self.assertEqual([ban.email for ban in global_ban_manager],
                      ['*****@*****.**'])
コード例 #5
0
 def test_delete_list_does_not_delete_global_bans(self):
     # Global bans are not deleted when the list is deleted.
     global_ban_manager = IBanManager(None)
     global_ban_manager.ban('*****@*****.**')
     getUtility(IListManager).delete(self._mlist)
     self.assertEqual([ban.email for ban in global_ban_manager],
                      ['*****@*****.**'])
コード例 #6
0
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     ban_manager = IBanManager(mlist)
     user_manager = getUtility(IUserManager)
     # The NonmemberModeration rule misses unconditionally if any of the
     # senders are banned.
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             return False
     # Every sender email must be a member or nonmember directly.  If it is
     # neither, make the email a nonmembers.
     for sender in msg.senders:
         if (mlist.members.get_member(sender) is None
                 and mlist.nonmembers.get_member(sender) is None):  # noqa
             # The email must already be registered, since this happens in
             # the incoming runner itself.
             address = user_manager.get_address(sender)
             assert address is not None, (
                 'Posting address is not registered: {}'.format(sender))
             mlist.subscribe(address, MemberRole.nonmember)
     # Check to see if any of the sender emails is already a member.  If
     # so, then this rule misses.
     member = _find_sender_member(mlist, msg)
     if member is not None:
         return False
     # Do nonmember moderation check.
     for sender in msg.senders:
         nonmember = mlist.nonmembers.get_member(sender)
         assert nonmember is not None, (
             "sender didn't get subscribed as a nonmember".format(sender))
         # Check the '*_these_nonmembers' properties first.  XXX These are
         # legacy attributes from MM2.1; their database type is 'pickle' and
         # they should eventually get replaced.
         for action in ('accept', 'hold', 'reject', 'discard'):
             legacy_attribute_name = '{}_these_nonmembers'.format(action)
             checklist = getattr(mlist, legacy_attribute_name)
             for addr in checklist:
                 if ((addr.startswith('^') and re.match(addr, sender))
                         or addr == sender):  # noqa: W503
                     # The reason will get translated at the point of use.
                     reason = 'The sender is in the nonmember {} list'
                     _record_action(msgdata, action, sender,
                                    reason.format(action))
                     return True
         action = (mlist.default_nonmember_action
                   if nonmember.moderation_action is None else
                   nonmember.moderation_action)
         if action is Action.defer:
             # The regular moderation rules apply.
             return False
         elif action is not None:
             # We must stringify the moderation action so that it can be
             # stored in the pending request table.
             #
             # The reason will get translated at the point of use.
             reason = 'The message is not from a list member'
             _record_action(msgdata, action.name, sender, reason)
             return True
     # The sender must be a member, so this rule does not match.
     return False
コード例 #7
0
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     # The MemberModeration rule misses unconditionally if any of the
     # senders are banned.
     ban_manager = IBanManager(mlist)
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             return False
     member = _find_sender_member(mlist, msg)
     if member is None:
         return False
     action = (mlist.default_member_action
               if member.moderation_action is None else
               member.moderation_action)
     if action is Action.defer:
         # The regular moderation rules apply.
         return False
     elif action is not None:
         # We must stringify the moderation action so that it can be
         # stored in the pending request table.
         msgdata['moderation_action'] = action.name
         msgdata['moderation_sender'] = sender
         msgdata.setdefault('moderation_reasons', []).append(
             # This will get translated at the point of use.
             'The message comes from a moderated member')
         return True
     # The sender is not a member so this rule does not match.
     return False
コード例 #8
0
ファイル: test_moderation.py プロジェクト: aswinpj/Mailman
 def test_subscribe_other_role_banned_email_address(self):
     bans = IBanManager(self._mlist)
     with transaction():
         bans.ban('*****@*****.**')
     with self.assertRaises(HTTPError) as cm:
         call_api('http://*****:*****@example.com',
             'role': 'moderator',
             })
     self.assertEqual(cm.exception.code, 400)
     self.assertEqual(cm.exception.reason, b'Membership is banned')
コード例 #9
0
 def test_subscribe_other_role_banned_email_address(self):
     bans = IBanManager(self._mlist)
     with transaction():
         bans.ban('*****@*****.**')
     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')
コード例 #10
0
 def check(self, mlist, msg, msgdata):
     """See `IRule`."""
     ban_manager = IBanManager(mlist)
     for sender in msg.senders:
         if ban_manager.is_banned(sender):
             msgdata['moderation_sender'] = sender
             with _.defer_translation():
                 # This will be translated at the point of use.
                 msgdata.setdefault('moderation_reasons', []).append(
                     (_('Message sender {} is banned from this list'),
                      sender))
             return True
     return False
コード例 #11
0
ファイル: test_bans.py プロジェクト: simonsmiley/mailman-core
 def test_not_found_after_unbanning_global(self):
     manager = IBanManager(None)
     with transaction():
         manager.ban("*****@*****.**")
     url = "http://*****:*****@example.com"
     response, content = call_api(url)
     self.assertEqual(response["email"], "*****@*****.**")
     response, content = call_api(url, method="DELETE")
     self.assertEqual(content.status, 204)
     with self.assertRaises(HTTPError) as cm:
         call_api(url)
     self.assertEqual(cm.exception.code, 404)
     self.assertEqual(cm.exception.reason, b"Email is not banned: [email protected]")
コード例 #12
0
ファイル: test_bans.py プロジェクト: TommyLike/mailman
 def test_not_found_after_unbanning_global(self):
     manager = IBanManager(None)
     with transaction():
         manager.ban('*****@*****.**')
     url = ('http://*****:*****@example.com')
     response, content = call_api(url)
     self.assertEqual(response['email'], '*****@*****.**')
     response, content = call_api(url, method='DELETE')
     self.assertEqual(content.status, 204)
     with self.assertRaises(HTTPError) as cm:
         call_api(url)
     self.assertEqual(cm.exception.code, 404)
     self.assertEqual(cm.exception.reason,
                      b'Email is not banned: [email protected]')
コード例 #13
0
ファイル: test_bans.py プロジェクト: inonit/mailman
 def test_not_found_after_unbanning(self):
     manager = IBanManager(self._mlist)
     with transaction():
         manager.ban('*****@*****.**')
     url = ('http://*****:*****@example.com')
     json, response = call_api(url)
     self.assertEqual(json['email'], '*****@*****.**')
     json, response = call_api(url, method='DELETE')
     self.assertEqual(response.status_code, 204)
     with self.assertRaises(HTTPError) as cm:
         call_api(url)
     self.assertEqual(cm.exception.code, 404)
     self.assertEqual(cm.exception.reason,
                      'Email is not banned: [email protected]')
コード例 #14
0
 def test_add_member_banned_by_pattern(self):
     # Addresses matching regexp ban patterns cannot subscribe.
     IBanManager(self._mlist).ban('^.*@example.com')
     self.assertRaises(
         MembershipIsBannedError,
         add_member, self._mlist, '*****@*****.**', 'Anne Person',
         '123', DeliveryMode.regular, system_preferences.preferred_language)
コード例 #15
0
    def test_banned_address_linked_to_user(self):
        # Anne is subscribed to a mailing list as a user with her preferred
        # address.  She also has a secondary address which is banned and which
        # she uses to post to the mailing list.  Both the MemberModeration and
        # NonmemberModeration rules miss because the posting address is
        # banned.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_user('*****@*****.**')
        set_preferred(anne)
        self._mlist.subscribe(anne, MemberRole.member)
        anne.link(user_manager.create_address('*****@*****.**'))
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = moderation.MemberModeration()
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
        rule = moderation.NonmemberModeration()
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
コード例 #16
0
 def _step_sanity_checks(self):
     # Ensure that we have both an address and a user, even if the address
     # is not verified.  We can't set the preferred address until it is
     # verified.
     if self.user is None:
         # The address has no linked user so create one, link it, and set
         # the user's preferred address.
         assert self.address is not None, 'No address or user'
         self.user = getUtility(IUserManager).make_user(self.address.email)
     if self.address is None:
         assert self.user.preferred_address is None, (
             "Preferred address exists, but wasn't used in constructor")
         addresses = list(self.user.addresses)
         if len(addresses) == 0:
             raise AssertionError('User has no addresses: {}'.format(
                 self.user))
         # This is rather arbitrary, but we have no choice.
         self.address = addresses[0]
     assert self.user is not None and self.address is not None, (
         'Insane sanity check results')
     # Is this email address banned?
     if IBanManager(self.mlist).is_banned(self.address.email):
         raise MembershipIsBannedError(self.mlist, self.address.email)
     # Start out with the subscriber being the token owner.
     self.push('verification_checks')
コード例 #17
0
 def test_add_member_globally_banned(self):
     # Test that members who are banned by specific address cannot
     # subscribe to the mailing list.
     IBanManager(None).ban('*****@*****.**')
     self.assertRaises(
         MembershipIsBannedError,
         add_member, self._mlist, '*****@*****.**', 'Anne Person',
         '123', DeliveryMode.regular, system_preferences.preferred_language)
コード例 #18
0
 def test_add_member_banned_from_different_list(self):
     # Test that members who are banned by on a different list can still be
     # subscribed to other mlists.
     sample_list = create_list('*****@*****.**')
     IBanManager(sample_list).ban('*****@*****.**')
     member = add_member(self._mlist, '*****@*****.**',
                         'Anne Person', '123', DeliveryMode.regular,
                         system_preferences.preferred_language)
     self.assertEqual(member.address.email, '*****@*****.**')
コード例 #19
0
 def test_add_member_banned_from_different_list_by_pattern(self):
     # Addresses matching regexp ban patterns on one list can still
     # subscribe to other mailing lists.
     sample_list = create_list('*****@*****.**')
     IBanManager(sample_list).ban('^.*@example.com')
     member = add_member(self._mlist, '*****@*****.**',
                         'Anne Person', '123', DeliveryMode.regular,
                         system_preferences.preferred_language)
     self.assertEqual(member.address.email, '*****@*****.**')
コード例 #20
0
 def test_ban_list(self):
     banned = [
         ('*****@*****.**', '*****@*****.**'),
         ('^.*@example.com', '*****@*****.**'),
         ('non-ascii-\[email protected]', 'non-ascii-\[email protected]'),
     ]
     self._pckdict['ban_list'] = [b[0].encode('iso-8859-1') for b in banned]
     self._import()
     for _pattern, addr in banned:
         self.assertTrue(IBanManager(self._mlist).is_banned(addr))
コード例 #21
0
ファイル: membership.py プロジェクト: Monal5031/mailman3
def add_member(mlist, record, role=MemberRole.member):
    """Add a member right now.

    The member's subscription must be approved by whatever policy the list
    enforces.

    :param mlist: The mailing list to add the member to.
    :type mlist: `IMailingList`
    :param record: a subscription request record.
    :type record: RequestRecord
    :param role: The membership role for this subscription.
    :type role: `MemberRole`
    :return: The just created member.
    :rtype: `IMember`
    :raises AlreadySubscribedError: if the user is already subscribed to
        the mailing list.
    :raises InvalidEmailAddressError: if the email address is not valid.
    :raises MembershipIsBannedError: if the membership is not allowed.
    """
    # Check to see if the email address is banned.
    if IBanManager(mlist).is_banned(record.email):
        raise MembershipIsBannedError(mlist, record.email)
    # Make sure there is a user linked with the given address.
    user_manager = getUtility(IUserManager)
    user = user_manager.make_user(record.email, record.display_name)
    user.preferences.preferred_language = record.language
    # Subscribe the address, not the user.
    # We're looking for two versions of the email address, the case
    # preserved version and the case insensitive version.   We'll
    # subscribe the version with matching case if it exists, otherwise
    # we'll use one of the matching case-insensitively ones.  It's
    # undefined which one we pick.
    case_preserved = None
    case_insensitive = None
    for address in user.addresses:
        if address.original_email == record.email:
            case_preserved = address
        if address.email == record.email.lower():
            case_insensitive = address
    assert case_preserved is not None or case_insensitive is not None, (
        'Could not find a linked address for: {}'.format(record.email))
    address = (case_preserved
               if case_preserved is not None else case_insensitive)
    # Create the member and set the appropriate preferences.  It's
    # possible we're subscribing the lower cased version of the address;
    # if that's already subscribed re-issue the exception with the correct
    # email address (i.e. the one passed in here).
    try:
        member = mlist.subscribe(address, role)
    except AlreadySubscribedError as error:
        raise AlreadySubscribedError(error.fqdn_listname, record.email,
                                     error.role)
    member.preferences.preferred_language = record.language
    member.preferences.delivery_mode = record.delivery_mode
    return member
コード例 #22
0
 def test_add_member_banned(self):
     # Test that members who are banned by specific address cannot
     # subscribe to the mailing list.
     IBanManager(self._mlist).ban('*****@*****.**')
     with self.assertRaises(MembershipIsBannedError) as cm:
         add_member(self._mlist, '*****@*****.**', 'Anne Person',
                    '123', DeliveryMode.regular,
                    system_preferences.preferred_language)
     self.assertEqual(
         str(cm.exception),
         '[email protected] is not allowed to subscribe to [email protected]')
コード例 #23
0
class TestMailingListBans(unittest.TestCase):
    layer = ConfigLayer

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

    def test_delete_list(self):
        # All list bans must be deleted when the list is deleted.
        self._manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual(list(self._manager), [])

    def test_delete_list_does_not_delete_global_bans(self):
        # Global bans are not deleted when the list is deleted.
        global_ban_manager = IBanManager(None)
        global_ban_manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual([ban.email for ban in global_ban_manager],
                         ['*****@*****.**'])
コード例 #24
0
 def test_globally_banned_member_tries_to_join(self):
     # A user tries to join a list they are banned from.
     with transaction():
         IBanManager(None).ban('*****@*****.**')
     with self.assertRaises(HTTPError) as cm:
         call_api('http://*****:*****@example.com',
         })
     self.assertEqual(cm.exception.code, 400)
     self.assertEqual(cm.exception.reason, 'Membership is banned')
コード例 #25
0
ファイル: test_bans.py プロジェクト: aswinpj/Mailman
class TestMailingListBans(unittest.TestCase):
    layer = ConfigLayer

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

    def test_delete_list(self):
        # All list bans must be deleted when the list is deleted.
        self._manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual(list(self._manager), [])

    def test_delete_list_does_not_delete_global_bans(self):
        # Global bans are not deleted when the list is deleted.
        global_ban_manager = IBanManager(None)
        global_ban_manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual([ban.email for ban in global_ban_manager],
                         ['*****@*****.**'])
コード例 #26
0
 def test_banned_address(self):
     IBanManager(self._mlist).ban('*****@*****.**')
     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,
        'Membership is banned (skipping): '
        'Anne Person <*****@*****.**>\n'
        )
     self.assertEqual(len(list(self._mlist.members.members)), 0)
コード例 #27
0
 def test_confirm_banned_address(self):
     # Confirmation of a banned address should return an appropriate error.
     IBanManager(self._mlist).ban('*****@*****.**')
     result = Results()
     status = self._command.process(self._mlist, Message(), {},
                                    (self._token, ), result)
     self.assertEqual(status, ContinueProcessing.no)
     # Anne will not be subscribed.
     self.assertFalse(self._mlist.is_subscribed('*****@*****.**'))
     # The result will contain an error message.
     self.assertIn(
         '[email protected] is not allowed to subscribe to '
         '*****@*****.**', str(result))
コード例 #28
0
 def test_join_banned(self):
     # Try to subscribe someone who is banned.  Anne is a real
     # user, with a validated address, but she is not a member of the
     # mailing list and is banned from joining.
     # Add anne to the ban list.
     IBanManager(self._mlist).ban('*****@*****.**')
     # 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 not allowed to subscribe to [email protected]')
コード例 #29
0
 def _step_sanity_checks(self):
     # Ensure that we have both an address and a user, even if the address
     # is not verified.  We can't set the preferred address until it is
     # verified.
     if self.user is None:
         # The address has no linked user so create one, link it, and set
         # the user's preferred address.
         assert self.address is not None, 'No address or user'
         self.user = getUtility(IUserManager).make_user(self.address.email)
     if self.address is None:
         assert self.user.preferred_address is None, (
             "Preferred address exists, but wasn't used in constructor")
         addresses = list(self.user.addresses)
         if len(addresses) == 0:
             raise AssertionError('User has no addresses: {}'.format(
                 self.user))
         # This is rather arbitrary, but we have no choice.
         self.address = addresses[0]
     assert self.user is not None and self.address is not None, (
         'Insane sanity check results')
     # Is this subscriber already a member?
     if (self.which is WhichSubscriber.user
             and self.user.preferred_address is not None):
         subscriber = self.user
     else:
         subscriber = self.address
     if self.mlist.is_subscribed(subscriber):
         # 2017-04-22 BAW: This branch actually *does* get covered, as I've
         # verified by a full coverage run, but diffcov for some reason
         # claims that the test added in the branch that added this code
         # does not cover the change.  That seems like a bug in diffcov.
         raise AlreadySubscribedError(  # pragma: nocover
             self.mlist.fqdn_listname, self.address.email,
             MemberRole.member)
     # Is this email address banned?
     if IBanManager(self.mlist).is_banned(self.address.email):
         raise MembershipIsBannedError(self.mlist, self.address.email)
     # Don't allow the list posting address.
     if self.address.email.lower() == self.mlist.posting_address:
         raise InvalidEmailAddressError('List posting address not allowed')
     # Check if there is already a subscription request for this email.
     pendings = getUtility(IPendings).find(mlist=self.mlist,
                                           pend_type='subscription')
     for token, pendable in pendings:
         if pendable['email'] == self.address.email:
             raise SubscriptionPendingError(self.mlist, self.address.email)
     # Start out with the subscriber being the token owner.
     self.push('verification_checks')
コード例 #30
0
    def test_simple_banned_sender(self):
        # Simple case where the sender is banned.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_user('*****@*****.**')
        set_preferred(anne)
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = banned_address.BannedAddress()
        result = rule.check(self._mlist, msg, {})
        self.assertTrue(result)
コード例 #31
0
    def test_banned_sender_among_multiple_senders(self):
        # Two addresses are created, one of which is banned.  The rule matches
        # because all senders are checked.
        user_manager = getUtility(IUserManager)
        user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
Sender: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = banned_address.BannedAddress()
        result = rule.check(self._mlist, msg, {})
        self.assertTrue(result)
コード例 #32
0
 def subscribe(self, store, subscriber, role=MemberRole.member,
               send_welcome_message=None):
     """See `IMailingList`."""
     member, email = self._get_subscriber(store, subscriber, role)
     test_email = email or subscriber.lower()
     # Allow list posting address only for nonmember role.
     if (test_email == self.posting_address and
             role != MemberRole.nonmember):
         raise InvalidEmailAddressError('List posting address not allowed')
     if member is not None:
         raise AlreadySubscribedError(self.fqdn_listname, email, role)
     if IBanManager(self).is_banned(test_email):
         raise MembershipIsBannedError(self, test_email)
     member = Member(role=role,
                     list_id=self._list_id,
                     subscriber=subscriber)
     member.preferences = Preferences()
     store.add(member)
     notify(SubscriptionEvent(
         self, member, send_welcome_message=send_welcome_message))
     return member
コード例 #33
0
    def test_banned_sender_among_multiple_senders(self):
        # Two addresses are created, one of which is banned.  Even though the
        # The Nonmember moderation rule misses if any of the banned addresses
        # appear in the 'senders' headers of the message.
        user_manager = getUtility(IUserManager)
        user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        IBanManager(self._mlist).ban('*****@*****.**')
        rule = moderation.NonmemberModeration()
        msg = mfs("""\
From: [email protected]
Sender: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
コード例 #34
0
ファイル: test_banned_address.py プロジェクト: inonit/mailman
    def test_rule_returns_reason(self):
        # Ensure a reason is returned.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_user('*****@*****.**')
        set_preferred(anne)
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = banned_address.BannedAddress()
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata['moderation_reasons'], [
            ('Message sender {} is banned from this list', '*****@*****.**')
        ])
コード例 #35
0
    def test_banned_address_linked_to_user(self):
        # Anne is subscribed to a mailing list as a user with her preferred
        # address.  She also has a secondary address which is banned and which
        # she uses to post to the mailing list.  The rule matches because the
        # posting address is banned.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_user('*****@*****.**')
        set_preferred(anne)
        anne.link(user_manager.create_address('*****@*****.**'))
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = banned_address.BannedAddress()
        result = rule.check(self._mlist, msg, {})
        self.assertTrue(result)
コード例 #36
0
class TestMailingListBans(unittest.TestCase):
    layer = ConfigLayer

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

    def test_delete_list(self):
        # All list bans must be deleted when the list is deleted.
        self._manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual(list(self._manager), [])

    def test_delete_list_does_not_delete_global_bans(self):
        # Global bans are not deleted when the list is deleted.
        global_ban_manager = IBanManager(None)
        global_ban_manager.ban('*****@*****.**')
        getUtility(IListManager).delete(self._mlist)
        self.assertEqual([ban.email for ban in global_ban_manager],
                         ['*****@*****.**'])

    def test_bans_sequence(self):
        # Bans returns a pageable sorted sequence.
        self._manager.ban('*****@*****.**')
        self._manager.ban('*****@*****.**')
        self._manager.ban('*****@*****.**')
        # The results can be len()'d.
        count = len(self._manager.bans)
        self.assertEqual(count, 3)
        # The results can be iterated.
        self.assertEqual(
            ['*****@*****.**', '*****@*****.**', '*****@*****.**'],
            [ban.email for ban in self._manager.bans])
        # The results can be indexed.
        self.assertEqual(
            [self._manager.bans[i].email for i in range(count)],
            ['*****@*****.**', '*****@*****.**', '*****@*****.**'])
コード例 #37
0
ファイル: importer.py プロジェクト: khushboo9293/mailman
def import_config_pck(mlist, config_dict):
    """Apply a config.pck configuration dictionary to a mailing list.

    :param mlist: The mailing list.
    :type mlist: IMailingList
    :param config_dict: The Mailman 2.1 configuration dictionary.
    :type config_dict: dict
    """
    for key, value in config_dict.items():
        # Some attributes must not be directly imported.
        if key in EXCLUDES:
            continue
        # These objects need explicit type conversions.
        if key in DATETIME_COLUMNS:
            continue
        # Some attributes from Mailman 2 were renamed in Mailman 3.
        key = NAME_MAPPINGS.get(key, key)
        # Handle the simple case where the key is an attribute of the
        # IMailingList and the types are the same (modulo 8-bit/unicode
        # strings).
        #
        # If the mailing list has a preferred language that isn't registered
        # in the configuration file, hasattr() will swallow the KeyError this
        # raises and return False.  Treat that attribute specially.
        if key == "preferred_language" or hasattr(mlist, key):
            if isinstance(value, bytes):
                value = bytes_to_str(value)
            # Some types require conversion.
            converter = TYPES.get(key)
            if converter is None:
                column = getattr(mlist.__class__, key, None)
                if column is not None and isinstance(column.type, Boolean):
                    converter = bool
            try:
                if converter is not None:
                    value = converter(value)
                setattr(mlist, key, value)
            except (TypeError, KeyError):
                print('Type conversion error for key "{}": {}'.format(key, value), file=sys.stderr)
    for key in DATETIME_COLUMNS:
        try:
            value = datetime.datetime.utcfromtimestamp(config_dict[key])
        except KeyError:
            continue
        if key == "last_post_time":
            setattr(mlist, "last_post_at", value)
            continue
        setattr(mlist, key, value)
    # Handle the moderation policy.
    #
    # The mlist.default_member_action and mlist.default_nonmember_action enum
    # values are different in Mailman 2.1, because they have been merged into a
    # single enum in Mailman 3.
    #
    # Unmoderated lists used to have default_member_moderation set to a false
    # value; this translates to the Defer default action.  Moderated lists with
    # the default_member_moderation set to a true value used to store the
    # action in the member_moderation_action flag, the values were: 0==Hold,
    # 1=Reject, 2==Discard
    if bool(config_dict.get("default_member_moderation", 0)):
        mlist.default_member_action = member_moderation_action_mapping(config_dict.get("member_moderation_action"))
    else:
        mlist.default_member_action = Action.defer
    # Handle the archiving policy.  In MM2.1 there were two boolean options
    # but only three of the four possible states were valid.  Now there's just
    # an enum.
    if config_dict.get("archive"):
        # For maximum safety, if for some strange reason there's no
        # archive_private key, treat the list as having private archives.
        if config_dict.get("archive_private", True):
            mlist.archive_policy = ArchivePolicy.private
        else:
            mlist.archive_policy = ArchivePolicy.public
    else:
        mlist.archive_policy = ArchivePolicy.never
    # Handle ban list.
    ban_manager = IBanManager(mlist)
    for address in config_dict.get("ban_list", []):
        ban_manager.ban(bytes_to_str(address))
    # Handle acceptable aliases.
    acceptable_aliases = config_dict.get("acceptable_aliases", "")
    if isinstance(acceptable_aliases, bytes):
        acceptable_aliases = acceptable_aliases.decode("utf-8")
    if isinstance(acceptable_aliases, str):
        acceptable_aliases = acceptable_aliases.splitlines()
    alias_set = IAcceptableAliasSet(mlist)
    for address in acceptable_aliases:
        address = address.strip()
        if len(address) == 0:
            continue
        address = bytes_to_str(address)
        try:
            alias_set.add(address)
        except ValueError:
            # When .add() rejects this, the line probably contains a regular
            # expression.  Make that explicit for MM3.
            alias_set.add("^" + address)
    # Handle conversion to URIs.  In MM2.1, the decorations are strings
    # containing placeholders, and there's no provision for language-specific
    # templates.  In MM3, template locations are specified by URLs with the
    # special `mailman:` scheme indicating a file system path.  What we do
    # here is look to see if the list's decoration is different than the
    # default, and if so, we'll write the new decoration template to a
    # `mailman:` scheme path.
    convert_to_uri = {
        "welcome_msg": "welcome_message_uri",
        "goodbye_msg": "goodbye_message_uri",
        "msg_header": "header_uri",
        "msg_footer": "footer_uri",
        "digest_header": "digest_header_uri",
        "digest_footer": "digest_footer_uri",
    }
    # The best we can do is convert only the most common ones.  These are
    # order dependent; the longer substitution with the common prefix must
    # show up earlier.
    convert_placeholders = [
        ("%(real_name)s@%(host_name)s", "$fqdn_listname"),
        ("%(real_name)s", "$display_name"),
        ("%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s", "$listinfo_uri"),
    ]
    # Collect defaults.
    defaults = {}
    for oldvar, newvar in convert_to_uri.items():
        default_value = getattr(mlist, newvar, None)
        if not default_value:
            continue
        # Check if the value changed from the default.
        try:
            default_text = decorate(mlist, default_value)
        except (URLError, KeyError):
            # Use case: importing the old [email protected] into [email protected].  We can't
            # check if it changed from the default so don't import, we may do
            # more harm than good and it's easy to change if needed.
            # TESTME
            print(
                "Unable to convert mailing list attribute:",
                oldvar,
                'with old value "{}"'.format(default_value),
                file=sys.stderr,
            )
            continue
        defaults[newvar] = (default_value, default_text)
    for oldvar, newvar in convert_to_uri.items():
        if oldvar not in config_dict:
            continue
        text = config_dict[oldvar]
        if isinstance(text, bytes):
            text = text.decode("utf-8", "replace")
        for oldph, newph in convert_placeholders:
            text = text.replace(oldph, newph)
        default_value, default_text = defaults.get(newvar, (None, None))
        if not text and not (default_value or default_text):
            # Both are empty, leave it.
            continue
        # Check if the value changed from the default
        try:
            expanded_text = decorate_template(mlist, text)
        except KeyError:
            # Use case: importing the old [email protected] into [email protected]
            # We can't check if it changed from the default
            # -> don't import, we may do more harm than good and it's easy to
            # change if needed
            # TESTME
            print("Unable to convert mailing list attribute:", oldvar, 'with value "{}"'.format(text), file=sys.stderr)
            continue
        if expanded_text and default_text and expanded_text.strip() == default_text.strip():
            # Keep the default.
            continue
        # Write the custom value to the right file.
        base_uri = "mailman:///$listname/$language/"
        if default_value:
            filename = default_value.rpartition("/")[2]
        else:
            filename = "{}.txt".format(newvar[:-4])
        if not default_value or not default_value.startswith(base_uri):
            setattr(mlist, newvar, base_uri + filename)
        filepath = list(search(filename, mlist))[0]
        makedirs(os.path.dirname(filepath))
        with codecs.open(filepath, "w", encoding="utf-8") as fp:
            fp.write(text)
    # Import rosters.
    regulars_set = set(config_dict.get("members", {}))
    digesters_set = set(config_dict.get("digest_members", {}))
    members = regulars_set.union(digesters_set)
    # Don't send welcome messages when we import the rosters.
    send_welcome_message = mlist.send_welcome_message
    mlist.send_welcome_message = False
    try:
        import_roster(mlist, config_dict, members, MemberRole.member)
        import_roster(mlist, config_dict, config_dict.get("owner", []), MemberRole.owner)
        import_roster(mlist, config_dict, config_dict.get("moderator", []), MemberRole.moderator)
        # Now import the '*_these_nonmembers' properties, filtering out the
        # regexps which will remain in the property.
        for action_name in ("accept", "hold", "reject", "discard"):
            prop_name = "{}_these_nonmembers".format(action_name)
            emails = [addr for addr in config_dict.get(prop_name, []) if not addr.startswith("^")]
            import_roster(mlist, config_dict, emails, MemberRole.nonmember, Action[action_name])
            # Only keep the regexes in the legacy list property.
            list_prop = getattr(mlist, prop_name)
            for email in emails:
                list_prop.remove(email)
    finally:
        mlist.send_welcome_message = send_welcome_message
コード例 #38
0
ファイル: test_bans.py プロジェクト: aswinpj/Mailman
 def setUp(self):
     self._mlist = create_list('*****@*****.**')
     self._manager = IBanManager(self._mlist)
コード例 #39
0
ファイル: importer.py プロジェクト: adam-iris/mailman
def import_config_pck(mlist, config_dict):
    """Apply a config.pck configuration dictionary to a mailing list.

    :param mlist: The mailing list.
    :type mlist: IMailingList
    :param config_dict: The Mailman 2.1 configuration dictionary.
    :type config_dict: dict
    """
    for key, value in config_dict.items():
        # Some attributes must not be directly imported.
        if key in EXCLUDES:
            continue
        # These objects need explicit type conversions.
        if key in DATETIME_COLUMNS:
            continue
        # Some attributes from Mailman 2 were renamed in Mailman 3.
        key = NAME_MAPPINGS.get(key, key)
        # Handle the simple case where the key is an attribute of the
        # IMailingList and the types are the same (modulo 8-bit/unicode
        # strings).
        #
        # If the mailing list has a preferred language that isn't registered
        # in the configuration file, hasattr() will swallow the KeyError this
        # raises and return False.  Treat that attribute specially.
        if hasattr(mlist, key) or key == 'preferred_language':
            if isinstance(value, str):
                value = str_to_unicode(value)
            # Some types require conversion.
            converter = TYPES.get(key)
            try:
                if converter is not None:
                    value = converter(value)
                setattr(mlist, key, value)
            except (TypeError, KeyError):
                print('Type conversion error for key "{}": {}'.format(
                    key, value), file=sys.stderr)
    for key in DATETIME_COLUMNS:
        try:
            value = datetime.datetime.utcfromtimestamp(config_dict[key])
        except KeyError:
            continue
        if key == 'last_post_time':
            setattr(mlist, 'last_post_at', value)
            continue
        setattr(mlist, key, value)
    # Handle the archiving policy.  In MM2.1 there were two boolean options
    # but only three of the four possible states were valid.  Now there's just
    # an enum.
    if config_dict.get('archive'):
        # For maximum safety, if for some strange reason there's no
        # archive_private key, treat the list as having private archives.
        if config_dict.get('archive_private', True):
            mlist.archive_policy = ArchivePolicy.private
        else:
            mlist.archive_policy = ArchivePolicy.public
    else:
        mlist.archive_policy = ArchivePolicy.never
    # Handle ban list.
    ban_manager = IBanManager(mlist)
    for address in config_dict.get('ban_list', []):
        ban_manager.ban(str_to_unicode(address))
    # Handle acceptable aliases.
    acceptable_aliases = config_dict.get('acceptable_aliases', '')
    if isinstance(acceptable_aliases, basestring):
        acceptable_aliases = acceptable_aliases.splitlines()
    alias_set = IAcceptableAliasSet(mlist)
    for address in acceptable_aliases:
        address = address.strip()
        if len(address) == 0:
            continue
        address = str_to_unicode(address)
        try:
            alias_set.add(address)
        except ValueError:
            # When .add() rejects this, the line probably contains a regular
            # expression.  Make that explicit for MM3.
            alias_set.add('^' + address)
    # Handle conversion to URIs.  In MM2.1, the decorations are strings
    # containing placeholders, and there's no provision for language-specific
    # templates.  In MM3, template locations are specified by URLs with the
    # special `mailman:` scheme indicating a file system path.  What we do
    # here is look to see if the list's decoration is different than the
    # default, and if so, we'll write the new decoration template to a
    # `mailman:` scheme path.
    convert_to_uri = {
        'welcome_msg': 'welcome_message_uri',
        'goodbye_msg': 'goodbye_message_uri',
        'msg_header': 'header_uri',
        'msg_footer': 'footer_uri',
        'digest_header': 'digest_header_uri',
        'digest_footer': 'digest_footer_uri',
        }
    # The best we can do is convert only the most common ones.  These are
    # order dependent; the longer substitution with the common prefix must
    # show up earlier.
    convert_placeholders = [
        ('%(real_name)s@%(host_name)s', '$fqdn_listname'),
        ('%(real_name)s', '$display_name'),
        ('%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s',
         '$listinfo_uri'),
        ]
    # Collect defaults.
    defaults = {}
    for oldvar, newvar in convert_to_uri.items():
        default_value = getattr(mlist, newvar, None)
        if not default_value:
            continue
        # Check if the value changed from the default.
        try:
            default_text = decorate(mlist, default_value)
        except (URLError, KeyError):
            # Use case: importing the old [email protected] into [email protected].  We can't
            # check if it changed from the default so don't import, we may do
            # more harm than good and it's easy to change if needed.
            # TESTME
            print('Unable to convert mailing list attribute:', oldvar,
                  'with old value "{}"'.format(default_value),
                  file=sys.stderr)
            continue
        defaults[newvar] = (default_value, default_text)
    for oldvar, newvar in convert_to_uri.items():
        if oldvar not in config_dict:
            continue
        text = config_dict[oldvar]
        text = text.decode('utf-8', 'replace')
        for oldph, newph in convert_placeholders:
            text = text.replace(oldph, newph)
        default_value, default_text  = defaults.get(newvar, (None, None))
        if not text and not (default_value or default_text):
            # Both are empty, leave it.
            continue
        # Check if the value changed from the default
        try:
            expanded_text = decorate_template(mlist, text)
        except KeyError:
            # Use case: importing the old [email protected] into [email protected]
            # We can't check if it changed from the default
            # -> don't import, we may do more harm than good and it's easy to
            # change if needed
            # TESTME
            print('Unable to convert mailing list attribute:', oldvar,
                  'with value "{}"'.format(text),
                  file=sys.stderr)
            continue
        if (expanded_text and default_text
                and expanded_text.strip() == default_text.strip()):
            # Keep the default.
            continue
        # Write the custom value to the right file.
        base_uri = 'mailman:///$listname/$language/'
        if default_value:
            filename = default_value.rpartition('/')[2]
        else:
            filename = '{}.txt'.format(newvar[:-4])
        if not default_value or not default_value.startswith(base_uri):
            setattr(mlist, newvar, base_uri + filename)
        filepath = list(search(filename, mlist))[0]
        makedirs(os.path.dirname(filepath))
        with codecs.open(filepath, 'w', encoding='utf-8') as fp:
            fp.write(text)
    # Import rosters.
    members = set(config_dict.get('members', {}).keys()
                + config_dict.get('digest_members', {}).keys())
    import_roster(mlist, config_dict, members, MemberRole.member)
    import_roster(mlist, config_dict, config_dict.get('owner', []),
                  MemberRole.owner)
    import_roster(mlist, config_dict, config_dict.get('moderator', []),
                  MemberRole.moderator)