Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ 1e361a6d

History | View | Annotate | Download (9.5 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 0dd46210 Sofia Papagiannaki
    (if True returns ``astakos.im.activation_backends.InvitationsBackend``
60 0dd46210 Sofia Papagiannaki
    and if False
61 8f5a3a06 Sofia Papagiannaki
    returns ``astakos.im.activation_backends.SimpleBackend``).
62 bef3bf46 Kostas Papadimitriou

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

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

180 890b0eaf Sofia Papagiannaki
        Throws Invitation.DoesNotExist in case ``code`` is not valid.
181 890b0eaf Sofia Papagiannaki
        """
182 5ed6816e Sofia Papagiannaki
        request = self.request
183 15efc749 Sofia Papagiannaki
        invitation = self.invitation
184 65d85494 Sofia Papagiannaki
        initial_data = None
185 890b0eaf Sofia Papagiannaki
        if request.method == 'GET':
186 15efc749 Sofia Papagiannaki
            if invitation:
187 65d85494 Sofia Papagiannaki
                # create a tmp user with the invitation realname
188 65d85494 Sofia Papagiannaki
                # to extract first and last name
189 5ce3ce4f Sofia Papagiannaki
                u = AstakosUser(realname=invitation.realname)
190 5ce3ce4f Sofia Papagiannaki
                initial_data = {'email': invitation.username,
191 5ce3ce4f Sofia Papagiannaki
                                'inviter': invitation.inviter.realname,
192 5ce3ce4f Sofia Papagiannaki
                                'first_name': u.first_name,
193 5ce3ce4f Sofia Papagiannaki
                                'last_name': u.last_name,
194 5ce3ce4f Sofia Papagiannaki
                                'provider': provider}
195 15efc749 Sofia Papagiannaki
        else:
196 15efc749 Sofia Papagiannaki
            if provider == request.POST.get('provider', ''):
197 15efc749 Sofia Papagiannaki
                initial_data = request.POST
198 15efc749 Sofia Papagiannaki
        return initial_data
199 bef3bf46 Kostas Papadimitriou
200 890b0eaf Sofia Papagiannaki
    def _is_preaccepted(self, user):
201 890b0eaf Sofia Papagiannaki
        """
202 7368111c Sofia Papagiannaki
        Extends _is_preaccepted and if there is a valid, not-consumed invitation
203 7368111c Sofia Papagiannaki
        code for the specific user returns True else returns False.
204 890b0eaf Sofia Papagiannaki
        """
205 8316698a Sofia Papagiannaki
        if super(InvitationsBackend, self)._is_preaccepted(user):
206 8316698a Sofia Papagiannaki
            return True
207 5ed6816e Sofia Papagiannaki
        invitation = self.invitation
208 890b0eaf Sofia Papagiannaki
        if not invitation:
209 7233d542 Kostas Papadimitriou
            return not astakos_settings.MODERATION_ENABLED
210 5ed6816e Sofia Papagiannaki
        if invitation.username == user.email and not invitation.is_consumed:
211 5ed6816e Sofia Papagiannaki
            invitation.consume()
212 890b0eaf Sofia Papagiannaki
            return True
213 890b0eaf Sofia Papagiannaki
        return False
214 bef3bf46 Kostas Papadimitriou
215 5ce3ce4f Sofia Papagiannaki
216 0a569195 Sofia Papagiannaki
class SimpleBackend(ActivationBackend):
217 890b0eaf Sofia Papagiannaki
    """
218 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
219 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, an incative user account is
220 890b0eaf Sofia Papagiannaki
    created and receives an email in order to activate his/her account.
221 890b0eaf Sofia Papagiannaki
    """
222 8316698a Sofia Papagiannaki
    def _is_preaccepted(self, user):
223 8316698a Sofia Papagiannaki
        if super(SimpleBackend, self)._is_preaccepted(user):
224 8316698a Sofia Papagiannaki
            return True
225 9d20fe23 Kostas Papadimitriou
226 9d20fe23 Kostas Papadimitriou
        return user.get_auth_provider().get_automoderate_policy
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)