Commit 468a74c6 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

Refine shibboleth signup mechanism

Refs: #3041
parent c64ab302
...@@ -64,10 +64,12 @@ class EmailBackend(ModelBackend): ...@@ -64,10 +64,12 @@ class EmailBackend(ModelBackend):
def authenticate(self, username=None, password=None): def authenticate(self, username=None, password=None):
#If username is an email address, then try to pull it up #If username is an email address, then try to pull it up
if email_re.search(username): if email_re.search(username):
try: users = AstakosUser.objects.filter(email=username)
user = AstakosUser.objects.get(email=username, is_active=True) if not users:
except AstakosUser.DoesNotExist:
return None return None
for user in users:
if user.check_password(password):
return user
else: else:
#We have a non-email address username we #We have a non-email address username we
#should try username #should try username
......
...@@ -48,10 +48,11 @@ from django.contrib import messages ...@@ -48,10 +48,11 @@ from django.contrib import messages
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from astakos.im.models import AstakosUser, Invitation, get_latest_terms, EmailChange from astakos.im.models import AstakosUser, Invitation, get_latest_terms, EmailChange
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, \ from astakos.im.settings import (INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL,
BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL, \ BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL,
RECAPTCHA_ENABLED, LOGGING_LEVEL, PASSWORD_RESET_EMAIL_SUBJECT, \ RECAPTCHA_ENABLED, LOGGING_LEVEL, PASSWORD_RESET_EMAIL_SUBJECT,
NEWPASSWD_INVALIDATE_TOKEN NEWPASSWD_INVALIDATE_TOKEN, THIRDPARTY_ACC_ADDITIONAL_FIELDS
)
from astakos.im.widgets import DummyWidget, RecaptchaWidget from astakos.im.widgets import DummyWidget, RecaptchaWidget
from astakos.im.functions import send_change_email from astakos.im.functions import send_change_email
...@@ -182,6 +183,10 @@ class InvitedLocalUserCreationForm(LocalUserCreationForm): ...@@ -182,6 +183,10 @@ class InvitedLocalUserCreationForm(LocalUserCreationForm):
return user return user
class ThirdPartyUserCreationForm(forms.ModelForm): class ThirdPartyUserCreationForm(forms.ModelForm):
third_party_identifier = forms.CharField(
widget=forms.HiddenInput(),
label=''
)
class Meta: class Meta:
model = AstakosUser model = AstakosUser
fields = ("email", "first_name", "last_name", "third_party_identifier", "has_signed_terms") fields = ("email", "first_name", "last_name", "third_party_identifier", "has_signed_terms")
...@@ -193,15 +198,16 @@ class ThirdPartyUserCreationForm(forms.ModelForm): ...@@ -193,15 +198,16 @@ class ThirdPartyUserCreationForm(forms.ModelForm):
self.request = kwargs.get('request', None) self.request = kwargs.get('request', None)
if self.request: if self.request:
kwargs.pop('request') kwargs.pop('request')
latest_terms = get_latest_terms()
if latest_terms:
self._meta.fields.append('has_signed_terms')
super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs) super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['email', 'first_name', 'last_name', 'third_party_identifier']
if get_latest_terms(): if latest_terms:
self.fields.keyOrder.append('has_signed_terms') self.fields.keyOrder.append('has_signed_terms')
#set readonly form fields
ro = ["third_party_identifier"]
for f in ro:
self.fields[f].widget.attrs['readonly'] = True
if 'has_signed_terms' in self.fields: if 'has_signed_terms' in self.fields:
# Overriding field label since we need to apply a link # Overriding field label since we need to apply a link
# to the terms within the label # to the terms within the label
...@@ -262,20 +268,23 @@ class ShibbolethUserCreationForm(ThirdPartyUserCreationForm): ...@@ -262,20 +268,23 @@ class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs) super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
self.fields.keyOrder.append('additional_email')
# copy email value to additional_mail in case user will change it # copy email value to additional_mail in case user will change it
name = 'email' name = 'email'
field = self.fields[name] field = self.fields[name]
self.initial['additional_email'] = self.initial.get(name, field.initial) self.initial['additional_email'] = self.initial.get(name, field.initial)
self.initial['email'] = None
def clean_email(self): def clean_email(self):
email = self.cleaned_data['email'] email = self.cleaned_data['email']
for user in AstakosUser.objects.filter(email = email): for user in AstakosUser.objects.filter(email = email):
if user.provider == 'shibboleth': if user.provider == 'shibboleth':
raise forms.ValidationError(_("This email is already associated with another shibboleth account.")) raise forms.ValidationError(_(
elif not user.is_active: "This email is already associated with another shibboleth \
raise forms.ValidationError(_("This email is already associated with an inactive account. \ account."
You need to wait to be activated before being able to switch to a shibboleth account.")) )
)
else:
raise forms.ValidationError(_("This email is already used"))
super(ShibbolethUserCreationForm, self).clean_email() super(ShibbolethUserCreationForm, self).clean_email()
return email return email
...@@ -321,7 +330,7 @@ class LoginForm(AuthenticationForm): ...@@ -321,7 +330,7 @@ class LoginForm(AuthenticationForm):
check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip) check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
if not check.is_valid: if not check.is_valid:
raise forms.ValidationError(_('You have not entered the correct words')) raise forms.ValidationError(_('You have not entered the correct words'))
def clean(self): def clean(self):
super(LoginForm, self).clean() super(LoginForm, self).clean()
if self.user_cache and self.user_cache.provider not in ('local', ''): if self.user_cache and self.user_cache.provider not in ('local', ''):
......
...@@ -249,23 +249,6 @@ def activate(user, email_template_name='im/welcome_email.txt', ...@@ -249,23 +249,6 @@ def activate(user, email_template_name='im/welcome_email.txt',
send_helpdesk_notification(user, helpdesk_email_template_name) send_helpdesk_notification(user, helpdesk_email_template_name)
send_greeting(user, email_template_name) send_greeting(user, email_template_name)
def switch_account_to_shibboleth(user, local_user, greeting_template_name='im/welcome_email.txt'):
if not user or not isinstance(user, AstakosUser):
return
if not local_user or not isinstance(user, AstakosUser):
return
if not user.provider == 'shibboleth':
return
user.delete()
local_user.provider = 'shibboleth'
local_user.third_party_identifier = user.third_party_identifier
local_user.save()
send_greeting(local_user, greeting_template_name)
return local_user
def invite(invitation, inviter, email_template_name='im/welcome_email.txt'): def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
""" """
Send an invitation email and upon success reduces inviter's invitation by one. Send an invitation email and upon success reduces inviter's invitation by one.
......
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'PendingThirdPartyUser'
db.create_table('im_pendingthirdpartyuser', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('third_party_identifier', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('provider', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('email', self.gf('django.db.models.fields.EmailField')(max_length=75, blank=True)),
('first_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
('last_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
('affiliation', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=30)),
))
db.send_create_signal('im', ['PendingThirdPartyUser'])
def backwards(self, orm):
# Deleting model 'PendingThirdPartyUser'
db.delete_table('im_pendingthirdpartyuser')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'im.additionalmail': {
'Meta': {'object_name': 'AdditionalMail'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
},
'im.approvalterms': {
'Meta': {'object_name': 'ApprovalTerms'},
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 15, 13, 7, 38, 626139)', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'im.astakosuser': {
'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'updated': ('django.db.models.fields.DateTimeField', [], {}),
'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
},
'im.emailchange': {
'Meta': {'object_name': 'EmailChange'},
'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 15, 13, 7, 38, 627944)'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
},
'im.invitation': {
'Meta': {'object_name': 'Invitation'},
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'im.pendingthirdpartyuser': {
'Meta': {'object_name': 'PendingThirdPartyUser'},
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'im.service': {
'Meta': {'object_name': 'Service'},
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
}
}
complete_apps = ['im']
...@@ -39,7 +39,7 @@ import json ...@@ -39,7 +39,7 @@ import json
from time import asctime from time import asctime
from datetime import datetime, timedelta from datetime import datetime, timedelta
from base64 import b64encode from base64 import b64encode
from urlparse import urlparse, urlunparse from urlparse import urlparse
from random import randint from random import randint
from django.db import models, IntegrityError from django.db import models, IntegrityError
...@@ -374,6 +374,42 @@ class AdditionalMail(models.Model): ...@@ -374,6 +374,42 @@ class AdditionalMail(models.Model):
owner = models.ForeignKey(AstakosUser) owner = models.ForeignKey(AstakosUser)
email = models.EmailField() email = models.EmailField()
class PendingThirdPartyUser(models.Model):
"""
Model for registring successful third party user authentications
"""
third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
provider = models.CharField('Provider', max_length=255, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
affiliation = models.CharField('Affiliation', max_length=255, blank=True)
username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
@property
def realname(self):
return '%s %s' %(self.first_name, self.last_name)
@realname.setter
def realname(self, value):
parts = value.split(' ')
if len(parts) == 2:
self.first_name = parts[0]
self.last_name = parts[1]
else:
self.last_name = parts[0]
def save(self, **kwargs):
if not self.id:
# set username
while not self.username:
username = uuid.uuid4().hex[:30]
try:
AstakosUser.objects.get(username = username)
except AstakosUser.DoesNotExist, e:
self.username = username
super(PendingThirdPartyUser, self).save(**kwargs)
def create_astakos_user(u): def create_astakos_user(u):
try: try:
AstakosUser.objects.get(user_ptr=u.pk) AstakosUser.objects.get(user_ptr=u.pk)
......
...@@ -119,4 +119,14 @@ PASSWORD_RESET_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_PASSWORD_RESET_EMAIL_S ...@@ -119,4 +119,14 @@ PASSWORD_RESET_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_PASSWORD_RESET_EMAIL_S
'Password reset on %s alpha2 testing' % SITENAME) 'Password reset on %s alpha2 testing' % SITENAME)
# Enforce token renewal on password change/reset # Enforce token renewal on password change/reset
NEWPASSWD_INVALIDATE_TOKEN = getattr(settings, 'ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN', True) NEWPASSWD_INVALIDATE_TOKEN = getattr(settings, 'ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN', True)
\ No newline at end of file
# Permit local account migration
ENABLE_LOCAL_ACCOUNT_MIGRATION = getattr(settings, 'ASTAKOS_ENABLE_LOCAL_ACCOUNT_MIGRATION', True)
# A dictionary describing the user fields appearing during the second step of third party account creation
from django import forms
THIRDPARTY_ACC_ADDITIONAL_FIELDS = getattr(settings, 'ASTAKOS_THIRDPARTY_ACC_ADDITIONAL_FIELDS', {
'first_name':None,
'last_name':None,
})
\ No newline at end of file
...@@ -39,12 +39,14 @@ from django.contrib import messages ...@@ -39,12 +39,14 @@ from django.contrib import messages
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.core.urlresolvers import reverse
from astakos.im.util import prepare_response, get_query from astakos.im.util import prepare_response, get_query
from astakos.im.views import requires_anonymous from astakos.im.views import requires_anonymous
from astakos.im.models import AstakosUser from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm from astakos.im.forms import LoginForm
from astakos.im.settings import RATELIMIT_RETRIES_ALLOWED from astakos.im.settings import RATELIMIT_RETRIES_ALLOWED
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION
from ratelimit.decorators import ratelimit from ratelimit.decorators import ratelimit
...@@ -62,11 +64,16 @@ def login(request, on_failure='im/login.html'): ...@@ -62,11 +64,16 @@ def login(request, on_failure='im/login.html'):
was_limited = getattr(request, 'limited', False) was_limited = getattr(request, 'limited', False)
form = LoginForm(data=request.POST, was_limited=was_limited, request=request) form = LoginForm(data=request.POST, was_limited=was_limited, request=request)
next = get_query(request).get('next', '') next = get_query(request).get('next', '')
username = get_query(request).get('key')
if not form.is_valid(): if not form.is_valid():
return render_to_response(on_failure, return render_to_response(
{'login_form':form, on_failure,
'next':next}, {'login_form':form,
context_instance=RequestContext(request)) 'next':next,
'key':username},
context_instance=RequestContext(request)
)
# get the user from the cash # get the user from the cash
user = form.user_cache user = form.user_cache
...@@ -74,11 +81,39 @@ def login(request, on_failure='im/login.html'): ...@@ -74,11 +81,39 @@ def login(request, on_failure='im/login.html'):
if not user: if not user:
message = _('Cannot authenticate account') message = _('Cannot authenticate account')
elif not user.is_active: elif not user.is_active:
message = _('Inactive account') if user.sent_activation:
message = _('Your request is pending activation')
else:
message = _('You have not followed the activation link')
if message: if message:
messages.add_message(request, messages.ERROR, message) messages.add_message(request, messages.ERROR, message)
return render_to_response(on_failure, return render_to_response(on_failure,
{'form':form}, {'form':form},
context_instance=RequestContext(request)) context_instance=RequestContext(request))
return prepare_response(request, user, next) # hook for switching account to use third party authentication
if ENABLE_LOCAL_ACCOUNT_MIGRATION and username:
try:
new = PendingThirdPartyUser.objects.get(
username=username)
except:
messages.error(
request,
_('Account failed to switch to %(provider)s' % locals())
)
return render_to_response(
on_failure,
{'login_form':form,
'next':next},
context_instance=RequestContext(request)
)
else:
user.provider = new.provider
user.third_party_identifier = new.third_party_identifier
user.save()
new.delete()
messages.success(
request,
_('Account successfully switched to %(provider)s' % user.__dict__)
)
return prepare_response(request, user, next)
\ No newline at end of file
...@@ -36,14 +36,25 @@ from django.utils.translation import ugettext as _ ...@@ -36,14 +36,25 @@ from django.utils.translation import ugettext as _
from django.contrib import messages from django.contrib import messages
from django.template import RequestContext from django.template import RequestContext
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from urlparse import urlunsplit, urlsplit
from django.utils.http import urlencode
from astakos.im.util import prepare_response, get_context, get_invitation from astakos.im.util import prepare_response, get_context, get_invitation
from astakos.im.views import requires_anonymous, render_response from astakos.im.views import requires_anonymous, render_response
from astakos.im.settings import DEFAULT_USER_LEVEL from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
from astakos.im.models import AstakosUser, Invitation, AdditionalMail
from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm from astakos.im.forms import LoginForm
from astakos.im.activation_backends import get_backend, SimpleBackend from astakos.im.activation_backends import get_backend, SimpleBackend
import logging
logger = logging.getLogger(__name__)
class Tokens: class Tokens:
# these are mapped by the Shibboleth SP software # these are mapped by the Shibboleth SP software
SHIB_EPPN = "HTTP_EPPN" # eduPersonPrincipalName SHIB_EPPN = "HTTP_EPPN" # eduPersonPrincipalName
...@@ -57,7 +68,13 @@ class Tokens: ...@@ -57,7 +68,13 @@ class Tokens:
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
@requires_anonymous @requires_anonymous
def login(request, backend=None, on_login_template='im/login.html', on_creation_template='im/third_party_registration.html', extra_context={}): def login(
request,
on_login_template='im/login.html',
on_signup_template='im/third_party_check_local.html',
extra_context=None):
extra_context = extra_context or {}
tokens = request.META tokens = request.META
try: try:
...@@ -75,10 +92,13 @@ def login(request, backend=None, on_login_template='im/login.html', on_creation ...@@ -75,10 +92,13 @@ def login(request, backend=None, on_login_template='im/login.html', on_creation
return HttpResponseBadRequest("Missing user name in request") return HttpResponseBadRequest("Missing user name in request")
affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '') affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
email = tokens.get(Tokens.SHIB_MAIL, None) email = tokens.get(Tokens.SHIB_MAIL, '')
try: try:
user = AstakosUser.objects.get(provider='shibboleth', third_party_identifier=eppn) user = AstakosUser.objects.get(
provider='shibboleth',
third_party_identifier=eppn
)
if user.is_active: if user.is_active:
return prepare_response(request, return prepare_response(request,
user, user,
...@@ -91,17 +111,74 @@ def login(request, backend=None, on_login_template='im/login.html', on_creation ...@@ -91,17 +111,74 @@ def login(request, backend=None, on_login_template='im/login.html', on_creation
login_form = LoginForm(request=request), login_form = LoginForm(request=request),
context_instance=RequestContext(request)) context_instance=RequestContext(request))