Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ 674f9a52

History | View | Annotate | Download (9.9 kB)

1 0905ccd2 Sofia Papagiannaki
# Copyright 2011 GRNET S.A. All rights reserved.
2 0905ccd2 Sofia Papagiannaki
#
3 0905ccd2 Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 0905ccd2 Sofia Papagiannaki
# without modification, are permitted provided that the following
5 0905ccd2 Sofia Papagiannaki
# conditions are met:
6 0905ccd2 Sofia Papagiannaki
#
7 0905ccd2 Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 0905ccd2 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 0905ccd2 Sofia Papagiannaki
#      disclaimer.
10 0905ccd2 Sofia Papagiannaki
#
11 0905ccd2 Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 0905ccd2 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 0905ccd2 Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 0905ccd2 Sofia Papagiannaki
#      provided with the distribution.
15 0905ccd2 Sofia Papagiannaki
#
16 0905ccd2 Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 0905ccd2 Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 0905ccd2 Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 0905ccd2 Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 0905ccd2 Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 0905ccd2 Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 0905ccd2 Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 0905ccd2 Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 0905ccd2 Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 0905ccd2 Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 0905ccd2 Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 0905ccd2 Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 0905ccd2 Sofia Papagiannaki
#
29 0905ccd2 Sofia Papagiannaki
# The views and conclusions contained in the software and
30 0905ccd2 Sofia Papagiannaki
# documentation are those of the authors and should not be
31 0905ccd2 Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 0905ccd2 Sofia Papagiannaki
# or implied, of GRNET S.A.
33 0905ccd2 Sofia Papagiannaki
34 0905ccd2 Sofia Papagiannaki
from django.utils.importlib import import_module
35 890b0eaf Sofia Papagiannaki
from django.core.exceptions import ImproperlyConfigured
36 18ffbee1 Sofia Papagiannaki
from django.utils.translation import ugettext as _
37 890b0eaf Sofia Papagiannaki
38 aab4d540 Sofia Papagiannaki
from astakos.im.models import AstakosUser
39 705a9ae0 Sofia Papagiannaki
from astakos.im.forms import LocalUserCreationForm, ShibbolethUserCreationForm
40 15efc749 Sofia Papagiannaki
from astakos.im.util import get_invitation
41 9a06d96f Olga Brani
from astakos.im.functions import (send_verification, send_activation,
42 9a06d96f Olga Brani
                                  send_account_creation_notification,
43 9a06d96f Olga Brani
                                  send_group_creation_notification, activate)
44 674f9a52 Sofia Papagiannaki
from astakos.im.settings import (INVITATIONS_ENABLED, MODERATION_ENABLED,
45 674f9a52 Sofia Papagiannaki
    SITENAME, RE_USER_EMAIL_PATTERNS
46 674f9a52 Sofia Papagiannaki
)
47 674f9a52 Sofia Papagiannaki
from astakos.im.messages import as astakos_messages
48 0905ccd2 Sofia Papagiannaki
49 5ed6816e Sofia Papagiannaki
import logging
50 8316698a Sofia Papagiannaki
import re
51 5ed6816e Sofia Papagiannaki
52 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
53 e015e9e6 Sofia Papagiannaki
54 5ce3ce4f Sofia Papagiannaki
55 5ed6816e Sofia Papagiannaki
def get_backend(request):
56 0905ccd2 Sofia Papagiannaki
    """
57 18ffbee1 Sofia Papagiannaki
    Returns an instance of an activation backend,
58 890b0eaf Sofia Papagiannaki
    according to the INVITATIONS_ENABLED setting
59 8f5a3a06 Sofia Papagiannaki
    (if True returns ``astakos.im.activation_backends.InvitationsBackend`` and if False
60 8f5a3a06 Sofia Papagiannaki
    returns ``astakos.im.activation_backends.SimpleBackend``).
61 bef3bf46 Kostas Papadimitriou

62 890b0eaf Sofia Papagiannaki
    If the backend cannot be located ``django.core.exceptions.ImproperlyConfigured``
63 890b0eaf Sofia Papagiannaki
    is raised.
64 0905ccd2 Sofia Papagiannaki
    """
65 8f5a3a06 Sofia Papagiannaki
    module = 'astakos.im.activation_backends'
66 92defad4 Sofia Papagiannaki
    prefix = 'Invitations' if INVITATIONS_ENABLED else 'Simple'
67 5ce3ce4f Sofia Papagiannaki
    backend_class_name = '%sBackend' % prefix
68 0905ccd2 Sofia Papagiannaki
    try:
69 0905ccd2 Sofia Papagiannaki
        mod = import_module(module)
70 0905ccd2 Sofia Papagiannaki
    except ImportError, e:
