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