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, TemporaryLoginRequestForm, \
 
  23         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 from forum.settings import REP_GAIN_BY_EMAIL_VALIDATION
 
  33 from vars import ON_SIGNIN_SESSION_ATTR, PENDING_SUBMISSION_SESSION_ATTR
 
  35 def signin_page(request):
 
  36     referer = request.META.get('HTTP_REFERER', '/')
 
  38     # If the referer is equal to the sign up page, e. g. if the previous login attempt was not successful we do not
 
  39     # change the sign in URL. The user should go to the same page.
 
  40     if not referer.replace(settings.APP_URL, '') == reverse('auth_signin'):
 
  41         request.session[ON_SIGNIN_SESSION_ATTR] = referer
 
  43     all_providers = [provider.context for provider in AUTH_PROVIDERS.values() if provider.context]
 
  45     sort = lambda c1, c2: c1.weight - c2.weight
 
  46     can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
 
  48     bigicon_providers = sorted([
 
  49     context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
 
  52     smallicon_providers = sorted([
 
  53     context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
 
  56     top_stackitem_providers = sorted([
 
  57     context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context)
 
  60     stackitem_providers = sorted([
 
  61     context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
 
  65         msg = request.session['auth_error']
 
  66         del request.session['auth_error']
 
  70     return render_to_response(
 
  74             'all_providers': all_providers,
 
  75             'bigicon_providers': bigicon_providers,
 
  76             'top_stackitem_providers': top_stackitem_providers,
 
  77             'stackitem_providers': stackitem_providers,
 
  78             'smallicon_providers': smallicon_providers,
 
  80             RequestContext(request))
 
  82 def prepare_provider_signin(request, provider):
 
  83     force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
 
  84     request.session['force_email_request'] = force_email_request
 
  86     if provider in AUTH_PROVIDERS:
 
  87         provider_class = AUTH_PROVIDERS[provider].consumer
 
  90             request_url = provider_class.prepare_authentication_request(request,
 
  91                                                                         reverse('auth_provider_done',
 
  92                                                                                 kwargs={'provider': provider}))
 
  94             return HttpResponseRedirect(request_url)
 
  95         except NotImplementedError, e:
 
  96             return process_provider_signin(request, provider)
 
  97         except InvalidAuthentication, e:
 
  98             request.session['auth_error'] = e.message
 
 100         return HttpResponseRedirect(reverse('auth_signin'))
 
 105 def process_provider_signin(request, provider):
 
 106     if provider in AUTH_PROVIDERS:
 
 107         provider_class = AUTH_PROVIDERS[provider].consumer
 
 110             assoc_key = provider_class.process_authentication_request(request)
 
 111         except InvalidAuthentication, e:
 
 112             request.session['auth_error'] = e.message
 
 113             return HttpResponseRedirect(reverse('auth_signin'))
 
 115         if request.user.is_authenticated():
 
 116             if isinstance(assoc_key, (type, User)):
 
 117                 if request.user != assoc_key:
 
 118                     request.session['auth_error'] = _(
 
 119                             "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
 
 122                     request.session['auth_error'] = _("You are already logged in with that user.")
 
 125                     assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
 
 126                     if assoc.user == request.user:
 
 127                         request.session['auth_error'] = _(
 
 128                                 "These login credentials are already associated with your account.")
 
 130                         request.session['auth_error'] = _(
 
 131                                 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
 
 134                     uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
 
 136                     request.user.message_set.create(
 
 137                             message=_('The new credentials are now associated with your account'))
 
 138                     return HttpResponseRedirect(reverse('user_authsettings', args=[request.user.id]))
 
 140             return HttpResponseRedirect(reverse('auth_signin'))
 
 142             if isinstance(assoc_key, User):
 
 143                 return login_and_forward(request, assoc_key)
 
 146             assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
 
 148             return login_and_forward(request, user_)
 
 149         except AuthKeyUserAssociation.DoesNotExist:
 
 150             request.session['assoc_key'] = assoc_key
 
 151             request.session['auth_provider'] = provider
 
 152             return HttpResponseRedirect(reverse('auth_external_register'))
 
 154     return HttpResponseRedirect(reverse('auth_signin'))
 
 156 def external_register(request):
 
 157     if request.method == 'POST' and 'bnewaccount' in request.POST:
 
 158         form1 = SimpleRegistrationForm(request.POST)
 
 161             user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email'])
 
 162             user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email']
 
 163             user_.set_unusable_password()
 
 165             if User.objects.all().count() == 0:
 
 166                 user_.is_superuser = True
 
 167                 user_.is_staff = True
 
 170             UserJoinsAction(user=user_, ip=request.META['REMOTE_ADDR']).save()
 
 173                 assoc_key = request.session['assoc_key']
 
 174                 auth_provider = request.session['auth_provider']
 
 176                 request.session['auth_error'] = _(
 
 177                         "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."
 
 179                 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
 
 180                         ["%s: %s" % (k, v) for k, v in request.META.items()]))
 
 181                 return HttpResponseRedirect(reverse('auth_signin'))
 
 183             uassoc = AuthKeyUserAssociation(user=user_, key=assoc_key, provider=auth_provider)
 
 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,
 
 217     provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
 
 219     return render_to_response('auth/complete.html', {
 
 221     'provider':provider_context and mark_safe(provider_context.human_name) or _('unknown'),
 
 222     'login_type':provider_context.id,
 
 223     'gravatar_faq_url':reverse('faq') + '#gravatar',
 
 224     }, context_instance=RequestContext(request))
 
 226 def request_temp_login(request):
 
 227     if request.method == 'POST':
 
 228         form = TemporaryLoginRequestForm(request.POST)
 
 231             users = form.user_cache
 
 235                     return forward_suspended_user(request, u, False)
 
 239                     hash = get_object_or_404(ValidationHash, user=u, type='templogin')
 
 240                     if hash.expiration < datetime.datetime.now():
 
 242                         return request_temp_login(request)
 
 244                     hash = ValidationHash.objects.create_new(u, 'templogin', [u.id])
 
 246                 send_template_email([u], "auth/temp_login_email.html", {'temp_login_code': hash})
 
 248                 request.user.message_set.create(message=_("An email has been sent with your temporary login key"))
 
 250             return HttpResponseRedirect(reverse('index'))
 
 252         form = TemporaryLoginRequestForm()
 
 254     return render_to_response(
 
 255             'auth/temp_login_request.html', {'form': form},
 
 256             context_instance=RequestContext(request))
 
 258 def temp_signin(request, user, code):
 
 259     user = get_object_or_404(User, id=user)
 
 261     if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])):
 
 263         # If the user requests temp_signin he must have forgotten his password. So we mark it as unusable.
 
 264         user.set_unusable_password()
 
 267         return login_and_forward(request, user, reverse('user_authsettings', kwargs={'id': user.id}),
 
 269                                          "You are logged in with a temporary access key, please take the time to fix your issue with authentication."
 
 274 def send_validation_email(request):
 
 275     if not request.user.is_authenticated():
 
 276         return HttpResponseUnauthorized(request)
 
 278         # We check if there are some old validation hashes. If there are -- we delete them.
 
 280             hash = ValidationHash.objects.get(user=request.user, type='email')
 
 285         # We don't care if there are previous cashes in the database... In every case we have to create a new one
 
 286         hash = ValidationHash.objects.create_new(request.user, 'email', [request.user.email])
 
 288         send_template_email([request.user], "auth/mail_validation.html", {'validation_code': hash})
 
 289         request.user.message_set.create(message=_("A message with an email validation link was just sent to your address."))
 
 290         return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
 
 294 def validate_email(request, user, code):
 
 295     user = get_object_or_404(User, id=user)
 
 297     if (ValidationHash.objects.validate(code, user, 'email', [user.email])):
 
 298         user.email_isvalid = True
 
 300         return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated."))
 
 302         return render_to_response('auth/mail_already_validated.html', { 'user' : user }, RequestContext(request))
 
 304 def auth_settings(request, id):
 
 305     user_ = get_object_or_404(User, id=id)
 
 307     if not (request.user.is_superuser or request.user == user_):
 
 308         return HttpResponseUnauthorized(request)
 
 310     auth_keys = user_.auth_keys.all()
 
 312     if request.user.is_superuser or (not user_.has_usable_password()):
 
 313         FormClass = SetPasswordForm
 
 315         FormClass = ChangePasswordForm
 
 318         form = FormClass(request.POST, user=user_)
 
 320             is_new_pass = not user_.has_usable_password()
 
 321             user_.set_password(form.cleaned_data['password1'])
 
 325                 request.user.message_set.create(message=_("New password set"))
 
 326                 if not request.user.is_superuser:
 
 327                     form = ChangePasswordForm(user=user_)
 
 329                 request.user.message_set.create(message=_("Your password was changed"))
 
 331             return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': user_.id}))
 
 333         form = FormClass(user=user_)
 
 338         provider = AUTH_PROVIDERS.get(k.provider, None)
 
 340         if provider is not None:
 
 341             name =  "%s: %s" % (provider.context.human_name, provider.context.readable_key(k))
 
 343             from forum.authentication.base import ConsumerTemplateContext
 
 344             "unknown: %s" % ConsumerTemplateContext.readable_key(k)
 
 346         auth_keys_list.append({
 
 351     return render_to_response('auth/auth_settings.html', {
 
 353     "can_view_private": (user_ == request.user) or request.user.is_superuser,
 
 355     'has_password': user_.has_usable_password(),
 
 356     'auth_keys': auth_keys_list,
 
 357     'allow_local_auth': AUTH_PROVIDERS.get('local', None),
 
 358     }, context_instance=RequestContext(request))
 
 360 def remove_external_provider(request, id):
 
 361     association = get_object_or_404(AuthKeyUserAssociation, id=id)
 
 362     if not (request.user.is_superuser or request.user == association.user):
 
 363         return HttpResponseUnauthorized(request)
 
 365     request.user.message_set.create(message=_("You removed the association with %s") % association.provider)
 
 367     return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': association.user.id}))
 
 369 def login_and_forward(request, user, forward=None, message=None):
 
 370     if user.is_suspended():
 
 371         return forward_suspended_user(request, user)
 
 373     user.backend = "django.contrib.auth.backends.ModelBackend"
 
 377         message = _("Welcome back %s, you are now logged in") % user.username
 
 379     request.user.message_set.create(message=message)
 
 382         forward = request.session.get(ON_SIGNIN_SESSION_ATTR, reverse('index'))
 
 384     pending_data = request.session.get(PENDING_SUBMISSION_SESSION_ATTR, None)
 
 386     if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO):
 
 387         submission_time = pending_data['time']
 
 388         if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)):
 
 389             del request.session[PENDING_SUBMISSION_SESSION_ATTR]
 
 390         elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)):
 
 391             user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % (
 
 392                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")),
 
 393                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('review')}), _("review")),
 
 394                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('cancel')}), _("cancel"))
 
 397             return manage_pending_data(request, _('save'), forward)
 
 399     return HttpResponseRedirect(forward)
 
 401 def forward_suspended_user(request, user, show_private_msg=True):
 
 402     message = _("Sorry, but this account is suspended")
 
 404         msg_type = 'privatemsg'
 
 406         msg_type = 'publicmsg'
 
 408     suspension = user.suspension
 
 410         message += (":<br />" + suspension.extra.get(msg_type, ''))
 
 412     request.user.message_set.create(message)
 
 413     return HttpResponseRedirect(reverse('index'))
 
 415 @decorate.withfn(login_required)
 
 416 def signout(request):
 
 418     return HttpResponseRedirect(reverse('index'))