71 5ce3ce4f Sofia Papagiannaki
        raise ImproperlyConfigured(
72 5ce3ce4f Sofia Papagiannaki
            'Error loading activation backend %s: "%s"' % (module, e))
73 0905ccd2 Sofia Papagiannaki
    try:
74 0905ccd2 Sofia Papagiannaki
        backend_class = getattr(mod, backend_class_name)
75 0905ccd2 Sofia Papagiannaki
    except AttributeError:
76 aab4d540 Sofia Papagiannaki
        raise ImproperlyConfigured('Module "%s" does not define a activation backend named "%s"' % (module, backend_class_name))
77 5ed6816e Sofia Papagiannaki
    return backend_class(request)
78 890b0eaf Sofia Papagiannaki
79 5ce3ce4f Sofia Papagiannaki
80 0a569195 Sofia Papagiannaki
class ActivationBackend(object):
81 aab4d540 Sofia Papagiannaki
    def __init__(self, request):
82 aab4d540 Sofia Papagiannaki
        self.request = request
83 5ce3ce4f Sofia Papagiannaki
84 8316698a Sofia Papagiannaki
    def _is_preaccepted(self, user):
85 8316698a Sofia Papagiannaki
        # return True if user email matches specific patterns
86 8316698a Sofia Papagiannaki
        for pattern in RE_USER_EMAIL_PATTERNS:
87 8316698a Sofia Papagiannaki
            if re.match(pattern, user.email):
88 8316698a Sofia Papagiannaki
                return True
89 8316698a Sofia Papagiannaki
        return False
90 5ce3ce4f Sofia Papagiannaki
91 0a569195 Sofia Papagiannaki
    def get_signup_form(self, provider='local', instance=None):
92 0a569195 Sofia Papagiannaki
        """
93 b669d9c0 Sofia Papagiannaki
        Returns a form instance of the relevant class
94 0a569195 Sofia Papagiannaki
        """
95 0a569195 Sofia Papagiannaki
        main = provider.capitalize() if provider == 'local' else 'ThirdParty'
96 5ce3ce4f Sofia Papagiannaki
        suffix = 'UserCreationForm'
97 0a569195 Sofia Papagiannaki
        formclass = '%s%s' % (main, suffix)
98 0a569195 Sofia Papagiannaki
        request = self.request
99 0a569195 Sofia Papagiannaki
        initial_data = None
100 0a569195 Sofia Papagiannaki
        if request.method == 'POST':
101 0a569195 Sofia Papagiannaki
            if provider == request.POST.get('provider', ''):
102 0a569195 Sofia Papagiannaki
                initial_data = request.POST
103 0a569195 Sofia Papagiannaki
        return globals()[formclass](initial_data, instance=instance, request=request)
104 5ce3ce4f Sofia Papagiannaki
105 5ce3ce4f Sofia Papagiannaki
    def handle_activation(self, user,
106 5ce3ce4f Sofia Papagiannaki
                          activation_template_name='im/activation_email.txt',
107 5ce3ce4f Sofia Papagiannaki
                          greeting_template_name='im/welcome_email.txt',
108 5ce3ce4f Sofia Papagiannaki
                          admin_email_template_name='im/account_notification.txt',
109 0a569195 Sofia Papagiannaki
                          switch_accounts_email_template_name='im/switch_accounts_email.txt'):
110 0a569195 Sofia Papagiannaki
        """
111 0a569195 Sofia Papagiannaki
        If the user is already active returns immediately.
112 0a569195 Sofia Papagiannaki
        If the user is not active and there is another account associated with
113 0a569195 Sofia Papagiannaki
        the specific email, it sends an informative email to the user whether
114 0a569195 Sofia Papagiannaki
        wants to switch to this account.
115 0a569195 Sofia Papagiannaki
        If the user is preaccepted and the email is verified, the account is
116 0a569195 Sofia Papagiannaki
        activated automatically. Otherwise, if the email is not verified,
117 0a569195 Sofia Papagiannaki
        it sends a verification email to the user.
118 0a569195 Sofia Papagiannaki
        If the user is not preaccepted, it sends an email to the administrators
119 0a569195 Sofia Papagiannaki
        and informs the user that the account is pending activation.
120 0a569195 Sofia Papagiannaki
        """
121 0a569195 Sofia Papagiannaki
        try:
122 0a569195 Sofia Papagiannaki
            if user.is_active:
123 0a569195 Sofia Papagiannaki
                return RegistationCompleted()
124 0a569195 Sofia Papagiannaki
            if user.conflicting_email():
125 0a569195 Sofia Papagiannaki
                send_verification(user, switch_accounts_email_template_name)
126 0a569195 Sofia Papagiannaki
                return SwitchAccountsVerificationSent(user.email)
