root / snf-astakos-app / astakos / im / activation_backends.py @ 9a94c0f1
History | View | Annotate | Download (19.8 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 | e066eedb | Giorgos Korfiatis | from astakos.im import models |
39 | e7cb4085 | Kostas Papadimitriou | from astakos.im import functions |
40 | e7cb4085 | Kostas Papadimitriou | from astakos.im import settings |
41 | e7cb4085 | Kostas Papadimitriou | from astakos.im import forms |
42 | e7cb4085 | Kostas Papadimitriou | |
43 | d1a767f7 | Olga Brani | import astakos.im.messages as astakos_messages |
44 | 0905ccd2 | Sofia Papagiannaki | |
45 | e7cb4085 | Kostas Papadimitriou | import datetime |
46 | 5ed6816e | Sofia Papagiannaki | import logging |
47 | 8316698a | Sofia Papagiannaki | import re |
48 | e7cb4085 | Kostas Papadimitriou | import json |
49 | 5ed6816e | Sofia Papagiannaki | |
50 | e015e9e6 | Sofia Papagiannaki | logger = logging.getLogger(__name__) |
51 | e015e9e6 | Sofia Papagiannaki | |
52 | 5ce3ce4f | Sofia Papagiannaki | |
53 | e7cb4085 | Kostas Papadimitriou | def get_backend(): |
54 | 0905ccd2 | Sofia Papagiannaki | """
|
55 | 18ffbee1 | Sofia Papagiannaki | Returns an instance of an activation backend,
|
56 | 890b0eaf | Sofia Papagiannaki | according to the INVITATIONS_ENABLED setting
|
57 | 0dd46210 | Sofia Papagiannaki | (if True returns ``astakos.im.activation_backends.InvitationsBackend``
|
58 | 0dd46210 | Sofia Papagiannaki | and if False
|
59 | 8f5a3a06 | Sofia Papagiannaki | returns ``astakos.im.activation_backends.SimpleBackend``).
|
60 | bef3bf46 | Kostas Papadimitriou |
|
61 | 0dd46210 | Sofia Papagiannaki | If the backend cannot be located
|
62 | 0dd46210 | Sofia Papagiannaki | ``django.core.exceptions.ImproperlyConfigured`` is raised.
|
63 | 0905ccd2 | Sofia Papagiannaki | """
|
64 | 8f5a3a06 | Sofia Papagiannaki | module = 'astakos.im.activation_backends'
|
65 | e7cb4085 | Kostas Papadimitriou | prefix = 'Invitations' if settings.INVITATIONS_ENABLED else 'Simple' |
66 | 5ce3ce4f | Sofia Papagiannaki | backend_class_name = '%sBackend' % prefix
|
67 | 0905ccd2 | Sofia Papagiannaki | try:
|
68 | 0905ccd2 | Sofia Papagiannaki | mod = import_module(module) |
69 | 0905ccd2 | Sofia Papagiannaki | except ImportError, e: |
70 | 5ce3ce4f | Sofia Papagiannaki | raise ImproperlyConfigured(
|
71 | 5ce3ce4f | Sofia Papagiannaki | 'Error loading activation backend %s: "%s"' % (module, e))
|
72 | 0905ccd2 | Sofia Papagiannaki | try:
|
73 | 0905ccd2 | Sofia Papagiannaki | backend_class = getattr(mod, backend_class_name)
|
74 | 0905ccd2 | Sofia Papagiannaki | except AttributeError: |
75 | 0dd46210 | Sofia Papagiannaki | raise ImproperlyConfigured(
|
76 | 0dd46210 | Sofia Papagiannaki | 'Module "%s" does not define a activation backend named "%s"' % (
|
77 | 0dd46210 | Sofia Papagiannaki | module, backend_class_name)) |
78 | e7cb4085 | Kostas Papadimitriou | return backend_class(settings.MODERATION_ENABLED)
|
79 | 890b0eaf | Sofia Papagiannaki | |
80 | 5ce3ce4f | Sofia Papagiannaki | |
81 | 0a569195 | Sofia Papagiannaki | class ActivationBackend(object): |
82 | e7cb4085 | Kostas Papadimitriou | """
|
83 | e7cb4085 | Kostas Papadimitriou | ActivationBackend handles user verification/activation.
|
84 | e7cb4085 | Kostas Papadimitriou |
|
85 | e7cb4085 | Kostas Papadimitriou | Example usage::
|
86 | e7cb4085 | Kostas Papadimitriou | >>> # it is wise to not instantiate a backend class directly but use
|
87 | e7cb4085 | Kostas Papadimitriou | >>> # get_backend method instead.
|
88 | e7cb4085 | Kostas Papadimitriou | >>> backend = get_backend()
|
89 | e7cb4085 | Kostas Papadimitriou | >>> formCls = backend.get_signup_form(request.POST)
|
90 | e7cb4085 | Kostas Papadimitriou | >>> if form.is_valid():
|
91 | 1808f7bc | Giorgos Korfiatis | >>> user = form.create_user()
|
92 | e7cb4085 | Kostas Papadimitriou | >>> activation = backend.handle_registration(user)
|
93 | e7cb4085 | Kostas Papadimitriou | >>> # activation.status is one of backend.Result.{*} activation result
|
94 | e7cb4085 | Kostas Papadimitriou | >>> # types
|
95 | e7cb4085 | Kostas Papadimitriou | >>>
|
96 | e7cb4085 | Kostas Papadimitriou | >>> # sending activation notifications is not done automatically
|
97 | e7cb4085 | Kostas Papadimitriou | >>> # we need to call send_result_notifications
|
98 | e7cb4085 | Kostas Papadimitriou | >>> backend.send_result_notifications(activation)
|
99 | e7cb4085 | Kostas Papadimitriou | >>> return HttpResponse(activation.message)
|
100 | e7cb4085 | Kostas Papadimitriou | """
|
101 | e7cb4085 | Kostas Papadimitriou | |
102 | e7cb4085 | Kostas Papadimitriou | verification_template_name = 'im/activation_email.txt'
|
103 | e7cb4085 | Kostas Papadimitriou | greeting_template_name = 'im/welcome_email.txt'
|
104 | e7cb4085 | Kostas Papadimitriou | pending_moderation_template_name = \ |
105 | e7cb4085 | Kostas Papadimitriou | 'im/account_pending_moderation_notification.txt'
|
106 | e7cb4085 | Kostas Papadimitriou | activated_email_template_name = 'im/account_activated_notification.txt'
|
107 | e7cb4085 | Kostas Papadimitriou | |
108 | e7cb4085 | Kostas Papadimitriou | class Result: |
109 | e7cb4085 | Kostas Papadimitriou | # user created, email verification sent
|
110 | e7cb4085 | Kostas Papadimitriou | PENDING_VERIFICATION = 1
|
111 | e7cb4085 | Kostas Papadimitriou | # email verified
|
112 | e7cb4085 | Kostas Papadimitriou | PENDING_MODERATION = 2
|
113 | e7cb4085 | Kostas Papadimitriou | # user moderated
|
114 | e7cb4085 | Kostas Papadimitriou | ACCEPTED = 3
|
115 | e7cb4085 | Kostas Papadimitriou | # user rejected
|
116 | e7cb4085 | Kostas Papadimitriou | REJECTED = 4
|
117 | e7cb4085 | Kostas Papadimitriou | # inactive user activated
|
118 | e7cb4085 | Kostas Papadimitriou | ACTIVATED = 5
|
119 | e7cb4085 | Kostas Papadimitriou | # active user deactivated
|
120 | e7cb4085 | Kostas Papadimitriou | DEACTIVATED = 6
|
121 | e7cb4085 | Kostas Papadimitriou | # something went wrong
|
122 | e7cb4085 | Kostas Papadimitriou | ERROR = -1
|
123 | e7cb4085 | Kostas Papadimitriou | |
124 | e7cb4085 | Kostas Papadimitriou | def __init__(self, moderation_enabled): |
125 | e7cb4085 | Kostas Papadimitriou | self.moderation_enabled = moderation_enabled
|
126 | 5ce3ce4f | Sofia Papagiannaki | |
127 | 8316698a | Sofia Papagiannaki | def _is_preaccepted(self, user): |
128 | e7cb4085 | Kostas Papadimitriou | """
|
129 | e7cb4085 | Kostas Papadimitriou | Decide whether user should be automatically moderated. The method gets
|
130 | e7cb4085 | Kostas Papadimitriou | called only when self.moderation_enabled is set to True.
|
131 | e7cb4085 | Kostas Papadimitriou |
|
132 | e7cb4085 | Kostas Papadimitriou | The method returns False or a string identifier which later will be
|
133 | e7cb4085 | Kostas Papadimitriou | stored in user's accepted_policy field. This is helpfull for
|
134 | e7cb4085 | Kostas Papadimitriou | administrators to be aware of the reason a created user was
|
135 | e7cb4085 | Kostas Papadimitriou | automatically activated.
|
136 | e7cb4085 | Kostas Papadimitriou | """
|
137 | e7cb4085 | Kostas Papadimitriou | |
138 | e7cb4085 | Kostas Papadimitriou | # check preaccepted mail patterns
|
139 | e7cb4085 | Kostas Papadimitriou | for pattern in settings.RE_USER_EMAIL_PATTERNS: |
140 | 8316698a | Sofia Papagiannaki | if re.match(pattern, user.email):
|
141 | e7cb4085 | Kostas Papadimitriou | return 'email' |
142 | e7cb4085 | Kostas Papadimitriou | |
143 | e7cb4085 | Kostas Papadimitriou | # provider automoderate policy is on
|
144 | e7cb4085 | Kostas Papadimitriou | if user.get_auth_provider().get_automoderate_policy:
|
145 | e7cb4085 | Kostas Papadimitriou | return 'auth_provider_%s' % user.get_auth_provider().module |
146 | e7cb4085 | Kostas Papadimitriou | |
147 | 8316698a | Sofia Papagiannaki | return False |
148 | 5ce3ce4f | Sofia Papagiannaki | |
149 | e7cb4085 | Kostas Papadimitriou | def get_signup_form(self, provider='local', initial_data=None, **kwargs): |
150 | 0a569195 | Sofia Papagiannaki | """
|
151 | e7cb4085 | Kostas Papadimitriou | Returns a form instance for the type of registration the user chosen.
|
152 | e7cb4085 | Kostas Papadimitriou | This can be either a LocalUserCreationForm for classic method signups
|
153 | e7cb4085 | Kostas Papadimitriou | or ThirdPartyUserCreationForm for users who chosen to signup using a
|
154 | e7cb4085 | Kostas Papadimitriou | federated login method.
|
155 | 0a569195 | Sofia Papagiannaki | """
|
156 | 0a569195 | Sofia Papagiannaki | main = provider.capitalize() if provider == 'local' else 'ThirdParty' |
157 | 5ce3ce4f | Sofia Papagiannaki | suffix = 'UserCreationForm'
|
158 | e7cb4085 | Kostas Papadimitriou | formclass = getattr(forms, '%s%s' % (main, suffix)) |
159 | e7cb4085 | Kostas Papadimitriou | kwargs['provider'] = provider
|
160 | e7cb4085 | Kostas Papadimitriou | return formclass(initial_data, **kwargs)
|
161 | e7cb4085 | Kostas Papadimitriou | |
162 | e7cb4085 | Kostas Papadimitriou | def prepare_user(self, user, email_verified=None): |
163 | e7cb4085 | Kostas Papadimitriou | """
|
164 | e7cb4085 | Kostas Papadimitriou | Initialization of a newly registered user. The method sets email
|
165 | e7cb4085 | Kostas Papadimitriou | verification code. If email_verified is set to True we automatically
|
166 | e7cb4085 | Kostas Papadimitriou | process user through the verification step.
|
167 | e7cb4085 | Kostas Papadimitriou | """
|
168 | e7cb4085 | Kostas Papadimitriou | logger.info("Initializing user registration %s", user.log_display)
|
169 | e7cb4085 | Kostas Papadimitriou | |
170 | e7cb4085 | Kostas Papadimitriou | if not email_verified: |
171 | e7cb4085 | Kostas Papadimitriou | email_verified = settings.SKIP_EMAIL_VERIFICATION |
172 | e7cb4085 | Kostas Papadimitriou | |
173 | e7cb4085 | Kostas Papadimitriou | user.renew_verification_code() |
174 | e7cb4085 | Kostas Papadimitriou | user.save() |
175 | e7cb4085 | Kostas Papadimitriou | |
176 | e7cb4085 | Kostas Papadimitriou | if email_verified:
|
177 | e7cb4085 | Kostas Papadimitriou | logger.info("Auto verifying user email. %s",
|
178 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
179 | e7cb4085 | Kostas Papadimitriou | return self.verify_user(user, |
180 | e7cb4085 | Kostas Papadimitriou | user.verification_code) |
181 | e7cb4085 | Kostas Papadimitriou | |
182 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.PENDING_VERIFICATION) |
183 | e7cb4085 | Kostas Papadimitriou | |
184 | e7cb4085 | Kostas Papadimitriou | def verify_user(self, user, verification_code): |
185 | e7cb4085 | Kostas Papadimitriou | """
|
186 | e7cb4085 | Kostas Papadimitriou | Process user verification using provided verification_code. This
|
187 | e7cb4085 | Kostas Papadimitriou | should take place in user activation view. If no moderation is enabled
|
188 | e7cb4085 | Kostas Papadimitriou | we automatically process user through activation process.
|
189 | e7cb4085 | Kostas Papadimitriou | """
|
190 | e7cb4085 | Kostas Papadimitriou | logger.info("Verifying user: %s", user.log_display)
|
191 | e7cb4085 | Kostas Papadimitriou | |
192 | e7cb4085 | Kostas Papadimitriou | if user.email_verified:
|
193 | e7cb4085 | Kostas Papadimitriou | logger.warning("User email already verified: %s",
|
194 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
195 | e7cb4085 | Kostas Papadimitriou | msg = astakos_messages.ACCOUNT_ALREADY_VERIFIED |
196 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
197 | e7cb4085 | Kostas Papadimitriou | |
198 | e7cb4085 | Kostas Papadimitriou | if user.verification_code and \ |
199 | e7cb4085 | Kostas Papadimitriou | user.verification_code == verification_code: |
200 | e7cb4085 | Kostas Papadimitriou | user.email_verified = True
|
201 | e7cb4085 | Kostas Papadimitriou | user.verified_at = datetime.datetime.now() |
202 | e7cb4085 | Kostas Papadimitriou | # invalidate previous code
|
203 | e7cb4085 | Kostas Papadimitriou | user.renew_verification_code() |
204 | e7cb4085 | Kostas Papadimitriou | user.save() |
205 | e7cb4085 | Kostas Papadimitriou | logger.info("User email verified: %s", user.log_display)
|
206 | e7cb4085 | Kostas Papadimitriou | else:
|
207 | e7cb4085 | Kostas Papadimitriou | logger.error("User email verification failed "
|
208 | e7cb4085 | Kostas Papadimitriou | "(invalid verification code): %s", user.log_display)
|
209 | e7cb4085 | Kostas Papadimitriou | msg = astakos_messages.VERIFICATION_FAILED |
210 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
211 | e7cb4085 | Kostas Papadimitriou | |
212 | e7cb4085 | Kostas Papadimitriou | if not self.moderation_enabled: |
213 | e7cb4085 | Kostas Papadimitriou | logger.warning("User preaccepted (%s): %s", 'auto_moderation', |
214 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
215 | e7cb4085 | Kostas Papadimitriou | return self.accept_user(user, policy='auto_moderation') |
216 | e7cb4085 | Kostas Papadimitriou | |
217 | e7cb4085 | Kostas Papadimitriou | preaccepted = self._is_preaccepted(user)
|
218 | e7cb4085 | Kostas Papadimitriou | if preaccepted:
|
219 | e7cb4085 | Kostas Papadimitriou | logger.warning("User preaccepted (%s): %s", preaccepted,
|
220 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
221 | e7cb4085 | Kostas Papadimitriou | return self.accept_user(user, policy=preaccepted) |
222 | e7cb4085 | Kostas Papadimitriou | |
223 | e7cb4085 | Kostas Papadimitriou | if user.moderated:
|
224 | e7cb4085 | Kostas Papadimitriou | # set moderated to false because accept_user will return error
|
225 | e7cb4085 | Kostas Papadimitriou | # result otherwise.
|
226 | e7cb4085 | Kostas Papadimitriou | user.moderated = False
|
227 | e7cb4085 | Kostas Papadimitriou | return self.accept_user(user, policy='already_moderated') |
228 | e7cb4085 | Kostas Papadimitriou | else:
|
229 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.PENDING_MODERATION) |
230 | e7cb4085 | Kostas Papadimitriou | |
231 | e7cb4085 | Kostas Papadimitriou | def accept_user(self, user, policy='manual'): |
232 | e7cb4085 | Kostas Papadimitriou | logger.info("Moderating user: %s", user.log_display)
|
233 | e7cb4085 | Kostas Papadimitriou | if user.moderated and user.is_active: |
234 | e7cb4085 | Kostas Papadimitriou | logger.warning("User already accepted, moderation"
|
235 | e7cb4085 | Kostas Papadimitriou | " skipped: %s", user.log_display)
|
236 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_ALREADY_MODERATED) |
237 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
238 | e7cb4085 | Kostas Papadimitriou | |
239 | e7cb4085 | Kostas Papadimitriou | if not user.email_verified: |
240 | e7cb4085 | Kostas Papadimitriou | logger.warning("Cannot accept unverified user: %s",
|
241 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
242 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_NOT_VERIFIED) |
243 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
244 | e7cb4085 | Kostas Papadimitriou | |
245 | e7cb4085 | Kostas Papadimitriou | # store a snapshot of user details by the time he
|
246 | e7cb4085 | Kostas Papadimitriou | # got accepted.
|
247 | e7cb4085 | Kostas Papadimitriou | if not user.accepted_email: |
248 | e7cb4085 | Kostas Papadimitriou | user.accepted_email = user.email |
249 | e7cb4085 | Kostas Papadimitriou | user.accepted_policy = policy |
250 | e7cb4085 | Kostas Papadimitriou | user.moderated = True
|
251 | e7cb4085 | Kostas Papadimitriou | user.moderated_at = datetime.datetime.now() |
252 | e7cb4085 | Kostas Papadimitriou | user.moderated_data = json.dumps(user.__dict__, |
253 | e7cb4085 | Kostas Papadimitriou | default=lambda obj:
|
254 | e7cb4085 | Kostas Papadimitriou | str(obj))
|
255 | e7cb4085 | Kostas Papadimitriou | user.save() |
256 | 2c960473 | Giorgos Korfiatis | functions.enable_base_project(user) |
257 | e7cb4085 | Kostas Papadimitriou | |
258 | e7cb4085 | Kostas Papadimitriou | if user.is_rejected:
|
259 | e7cb4085 | Kostas Papadimitriou | logger.warning("User has previously been "
|
260 | e7cb4085 | Kostas Papadimitriou | "rejected, reseting rejection state: %s",
|
261 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
262 | e7cb4085 | Kostas Papadimitriou | user.is_rejected = False
|
263 | e7cb4085 | Kostas Papadimitriou | user.rejected_at = None
|
264 | e7cb4085 | Kostas Papadimitriou | |
265 | e7cb4085 | Kostas Papadimitriou | user.save() |
266 | e7cb4085 | Kostas Papadimitriou | logger.info("User accepted: %s", user.log_display)
|
267 | e7cb4085 | Kostas Papadimitriou | self.activate_user(user)
|
268 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ACCEPTED) |
269 | e7cb4085 | Kostas Papadimitriou | |
270 | e7cb4085 | Kostas Papadimitriou | def activate_user(self, user): |
271 | e7cb4085 | Kostas Papadimitriou | if not user.email_verified: |
272 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_NOT_VERIFIED) |
273 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
274 | e7cb4085 | Kostas Papadimitriou | |
275 | e7cb4085 | Kostas Papadimitriou | if not user.moderated: |
276 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_NOT_MODERATED) |
277 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
278 | e7cb4085 | Kostas Papadimitriou | |
279 | e7cb4085 | Kostas Papadimitriou | if user.is_rejected:
|
280 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_REJECTED) |
281 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
282 | e7cb4085 | Kostas Papadimitriou | |
283 | e7cb4085 | Kostas Papadimitriou | if user.is_active:
|
284 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE) |
285 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
286 | e7cb4085 | Kostas Papadimitriou | |
287 | e7cb4085 | Kostas Papadimitriou | user.is_active = True
|
288 | e7cb4085 | Kostas Papadimitriou | user.deactivated_reason = None
|
289 | e7cb4085 | Kostas Papadimitriou | user.deactivated_at = None
|
290 | e7cb4085 | Kostas Papadimitriou | user.save() |
291 | e7cb4085 | Kostas Papadimitriou | logger.info("User activated: %s", user.log_display)
|
292 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ACTIVATED) |
293 | e7cb4085 | Kostas Papadimitriou | |
294 | e7cb4085 | Kostas Papadimitriou | def deactivate_user(self, user, reason=''): |
295 | e7cb4085 | Kostas Papadimitriou | user.is_active = False
|
296 | e7cb4085 | Kostas Papadimitriou | user.deactivated_reason = reason |
297 | e7cb4085 | Kostas Papadimitriou | if user.is_active:
|
298 | e7cb4085 | Kostas Papadimitriou | user.deactivated_at = datetime.datetime.now() |
299 | e7cb4085 | Kostas Papadimitriou | user.save() |
300 | e7cb4085 | Kostas Papadimitriou | logger.info("User deactivated: %s", user.log_display)
|
301 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.DEACTIVATED) |
302 | e7cb4085 | Kostas Papadimitriou | |
303 | e7cb4085 | Kostas Papadimitriou | def reject_user(self, user, reason): |
304 | e7cb4085 | Kostas Papadimitriou | logger.info("Rejecting user: %s", user.log_display)
|
305 | e7cb4085 | Kostas Papadimitriou | if user.moderated:
|
306 | e7cb4085 | Kostas Papadimitriou | logger.warning("User already moderated: %s", user.log_display)
|
307 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_ALREADY_MODERATED) |
308 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
309 | e7cb4085 | Kostas Papadimitriou | |
310 | e7cb4085 | Kostas Papadimitriou | if user.is_active:
|
311 | e7cb4085 | Kostas Papadimitriou | logger.warning("Cannot reject unverified user: %s",
|
312 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
313 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_NOT_VERIFIED) |
314 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
315 | e7cb4085 | Kostas Papadimitriou | |
316 | e7cb4085 | Kostas Papadimitriou | if not user.email_verified: |
317 | e7cb4085 | Kostas Papadimitriou | logger.warning("Cannot reject unverified user: %s",
|
318 | e7cb4085 | Kostas Papadimitriou | user.log_display) |
319 | e7cb4085 | Kostas Papadimitriou | msg = _(astakos_messages.ACCOUNT_NOT_VERIFIED) |
320 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.ERROR, msg) |
321 | e7cb4085 | Kostas Papadimitriou | |
322 | e7cb4085 | Kostas Papadimitriou | user.moderated = True
|
323 | e7cb4085 | Kostas Papadimitriou | user.moderated_at = datetime.datetime.now() |
324 | e7cb4085 | Kostas Papadimitriou | user.moderated_data = json.dumps(user.__dict__, |
325 | e7cb4085 | Kostas Papadimitriou | default=lambda obj:
|
326 | e7cb4085 | Kostas Papadimitriou | str(obj))
|
327 | e7cb4085 | Kostas Papadimitriou | user.is_rejected = True
|
328 | e7cb4085 | Kostas Papadimitriou | user.rejected_reason = reason |
329 | 4ed19ae2 | Giorgos Korfiatis | user.save() |
330 | e7cb4085 | Kostas Papadimitriou | logger.info("User rejected: %s", user.log_display)
|
331 | e7cb4085 | Kostas Papadimitriou | return ActivationResult(self.Result.REJECTED) |
332 | e7cb4085 | Kostas Papadimitriou | |
333 | e7cb4085 | Kostas Papadimitriou | def handle_registration(self, user, email_verified=False): |
334 | e7cb4085 | Kostas Papadimitriou | logger.info("Handling new user registration: %s", user.log_display)
|
335 | e7cb4085 | Kostas Papadimitriou | return self.prepare_user(user, email_verified=email_verified) |
336 | e7cb4085 | Kostas Papadimitriou | |
337 | e7cb4085 | Kostas Papadimitriou | def handle_verification(self, user, activation_code): |
338 | e7cb4085 | Kostas Papadimitriou | logger.info("Handling user email verirfication: %s", user.log_display)
|
339 | e7cb4085 | Kostas Papadimitriou | return self.verify_user(user, activation_code) |
340 | e7cb4085 | Kostas Papadimitriou | |
341 | e7cb4085 | Kostas Papadimitriou | def handle_moderation(self, user, accept=True, reject_reason=None): |
342 | e7cb4085 | Kostas Papadimitriou | logger.info("Handling user moderation (%r): %s",
|
343 | e7cb4085 | Kostas Papadimitriou | accept, user.log_display) |
344 | e7cb4085 | Kostas Papadimitriou | if accept:
|
345 | e7cb4085 | Kostas Papadimitriou | return self.accept_user(user) |
346 | e7cb4085 | Kostas Papadimitriou | else:
|
347 | e7cb4085 | Kostas Papadimitriou | return self.reject_user(user, reject_reason) |
348 | e7cb4085 | Kostas Papadimitriou | |
349 | e7cb4085 | Kostas Papadimitriou | def send_user_verification_email(self, user): |
350 | e7cb4085 | Kostas Papadimitriou | if user.is_active:
|
351 | e7cb4085 | Kostas Papadimitriou | raise Exception("User already active") |
352 | e7cb4085 | Kostas Papadimitriou | |
353 | e7cb4085 | Kostas Papadimitriou | # invalidate previous code
|
354 | e7cb4085 | Kostas Papadimitriou | user.renew_verification_code() |
355 | e7cb4085 | Kostas Papadimitriou | user.save() |
356 | e7cb4085 | Kostas Papadimitriou | functions.send_verification(user) |
357 | e7cb4085 | Kostas Papadimitriou | user.activation_sent = datetime.datetime.now() |
358 | e7cb4085 | Kostas Papadimitriou | user.save() |
359 | e7cb4085 | Kostas Papadimitriou | |
360 | e7cb4085 | Kostas Papadimitriou | def send_result_notifications(self, result, user): |
361 | e7cb4085 | Kostas Papadimitriou | """
|
362 | e7cb4085 | Kostas Papadimitriou | Send corresponding notifications based on the status of activation
|
363 | e7cb4085 | Kostas Papadimitriou | result.
|
364 | e7cb4085 | Kostas Papadimitriou |
|
365 | e7cb4085 | Kostas Papadimitriou | Result.PENDING_VERIRFICATION
|
366 | e7cb4085 | Kostas Papadimitriou | * Send user the email verification url
|
367 | e7cb4085 | Kostas Papadimitriou |
|
368 | e7cb4085 | Kostas Papadimitriou | Result.PENDING_MODERATION
|
369 | e7cb4085 | Kostas Papadimitriou | * Notify admin for account moderation
|
370 | e7cb4085 | Kostas Papadimitriou |
|
371 | e7cb4085 | Kostas Papadimitriou | Result.ACCEPTED
|
372 | e7cb4085 | Kostas Papadimitriou | * Send user greeting notification
|
373 | e7cb4085 | Kostas Papadimitriou |
|
374 | e7cb4085 | Kostas Papadimitriou | Result.REJECTED
|
375 | e7cb4085 | Kostas Papadimitriou | * Send nothing
|
376 | e7cb4085 | Kostas Papadimitriou | """
|
377 | e7cb4085 | Kostas Papadimitriou | if result.status == self.Result.PENDING_VERIFICATION: |
378 | e7cb4085 | Kostas Papadimitriou | logger.info("Sending notifications for user"
|
379 | e7cb4085 | Kostas Papadimitriou | " creation: %s", user.log_display)
|
380 | e7cb4085 | Kostas Papadimitriou | # email user that contains the activation link
|
381 | e7cb4085 | Kostas Papadimitriou | self.send_user_verification_email(user)
|
382 | e7cb4085 | Kostas Papadimitriou | # TODO: optionally notify admins for new accounts
|
383 | e7cb4085 | Kostas Papadimitriou | |
384 | e7cb4085 | Kostas Papadimitriou | if result.status == self.Result.PENDING_MODERATION: |
385 | e7cb4085 | Kostas Papadimitriou | logger.info("Sending notifications for user"
|
386 | e7cb4085 | Kostas Papadimitriou | " verification: %s", user.log_display)
|
387 | 8fb8d0cf | Giorgos Korfiatis | functions.send_account_pending_moderation_notification( |
388 | 8fb8d0cf | Giorgos Korfiatis | user, |
389 | 8fb8d0cf | Giorgos Korfiatis | self.pending_moderation_template_name)
|
390 | e7cb4085 | Kostas Papadimitriou | # TODO: notify user
|
391 | e7cb4085 | Kostas Papadimitriou | |
392 | e7cb4085 | Kostas Papadimitriou | if result.status == self.Result.ACCEPTED: |
393 | e7cb4085 | Kostas Papadimitriou | logger.info("Sending notifications for user"
|
394 | e7cb4085 | Kostas Papadimitriou | " moderation: %s", user.log_display)
|
395 | 8fb8d0cf | Giorgos Korfiatis | functions.send_account_activated_notification( |
396 | 8fb8d0cf | Giorgos Korfiatis | user, |
397 | 8fb8d0cf | Giorgos Korfiatis | self.activated_email_template_name)
|
398 | e7cb4085 | Kostas Papadimitriou | functions.send_greeting(user, |
399 | e7cb4085 | Kostas Papadimitriou | self.greeting_template_name)
|
400 | e7cb4085 | Kostas Papadimitriou | # TODO: notify admins
|
401 | e7cb4085 | Kostas Papadimitriou | |
402 | e7cb4085 | Kostas Papadimitriou | if result.status == self.Result.REJECTED: |
403 | e7cb4085 | Kostas Papadimitriou | logger.info("Sending notifications for user"
|
404 | e7cb4085 | Kostas Papadimitriou | " rejection: %s", user.log_display)
|
405 | e7cb4085 | Kostas Papadimitriou | # TODO: notify user and admins
|
406 | 8316698a | Sofia Papagiannaki | |
407 | 5ce3ce4f | Sofia Papagiannaki | |
408 | 0a569195 | Sofia Papagiannaki | class InvitationsBackend(ActivationBackend): |
409 | 890b0eaf | Sofia Papagiannaki | """
|
410 | 18ffbee1 | Sofia Papagiannaki | A activation backend which implements the following workflow: a user
|
411 | e7cb4085 | Kostas Papadimitriou | supplies the necessary registation information, if the request contains a
|
412 | e7cb4085 | Kostas Papadimitriou | valid inivation code the user is automatically activated otherwise an
|
413 | e7cb4085 | Kostas Papadimitriou | inactive user account is created and the user is going to receive an email
|
414 | e7cb4085 | Kostas Papadimitriou | as soon as an administrator activates his/her account.
|
415 | 890b0eaf | Sofia Papagiannaki | """
|
416 | bef3bf46 | Kostas Papadimitriou | |
417 | 0d48fd8f | Kostas Papadimitriou | def get_signup_form(self, invitation, provider='local', initial_data=None, |
418 | e7cb4085 | Kostas Papadimitriou | instance=None):
|
419 | 15efc749 | Sofia Papagiannaki | """
|
420 | b669d9c0 | Sofia Papagiannaki | Returns a form instance of the relevant class
|
421 | 5ce3ce4f | Sofia Papagiannaki |
|
422 | 0a569195 | Sofia Papagiannaki | raises Invitation.DoesNotExist and ValueError if invitation is consumed
|
423 | 0a569195 | Sofia Papagiannaki | or invitation username is reserved.
|
424 | 15efc749 | Sofia Papagiannaki | """
|
425 | e7cb4085 | Kostas Papadimitriou | self.invitation = invitation
|
426 | 0a569195 | Sofia Papagiannaki | prefix = 'Invited' if invitation else '' |
427 | d271dd21 | Kostas Papadimitriou | main = provider.capitalize() if provider == 'local' else 'ThirdParty' |
428 | 5ce3ce4f | Sofia Papagiannaki | suffix = 'UserCreationForm'
|
429 | e7cb4085 | Kostas Papadimitriou | formclass = getattr(forms, '%s%s%s' % (prefix, main, suffix)) |
430 | e7cb4085 | Kostas Papadimitriou | return formclass(initial_data, instance=instance)
|
431 | bef3bf46 | Kostas Papadimitriou | |
432 | e7cb4085 | Kostas Papadimitriou | def get_signup_initial_data(self, request, provider): |
433 | 890b0eaf | Sofia Papagiannaki | """
|
434 | e7cb4085 | Kostas Papadimitriou | Returns the necassary activation form depending the user is invited or
|
435 | e7cb4085 | Kostas Papadimitriou | not.
|
436 | bef3bf46 | Kostas Papadimitriou |
|
437 | 890b0eaf | Sofia Papagiannaki | Throws Invitation.DoesNotExist in case ``code`` is not valid.
|
438 | 890b0eaf | Sofia Papagiannaki | """
|
439 | 15efc749 | Sofia Papagiannaki | invitation = self.invitation
|
440 | 65d85494 | Sofia Papagiannaki | initial_data = None
|
441 | 890b0eaf | Sofia Papagiannaki | if request.method == 'GET': |
442 | 15efc749 | Sofia Papagiannaki | if invitation:
|
443 | e066eedb | Giorgos Korfiatis | first, last = models.split_realname(invitation.realname) |
444 | 5ce3ce4f | Sofia Papagiannaki | initial_data = {'email': invitation.username,
|
445 | 5ce3ce4f | Sofia Papagiannaki | 'inviter': invitation.inviter.realname,
|
446 | e066eedb | Giorgos Korfiatis | 'first_name': first,
|
447 | e066eedb | Giorgos Korfiatis | 'last_name': last,
|
448 | 5ce3ce4f | Sofia Papagiannaki | 'provider': provider}
|
449 | 15efc749 | Sofia Papagiannaki | else:
|
450 | 15efc749 | Sofia Papagiannaki | if provider == request.POST.get('provider', ''): |
451 | 15efc749 | Sofia Papagiannaki | initial_data = request.POST |
452 | 15efc749 | Sofia Papagiannaki | return initial_data
|
453 | bef3bf46 | Kostas Papadimitriou | |
454 | 890b0eaf | Sofia Papagiannaki | def _is_preaccepted(self, user): |
455 | 890b0eaf | Sofia Papagiannaki | """
|
456 | e7cb4085 | Kostas Papadimitriou | Extends _is_preaccepted and if there is a valid, not-consumed
|
457 | e7cb4085 | Kostas Papadimitriou | invitation code for the specific user returns True else returns False.
|
458 | 890b0eaf | Sofia Papagiannaki | """
|
459 | e7cb4085 | Kostas Papadimitriou | preaccepted = super(InvitationsBackend, self)._is_preaccepted(user) |
460 | e7cb4085 | Kostas Papadimitriou | if preaccepted:
|
461 | e7cb4085 | Kostas Papadimitriou | return preaccepted
|
462 | 5ed6816e | Sofia Papagiannaki | invitation = self.invitation
|
463 | 890b0eaf | Sofia Papagiannaki | if not invitation: |
464 | e7cb4085 | Kostas Papadimitriou | if not self.moderation_enabled: |
465 | e7cb4085 | Kostas Papadimitriou | return 'auto_moderation' |
466 | 5ed6816e | Sofia Papagiannaki | if invitation.username == user.email and not invitation.is_consumed: |
467 | 5ed6816e | Sofia Papagiannaki | invitation.consume() |
468 | e7cb4085 | Kostas Papadimitriou | return 'invitation' |
469 | 890b0eaf | Sofia Papagiannaki | return False |
470 | bef3bf46 | Kostas Papadimitriou | |
471 | 5ce3ce4f | Sofia Papagiannaki | |
472 | 0a569195 | Sofia Papagiannaki | class SimpleBackend(ActivationBackend): |
473 | 890b0eaf | Sofia Papagiannaki | """
|
474 | e7cb4085 | Kostas Papadimitriou | The common activation backend.
|
475 | 890b0eaf | Sofia Papagiannaki | """
|
476 | 9d20fe23 | Kostas Papadimitriou | |
477 | e7cb4085 | Kostas Papadimitriou | # shortcut
|
478 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus = ActivationBackend.Result |
479 | 8f5a3a06 | Sofia Papagiannaki | |
480 | 5ce3ce4f | Sofia Papagiannaki | |
481 | 8f5a3a06 | Sofia Papagiannaki | class ActivationResult(object): |
482 | 8f5a3a06 | Sofia Papagiannaki | |
483 | e7cb4085 | Kostas Papadimitriou | MESSAGE_BY_STATUS = { |
484 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.PENDING_VERIFICATION: |
485 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.VERIFICATION_SENT), |
486 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.PENDING_MODERATION: |
487 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.NOTIFICATION_SENT), |
488 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ACCEPTED: |
489 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.ACCOUNT_ACTIVATED), |
490 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ACTIVATED: |
491 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.ACCOUNT_ACTIVATED), |
492 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.DEACTIVATED: |
493 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.ACCOUNT_DEACTIVATED), |
494 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ERROR: |
495 | e7cb4085 | Kostas Papadimitriou | _(astakos_messages.GENERIC_ERROR) |
496 | e7cb4085 | Kostas Papadimitriou | } |
497 | e7cb4085 | Kostas Papadimitriou | |
498 | e7cb4085 | Kostas Papadimitriou | STATUS_DISPLAY = { |
499 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.PENDING_VERIFICATION: 'PENDING_VERIFICATION',
|
500 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.PENDING_MODERATION: 'PENDING_MODERATION',
|
501 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ACCEPTED: 'ACCEPTED',
|
502 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ACTIVATED: 'ACTIVATED',
|
503 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.DEACTIVATED: 'DEACTIVATED',
|
504 | e7cb4085 | Kostas Papadimitriou | ActivationResultStatus.ERROR: 'ERROR'
|
505 | e7cb4085 | Kostas Papadimitriou | } |
506 | 5ce3ce4f | Sofia Papagiannaki | |
507 | e7cb4085 | Kostas Papadimitriou | def __init__(self, status, message=None): |
508 | e7cb4085 | Kostas Papadimitriou | if message is None: |
509 | e7cb4085 | Kostas Papadimitriou | message = self.MESSAGE_BY_STATUS.get(status)
|
510 | e7cb4085 | Kostas Papadimitriou | |
511 | e7cb4085 | Kostas Papadimitriou | self.message = message
|
512 | e7cb4085 | Kostas Papadimitriou | self.status = status
|
513 | 8f5a3a06 | Sofia Papagiannaki | |
514 | e7cb4085 | Kostas Papadimitriou | def status_display(self): |
515 | e7cb4085 | Kostas Papadimitriou | return self.STATUS_DISPLAY.get(self.status) |
516 | 8f5a3a06 | Sofia Papagiannaki | |
517 | e7cb4085 | Kostas Papadimitriou | def __repr__(self): |
518 | e7cb4085 | Kostas Papadimitriou | return "ActivationResult [%s]: %s" % (self.status_display(), |
519 | e7cb4085 | Kostas Papadimitriou | self.message)
|
520 | 5ce3ce4f | Sofia Papagiannaki | |
521 | e7cb4085 | Kostas Papadimitriou | def is_error(self): |
522 | e7cb4085 | Kostas Papadimitriou | return self.status == ActivationResultStatus.ERROR |