34
34
MemberRole, MembershipIsBannedError, MissingPreferredAddressError)
35
35
from mailman.interfaces.pending import IPendings
36
36
from mailman.interfaces.subscriptions import (
37
MissingUserError, ISubscriptionService)
37
ISubscriptionService, MissingUserError, TokenOwner)
38
38
from mailman.testing.helpers import LogFileMark, get_queue_messages
39
39
from mailman.testing.layers import ConfigLayer
40
40
from mailman.interfaces.mailinglist import SubscriptionPolicy
48
class TestJoin(unittest.TestCase):
52
self._mlist = create_list('test@example.com')
53
self._service = getUtility(ISubscriptionService)
55
def test_join_user_with_bogus_id(self):
56
# When `subscriber` is a missing user id, an exception is raised.
57
with self.assertRaises(MissingUserError) as cm:
58
self._service.join('test.example.com', uuid.UUID(int=99))
59
self.assertEqual(cm.exception.user_id, uuid.UUID(int=99))
61
def test_join_user_with_invalid_email_address(self):
62
# When `subscriber` is a string that is not an email address, an
63
# exception is raised.
64
with self.assertRaises(InvalidEmailAddressError) as cm:
65
self._service.join('test.example.com', 'bogus')
66
self.assertEqual(cm.exception.email, 'bogus')
68
def test_missing_preferred_address(self):
69
# A user cannot join a mailing list if they have no preferred address.
70
anne = self._service.join(
71
'test.example.com', 'anne@example.com', 'Anne Person')
72
# Try to join Anne as a user with a different role. Her user has no
73
# preferred address, so this will fail.
74
self.assertRaises(MissingPreferredAddressError,
76
'test.example.com', anne.user.user_id,
77
role=MemberRole.owner)
81
48
class TestSubscriptionWorkflow(unittest.TestCase):
82
49
layer = ConfigLayer
88
55
self._anne = 'anne@example.com'
89
56
self._user_manager = getUtility(IUserManager)
58
def test_start_state(self):
59
# The workflow starts with no tokens or member.
60
workflow = SubscriptionWorkflow(self._mlist)
61
self.assertIsNone(workflow.token)
62
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
63
self.assertIsNone(workflow.member)
91
65
def test_user_or_address_required(self):
92
66
# The `subscriber` attribute must be a user or address.
93
67
workflow = SubscriptionWorkflow(self._mlist)
230
204
def test_confirmation_checks_confirm_pre_confirmed(self):
231
205
# The subscription policy requires user confirmation, but their
232
# subscription is pre-confirmed.
206
# subscription is pre-confirmed. Since moderation is not required,
207
# the user will be immediately subscribed.
233
208
self._mlist.subscription_policy = SubscriptionPolicy.confirm
234
209
anne = self._user_manager.create_address(self._anne)
235
210
workflow = SubscriptionWorkflow(self._mlist, anne,
236
211
pre_verified=True,
237
212
pre_confirmed=True)
238
213
workflow.run_thru('confirmation_checks')
214
with patch.object(workflow, '_step_do_subscription') as step:
216
step.assert_called_once_with()
218
def test_confirmation_checks_confirm_then_moderate_pre_confirmed(self):
219
# The subscription policy requires user confirmation, but their
220
# subscription is pre-confirmed. Since moderation is required, that
221
# check will be performed.
222
self._mlist.subscription_policy = \
223
SubscriptionPolicy.confirm_then_moderate
224
anne = self._user_manager.create_address(self._anne)
225
workflow = SubscriptionWorkflow(self._mlist, anne,
228
workflow.run_thru('confirmation_checks')
239
229
with patch.object(workflow, '_step_moderation_checks') as step:
241
231
step.assert_called_once_with()
311
301
# Anne is now a member of the mailing list.
312
302
member = self._mlist.regular_members.get_member(self._anne)
313
303
self.assertEqual(member.address, anne)
304
self.assertEqual(workflow.member, member)
305
# No further token is needed.
306
self.assertIsNone(workflow.token)
307
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
315
309
def test_do_subscription_pre_approved(self):
316
310
# An moderation-requiring subscription policy plus a pre-verified and
326
320
# Anne is now a member of the mailing list.
327
321
member = self._mlist.regular_members.get_member(self._anne)
328
322
self.assertEqual(member.address, anne)
323
self.assertEqual(workflow.member, member)
324
# No further token is needed.
325
self.assertIsNone(workflow.token)
326
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
330
328
def test_do_subscription_pre_approved_pre_confirmed(self):
331
329
# An moderation-requiring subscription policy plus a pre-verified and
343
341
# Anne is now a member of the mailing list.
344
342
member = self._mlist.regular_members.get_member(self._anne)
345
343
self.assertEqual(member.address, anne)
344
self.assertEqual(workflow.member, member)
345
# No further token is needed.
346
self.assertIsNone(workflow.token)
347
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
347
349
def test_do_subscription_cleanups(self):
348
350
# Once the user is subscribed, the token, and its associated pending
360
362
# Anne is now a member of the mailing list.
361
363
member = self._mlist.regular_members.get_member(self._anne)
362
364
self.assertEqual(member.address, anne)
365
self.assertEqual(workflow.member, member)
363
366
# The workflow is done, so it has no token.
364
367
self.assertIsNone(workflow.token)
368
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
365
369
# The pendable associated with the token has been evicted.
366
370
self.assertIsNone(getUtility(IPendings).confirm(token, expunge=False))
367
371
# There is no saved workflow associated with the token. This shows up
384
388
# The user is not currently subscribed to the mailing list.
385
389
member = self._mlist.regular_members.get_member(self._anne)
386
390
self.assertIsNone(member)
391
self.assertIsNone(workflow.member)
392
# The token is owned by the moderator.
393
self.assertIsNotNone(workflow.token)
394
self.assertEqual(workflow.token_owner, TokenOwner.moderator)
387
395
# Create a new workflow with the previous workflow's save token, and
388
396
# restore its state. This models an approved subscription and should
389
397
# result in the user getting subscribed.
394
402
# Now the user is subscribed to the mailing list.
395
403
member = self._mlist.regular_members.get_member(self._anne)
396
404
self.assertEqual(member.address, anne)
405
self.assertEqual(approved_workflow.member, member)
406
# No further token is needed.
407
self.assertIsNone(approved_workflow.token)
408
self.assertEqual(approved_workflow.token_owner, TokenOwner.no_one)
398
410
def test_get_moderator_approval_log_on_hold(self):
399
411
# When the subscription is held for moderator approval, a message is
529
541
self.assertIsNone(anne.verified_on)
530
542
workflow = SubscriptionWorkflow(self._mlist, anne)
532
self.assertIsNone(self._mlist.regular_members.get_member(self._anne))
544
# Anne is not yet a member.
545
member = self._mlist.regular_members.get_member(self._anne)
546
self.assertIsNone(member)
547
self.assertIsNone(workflow.member)
548
# The token is owned by the subscriber.
549
self.assertIsNotNone(workflow.token)
550
self.assertEqual(workflow.token_owner, TokenOwner.subscriber)
533
552
confirm_workflow = SubscriptionWorkflow(self._mlist)
534
553
confirm_workflow.token = workflow.token
535
554
confirm_workflow.restore()
536
555
list(confirm_workflow)
537
556
self.assertIsNotNone(anne.verified_on)
539
self._mlist.regular_members.get_member(self._anne).address, anne)
557
# Anne is now a member.
558
member = self._mlist.regular_members.get_member(self._anne)
559
self.assertEqual(member.address, anne)
560
self.assertEqual(confirm_workflow.member, member)
561
# No further token is needed.
562
self.assertIsNone(confirm_workflow.token)
563
self.assertEqual(confirm_workflow.token_owner, TokenOwner.no_one)
541
565
def test_prevent_confirmation_replay_attacks(self):
542
566
# Ensure that if the workflow requires two confirmations, e.g. first
553
577
# Anne is not yet a member of the mailing list.
554
578
member = self._mlist.regular_members.get_member(self._anne)
555
579
self.assertIsNone(member)
580
self.assertIsNone(workflow.member)
581
# The token is owned by the subscriber.
582
self.assertIsNotNone(workflow.token)
583
self.assertEqual(workflow.token_owner, TokenOwner.subscriber)
556
584
# The old token will not work for moderator approval.
557
585
moderator_workflow = SubscriptionWorkflow(self._mlist)
558
586
moderator_workflow.token = token
559
587
moderator_workflow.restore()
560
588
list(moderator_workflow)
589
# The token is owned by the moderator.
590
self.assertIsNotNone(moderator_workflow.token)
591
self.assertEqual(moderator_workflow.token_owner, TokenOwner.moderator)
561
592
# While we wait for the moderator to approve the subscription, note
562
593
# that there's a new token for the next steps.
563
594
self.assertNotEqual(token, moderator_workflow.token)
570
601
# Anne is still not subscribed.
571
602
member = self._mlist.regular_members.get_member(self._anne)
572
603
self.assertIsNone(member)
604
self.assertIsNone(final_workflow.member)
573
605
# However, if we use the new token, her subscription request will be
574
606
# approved by the moderator.
575
607
final_workflow.token = moderator_workflow.token
578
610
# And now Anne is a member.
579
611
member = self._mlist.regular_members.get_member(self._anne)
580
612
self.assertEqual(member.address.email, self._anne)
613
self.assertEqual(final_workflow.member, member)
614
# No further token is needed.
615
self.assertIsNone(final_workflow.token)
616
self.assertEqual(final_workflow.token_owner, TokenOwner.no_one)
618
def test_confirmation_needed_and_pre_confirmed(self):
619
# The subscription policy is 'confirm' but the subscription is
620
# pre-confirmed so the moderation checks can be skipped.
621
self._mlist.subscription_policy = SubscriptionPolicy.confirm
622
anne = self._user_manager.create_address(self._anne)
623
workflow = SubscriptionWorkflow(
625
pre_verified=True, pre_confirmed=True, pre_approved=True)
627
# Anne was subscribed.
628
self.assertIsNone(workflow.token)
629
self.assertEqual(workflow.token_owner, TokenOwner.no_one)
630
self.assertEqual(workflow.member.address, anne)