127 5ce3ce4f Sofia Papagiannaki
128 0a569195 Sofia Papagiannaki
            if self._is_preaccepted(user):
129 0a569195 Sofia Papagiannaki
                if user.email_verified:
130 0a569195 Sofia Papagiannaki
                    activate(user, greeting_template_name)
131 0a569195 Sofia Papagiannaki
                    return RegistationCompleted()
132 0a569195 Sofia Papagiannaki
                else:
133 751d24cf Sofia Papagiannaki
                    send_activation(user, activation_template_name)
134 0a569195 Sofia Papagiannaki
                    return VerificationSent()
135 0a569195 Sofia Papagiannaki
            else:
136 9a06d96f Olga Brani
                send_account_creation_notification(
137 3abf6c78 Sofia Papagiannaki
                    template_name=admin_email_template_name,
138 a3637508 Sofia Papagiannaki
                    dictionary={'user': user.__dict__, 'group_creation': True}
139 3abf6c78 Sofia Papagiannaki
                )
140 0a569195 Sofia Papagiannaki
                return NotificationSent()
141 0a569195 Sofia Papagiannaki
        except BaseException, e:
142 0a569195 Sofia Papagiannaki
            logger.exception(e)
143 0a569195 Sofia Papagiannaki
            raise e
144 8316698a Sofia Papagiannaki
145 5ce3ce4f Sofia Papagiannaki
146 0a569195 Sofia Papagiannaki
class InvitationsBackend(ActivationBackend):
147 890b0eaf Sofia Papagiannaki
    """
148 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
149 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, if the request contains a valid
150 890b0eaf Sofia Papagiannaki
    inivation code the user is automatically activated otherwise an inactive user
151 890b0eaf Sofia Papagiannaki
    account is created and the user is going to receive an email as soon as an
152 890b0eaf Sofia Papagiannaki
    administrator activates his/her account.
153 890b0eaf Sofia Papagiannaki
    """
154 bef3bf46 Kostas Papadimitriou
155 4e30244e Sofia Papagiannaki
    def get_signup_form(self, provider='local', instance=None):
156 15efc749 Sofia Papagiannaki
        """
157 b669d9c0 Sofia Papagiannaki
        Returns a form instance of the relevant class
158 5ce3ce4f Sofia Papagiannaki

159 0a569195 Sofia Papagiannaki
        raises Invitation.DoesNotExist and ValueError if invitation is consumed
160 0a569195 Sofia Papagiannaki
        or invitation username is reserved.
161 15efc749 Sofia Papagiannaki
        """
162 0a569195 Sofia Papagiannaki
        self.invitation = get_invitation(self.request)
163 0a569195 Sofia Papagiannaki
        invitation = self.invitation
164 0a569195 Sofia Papagiannaki
        initial_data = self.get_signup_initial_data(provider)
165 0a569195 Sofia Papagiannaki
        prefix = 'Invited' if invitation else ''
166 0a569195 Sofia Papagiannaki
        main = provider.capitalize()
167 5ce3ce4f Sofia Papagiannaki
        suffix = 'UserCreationForm'
168 0a569195 Sofia Papagiannaki
        formclass = '%s%s%s' % (prefix, main, suffix)
169 0a569195 Sofia Papagiannaki
        return globals()[formclass](initial_data, instance=instance, request=self.request)
170 bef3bf46 Kostas Papadimitriou
171 15efc749 Sofia Papagiannaki
    def get_signup_initial_data(self, provider):
172 890b0eaf Sofia Papagiannaki
        """
173 18ffbee1 Sofia Papagiannaki
        Returns the necassary activation form depending the user is invited or not
174 bef3bf46 Kostas Papadimitriou

175 890b0eaf Sofia Papagiannaki
        Throws Invitation.DoesNotExist in case ``code`` is not valid.
176 890b0eaf Sofia Papagiannaki
        """
177 5ed6816e Sofia Papagiannaki
        request = self.request
178 15efc749 Sofia Papagiannaki
        invitation = self.invitation
179 65d85494 Sofia Papagiannaki
        initial_data = None
180 890b0eaf Sofia Papagiannaki
        if request.method == 'GET':
181 15efc749 Sofia Papagiannaki
            if invitation:
182 65d85494 Sofia Papagiannaki
                # create a tmp user with the invitation realname
183 65d85494 Sofia Papagiannaki
                # to extract first and last name
184 5ce3ce4f Sofia Papagiannaki
                u = AstakosUser(realname=invitation.realname)
185 5ce3ce4f Sofia Papagiannaki
                initial_data = {'email': invitation.username,
186 5ce3ce4f Sofia Papagiannaki
                                'inviter': invitation.inviter.realname,
187 5ce3ce4f Sofia Papagiannaki
                                'first_name': u.first_name,
188 5ce3ce4f Sofia Papagiannaki
                                'last_name': u.last_name,
189 5ce3ce4f Sofia Papagiannaki
                                'provider': provider}
