Revision 34a76cdb
b/snf-astakos-app/astakos/im/forms.py | ||
---|---|---|
511 | 511 |
|
512 | 512 |
|
513 | 513 |
class EmailChangeForm(forms.ModelForm): |
514 |
|
|
514 | 515 |
class Meta: |
515 | 516 |
model = EmailChange |
516 | 517 |
fields = ('new_email_address',) |
... | ... | |
533 | 534 |
|
534 | 535 |
|
535 | 536 |
class SignApprovalTermsForm(forms.ModelForm): |
537 |
|
|
536 | 538 |
class Meta: |
537 | 539 |
model = AstakosUser |
538 | 540 |
fields = ("has_signed_terms",) |
... | ... | |
548 | 550 |
|
549 | 551 |
|
550 | 552 |
class InvitationForm(forms.ModelForm): |
553 |
|
|
551 | 554 |
username = forms.EmailField(label=_("Email")) |
552 | 555 |
|
553 | 556 |
def __init__(self, *args, **kwargs): |
b/snf-astakos-app/astakos/im/functions.py | ||
---|---|---|
260 | 260 |
|
261 | 261 |
def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'): |
262 | 262 |
try: |
263 |
url = reverse('email_change_confirm', |
|
264 |
kwargs={'activation_key': ec.activation_key}) |
|
263 |
url = ec.get_url() |
|
265 | 264 |
url = request.build_absolute_uri(url) |
266 | 265 |
t = loader.get_template(email_template_name) |
267 | 266 |
c = {'url': url, 'site_name': SITENAME} |
b/snf-astakos-app/astakos/im/messages.py | ||
---|---|---|
72 | 72 |
MAX_INVITATION_NUMBER_REACHED = 'There are no invitations left.' |
73 | 73 |
GROUP_MAX_PARTICIPANT_NUMBER_REACHED = 'Group maximum participant number has been reached.' |
74 | 74 |
NO_APPROVAL_TERMS = 'There are no approval terms.' |
75 |
PENDING_EMAIL_CHANGE_REQUEST = 'There is already a pending change email request.' |
|
75 |
PENDING_EMAIL_CHANGE_REQUEST = 'There is already a pending change email request. ' + \ |
|
76 |
'Submiting a new email will cancel any previous requests.' |
|
76 | 77 |
OBJECT_CREATED_FAILED = 'The %(verbose_name)s creation failed: %(reason)s.' |
77 | 78 |
GROUP_JOIN_FAILURE = 'Failed to join group.' |
78 | 79 |
GROUPKIND_UNKNOWN = 'There is no such a group kind' |
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
570 | 570 |
if q.count() != 0: |
571 | 571 |
raise ValidationError({'__all__': [_(astakos_messages.UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR)]}) |
572 | 572 |
|
573 |
def email_change_is_pending(self): |
|
574 |
return self.emailchanges.count() > 0 |
|
575 |
|
|
573 | 576 |
@property |
574 | 577 |
def signed_terms(self): |
575 | 578 |
term = get_latest_terms() |
... | ... | |
960 | 963 |
|
961 | 964 |
|
962 | 965 |
class EmailChangeManager(models.Manager): |
966 |
|
|
963 | 967 |
@transaction.commit_on_success |
964 | 968 |
def change_email(self, activation_key): |
965 | 969 |
""" |
... | ... | |
993 | 997 |
raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED)) |
994 | 998 |
# update user |
995 | 999 |
user = AstakosUser.objects.get(pk=email_change.user_id) |
1000 |
old_email = user.email |
|
996 | 1001 |
user.email = email_change.new_email_address |
997 | 1002 |
user.save() |
998 | 1003 |
email_change.delete() |
1004 |
msg = "User %d changed email from %s to %s" % (user.pk, old_email, |
|
1005 |
user.email) |
|
1006 |
logger.log(LOGGING_LEVEL, msg) |
|
999 | 1007 |
return user |
1000 | 1008 |
except EmailChange.DoesNotExist: |
1001 | 1009 |
raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY)) |
... | ... | |
1005 | 1013 |
new_email_address = models.EmailField(_(u'new e-mail address'), |
1006 | 1014 |
help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP)) |
1007 | 1015 |
user = models.ForeignKey( |
1008 |
AstakosUser, unique=True, related_name='emailchange_user')
|
|
1016 |
AstakosUser, unique=True, related_name='emailchanges')
|
|
1009 | 1017 |
requested_at = models.DateTimeField(default=datetime.now()) |
1010 | 1018 |
activation_key = models.CharField( |
1011 | 1019 |
max_length=40, unique=True, db_index=True) |
1012 | 1020 |
|
1013 | 1021 |
objects = EmailChangeManager() |
1014 | 1022 |
|
1023 |
def get_url(self): |
|
1024 |
return reverse('email_change_confirm', |
|
1025 |
kwargs={'activation_key': self.activation_key}) |
|
1026 |
|
|
1015 | 1027 |
def activation_key_expired(self): |
1016 | 1028 |
expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS) |
1017 | 1029 |
return self.requested_at + expiration_date < datetime.now() |
b/snf-astakos-app/astakos/im/tests.py | ||
---|---|---|
47 | 47 |
|
48 | 48 |
from astakos.im import messages |
49 | 49 |
|
50 |
|
|
51 |
astakos_settings.EMAILCHANGE_ENABLED = True |
|
52 |
|
|
50 | 53 |
class ShibbolethClient(Client): |
51 | 54 |
""" |
52 | 55 |
A shibboleth agnostic client. |
... | ... | |
624 | 627 |
self.assertContains(r, "Password change for this account is not" |
625 | 628 |
" supported") |
626 | 629 |
|
630 |
class UserActionsTests(TestCase): |
|
631 |
|
|
632 |
def setUp(self): |
|
633 |
kind = GroupKind.objects.create(name="default") |
|
634 |
AstakosGroup.objects.create(name="default", kind=kind) |
|
635 |
|
|
636 |
def test_email_change(self): |
|
637 |
# to test existing email validation |
|
638 |
existing_user = get_local_user('existing@grnet.gr') |
|
639 |
|
|
640 |
# local user |
|
641 |
user = get_local_user('kpap@grnet.gr') |
|
642 |
|
|
643 |
# login as kpap |
|
644 |
self.client.login(username='kpap@grnet.gr', password='password') |
|
645 |
r = self.client.get('/im/profile', follow=True) |
|
646 |
user = r.context['request'].user |
|
647 |
self.assertTrue(user.is_authenticated()) |
|
648 |
|
|
649 |
# change email is enabled |
|
650 |
r = self.client.get('/im/email_change') |
|
651 |
self.assertEqual(r.status_code, 200) |
|
652 |
self.assertFalse(user.email_change_is_pending()) |
|
653 |
|
|
654 |
# request email change to an existing email fails |
|
655 |
data = {'new_email_address': 'existing@grnet.gr'} |
|
656 |
r = self.client.post('/im/email_change', data) |
|
657 |
self.assertContains(r, messages.EMAIL_USED) |
|
658 |
|
|
659 |
# proper email change |
|
660 |
data = {'new_email_address': 'kpap@gmail.com'} |
|
661 |
r = self.client.post('/im/email_change', data, follow=True) |
|
662 |
self.assertRedirects(r, '/im/profile') |
|
663 |
self.assertContains(r, messages.EMAIL_CHANGE_REGISTERED) |
|
664 |
change1 = EmailChange.objects.get() |
|
665 |
|
|
666 |
# user sees a warning |
|
667 |
r = self.client.get('/im/email_change') |
|
668 |
self.assertEqual(r.status_code, 200) |
|
669 |
self.assertContains(r, messages.PENDING_EMAIL_CHANGE_REQUEST) |
|
670 |
self.assertTrue(user.email_change_is_pending()) |
|
671 |
|
|
672 |
# link was sent |
|
673 |
self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 0) |
|
674 |
self.assertEqual(len(get_mailbox('kpap@gmail.com')), 1) |
|
675 |
|
|
676 |
# proper email change |
|
677 |
data = {'new_email_address': 'kpap@yahoo.com'} |
|
678 |
r = self.client.post('/im/email_change', data, follow=True) |
|
679 |
self.assertRedirects(r, '/im/profile') |
|
680 |
self.assertContains(r, messages.EMAIL_CHANGE_REGISTERED) |
|
681 |
self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 0) |
|
682 |
self.assertEqual(len(get_mailbox('kpap@yahoo.com')), 1) |
|
683 |
change2 = EmailChange.objects.get() |
|
684 |
|
|
685 |
r = self.client.get(change1.get_url()) |
|
686 |
self.assertEquals(r.status_code, 302) |
|
687 |
self.client.logout() |
|
688 |
|
|
689 |
r = self.client.post('/im/local?next=' + change2.get_url(), |
|
690 |
{'username': 'kpap@grnet.gr', |
|
691 |
'password': 'password', |
|
692 |
'next': change2.get_url()}, |
|
693 |
follow=True) |
|
694 |
self.assertRedirects(r, '/im/profile') |
|
695 |
user = r.context['request'].user |
|
696 |
self.assertEquals(user.email, 'kpap@yahoo.com') |
|
697 |
self.assertEquals(user.username, 'kpap@yahoo.com') |
|
698 |
|
|
699 |
|
|
700 |
self.client.logout() |
|
701 |
r = self.client.post('/im/local?next=' + change2.get_url(), |
|
702 |
{'username': 'kpap@grnet.gr', |
|
703 |
'password': 'password', |
|
704 |
'next': change2.get_url()}, |
|
705 |
follow=True) |
|
706 |
self.assertContains(r, "Please enter a correct username and password") |
|
707 |
self.assertEqual(user.emailchanges.count(), 0) |
|
708 |
|
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
670 | 670 |
confirm_template_name='registration/email_change_done.html', |
671 | 671 |
extra_context=None): |
672 | 672 |
extra_context = extra_context or {} |
673 |
|
|
674 |
|
|
673 | 675 |
if activation_key: |
674 | 676 |
try: |
675 | 677 |
user = EmailChange.objects.change_email(activation_key) |
... | ... | |
679 | 681 |
auth_logout(request) |
680 | 682 |
response = prepare_response(request, user) |
681 | 683 |
transaction.commit() |
682 |
return response
|
|
684 |
return HttpResponseRedirect(reverse('edit_profile'))
|
|
683 | 685 |
except ValueError, e: |
684 | 686 |
messages.error(request, e) |
687 |
transaction.rollback() |
|
688 |
return HttpResponseRedirect(reverse('index')) |
|
689 |
|
|
685 | 690 |
return render_response(confirm_template_name, |
686 |
modified_user=user if 'user' in locals( |
|
687 |
) else None, |
|
688 |
context_instance=get_context(request, |
|
691 |
modified_user=user if 'user' in locals() \ |
|
692 |
else None, context_instance=get_context(request, |
|
689 | 693 |
extra_context)) |
690 | 694 |
|
691 | 695 |
if not request.user.is_authenticated(): |
692 | 696 |
path = quote(request.get_full_path()) |
693 | 697 |
url = request.build_absolute_uri(reverse('index')) |
694 | 698 |
return HttpResponseRedirect(url + '?next=' + path) |
699 |
|
|
700 |
# clean up expired email changes |
|
701 |
if request.user.email_change_is_pending(): |
|
702 |
change = request.user.emailchanges.get() |
|
703 |
if change.activation_key_expired(): |
|
704 |
change.delete() |
|
705 |
transaction.commit() |
|
706 |
return HttpResponseRedirect(reverse('email_change')) |
|
707 |
|
|
695 | 708 |
form = EmailChangeForm(request.POST or None) |
696 | 709 |
if request.method == 'POST' and form.is_valid(): |
697 | 710 |
try: |
711 |
# delete pending email changes |
|
712 |
request.user.emailchanges.all().delete() |
|
698 | 713 |
ec = form.save(email_template_name, request) |
699 | 714 |
except SendMailError, e: |
700 | 715 |
msg = e |
701 | 716 |
messages.error(request, msg) |
702 | 717 |
transaction.rollback() |
703 |
except IntegrityError, e: |
|
704 |
msg = _(astakos_messages.PENDING_EMAIL_CHANGE_REQUEST) |
|
705 |
messages.error(request, msg) |
|
718 |
return HttpResponseRedirect(reverse('edit_profile')) |
|
706 | 719 |
else: |
707 | 720 |
msg = _(astakos_messages.EMAIL_CHANGE_REGISTERED) |
708 | 721 |
messages.success(request, msg) |
709 | 722 |
transaction.commit() |
723 |
return HttpResponseRedirect(reverse('edit_profile')) |
|
724 |
|
|
725 |
if request.user.email_change_is_pending(): |
|
726 |
messages.warning(request, astakos_messages.PENDING_EMAIL_CHANGE_REQUEST) |
|
727 |
|
|
710 | 728 |
return render_response( |
711 | 729 |
form_template_name, |
712 | 730 |
form=form, |
Also available in: Unified diff