Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ ee45eb81

History | View | Annotate | Download (9.4 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 73fbaec4 Sofia Papagiannaki
    send_activation, send_account_creation_notification, activate)
42 c0b26605 Sofia Papagiannaki
from astakos.im.settings import (
43 73fbaec4 Sofia Papagiannaki
    INVITATIONS_ENABLED, RE_USER_EMAIL_PATTERNS)
44 7233d542 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
45 11c48149 Sofia Papagiannaki
from astakos.im.forms import *
46 d1a767f7 Olga Brani
47 d1a767f7 Olga Brani
import astakos.im.messages 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 7233d542 Kostas Papadimitriou
105 678b2236 Sofia Papagiannaki
    def handle_activation(
106 678b2236 Sofia Papagiannaki
        self, user, activation_template_name='im/activation_email.txt',
107 678b2236 Sofia Papagiannaki
        greeting_template_name='im/welcome_email.txt',
108 f9aea9c8 Sofia Papagiannaki
        admin_email_template_name='im/account_creation_notification.txt',
109 f9aea9c8 Sofia Papagiannaki
        helpdesk_email_template_name='im/helpdesk_notification.txt'
110 678b2236 Sofia Papagiannaki
    ):
111 0a569195 Sofia Papagiannaki
        """
112 0a569195 Sofia Papagiannaki
        If the user is already active returns immediately.
113 0a569195 Sofia Papagiannaki
        If the user is preaccepted and the email is verified, the account is
114 0a569195 Sofia Papagiannaki
        activated automatically. Otherwise, if the email is not verified,
115 0a569195 Sofia Papagiannaki
        it sends a verification email to the user.
116 0a569195 Sofia Papagiannaki
        If the user is not preaccepted, it sends an email to the administrators
117 0a569195 Sofia Papagiannaki
        and informs the user that the account is pending activation.
118 0a569195 Sofia Papagiannaki
        """
119 0a569195 Sofia Papagiannaki
        try:
120 0a569195 Sofia Papagiannaki
            if user.is_active:
121 0a569195 Sofia Papagiannaki
                return RegistationCompleted()
122 7233d542 Kostas Papadimitriou
123 0a569195 Sofia Papagiannaki
            if self._is_preaccepted(user):
124 0a569195 Sofia Papagiannaki
                if user.email_verified:
125 f9aea9c8 Sofia Papagiannaki
                    activate(
126 f9aea9c8 Sofia Papagiannaki
                        user,
127 f9aea9c8 Sofia Papagiannaki
                        greeting_template_name,
128 f9aea9c8 Sofia Papagiannaki
                        helpdesk_email_template_name
129 f9aea9c8 Sofia Papagiannaki
                    )
130 0a569195 Sofia Papagiannaki
                    return RegistationCompleted()
131 0a569195 Sofia Papagiannaki
                else:
132 f9aea9c8 Sofia Papagiannaki
                    send_activation(
133 f9aea9c8 Sofia Papagiannaki
                        user,
134 f9aea9c8 Sofia Papagiannaki
                        activation_template_name
135 f9aea9c8 Sofia Papagiannaki
                    )
136 0a569195 Sofia Papagiannaki
                    return VerificationSent()
137 0a569195 Sofia Papagiannaki
            else:
138 9a06d96f Olga Brani
                send_account_creation_notification(
139 3abf6c78 Sofia Papagiannaki
                    template_name=admin_email_template_name,
140 a3637508 Sofia Papagiannaki
                    dictionary={'user': user.__dict__, 'group_creation': True}
141 3abf6c78 Sofia Papagiannaki
                )
142 0a569195 Sofia Papagiannaki
                return NotificationSent()
143 0a569195 Sofia Papagiannaki
        except BaseException, e:
144 0a569195 Sofia Papagiannaki
            logger.exception(e)
145 0a569195 Sofia Papagiannaki
            raise e
146 8316698a Sofia Papagiannaki
147 5ce3ce4f Sofia Papagiannaki
148 0a569195 Sofia Papagiannaki
class InvitationsBackend(ActivationBackend):
149 890b0eaf Sofia Papagiannaki
    """
150 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
151 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, if the request contains a valid
152 890b0eaf Sofia Papagiannaki
    inivation code the user is automatically activated otherwise an inactive user
153 890b0eaf Sofia Papagiannaki
    account is created and the user is going to receive an email as soon as an
154 890b0eaf Sofia Papagiannaki
    administrator activates his/her account.
155 890b0eaf Sofia Papagiannaki
    """
156 bef3bf46 Kostas Papadimitriou
157 4e30244e Sofia Papagiannaki
    def get_signup_form(self, provider='local', instance=None):
158 15efc749 Sofia Papagiannaki
        """
159 b669d9c0 Sofia Papagiannaki
        Returns a form instance of the relevant class
160 5ce3ce4f Sofia Papagiannaki

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

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