190 15efc749 Sofia Papagiannaki
        else:
191 15efc749 Sofia Papagiannaki
            if provider == request.POST.get('provider', ''):
192 15efc749 Sofia Papagiannaki
                initial_data = request.POST
193 15efc749 Sofia Papagiannaki
        return initial_data
194 bef3bf46 Kostas Papadimitriou
195 890b0eaf Sofia Papagiannaki
    def _is_preaccepted(self, user):
196 890b0eaf Sofia Papagiannaki
        """
197 890b0eaf Sofia Papagiannaki
        If there is a valid, not-consumed invitation code for the specific user
198 890b0eaf Sofia Papagiannaki
        returns True else returns False.
199 890b0eaf Sofia Papagiannaki
        """
200 8316698a Sofia Papagiannaki
        if super(InvitationsBackend, self)._is_preaccepted(user):
201 8316698a Sofia Papagiannaki
            return True
202 5ed6816e Sofia Papagiannaki
        invitation = self.invitation
203 890b0eaf Sofia Papagiannaki
        if not invitation:
204 890b0eaf Sofia Papagiannaki
            return False
205 5ed6816e Sofia Papagiannaki
        if invitation.username == user.email and not invitation.is_consumed:
206 5ed6816e Sofia Papagiannaki
            invitation.consume()
207 890b0eaf Sofia Papagiannaki
            return True
208 890b0eaf Sofia Papagiannaki
        return False
209 bef3bf46 Kostas Papadimitriou
210 5ce3ce4f Sofia Papagiannaki
211 0a569195 Sofia Papagiannaki
class SimpleBackend(ActivationBackend):
212 890b0eaf Sofia Papagiannaki
    """
213 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
214 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, an incative user account is
215 890b0eaf Sofia Papagiannaki
    created and receives an email in order to activate his/her account.
216 890b0eaf Sofia Papagiannaki
    """
217 8316698a Sofia Papagiannaki
    def _is_preaccepted(self, user):
218 8316698a Sofia Papagiannaki
        if super(SimpleBackend, self)._is_preaccepted(user):
219 8316698a Sofia Papagiannaki
            return True
220 8316698a Sofia Papagiannaki
        if MODERATION_ENABLED:
221 8316698a Sofia Papagiannaki
            return False
222 8316698a Sofia Papagiannaki
        return True
223 8f5a3a06 Sofia Papagiannaki
224 5ce3ce4f Sofia Papagiannaki
225 8f5a3a06 Sofia Papagiannaki
class ActivationResult(object):
226 8f5a3a06 Sofia Papagiannaki
    def __init__(self, message):
227 8f5a3a06 Sofia Papagiannaki
        self.message = message
228 8f5a3a06 Sofia Papagiannaki
229 5ce3ce4f Sofia Papagiannaki
230 8f5a3a06 Sofia Papagiannaki
class VerificationSent(ActivationResult):
231 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
232 674f9a52 Sofia Papagiannaki
        message = _(astakos_messages.VERIFICATION_SENT)
233 8f5a3a06 Sofia Papagiannaki
        super(VerificationSent, self).__init__(message)
234 8f5a3a06 Sofia Papagiannaki
235 5ce3ce4f Sofia Papagiannaki
236 0a569195 Sofia Papagiannaki
class SwitchAccountsVerificationSent(ActivationResult):
237 0a569195 Sofia Papagiannaki
    def __init__(self, email):
238 674f9a52 Sofia Papagiannaki
        message = _(astakos_messages.SWITCH_ACCOUNT_LINK_SENT)
239 0a569195 Sofia Papagiannaki
        super(SwitchAccountsVerificationSent, self).__init__(message)
240 0a569195 Sofia Papagiannaki
241 5ce3ce4f Sofia Papagiannaki
242 8f5a3a06 Sofia Papagiannaki
class NotificationSent(ActivationResult):
243 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
244 674f9a52 Sofia Papagiannaki
        message = _(astakos_messages.NOTIFACATION_SENT)
245 8f5a3a06 Sofia Papagiannaki
        super(NotificationSent, self).__init__(message)
246 8f5a3a06 Sofia Papagiannaki
247 5ce3ce4f Sofia Papagiannaki
248 8f5a3a06 Sofia Papagiannaki
class RegistationCompleted(ActivationResult):
249 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
250 674f9a52 Sofia Papagiannaki
        message = _(astakos_messages.REGISTRATION_COMPLETED)
251 5ce3ce4f Sofia Papagiannaki
        super(RegistationCompleted, self).__init__(message)