X-Git-Url: https://git.openstreetmap.org./osqa.git/blobdiff_plain/ca88510e4dd14206f623cababfda1e8a9daf70d4..2444d6ffd7995be782615589fb1d5aef2c8277ce:/forum/views/auth.py diff --git a/forum/views/auth.py b/forum/views/auth.py index 6b7f5d1..ad4785d 100644 --- a/forum/views/auth.py +++ b/forum/views/auth.py @@ -2,19 +2,26 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.core.urlresolvers import reverse from forum.models import User -from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden +from django.http import HttpResponseRedirect, Http404 +from forum.http_responses import HttpResponseUnauthorized from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ from django.utils.http import urlquote_plus -from django.contrib.auth.decorators import login_required -from django.contrib.auth import login, logout +from forum.views.decorators import login_required +from forum.modules import decorate +from django.contrib.auth import login, logout from django.http import get_host +from forum.actions import SuspendAction +from forum.utils import html +from forum import settings +from writers import manage_pending_data import types import datetime +import logging -from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \ +from forum.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \ TemporaryLoginRequestForm, ChangePasswordForm, SetPasswordForm -from forum.utils.mail import send_email +from forum.utils.mail import send_template_email from forum.authentication.base import InvalidAuthentication from forum.authentication import AUTH_PROVIDERS @@ -22,12 +29,11 @@ from forum.authentication import AUTH_PROVIDERS from forum.models import AuthKeyUserAssociation, ValidationHash, Question, Answer from forum.actions import UserJoinsAction -def signin_page(request, action=None): - if action is None: - request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/') - else: - request.session['on_signin_action'] = action - request.session['on_signin_url'] = reverse('auth_action_signin', kwargs={'action': action}) +def signin_page(request): + request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/') + + if reverse('auth_signin') == request.session['on_signin_url'].replace(settings.APP_URL, ''): + request.session['on_signin_url'] = reverse('index') all_providers = [provider.context for provider in AUTH_PROVIDERS.values()] @@ -35,19 +41,19 @@ def signin_page(request, action=None): can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user bigicon_providers = sorted([ - context for context in all_providers if context.mode == 'BIGICON' and can_show(context) + context for context in all_providers if context.mode == 'BIGICON' and can_show(context) ], sort) smallicon_providers = sorted([ - context for context in all_providers if context.mode == 'SMALLICON' and can_show(context) + context for context in all_providers if context.mode == 'SMALLICON' and can_show(context) ], sort) top_stackitem_providers = sorted([ - context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context) + context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context) ], sort) stackitem_providers = sorted([ - context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context) + context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context) ], sort) try: @@ -59,25 +65,26 @@ def signin_page(request, action=None): return render_to_response( 'auth/signin.html', { - 'msg': msg, - 'all_providers': all_providers, - 'bigicon_providers': bigicon_providers, - 'top_stackitem_providers': top_stackitem_providers, - 'stackitem_providers': stackitem_providers, - 'smallicon_providers': smallicon_providers, + 'msg': msg, + 'all_providers': all_providers, + 'bigicon_providers': bigicon_providers, + 'top_stackitem_providers': top_stackitem_providers, + 'stackitem_providers': stackitem_providers, + 'smallicon_providers': smallicon_providers, }, RequestContext(request)) def prepare_provider_signin(request, provider): force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes' request.session['force_email_request'] = force_email_request - + if provider in AUTH_PROVIDERS: provider_class = AUTH_PROVIDERS[provider].consumer try: request_url = provider_class.prepare_authentication_request(request, - reverse('auth_provider_done', kwargs={'provider': provider})) + reverse('auth_provider_done', + kwargs={'provider': provider})) return HttpResponseRedirect(request_url) except NotImplementedError, e: @@ -103,26 +110,32 @@ def process_provider_signin(request, provider): if request.user.is_authenticated(): if isinstance(assoc_key, (type, User)): if request.user != assoc_key: - request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.") + request.session['auth_error'] = _( + "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again." + ) else: request.session['auth_error'] = _("You are already logged in with that user.") else: try: assoc = AuthKeyUserAssociation.objects.get(key=assoc_key) if assoc.user == request.user: - request.session['auth_error'] = _("These login credentials are already associated with your account.") + request.session['auth_error'] = _( + "These login credentials are already associated with your account.") else: - request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.") + request.session['auth_error'] = _( + "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again." + ) except: uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider) uassoc.save() - request.user.message_set.create(message=_('The new credentials are now associated with your account')) + request.user.message_set.create( + message=_('The new credentials are now associated with your account')) return HttpResponseRedirect(reverse('user_authsettings', args=[request.user.id])) return HttpResponseRedirect(reverse('auth_signin')) else: if isinstance(assoc_key, User): - return login_and_forward(request, assoc_key) + return login_and_forward(request, assoc_key) try: assoc = AuthKeyUserAssociation.objects.get(key=assoc_key) @@ -147,19 +160,21 @@ def external_register(request): if User.objects.all().count() == 0: user_.is_superuser = True - + user_.is_staff = True + user_.save() UserJoinsAction(user=user_, ip=request.META['REMOTE_ADDR']).save() - if not user_.email_isvalid: - send_validation_email(user_) - try: assoc_key = request.session['assoc_key'] auth_provider = request.session['auth_provider'] except: - request.session['auth_error'] = _("Oops, something went wrong in the middle of this process. Please try again.") - return HttpResponseRedirect(request.session.get('on_signin_url', reverse('auth_signin'))) + request.session['auth_error'] = _( + "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." + ) + logging.error("Missing session data when trying to complete user registration: %s" % ", ".join( + ["%s: %s" % (k, v) for k, v in request.META.items()])) + return HttpResponseRedirect(reverse('auth_signin')) uassoc = AuthKeyUserAssociation(user=user_, key=assoc_key, provider=auth_provider) uassoc.save() @@ -171,9 +186,17 @@ def external_register(request): del request.session['assoc_key'] del request.session['auth_provider'] - return login_and_forward(request, user_) + return login_and_forward(request, user_, message=_("A welcome email has been sent to your email address. ")) else: - provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer + auth_provider = request.session.get('auth_provider', None) + if not auth_provider: + request.session['auth_error'] = _( + "Oops, something went wrong in the middle of this process. Please try again.") + logging.error("Missing session data when trying to complete user registration: %s" % ", ".join( + ["%s: %s" % (k, v) for k, v in request.META.items()])) + return HttpResponseRedirect(reverse('auth_signin')) + + provider_class = AUTH_PROVIDERS[auth_provider].consumer user_data = provider_class.get_user_data(request.session['assoc_key']) if not user_data: @@ -186,20 +209,20 @@ def external_register(request): request.session['auth_validated_email'] = email form1 = SimpleRegistrationForm(initial={ - 'next': '/', - 'username': username, - 'email': email, + 'next': '/', + 'username': username, + 'email': email, }) email_feeds_form = SimpleEmailSubscribeForm() provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context return render_to_response('auth/complete.html', { - 'form1': form1, - 'email_feeds_form': email_feeds_form, - 'provider':mark_safe(provider_context.human_name), - 'login_type':provider_context.id, - 'gravatar_faq_url':reverse('faq') + '#gravatar', + 'form1': form1, + 'email_feeds_form': email_feeds_form, + 'provider':mark_safe(provider_context.human_name), + 'login_type':provider_context.id, + 'gravatar_faq_url':reverse('faq') + '#gravatar', }, context_instance=RequestContext(request)) def request_temp_login(request): @@ -207,46 +230,69 @@ def request_temp_login(request): form = TemporaryLoginRequestForm(request.POST) if form.is_valid(): - user = form.user_cache + users = form.user_cache - try: - hash = get_object_or_404(ValidationHash, user=user, type='templogin') - if hash.expiration < datetime.datetime.now(): - hash.delete() - return request_temp_login(request) - except: - hash = ValidationHash.objects.create_new(user, 'templogin', [user.id]) + for u in users: + if u.is_suspended(): + return forward_suspended_user(request, u, False) - send_email(_("Temporary login link"), [(user.username, user.email)], "auth/temp_login_email.html", { - 'temp_login_code': hash, - 'user': user - }) + for u in users: + try: + hash = get_object_or_404(ValidationHash, user=u, type='templogin') + if hash.expiration < datetime.datetime.now(): + hash.delete() + return request_temp_login(request) + except: + hash = ValidationHash.objects.create_new(u, 'templogin', [u.id]) - request.user.message_set.create(message=_("An email has been sent with your temporary login key")) + send_template_email([u], "auth/temp_login_email.html", {'temp_login_code': hash}) + + request.user.message_set.create(message=_("An email has been sent with your temporary login key")) return HttpResponseRedirect(reverse('index')) else: form = TemporaryLoginRequestForm() return render_to_response( - 'auth/temp_login_request.html', {'form': form}, + 'auth/temp_login_request.html', {'form': form}, context_instance=RequestContext(request)) def temp_signin(request, user, code): user = get_object_or_404(User, id=user) if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])): - return login_and_forward(request, user, reverse('user_authsettings'), - _("You are logged in with a temporary access key, please take the time to fix your issue with authentication.")) + + # If the user requests temp_signin he must have forgotten his password. So we mark it as unusable. + user.set_unusable_password() + user.save() + + return login_and_forward(request, user, reverse('user_authsettings', kwargs={'id': user.id}), + _( + "You are logged in with a temporary access key, please take the time to fix your issue with authentication." + )) else: raise Http404() -def send_validation_email(user): - hash = ValidationHash.objects.create_new(user, 'email', [user.email]) - send_email(_("Email Validation"), [(user.username, user.email)], "auth/email_validation.html", { - 'validation_code': hash, - 'user': user - }) +def send_validation_email(request): + if not request.user.is_authenticated(): + return HttpResponseUnauthorized(request) + else: + try: + hash = ValidationHash.objects.get(user=request.user, type='email') + hash.delete() + + # If we were able to get a previous validation hash we should raise an + # Exception immediately. Otherwise new validation hash will not be created + # and users will not receive the desired e-mail vaidation link. + raise Exception("Validation has already been sent") + except: + hash = ValidationHash.objects.create_new(request.user, 'email', [request.user.email]) + + send_template_email([request.user], "auth/mail_validation.html", {'validation_code': hash}) + request.user.message_set.create(message=_("A message with an email validation link was just sent to your address.")) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + def validate_email(request, user, code): user = get_object_or_404(User, id=user) @@ -254,38 +300,40 @@ def validate_email(request, user, code): if (ValidationHash.objects.validate(code, user, 'email', [user.email])): user.email_isvalid = True user.save() - return login_and_forward(request, user, None, _("Thank you, your email is now validated.")) + return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated.")) else: - raise Http404() + return render_to_response('auth/mail_already_validated.html', { 'user' : user }, RequestContext(request)) -@login_required def auth_settings(request, id): user_ = get_object_or_404(User, id=id) if not (request.user.is_superuser or request.user == user_): - return HttpResponseForbidden() + return HttpResponseUnauthorized(request) auth_keys = user_.auth_keys.all() - if user_.has_usable_password(): - FormClass = ChangePasswordForm - else: + if request.user.is_superuser or (not user_.has_usable_password()): FormClass = SetPasswordForm + else: + FormClass = ChangePasswordForm if request.POST: form = FormClass(request.POST, user=user_) if form.is_valid(): - if user_.has_usable_password(): - request.user.message_set.create(message=_("Your password was changed")) - else: - request.user.message_set.create(message=_("New password set")) - FormClass = ChangePasswordForm - + is_new_pass = not user_.has_usable_password() user_.set_password(form.cleaned_data['password1']) user_.save() + + if is_new_pass: + request.user.message_set.create(message=_("New password set")) + if not request.user.is_superuser: + form = ChangePasswordForm(user=user_) + else: + request.user.message_set.create(message=_("Your password was changed")) + return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': user_.id})) - - form = FormClass(user=user_) + else: + form = FormClass(user=user_) auth_keys_list = [] @@ -299,83 +347,75 @@ def auth_settings(request, id): "unknown: %s" % ConsumerTemplateContext.readable_key(k) auth_keys_list.append({ - 'name': name, - 'id': k.id + 'name': name, + 'id': k.id }) return render_to_response('auth/auth_settings.html', { - 'view_user': user_, - "can_view_private": (user_ == request.user) or request.user.is_superuser, - 'form': form, - 'has_password': user_.has_usable_password(), - 'auth_keys': auth_keys_list, + 'view_user': user_, + "can_view_private": (user_ == request.user) or request.user.is_superuser, + 'form': form, + 'has_password': user_.has_usable_password(), + 'auth_keys': auth_keys_list, + 'allow_local_auth': AUTH_PROVIDERS.get('local', None), }, context_instance=RequestContext(request)) def remove_external_provider(request, id): association = get_object_or_404(AuthKeyUserAssociation, id=id) if not (request.user.is_superuser or request.user == association.user): - return HttpResponseForbidden() + return HttpResponseUnauthorized(request) request.user.message_set.create(message=_("You removed the association with %s") % association.provider) association.delete() return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': association.user.id})) -def newquestion_signin_action(user): - question = Question.objects.filter(author=user).order_by('-added_at')[0] - return question.get_absolute_url() +def login_and_forward(request, user, forward=None, message=None): + if user.is_suspended(): + return forward_suspended_user(request, user) -def newanswer_signin_action(user): - answer = Answer.objects.filter(author=user).order_by('-added_at')[0] - return answer.get_absolute_url() - -POST_SIGNIN_ACTIONS = { - 'newquestion': newquestion_signin_action, - 'newanswer': newanswer_signin_action, -} - -def login_and_forward(request, user, forward=None, message=None): - old_session = request.session.session_key user.backend = "django.contrib.auth.backends.ModelBackend" - login(request, user) + login(request, user) - temp_data = request.session.pop('temp_node_data', None) - if temp_data: - request.POST = temp_data - node_type = request.session.pop('temp_node_type') + if message is None: + message = _("Welcome back %s, you are now logged in") % user.username - if node_type == "question": - from forum.views.writers import ask - return ask(request) - elif node_type == "answer": - from forum.views.writers import answer - return answer(request, request.session.pop('temp_question_id')) + request.user.message_set.create(message=message) if not forward: - signin_action = request.session.get('on_signin_action', None) - if not signin_action: - forward = request.session.get('on_signin_url', None) - - if not forward: - forward = reverse('index') + forward = request.session.get('on_signin_url', reverse('index')) + + pending_data = request.session.get('pending_submission_data', None) + + if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO): + submission_time = pending_data['time'] + if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)): + del request.session['pending_submission_data'] + elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)): + user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % ( + html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")), + html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('review')}), _("review")), + html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('cancel')}), _("cancel")) + )) else: - try: - forward = POST_SIGNIN_ACTIONS[signin_action](user) - except: - forward = reverse('index') + return manage_pending_data(request, _('save'), forward) - if message is None: - message = _("Welcome back %s, you are now logged in") % user.username - - request.user.message_set.create(message=message) return HttpResponseRedirect(forward) -@login_required -def signout(request): - """ - signout from the website. Remove openid from session and kill it. +def forward_suspended_user(request, user, show_private_msg=True): + message = _("Sorry, but this account is suspended") + if show_private_msg: + msg_type = 'privatemsg' + else: + msg_type = 'publicmsg' + + suspension = user.suspension + if suspension: + message += (":
" + suspension.extra.get(msg_type, '')) - url : /signout/" - """ + request.user.message_set.create(message) + return HttpResponseRedirect(reverse('index')) +@decorate.withfn(login_required) +def signout(request): logout(request) - return HttpResponseRedirect(reverse('index')) \ No newline at end of file + return HttpResponseRedirect(reverse('index'))