~barry/mailman/subpolicy-2

« back to all changes in this revision

Viewing changes to src/mailman/app/registrar.py

  • Committer: Barry Warsaw
  • Date: 2015-04-14 16:46:11 UTC
  • Revision ID: barry@list.org-20150414164611-z3rku1j30uf0djzt
Checkpointing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
26
26
import logging
27
27
 
 
28
from mailman.app.subscriptions import SubscriptionWorkflow
28
29
from mailman.core.i18n import _
29
30
from mailman.email.message import UserNotification
30
 
from mailman.interfaces.address import IEmailValidator
31
 
from mailman.interfaces.listmanager import IListManager
32
 
from mailman.interfaces.member import DeliveryMode, MemberRole
33
31
from mailman.interfaces.pending import IPendable, IPendings
34
32
from mailman.interfaces.registrar import ConfirmationNeededEvent, IRegistrar
35
33
from mailman.interfaces.templates import ITemplateLoader
36
 
from mailman.interfaces.usermanager import IUserManager
37
 
from mailman.utilities.datetime import now
38
34
from zope.component import getUtility
39
 
from zope.event import notify
40
35
from zope.interface import implementer
41
36
 
42
37
 
54
49
class Registrar:
55
50
    """Handle registrations and confirmations for subscriptions."""
56
51
 
57
 
    def register(self, mlist, email, display_name=None, delivery_mode=None):
 
52
    def __init__(self, mlist):
 
53
        self._mlist = mlist
 
54
 
 
55
    def register(self, subscriber=None, *,
 
56
                 pre_verified=False, pre_confirmed=False, pre_approved=False):
58
57
        """See `IRegistrar`."""
59
 
        if delivery_mode is None:
60
 
            delivery_mode = DeliveryMode.regular
61
 
        # First, do validation on the email address.  If the address is
62
 
        # invalid, it will raise an exception, otherwise it just returns.
63
 
        getUtility(IEmailValidator).validate(email)
64
 
        # Create a pendable for the registration.
65
 
        pendable = PendableRegistration(
66
 
            type=PendableRegistration.PEND_KEY,
67
 
            email=email,
68
 
            display_name=display_name,
69
 
            delivery_mode=delivery_mode.name,
70
 
            list_id=mlist.list_id)
71
 
        token = getUtility(IPendings).add(pendable)
72
 
        # We now have everything we need to begin the confirmation dance.
73
 
        # Trigger the event to start the ball rolling, and return the
74
 
        # generated token.
75
 
        notify(ConfirmationNeededEvent(mlist, pendable, token))
76
 
        return token
 
58
        workflow = SubscriptionWorkflow(
 
59
            self._mlist, subscriber,
 
60
            pre_verified=pre_verified,
 
61
            pre_confirmed=pre_confirmed,
 
62
            pre_approved=pre_approved)
 
63
        list(workflow)
 
64
        return workflow.token
77
65
 
78
66
    def confirm(self, token):
79
67
        """See `IRegistrar`."""
80
 
        # For convenience
81
 
        pendable = getUtility(IPendings).confirm(token)
82
 
        if pendable is None:
83
 
            return False
84
 
        missing = object()
85
 
        email = pendable.get('email', missing)
86
 
        display_name = pendable.get('display_name', missing)
87
 
        pended_delivery_mode = pendable.get('delivery_mode', 'regular')
88
 
        try:
89
 
            delivery_mode = DeliveryMode[pended_delivery_mode]
90
 
        except ValueError:
91
 
            log.error('Invalid pended delivery_mode for {0}: {1}',
92
 
                      email, pended_delivery_mode)
93
 
            delivery_mode = DeliveryMode.regular
94
 
        if pendable.get('type') != PendableRegistration.PEND_KEY:
95
 
            # It seems like it would be very difficult to accurately guess
96
 
            # tokens, or brute force an attack on the SHA1 hash, so we'll just
97
 
            # throw the pendable away in that case.  It's possible we'll need
98
 
            # to repend the event or adjust the API to handle this case
99
 
            # better, but for now, the simpler the better.
100
 
            return False
101
 
        # We are going to end up with an IAddress for the verified address
102
 
        # and an IUser linked to this IAddress.  See if any of these objects
103
 
        # currently exist in our database.
104
 
        user_manager = getUtility(IUserManager)
105
 
        address = (user_manager.get_address(email)
106
 
                   if email is not missing else None)
107
 
        user = (user_manager.get_user(email)
108
 
                if email is not missing else None)
109
 
        # If there is neither an address nor a user matching the confirmed
110
 
        # record, then create the user, which will in turn create the address
111
 
        # and link the two together
112
 
        if address is None:
113
 
            assert user is None, 'How did we get a user but not an address?'
114
 
            user = user_manager.create_user(email, display_name)
115
 
            # Because the database changes haven't been flushed, we can't use
116
 
            # IUserManager.get_address() to find the IAddress just created
117
 
            # under the hood.  Instead, iterate through the IUser's addresses,
118
 
            # of which really there should be only one.
119
 
            for address in user.addresses:
120
 
                if address.email == email:
121
 
                    break
122
 
            else:
123
 
                raise AssertionError('Could not find expected IAddress')
124
 
        elif user is None:
125
 
            user = user_manager.create_user()
126
 
            user.display_name = display_name
127
 
            user.link(address)
128
 
        else:
129
 
            # The IAddress and linked IUser already exist, so all we need to
130
 
            # do is verify the address.
131
 
            pass
132
 
        address.verified_on = now()
133
 
        # If this registration is tied to a mailing list, subscribe the person
134
 
        # to the list right now.  That will generate a SubscriptionEvent,
135
 
        # which can be used to send a welcome message.
136
 
        list_id = pendable.get('list_id')
137
 
        if list_id is not None:
138
 
            mlist = getUtility(IListManager).get_by_list_id(list_id)
139
 
            if mlist is not None:
140
 
                member = mlist.subscribe(address, MemberRole.member)
141
 
                member.preferences.delivery_mode = delivery_mode
142
 
        return True
 
68
        workflow = SubscriptionWorkflow(self._mlist)
 
69
        workflow.token = token
 
70
        workflow.debug = True
 
71
        workflow.restore()
 
72
        list(workflow)
143
73
 
144
74
    def discard(self, token):
145
75
        # Throw the record away.