Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ 7233d542

History | View | Annotate | Download (9.1 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 15efc749 Sofia Papagiannaki
from astakos.im.util import get_invitation
40 c0b26605 Sofia Papagiannaki
from astakos.im.functions import (
41 c0b26605 Sofia Papagiannaki
    send_activation, send_account_creation_notification, activate
42 c0b26605 Sofia Papagiannaki
)
43 c0b26605 Sofia Papagiannaki
from astakos.im.settings import (
44 7233d542 Kostas Papadimitriou
    INVITATIONS_ENABLED, RE_USER_EMAIL_PATTERNS
45 ae497612 Olga Brani
)
46 7233d542 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
47 11c48149 Sofia Papagiannaki
from astakos.im.forms import *
48 d1a767f7 Olga Brani
49 d1a767f7 Olga Brani
import astakos.im.messages as astakos_messages
50 0905ccd2 Sofia Papagiannaki
51 5ed6816e Sofia Papagiannaki
import logging
52 8316698a Sofia Papagiannaki
import re
53 5ed6816e Sofia Papagiannaki
54 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
55 e015e9e6 Sofia Papagiannaki
56 5ce3ce4f Sofia Papagiannaki
57 5ed6816e Sofia Papagiannaki
def get_backend(request):
58 0905ccd2 Sofia Papagiannaki
    """
59 18ffbee1 Sofia Papagiannaki
    Returns an instance of an activation backend,
60 890b0eaf Sofia Papagiannaki
    according to the INVITATIONS_ENABLED setting
61 8f5a3a06 Sofia Papagiannaki
    (if True returns ``astakos.im.activation_backends.InvitationsBackend`` and if False
62 8f5a3a06 Sofia Papagiannaki
    returns ``astakos.im.activation_backends.SimpleBackend``).
63 bef3bf46 Kostas Papadimitriou

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

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

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