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 all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
37 sort = lambda c1, c2: c1.weight - c2.weight
38 can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
40 bigicon_providers = sorted([
41 context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
44 smallicon_providers = sorted([
45 context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
48 top_stackitem_providers = sorted([
49 context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context)
52 stackitem_providers = sorted([
53 context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
57 msg = request.session['auth_error']
58 del request.session['auth_error']
62 return render_to_response(
66 'all_providers': all_providers,
67 'bigicon_providers': bigicon_providers,
68 'top_stackitem_providers': top_stackitem_providers,
69 'stackitem_providers': stackitem_providers,
70 'smallicon_providers': smallicon_providers,
72 RequestContext(request))
74 def prepare_provider_signin(request, provider):
75 force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
76 request.session['force_email_request'] = force_email_request
78 if provider in AUTH_PROVIDERS:
79 provider_class = AUTH_PROVIDERS[provider].consumer
82 request_url = provider_class.prepare_authentication_request(request,
83 reverse('auth_provider_done',
84 kwargs={'provider': provider}))
86 return HttpResponseRedirect(request_url)
87 except NotImplementedError, e:
88 return process_provider_signin(request, provider)
89 except InvalidAuthentication, e:
90 request.session['auth_error'] = e.message
92 return HttpResponseRedirect(reverse('auth_signin'))
97 def process_provider_signin(request, provider):
98 if provider in AUTH_PROVIDERS:
99 provider_class = AUTH_PROVIDERS[provider].consumer
102 assoc_key = provider_class.process_authentication_request(request)
103 except InvalidAuthentication, e:
104 request.session['auth_error'] = e.message
105 return HttpResponseRedirect(reverse('auth_signin'))
107 if request.user.is_authenticated():
108 if isinstance(assoc_key, (type, User)):
109 if request.user != assoc_key:
110 request.session['auth_error'] = _(
111 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
114 request.session['auth_error'] = _("You are already logged in with that user.")
117 assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
118 if assoc.user == request.user:
119 request.session['auth_error'] = _(
120 "These login credentials are already associated with your account.")
122 request.session['auth_error'] = _(
123 "Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again."
126 uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
128 request.user.message_set.create(
129 message=_('The new credentials are now associated with your account'))
130 return HttpResponseRedirect(reverse('user_authsettings', args=[request.user.id]))
132 return HttpResponseRedirect(reverse('auth_signin'))
134 if isinstance(assoc_key, User):
135 return login_and_forward(request, assoc_key)
138 assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
140 return login_and_forward(request, user_)
142 request.session['assoc_key'] = assoc_key
143 request.session['auth_provider'] = provider
144 return HttpResponseRedirect(reverse('auth_external_register'))
146 return HttpResponseRedirect(reverse('auth_signin'))
148 def external_register(request):
149 if request.method == 'POST' and 'bnewaccount' in request.POST:
150 form1 = SimpleRegistrationForm(request.POST)
151 email_feeds_form = SimpleEmailSubscribeForm(request.POST)
153 if (form1.is_valid() and email_feeds_form.is_valid()):
154 user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email'])
155 user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email']
156 user_.set_unusable_password()
158 if User.objects.all().count() == 0:
159 user_.is_superuser = True
160 user_.is_staff = True
163 UserJoinsAction(user=user_, ip=request.META['REMOTE_ADDR']).save()
166 assoc_key = request.session['assoc_key']
167 auth_provider = request.session['auth_provider']
169 request.session['auth_error'] = _(
170 "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."
172 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
173 ["%s: %s" % (k, v) for k, v in request.META.items()]))
174 return HttpResponseRedirect(reverse('auth_signin'))
176 uassoc = AuthKeyUserAssociation(user=user_, key=assoc_key, provider=auth_provider)
179 if email_feeds_form.cleaned_data['subscribe'] == 'n':
180 user_.subscription_settings.enable_notifications = False
181 user_.subscription_settings.save()
183 del request.session['assoc_key']
184 del request.session['auth_provider']
186 return login_and_forward(request, user_, message=_("A welcome email has been sent to your email address. "))
188 auth_provider = request.session.get('auth_provider', None)
189 if not auth_provider:
190 request.session['auth_error'] = _(
191 "Oops, something went wrong in the middle of this process. Please try again.")
192 logging.error("Missing session data when trying to complete user registration: %s" % ", ".join(
193 ["%s: %s" % (k, v) for k, v in request.META.items()]))
194 return HttpResponseRedirect(reverse('auth_signin'))
196 provider_class = AUTH_PROVIDERS[auth_provider].consumer
197 user_data = provider_class.get_user_data(request.session['assoc_key'])
200 user_data = request.session.get('auth_consumer_data', {})
202 username = user_data.get('username', '')
203 email = user_data.get('email', '')
206 request.session['auth_validated_email'] = email
208 form1 = SimpleRegistrationForm(initial={
210 'username': username,
213 email_feeds_form = SimpleEmailSubscribeForm()
215 provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
217 return render_to_response('auth/complete.html', {
219 'email_feeds_form': email_feeds_form,
220 'provider':mark_safe(provider_context.human_name),
221 'login_type':provider_context.id,
222 'gravatar_faq_url':reverse('faq') + '#gravatar',
223 }, context_instance=RequestContext(request))
225 def request_temp_login(request):
226 if request.method == 'POST':
227 form = TemporaryLoginRequestForm(request.POST)
230 users = form.user_cache
234 return forward_suspended_user(request, u, False)
238 hash = get_object_or_404(ValidationHash, user=u, type='templogin')
239 if hash.expiration < datetime.datetime.now():
241 return request_temp_login(request)
243 hash = ValidationHash.objects.create_new(u, 'templogin', [u.id])
245 send_template_email([u], "auth/temp_login_email.html", {'temp_login_code': hash})
247 request.user.message_set.create(message=_("An email has been sent with your temporary login key"))
249 return HttpResponseRedirect(reverse('index'))
251 form = TemporaryLoginRequestForm()
253 return render_to_response(
254 'auth/temp_login_request.html', {'form': form},
255 context_instance=RequestContext(request))
257 def temp_signin(request, user, code):
258 user = get_object_or_404(User, id=user)
260 if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])):
261 return login_and_forward(request, user, reverse('user_authsettings', kwargs={'id': user.id}),
263 "You are logged in with a temporary access key, please take the time to fix your issue with authentication."
268 def send_validation_email(request):
269 if not request.user.is_authenticated():
270 return HttpResponseUnauthorized(request)
273 hash = ValidationHash.objects.get(user=request.user, type='email')
276 hash = ValidationHash.objects.create_new(request.user, 'email', [request.user.email])
278 send_template_email([request.user], "auth/mail_validation.html", {'validation_code': hash})
279 request.user.message_set.create(message=_("A message with an email validation link was just sent to your address."))
280 return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
284 def validate_email(request, user, code):
285 user = get_object_or_404(User, id=user)
287 if (ValidationHash.objects.validate(code, user, 'email', [user.email])):
288 user.email_isvalid = True
290 return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated."))
292 return render_to_response('auth/mail_already_validated.html', { 'user' : user }, RequestContext(request))
294 def auth_settings(request, id):
295 user_ = get_object_or_404(User, id=id)
297 if not (request.user.is_superuser or request.user == user_):
298 return HttpResponseUnauthorized(request)
300 auth_keys = user_.auth_keys.all()
302 if request.user.is_superuser or (not user_.has_usable_password()):
303 FormClass = SetPasswordForm
305 FormClass = ChangePasswordForm
308 form = FormClass(request.POST, user=user_)
310 is_new_pass = not user_.has_usable_password()
311 user_.set_password(form.cleaned_data['password1'])
315 request.user.message_set.create(message=_("New password set"))
316 if not request.user.is_superuser:
317 form = ChangePasswordForm(user=user_)
319 request.user.message_set.create(message=_("Your password was changed"))
321 return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': user_.id}))
323 form = FormClass(user=user_)
328 provider = AUTH_PROVIDERS.get(k.provider, None)
330 if provider is not None:
331 name = "%s: %s" % (provider.context.human_name, provider.context.readable_key(k))
333 from forum.authentication.base import ConsumerTemplateContext
334 "unknown: %s" % ConsumerTemplateContext.readable_key(k)
336 auth_keys_list.append({
341 return render_to_response('auth/auth_settings.html', {
343 "can_view_private": (user_ == request.user) or request.user.is_superuser,
345 'has_password': user_.has_usable_password(),
346 'auth_keys': auth_keys_list,
347 'allow_local_auth': AUTH_PROVIDERS.get('local', None),
348 }, context_instance=RequestContext(request))
350 def remove_external_provider(request, id):
351 association = get_object_or_404(AuthKeyUserAssociation, id=id)
352 if not (request.user.is_superuser or request.user == association.user):
353 return HttpResponseUnauthorized(request)
355 request.user.message_set.create(message=_("You removed the association with %s") % association.provider)
357 return HttpResponseRedirect(reverse('user_authsettings', kwargs={'id': association.user.id}))
359 def login_and_forward(request, user, forward=None, message=None):
360 if user.is_suspended():
361 return forward_suspended_user(request, user)
363 user.backend = "django.contrib.auth.backends.ModelBackend"
367 message = _("Welcome back %s, you are now logged in") % user.username
369 request.user.message_set.create(message=message)
372 forward = request.session.get('on_signin_url', reverse('index'))
374 pending_data = request.session.get('pending_submission_data', None)
376 if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO):
377 submission_time = pending_data['time']
378 if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)):
379 del request.session['pending_submission_data']
380 elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)):
381 user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % (
382 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")),
383 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('review')}), _("review")),
384 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('cancel')}), _("cancel"))
387 return manage_pending_data(request, _('save'), forward)
389 return HttpResponseRedirect(forward)
391 def forward_suspended_user(request, user, show_private_msg=True):
392 message = _("Sorry, but this account is suspended")
394 msg_type = 'privatemsg'
396 msg_type = 'publicmsg'
398 suspension = user.suspension
400 message += (":<br />" + suspension.extra.get(msg_type, ''))
402 request.user.message_set.create(message)
403 return HttpResponseRedirect(reverse('index'))
405 @decorate.withfn(login_required)
406 def signout(request):
408 return HttpResponseRedirect(reverse('index'))