Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ 4bdd7e3d

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 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 f9aea9c8 Sofia Papagiannaki
        admin_email_template_name='im/account_creation_notification.txt',
111 f9aea9c8 Sofia Papagiannaki
        helpdesk_email_template_name='im/helpdesk_notification.txt'
112 678b2236 Sofia Papagiannaki
    ):
113 0a569195 Sofia Papagiannaki
        """
114 0a569195 Sofia Papagiannaki
        If the user is already active returns immediately.
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 7233d542 Kostas Papadimitriou
125 0a569195 Sofia Papagiannaki
            if self._is_preaccepted(user):
126 0a569195 Sofia Papagiannaki
                if user.email_verified:
127 f9aea9c8 Sofia Papagiannaki
                    activate(
128 f9aea9c8 Sofia Papagiannaki
                        user,
129 f9aea9c8 Sofia Papagiannaki
                        greeting_template_name,
130 f9aea9c8 Sofia Papagiannaki
                        helpdesk_email_template_name
131 f9aea9c8 Sofia Papagiannaki
                    )
132 0a569195 Sofia Papagiannaki
                    return RegistationCompleted()
133 0a569195 Sofia Papagiannaki
                else:
134 f9aea9c8 Sofia Papagiannaki
                    send_activation(
135 f9aea9c8 Sofia Papagiannaki
                        user,
136 f9aea9c8 Sofia Papagiannaki
                        activation_template_name
137 f9aea9c8 Sofia Papagiannaki
                    )
138 0a569195 Sofia Papagiannaki
                    return VerificationSent()
139 0a569195 Sofia Papagiannaki
            else:
140 9a06d96f Olga Brani
                send_account_creation_notification(
141 3abf6c78 Sofia Papagiannaki
                    template_name=admin_email_template_name,
142 a3637508 Sofia Papagiannaki
                    dictionary={'user': user.__dict__, 'group_creation': True}
143 3abf6c78 Sofia Papagiannaki
                )
144 0a569195 Sofia Papagiannaki
                return NotificationSent()
145 0a569195 Sofia Papagiannaki
        except BaseException, e:
146 0a569195 Sofia Papagiannaki
            logger.exception(e)
147 0a569195 Sofia Papagiannaki
            raise e
148 8316698a Sofia Papagiannaki
149 5ce3ce4f Sofia Papagiannaki
150 0a569195 Sofia Papagiannaki
class InvitationsBackend(ActivationBackend):
151 890b0eaf Sofia Papagiannaki
    """
152 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
153 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, if the request contains a valid
154 890b0eaf Sofia Papagiannaki
    inivation code the user is automatically activated otherwise an inactive user
155 890b0eaf Sofia Papagiannaki
    account is created and the user is going to receive an email as soon as an
156 890b0eaf Sofia Papagiannaki
    administrator activates his/her account.
157 890b0eaf Sofia Papagiannaki
    """
158 bef3bf46 Kostas Papadimitriou
159 4e30244e Sofia Papagiannaki
    def get_signup_form(self, provider='local', instance=None):
160 15efc749 Sofia Papagiannaki
        """
161 b669d9c0 Sofia Papagiannaki
        Returns a form instance of the relevant class
162 5ce3ce4f Sofia Papagiannaki

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

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