1 from django.shortcuts import render_to_response, get_object_or_404
2 from django.template import RequestContext
3 from django.core.urlresolvers import reverse
4 from forum.models import User
5 from django.http import HttpResponseRedirect, Http404
6 from forum.http_responses import HttpResponseUnauthorized
7 from django.utils.safestring import mark_safe
8 from django.utils.translation import ugettext as _
9 from django.utils.http import urlquote_plus
10 from django.contrib.auth.decorators import login_required
11 from django.contrib.auth import login, logout
12 from django.http import get_host
13 from forum.actions import SuspendAction
14 from forum.utils import html
15 from forum import settings
16 from writers import manage_pending_data
21 from forum.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \
22 TemporaryLoginRequestForm, ChangePasswordForm, SetPasswordForm
23 from forum.utils.mail import send_template_email
25 from forum.authentication.base import InvalidAuthentication
26 from forum.authentication import AUTH_PROVIDERS
28 from forum.models import AuthKeyUserAssociation, ValidationHash, Question, Answer
29 from forum.actions import UserJoinsAction
31 def signin_page(request):
32 request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
34 all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
36 sort = lambda c1, c2: c1.weight - c2.weight
37 can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
39 bigicon_providers = sorted([
40 context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
43 smallicon_providers = sorted([
44 context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
47 top_stackitem_providers = sorted([
48 context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context)
51 stackitem_providers = sorted([
52 context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
56 msg = request.session['auth_error']
57 del request.session['auth_error']
61 return render_to_response(
65 'all_providers': all_providers,
66 'bigicon_providers': bigicon_providers,
67 'top_stackitem_providers': top_stackitem_providers,
68 'stackitem_providers': stackitem_providers,
69 'smallicon_providers': smallicon_providers,
71 RequestContext(request))
73 def prepare_provider_signin(request, provider):
74 force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
75 request.session['force_email_request'] = force_email_request
77 if provider in AUTH_PROVIDERS:
78 provider_class = AUTH_PROVIDERS[provider].consumer
81 request_url = provider_class.prepare_authentication_request(request,
82 reverse('auth_provider_done',
83 kwargs={'provider': provider}))
85 return HttpResponseRedirect(request_url)
86 except NotImplementedError, e:
87 return process_provider_signin(request, provider)
88 except InvalidAuthentication, e:
89 request.session['auth_error'] = e.message
91 return HttpResponseRedirect(reverse('auth_signin'))
96 def process_provider_signin(request, provider):
97 if provider in AUTH_PROVIDERS:
98 provider_class = AUTH_PROVIDERS[provider].consumer
101 assoc_key = provider_class.process_authentication_request(request)
102 except InvalidAuthentication, e:
103 request.session['auth_error'] = e.message
104 return HttpResponseRedirect(reverse('auth_signin'))
106 if request.user.is_authenticated():
107 if isinstance(assoc_key, (type, User)):
108 if request.user != assoc_key:
109 request.session['auth_error'] = _(
110 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
113 request.session['auth_error'] = _("You are already logged in with that user.")
116 assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
117 if assoc.user == request.user:
118 request.session['auth_error'] = _(
119 "These login credentials are already associated with your account.")
121 request.session['auth_error'] = _(
122 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
125 uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
127 request.user.message_set.create(
128 message=_('The new credentials are now associated with your account'))
129 return HttpResponseRedirect(reverse('user_authsettings', args=[request.user.id]))
131 return HttpResponseRedirect(reverse('auth_signin'))
133 if isinstance(assoc_key, User):
134 return login_and_forward(request, assoc_key)
137 assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
139 return login_and_forward(request, user_)
141 request.session['assoc_key'] = assoc_key
142 request.session['auth_provider'] = provider
143 return HttpResponseRedirect(reverse('auth_external_register'))
145 return HttpResponseRedirect(reverse('auth_signin'))
147 def external_register(request):
148 if request.method == 'POST' and 'bnewaccount' in request.POST:
149 form1 = SimpleRegistrationForm(request.POST)
150 email_feeds_form = SimpleEmailSubscribeForm(request.POST)
152 if (form1.is_valid() and email_feeds_form.is_valid()):
153 user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email'])
154 user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email']
155 user_.set_unusable_password()
157 if User.objects.all().count() == 0:
158 user_.is_superuser = True
159 user_.is_staff = True
162 UserJoinsAction(user=user_, ip=request.META['REMOTE_ADDR']).save()
165 assoc_key = request.session['assoc_key']
166 auth_provider = request.session['auth_provider']
168 request.session['auth_error'] = _(
169 "Oops, something went wrong in the middle of this process. Please try again. Note that you need to have cookies enabled for the authentication to work."
171 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
172 ["%s: %s" % (k, v) for k, v in request.META.items()]))
173 return HttpResponseRedirect(reverse('auth_signin'))
175 uassoc = AuthKeyUserAssociation(user=user_, key=assoc_key, provider=auth_provider)
178 if email_feeds_form.cleaned_data['subscribe'] == 'n':
179 user_.subscription_settings.enable_notifications = False
180 user_.subscription_settings.save()
182 del request.session['assoc_key']
183 del request.session['auth_provider']
185 return login_and_forward(request, user_, message=_("A welcome email has been sent to your email address. "))
187 auth_provider = request.session.get('auth_provider', None)
188 if not auth_provider:
189 request.session['auth_error'] = _(
190 "Oops, something went wrong in the middle of this process. Please try again.")
191 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
192 ["%s: %s" % (k, v) for k, v in request.META.items()]))
193 return HttpResponseRedirect(reverse('auth_signin'))
195 provider_class = AUTH_PROVIDERS[auth_provider].consumer
196 user_data = provider_class.get_user_data(request.session['assoc_key'])
199 user_data = request.session.get('auth_consumer_data', {})
201 username = user_data.get('username', '')
202 email = user_data.get('email', '')
205 request.session['auth_validated_email'] = email
207 form1 = SimpleRegistrationForm(initial={
209 'username': username,
212 email_feeds_form = SimpleEmailSubscribeForm()
214 provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
216 return render_to_response('auth/complete.html', {
218 'email_feeds_form': email_feeds_form,
219 'provider':mark_safe(provider_context.human_name),
220 'login_type':provider_context.id,
221 'gravatar_faq_url':reverse('faq') + '#gravatar',
222 }, context_instance=RequestContext(request))
224 def request_temp_login(request):
225 if request.method == 'POST':
226 form = TemporaryLoginRequestForm(request.POST)
229 users = form.user_cache
233 return forward_suspended_user(request, u, False)
237 hash = get_object_or_404(ValidationHash, user=u, type='templogin')
238 if hash.expiration < datetime.datetime.now():
240 return request_temp_login(request)
242 hash = ValidationHash.objects.create_new(u, 'templogin', [u.id])
244 send_template_email([u], "auth/temp_login_email.html", {'temp_login_code': hash})
246 request.user.message_set.create(message=_("An email has been sent with your temporary login key"))
248 return HttpResponseRedirect(reverse('index'))
250 form = TemporaryLoginRequestForm()
252 return render_to_response(
253 'auth/temp_login_request.html', {'form': form},
254 context_instance=RequestContext(request))
256 def temp_signin(request, user, code):
257 user = get_object_or_404(User, id=user)
259 if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])):
260 return login_and_forward(request, user, reverse('user_authsettings', kwargs={'id': user.id}),
262 "You are logged in with a temporary access key, please take the time to fix your issue with authentication."
267 def send_validation_email(request):
268 if not request.user.is_authenticated():
269 return HttpResponseUnauthorized(request)
272 hash = ValidationHash.objects.get(user=request.user, type='email')
273 if hash.expiration < datetime.datetime.now():
275 return send_validation_email(request)
277 hash = ValidationHash.objects.create_new(request.user, 'email', [request.user.email])
279 send_template_email([request.user], "auth/mail_validation.html", {'validation_code': hash})
280 request.user.message_set.create(message=_("A message with an email validation link was just sent to your address."))
281 return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
285 def validate_email(request, user, code):
286 user = get_object_or_404(User, id=user)
288 if (ValidationHash.objects.validate(code, user, 'email', [user.email])):
289 user.email_isvalid = True
291 return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated."))
296 def auth_settings(request, id):
297 user_ = get_object_or_404(User, id=id)
299 if not (request.user.is_superuser or request.user == user_):
300 return HttpResponseUnauthorized(request)
302 auth_keys = user_.auth_keys.all()
304 if request.user.is_superuser or (not user_.has_usable_password()):
305 FormClass = SetPasswordForm
307 FormClass = ChangePasswordForm
310 form = FormClass(request.POST, user=user_)
312 is_new_pass = not user_.has_usable_password()
313 user_.set_password(form.cleaned_data['password1'])
317 request.user.message_set.create(message=_("New password set"))
318 if not request.user.is_superuser:
319 form = ChangePasswordForm(user=user_)
321 request.user.message_set.create(message=_("Your password was changed"))
323 return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': user_.id}))
325 form = FormClass(user=user_)
330 provider = AUTH_PROVIDERS.get(k.provider, None)
332 if provider is not None:
333 name = "%s: %s" % (provider.context.human_name, provider.context.readable_key(k))
335 from forum.authentication.base import ConsumerTemplateContext
336 "unknown: %s" % ConsumerTemplateContext.readable_key(k)
338 auth_keys_list.append({
343 return render_to_response('auth/auth_settings.html', {
345 "can_view_private": (user_ == request.user) or request.user.is_superuser,
347 'has_password': user_.has_usable_password(),
348 'auth_keys': auth_keys_list,
349 'allow_local_auth': AUTH_PROVIDERS.get('local', None),
350 }, context_instance=RequestContext(request))
352 def remove_external_provider(request, id):
353 association = get_object_or_404(AuthKeyUserAssociation, id=id)
354 if not (request.user.is_superuser or request.user == association.user):
355 return HttpResponseUnauthorized(request)
357 request.user.message_set.create(message=_("You removed the association with %s") % association.provider)
359 return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': association.user.id}))
361 def login_and_forward(request, user, forward=None, message=None):
362 if user.is_suspended():
363 return forward_suspended_user(request, user)
365 user.backend = "django.contrib.auth.backends.ModelBackend"
369 message = _("Welcome back %s, you are now logged in") % user.username
371 request.user.message_set.create(message=message)
374 forward = request.session.get('on_signin_url', reverse('index'))
376 pending_data = request.session.get('pending_submission_data', None)
378 if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO):
379 submission_time = pending_data['time']
380 if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)):
381 del request.session['pending_submission_data']
382 elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)):
383 user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % (
384 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")),
385 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('review')}), _("review")),
386 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('cancel')}), _("cancel"))
389 return manage_pending_data(request, _('save'), forward)
391 return HttpResponseRedirect(forward)
393 def forward_suspended_user(request, user, show_private_msg=True):
394 message = _("Sorry, but this account is suspended")
396 msg_type = 'privatemsg'
398 msg_type = 'publicmsg'
400 suspension = user.suspension
402 message += (":<br />" + suspension.extra.get(msg_type, ''))
404 request.user.message_set.create(message)
405 return HttpResponseRedirect(reverse('index'))
408 def signout(request):
410 return HttpResponseRedirect(reverse('index'))