Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / activation_backends.py @ 7368111c

History | View | Annotate | Download (9.6 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 890b0eaf Sofia Papagiannaki
from django.core.mail import send_mail
37 890b0eaf Sofia Papagiannaki
from django.template.loader import render_to_string
38 890b0eaf Sofia Papagiannaki
from django.contrib import messages
39 1463659a Sofia Papagiannaki
from django.core.urlresolvers import reverse
40 18ffbee1 Sofia Papagiannaki
from django.utils.translation import ugettext as _
41 18ffbee1 Sofia Papagiannaki
from django.db import transaction
42 890b0eaf Sofia Papagiannaki
43 374611bc Sofia Papagiannaki
from urlparse import urljoin
44 890b0eaf Sofia Papagiannaki
45 890b0eaf Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
46 15efc749 Sofia Papagiannaki
from astakos.im.forms import *
47 15efc749 Sofia Papagiannaki
from astakos.im.util import get_invitation
48 751d24cf Sofia Papagiannaki
from astakos.im.functions import send_verification, send_activation, \
49 751d24cf Sofia Papagiannaki
    send_admin_notification, activate, SendMailError
50 751d24cf Sofia Papagiannaki
from astakos.im.settings import INVITATIONS_ENABLED, DEFAULT_CONTACT_EMAIL, \
51 751d24cf Sofia Papagiannaki
    DEFAULT_FROM_EMAIL, MODERATION_ENABLED, SITENAME, DEFAULT_ADMIN_EMAIL, RE_USER_EMAIL_PATTERNS
52 0905ccd2 Sofia Papagiannaki
53 5ed6816e Sofia Papagiannaki
import socket
54 5ed6816e Sofia Papagiannaki
import logging
55 8316698a Sofia Papagiannaki
import re
56 5ed6816e Sofia Papagiannaki
57 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
58 e015e9e6 Sofia Papagiannaki
59 5ed6816e Sofia Papagiannaki
def get_backend(request):
60 0905ccd2 Sofia Papagiannaki
    """
61 18ffbee1 Sofia Papagiannaki
    Returns an instance of an activation backend,
62 890b0eaf Sofia Papagiannaki
    according to the INVITATIONS_ENABLED setting
63 8f5a3a06 Sofia Papagiannaki
    (if True returns ``astakos.im.activation_backends.InvitationsBackend`` and if False
64 8f5a3a06 Sofia Papagiannaki
    returns ``astakos.im.activation_backends.SimpleBackend``).
65 bef3bf46 Kostas Papadimitriou

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

167 890b0eaf Sofia Papagiannaki
        Throws Invitation.DoesNotExist in case ``code`` is not valid.
168 890b0eaf Sofia Papagiannaki
        """
169 5ed6816e Sofia Papagiannaki
        request = self.request
170 15efc749 Sofia Papagiannaki
        invitation = self.invitation
171 65d85494 Sofia Papagiannaki
        initial_data = None
172 890b0eaf Sofia Papagiannaki
        if request.method == 'GET':
173 15efc749 Sofia Papagiannaki
            if invitation:
174 65d85494 Sofia Papagiannaki
                # create a tmp user with the invitation realname
175 65d85494 Sofia Papagiannaki
                # to extract first and last name
176 15efc749 Sofia Papagiannaki
                u = AstakosUser(realname = invitation.realname)
177 15efc749 Sofia Papagiannaki
                initial_data = {'email':invitation.username,
178 15efc749 Sofia Papagiannaki
                                'inviter':invitation.inviter.realname,
179 65d85494 Sofia Papagiannaki
                                'first_name':u.first_name,
180 4e30244e Sofia Papagiannaki
                                'last_name':u.last_name,
181 4e30244e Sofia Papagiannaki
                                'provider':provider}
182 15efc749 Sofia Papagiannaki
        else:
183 15efc749 Sofia Papagiannaki
            if provider == request.POST.get('provider', ''):
184 15efc749 Sofia Papagiannaki
                initial_data = request.POST
185 15efc749 Sofia Papagiannaki
        return initial_data
186 bef3bf46 Kostas Papadimitriou
187 890b0eaf Sofia Papagiannaki
    def _is_preaccepted(self, user):
188 890b0eaf Sofia Papagiannaki
        """
189 7368111c Sofia Papagiannaki
        Extends _is_preaccepted and if there is a valid, not-consumed invitation
190 7368111c Sofia Papagiannaki
        code for the specific user returns True else returns False.
191 890b0eaf Sofia Papagiannaki
        """
192 8316698a Sofia Papagiannaki
        if super(InvitationsBackend, self)._is_preaccepted(user):
193 8316698a Sofia Papagiannaki
            return True
194 5ed6816e Sofia Papagiannaki
        invitation = self.invitation
195 890b0eaf Sofia Papagiannaki
        if not invitation:
196 890b0eaf Sofia Papagiannaki
            return False
197 5ed6816e Sofia Papagiannaki
        if invitation.username == user.email and not invitation.is_consumed:
198 5ed6816e Sofia Papagiannaki
            invitation.consume()
199 890b0eaf Sofia Papagiannaki
            return True
200 890b0eaf Sofia Papagiannaki
        return False
201 bef3bf46 Kostas Papadimitriou
202 0a569195 Sofia Papagiannaki
class SimpleBackend(ActivationBackend):
203 890b0eaf Sofia Papagiannaki
    """
204 18ffbee1 Sofia Papagiannaki
    A activation backend which implements the following workflow: a user
205 890b0eaf Sofia Papagiannaki
    supplies the necessary registation information, an incative user account is
206 890b0eaf Sofia Papagiannaki
    created and receives an email in order to activate his/her account.
207 890b0eaf Sofia Papagiannaki
    """
208 5ed6816e Sofia Papagiannaki
    def __init__(self, request):
209 5ed6816e Sofia Papagiannaki
        self.request = request
210 8316698a Sofia Papagiannaki
        super(SimpleBackend, self).__init__()
211 8316698a Sofia Papagiannaki
    
212 8316698a Sofia Papagiannaki
    def _is_preaccepted(self, user):
213 8316698a Sofia Papagiannaki
        if super(SimpleBackend, self)._is_preaccepted(user):
214 8316698a Sofia Papagiannaki
            return True
215 8316698a Sofia Papagiannaki
        if MODERATION_ENABLED:
216 8316698a Sofia Papagiannaki
            return False
217 8316698a Sofia Papagiannaki
        return True
218 8f5a3a06 Sofia Papagiannaki
219 8f5a3a06 Sofia Papagiannaki
class ActivationResult(object):
220 8f5a3a06 Sofia Papagiannaki
    def __init__(self, message):
221 8f5a3a06 Sofia Papagiannaki
        self.message = message
222 8f5a3a06 Sofia Papagiannaki
223 8f5a3a06 Sofia Papagiannaki
class VerificationSent(ActivationResult):
224 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
225 8f5a3a06 Sofia Papagiannaki
        message = _('Verification sent.')
226 8f5a3a06 Sofia Papagiannaki
        super(VerificationSent, self).__init__(message)
227 8f5a3a06 Sofia Papagiannaki
228 8f5a3a06 Sofia Papagiannaki
class NotificationSent(ActivationResult):
229 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
230 8f5a3a06 Sofia Papagiannaki
        message = _('Your request for an account was successfully received and is now pending \
231 8f5a3a06 Sofia Papagiannaki
                    approval. You will be notified by email in the next few days. Thanks for \
232 8f5a3a06 Sofia Papagiannaki
                    your interest in ~okeanos! The GRNET team.')
233 8f5a3a06 Sofia Papagiannaki
        super(NotificationSent, self).__init__(message)
234 8f5a3a06 Sofia Papagiannaki
235 8f5a3a06 Sofia Papagiannaki
class RegistationCompleted(ActivationResult):
236 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
237 8f5a3a06 Sofia Papagiannaki
        message = _('Registration completed. You can now login.')
238 18ffbee1 Sofia Papagiannaki
        super(RegistationCompleted, self).__init__(message)