def test_prevent_confirmation_replay_attacks(self): # Ensure that if the workflow requires two confirmations, e.g. first # the user confirming their subscription, and then the moderator # approving it, that different tokens are used in these two cases. self._mlist.subscription_policy = \ SubscriptionPolicy.confirm_then_moderate anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True) # Run the state machine up to the first confirmation, and cache the # confirmation token. list(workflow) token = workflow.token # Anne is not yet a member of the mailing list. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(workflow.member) # The token is owned by the subscriber. self.assertIsNotNone(workflow.token) self.assertEqual(workflow.token_owner, TokenOwner.subscriber) # The old token will not work for moderator approval. moderator_workflow = SubscriptionWorkflow(self._mlist) moderator_workflow.token = token moderator_workflow.restore() list(moderator_workflow) # The token is owned by the moderator. self.assertIsNotNone(moderator_workflow.token) self.assertEqual(moderator_workflow.token_owner, TokenOwner.moderator) # While we wait for the moderator to approve the subscription, note # that there's a new token for the next steps. self.assertNotEqual(token, moderator_workflow.token) # The old token won't work. final_workflow = SubscriptionWorkflow(self._mlist) final_workflow.token = token self.assertRaises(LookupError, final_workflow.restore) # Running this workflow will fail. self.assertRaises(AssertionError, list, final_workflow) # Anne is still not subscribed. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(final_workflow.member) # However, if we use the new token, her subscription request will be # approved by the moderator. final_workflow.token = moderator_workflow.token final_workflow.restore() list(final_workflow) # And now Anne is a member. member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address.email, self._anne) self.assertEqual(final_workflow.member, member) # No further token is needed. self.assertIsNone(final_workflow.token) self.assertEqual(final_workflow.token_owner, TokenOwner.no_one)
def test_prevent_confirmation_replay_attacks(self): # Ensure that if the workflow requires two confirmations, e.g. first # the user confirming their subscription, and then the moderator # approving it, that different tokens are used in these two cases. self._mlist.subscription_policy = ( SubscriptionPolicy.confirm_then_moderate) anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True) # Run the state machine up to the first confirmation, and cache the # confirmation token. list(workflow) token = workflow.token # Anne is not yet a member of the mailing list. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(workflow.member) # The token is owned by the subscriber. self.assertIsNotNone(workflow.token) self.assertEqual(workflow.token_owner, TokenOwner.subscriber) # The old token will not work for moderator approval. moderator_workflow = SubscriptionWorkflow(self._mlist) moderator_workflow.token = token moderator_workflow.restore() list(moderator_workflow) # The token is owned by the moderator. self.assertIsNotNone(moderator_workflow.token) self.assertEqual(moderator_workflow.token_owner, TokenOwner.moderator) # While we wait for the moderator to approve the subscription, note # that there's a new token for the next steps. self.assertNotEqual(token, moderator_workflow.token) # The old token won't work. final_workflow = SubscriptionWorkflow(self._mlist) final_workflow.token = token self.assertRaises(LookupError, final_workflow.restore) # Running this workflow will fail. self.assertRaises(AssertionError, list, final_workflow) # Anne is still not subscribed. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(final_workflow.member) # However, if we use the new token, her subscription request will be # approved by the moderator. final_workflow.token = moderator_workflow.token final_workflow.restore() list(final_workflow) # And now Anne is a member. member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address.email, self._anne) self.assertEqual(final_workflow.member, member) # No further token is needed. self.assertIsNone(final_workflow.token) self.assertEqual(final_workflow.token_owner, TokenOwner.no_one)
def confirm(self, token): """See `IRegistrar`.""" workflow = SubscriptionWorkflow(self._mlist) workflow.token = token workflow.restore() list(workflow) return workflow.token, workflow.token_owner, workflow.member
def test_do_confirmation_subscribes_user(self): # Subscriptions to the mailing list must be confirmed. Once that's # done, the user's address (which is not initially verified) gets # subscribed to the mailing list. self._mlist.subscription_policy = SubscriptionPolicy.confirm anne = self._user_manager.create_address(self._anne) self.assertIsNone(anne.verified_on) workflow = SubscriptionWorkflow(self._mlist, anne) list(workflow) # Anne is not yet a member. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(workflow.member) # The token is owned by the subscriber. self.assertIsNotNone(workflow.token) self.assertEqual(workflow.token_owner, TokenOwner.subscriber) # Confirm. confirm_workflow = SubscriptionWorkflow(self._mlist) confirm_workflow.token = workflow.token confirm_workflow.restore() list(confirm_workflow) self.assertIsNotNone(anne.verified_on) # Anne is now a member. member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address, anne) self.assertEqual(confirm_workflow.member, member) # No further token is needed. self.assertIsNone(confirm_workflow.token) self.assertEqual(confirm_workflow.token_owner, TokenOwner.no_one)
def test_moderator_approves(self): # The workflow runs until moderator approval is required, at which # point the workflow is saved. Once the moderator approves, the # workflow resumes and the user is subscribed. self._mlist.subscription_policy = SubscriptionPolicy.moderate anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True, pre_confirmed=True) # Consume the entire state machine. list(workflow) # The user is not currently subscribed to the mailing list. member = self._mlist.regular_members.get_member(self._anne) self.assertIsNone(member) self.assertIsNone(workflow.member) # The token is owned by the moderator. self.assertIsNotNone(workflow.token) self.assertEqual(workflow.token_owner, TokenOwner.moderator) # Create a new workflow with the previous workflow's save token, and # restore its state. This models an approved subscription and should # result in the user getting subscribed. approved_workflow = SubscriptionWorkflow(self._mlist) approved_workflow.token = workflow.token approved_workflow.restore() list(approved_workflow) # Now the user is subscribed to the mailing list. member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address, anne) self.assertEqual(approved_workflow.member, member) # No further token is needed. self.assertIsNone(approved_workflow.token) self.assertEqual(approved_workflow.token_owner, TokenOwner.no_one)
def test_do_subscription_cleanups(self): # Once the user is subscribed, the token, and its associated pending # database record will be removed from the database. self._mlist.subscription_policy = SubscriptionPolicy.open anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True, pre_confirmed=True, pre_approved=True) # Cache the token. token = workflow.token # Consume the entire state machine. list(workflow) # Anne is now a member of the mailing list. member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address, anne) self.assertEqual(workflow.member, member) # The workflow is done, so it has no token. self.assertIsNone(workflow.token) self.assertEqual(workflow.token_owner, TokenOwner.no_one) # The pendable associated with the token has been evicted. self.assertIsNone(getUtility(IPendings).confirm(token, expunge=False)) # There is no saved workflow associated with the token. This shows up # as an exception when we try to restore the workflow. new_workflow = SubscriptionWorkflow(self._mlist) new_workflow.token = token self.assertRaises(LookupError, new_workflow.restore)
def test_do_confirm_verify_user(self): # A confirmation step is necessary when a user subscribes with their # preferred address, and we are not pre-confirming. anne = self._user_manager.create_user(self._anne) set_preferred(anne) # Run the workflow to model the confirmation step. There is no # subscriber attribute yet. workflow = SubscriptionWorkflow(self._mlist, anne) list(workflow) self.assertEqual(workflow.subscriber, anne) # Do a confirmation workflow, which should now set the subscriber. confirm_workflow = SubscriptionWorkflow(self._mlist) confirm_workflow.token = workflow.token confirm_workflow.restore() confirm_workflow.run_thru('do_confirm_verify') # The address is now verified. self.assertEqual(confirm_workflow.subscriber, anne)
def test_do_confirm_verify_address(self): # The address is not yet verified, nor are we pre-verifying. A # confirmation message will be sent. When the user confirms their # subscription request, the address will end up being verified. anne = self._user_manager.create_address(self._anne) self.assertIsNone(anne.verified_on) # Run the workflow to model the confirmation step. workflow = SubscriptionWorkflow(self._mlist, anne) list(workflow) # The address is still not verified. self.assertIsNone(anne.verified_on) confirm_workflow = SubscriptionWorkflow(self._mlist) confirm_workflow.token = workflow.token confirm_workflow.restore() confirm_workflow.run_thru('do_confirm_verify') # The address is now verified. self.assertIsNotNone(anne.verified_on)
def test_restore_user_absorbed(self): # The subscribing user is absorbed (and thus deleted) before the # moderator approves the subscription. self._mlist.subscription_policy = SubscriptionPolicy.moderate anne = self._user_manager.create_user(self._anne) bill = self._user_manager.create_user('*****@*****.**') set_preferred(bill) # anne subscribes. workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True) list(workflow) # bill absorbs anne. bill.absorb(anne) # anne's subscription request is approved. approved_workflow = SubscriptionWorkflow(self._mlist) approved_workflow.token = workflow.token approved_workflow.restore() self.assertEqual(approved_workflow.user, bill) # Run the workflow through. list(approved_workflow)