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 forum.views.decorators import login_required
 
  11 from forum.modules import decorate
 
  12 from django.contrib.auth import login, logout
 
  13 from django.http import get_host
 
  14 from forum.actions import SuspendAction
 
  15 from forum.utils import html
 
  16 from forum import settings
 
  17 from writers import manage_pending_data
 
  22 from forum.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \
 
  23         TemporaryLoginRequestForm, ChangePasswordForm, SetPasswordForm
 
  24 from forum.utils.mail import send_template_email
 
  26 from forum.authentication.base import InvalidAuthentication
 
  27 from forum.authentication import AUTH_PROVIDERS
 
  29 from forum.models import AuthKeyUserAssociation, ValidationHash, Question, Answer
 
  30 from forum.actions import UserJoinsAction
 
  32 def signin_page(request):
 
  33     request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
 
  35     if reverse('auth_signin') == request.session['on_signin_url'].replace(settings.APP_URL, ''):
 
  36         request.session['on_signin_url'] = reverse('index')
 
  38     all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
 
  40     sort = lambda c1, c2: c1.weight - c2.weight
 
  41     can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
 
  43     bigicon_providers = sorted([
 
  44     context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
 
  47     smallicon_providers = sorted([
 
  48     context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
 
  51     top_stackitem_providers = sorted([
 
  52     context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context)
 
  55     stackitem_providers = sorted([
 
  56     context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
 
  60         msg = request.session['auth_error']
 
  61         del request.session['auth_error']
 
  65     return render_to_response(
 
  69             'all_providers': all_providers,
 
  70             'bigicon_providers': bigicon_providers,
 
  71             'top_stackitem_providers': top_stackitem_providers,
 
  72             'stackitem_providers': stackitem_providers,
 
  73             'smallicon_providers': smallicon_providers,
 
  75             RequestContext(request))
 
  77 def prepare_provider_signin(request, provider):
 
  78     force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
 
  79     request.session['force_email_request'] = force_email_request
 
  81     if provider in AUTH_PROVIDERS:
 
  82         provider_class = AUTH_PROVIDERS[provider].consumer
 
  85             request_url = provider_class.prepare_authentication_request(request,
 
  86                                                                         reverse('auth_provider_done',
 
  87                                                                                 kwargs={'provider': provider}))
 
  89             return HttpResponseRedirect(request_url)
 
  90         except NotImplementedError, e:
 
  91             return process_provider_signin(request, provider)
 
  92         except InvalidAuthentication, e:
 
  93             request.session['auth_error'] = e.message
 
  95         return HttpResponseRedirect(reverse('auth_signin'))
 
 100 def process_provider_signin(request, provider):
 
 101     if provider in AUTH_PROVIDERS:
 
 102         provider_class = AUTH_PROVIDERS[provider].consumer
 
 105             assoc_key = provider_class.process_authentication_request(request)
 
 106         except InvalidAuthentication, e:
 
 107             request.session['auth_error'] = e.message
 
 108             return HttpResponseRedirect(reverse('auth_signin'))
 
 110         if request.user.is_authenticated():
 
 111             if isinstance(assoc_key, (type, User)):
 
 112                 if request.user != assoc_key:
 
 113                     request.session['auth_error'] = _(
 
 114                             "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
 
 117                     request.session['auth_error'] = _("You are already logged in with that user.")
 
 120                     assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
 
 121                     if assoc.user == request.user:
 
 122                         request.session['auth_error'] = _(
 
 123                                 "These login credentials are already associated with your account.")
 
 125                         request.session['auth_error'] = _(
 
 126                                 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
 
 129                     uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
 
 131                     request.user.message_set.create(
 
 132                             message=_('The new credentials are now associated with your account'))
 
 133                     return HttpResponseRedirect(reverse('user_authsettings', args=[request.user.id]))
 
 135             return HttpResponseRedirect(reverse('auth_signin'))
 
 137             if isinstance(assoc_key, User):
 
 138                 return login_and_forward(request, assoc_key)
 
 141             assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
 
 143             return login_and_forward(request, user_)
 
 145             request.session['assoc_key'] = assoc_key
 
 146             request.session['auth_provider'] = provider
 
 147             return HttpResponseRedirect(reverse('auth_external_register'))
 
 149     return HttpResponseRedirect(reverse('auth_signin'))
 
 151 def external_register(request):
 
 152     if request.method == 'POST' and 'bnewaccount' in request.POST:
 
 153         form1 = SimpleRegistrationForm(request.POST)
 
 154         email_feeds_form = SimpleEmailSubscribeForm(request.POST)
 
 156         if (form1.is_valid() and email_feeds_form.is_valid()):
 
 157             user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email'])
 
 158             user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email']
 
 159             user_.set_unusable_password()
 
 161             if User.objects.all().count() == 0:
 
 162                 user_.is_superuser = True
 
 163                 user_.is_staff = True
 
 166             UserJoinsAction(user=user_, ip=request.META['REMOTE_ADDR']).save()
 
 169                 assoc_key = request.session['assoc_key']
 
 170                 auth_provider = request.session['auth_provider']
 
 172                 request.session['auth_error'] = _(
 
 173                         "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."
 
 175                 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
 
 176                         ["%s: %s" % (k, v) for k, v in request.META.items()]))
 
 177                 return HttpResponseRedirect(reverse('auth_signin'))
 
 179             uassoc = AuthKeyUserAssociation(user=user_, key=assoc_key, provider=auth_provider)
 
 182             if email_feeds_form.cleaned_data['subscribe'] == 'n':
 
 183                 user_.subscription_settings.enable_notifications = False
 
 184                 user_.subscription_settings.save()
 
 186             del request.session['assoc_key']
 
 187             del request.session['auth_provider']
 
 189             return login_and_forward(request, user_, message=_("A welcome email has been sent to your email address. "))
 
 191         auth_provider = request.session.get('auth_provider', None)
 
 192         if not auth_provider:
 
 193             request.session['auth_error'] = _(
 
 194                     "Oops, something went wrong in the middle of this process. Please try again.")
 
 195             logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
 
 196                     ["%s: %s" % (k, v) for k, v in request.META.items()]))
 
 197             return HttpResponseRedirect(reverse('auth_signin'))
 
 199         provider_class = AUTH_PROVIDERS[auth_provider].consumer
 
 200         user_data = provider_class.get_user_data(request.session['assoc_key'])
 
 203             user_data = request.session.get('auth_consumer_data', {})
 
 205         username = user_data.get('username', '')
 
 206         email = user_data.get('email', '')
 
 209             request.session['auth_validated_email'] = email
 
 211         form1 = SimpleRegistrationForm(initial={
 
 213         'username': username,
 
 216         email_feeds_form = SimpleEmailSubscribeForm()
 
 218     provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
 
 220     return render_to_response('auth/complete.html', {
 
 222     'email_feeds_form': email_feeds_form,
 
 223     'provider':mark_safe(provider_context.human_name),
 
 224     'login_type':provider_context.id,
 
 225     'gravatar_faq_url':reverse('faq') + '#gravatar',
 
 226     }, context_instance=RequestContext(request))
 
 228 def request_temp_login(request):
 
 229     if request.method == 'POST':
 
 230         form = TemporaryLoginRequestForm(request.POST)
 
 233             users = form.user_cache
 
 237                     return forward_suspended_user(request, u, False)
 
 241                     hash = get_object_or_404(ValidationHash, user=u, type='templogin')
 
 242                     if hash.expiration < datetime.datetime.now():
 
 244                         return request_temp_login(request)
 
 246                     hash = ValidationHash.objects.create_new(u, 'templogin', [u.id])
 
 248                 send_template_email([u], "auth/temp_login_email.html", {'temp_login_code': hash})
 
 250                 request.user.message_set.create(message=_("An email has been sent with your temporary login key"))
 
 252             return HttpResponseRedirect(reverse('index'))
 
 254         form = TemporaryLoginRequestForm()
 
 256     return render_to_response(
 
 257             'auth/temp_login_request.html', {'form': form},
 
 258             context_instance=RequestContext(request))
 
 260 def temp_signin(request, user, code):
 
 261     user = get_object_or_404(User, id=user)
 
 263     if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])):
 
 265         # If the user requests temp_signin he must have forgotten his password. So we mark it as unusable.
 
 266         user.set_unusable_password()
 
 269         return login_and_forward(request, user, reverse('user_authsettings', kwargs={'id': user.id}),
 
 271                                          "You are logged in with a temporary access key, please take the time to fix your issue with authentication."
 
 276 def send_validation_email(request):
 
 277     if not request.user.is_authenticated():
 
 278         return HttpResponseUnauthorized(request)
 
 281             hash = ValidationHash.objects.get(user=request.user, type='email')
 
 284             # If we were able to get a previous validation hash we should raise an
 
 285             # Exception immediately. Otherwise new validation hash will not be created
 
 286             # and users will not receive the desired e-mail vaidation link.
 
 287             raise Exception("Validation has already been sent")
 
 289             hash = ValidationHash.objects.create_new(request.user, 'email', [request.user.email])
 
 291         send_template_email([request.user], "auth/mail_validation.html", {'validation_code': hash})
 
 292         request.user.message_set.create(message=_("A message with an email validation link was just sent to your address."))
 
 293         return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
 
 297 def validate_email(request, user, code):
 
 298     user = get_object_or_404(User, id=user)
 
 300     if (ValidationHash.objects.validate(code, user, 'email', [user.email])):
 
 301         user.email_isvalid = True
 
 303         return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated."))
 
 305         return render_to_response('auth/mail_already_validated.html', { 'user' : user }, RequestContext(request))
 
 307 def auth_settings(request, id):
 
 308     user_ = get_object_or_404(User, id=id)
 
 310     if not (request.user.is_superuser or request.user == user_):
 
 311         return HttpResponseUnauthorized(request)
 
 313     auth_keys = user_.auth_keys.all()
 
 315     if request.user.is_superuser or (not user_.has_usable_password()):
 
 316         FormClass = SetPasswordForm
 
 318         FormClass = ChangePasswordForm
 
 321         form = FormClass(request.POST, user=user_)
 
 323             is_new_pass = not user_.has_usable_password()
 
 324             user_.set_password(form.cleaned_data['password1'])
 
 328                 request.user.message_set.create(message=_("New password set"))
 
 329                 if not request.user.is_superuser:
 
 330                     form = ChangePasswordForm(user=user_)
 
 332                 request.user.message_set.create(message=_("Your password was changed"))
 
 334             return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': user_.id}))
 
 336         form = FormClass(user=user_)
 
 341         provider = AUTH_PROVIDERS.get(k.provider, None)
 
 343         if provider is not None:
 
 344             name =  "%s: %s" % (provider.context.human_name, provider.context.readable_key(k))
 
 346             from forum.authentication.base import ConsumerTemplateContext
 
 347             "unknown: %s" % ConsumerTemplateContext.readable_key(k)
 
 349         auth_keys_list.append({
 
 354     return render_to_response('auth/auth_settings.html', {
 
 356     "can_view_private": (user_ == request.user) or request.user.is_superuser,
 
 358     'has_password': user_.has_usable_password(),
 
 359     'auth_keys': auth_keys_list,
 
 360     'allow_local_auth': AUTH_PROVIDERS.get('local', None),
 
 361     }, context_instance=RequestContext(request))
 
 363 def remove_external_provider(request, id):
 
 364     association = get_object_or_404(AuthKeyUserAssociation, id=id)
 
 365     if not (request.user.is_superuser or request.user == association.user):
 
 366         return HttpResponseUnauthorized(request)
 
 368     request.user.message_set.create(message=_("You removed the association with %s") % association.provider)
 
 370     return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': association.user.id}))
 
 372 def login_and_forward(request, user, forward=None, message=None):
 
 373     if user.is_suspended():
 
 374         return forward_suspended_user(request, user)
 
 376     user.backend = "django.contrib.auth.backends.ModelBackend"
 
 380         message = _("Welcome back %s, you are now logged in") % user.username
 
 382     request.user.message_set.create(message=message)
 
 385         forward = request.session.get('on_signin_url', reverse('index'))
 
 387     pending_data = request.session.get('pending_submission_data', None)
 
 389     if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO):
 
 390         submission_time = pending_data['time']
 
 391         if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)):
 
 392             del request.session['pending_submission_data']
 
 393         elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)):
 
 394             user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % (
 
 395                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")),
 
 396                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('review')}), _("review")),
 
 397                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('cancel')}), _("cancel"))
 
 400             return manage_pending_data(request, _('save'), forward)
 
 402     return HttpResponseRedirect(forward)
 
 404 def forward_suspended_user(request, user, show_private_msg=True):
 
 405     message = _("Sorry, but this account is suspended")
 
 407         msg_type = 'privatemsg'
 
 409         msg_type = 'publicmsg'
 
 411     suspension = user.suspension
 
 413         message += (":<br />" + suspension.extra.get(msg_type, ''))
 
 415     request.user.message_set.create(message)
 
 416     return HttpResponseRedirect(reverse('index'))
 
 418 @decorate.withfn(login_required)
 
 419 def signout(request):
 
 421     return HttpResponseRedirect(reverse('index'))