Revision e7cb4085 snf-astakos-app/astakos/im/views.py
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
74 | 74 |
|
75 | 75 |
import astakos.im.messages as astakos_messages |
76 | 76 |
|
77 |
from astakos.im.activation_backends import get_backend, SimpleBackend
|
|
77 |
from astakos.im import activation_backends
|
|
78 | 78 |
from astakos.im import tables |
79 | 79 |
from astakos.im.models import ( |
80 | 80 |
AstakosUser, ApprovalTerms, |
... | ... | |
91 | 91 |
ProjectMembersSortForm) |
92 | 92 |
from astakos.im.forms import ExtendedProfileForm as ProfileForm |
93 | 93 |
from astakos.im.functions import ( |
94 |
send_feedback, SendMailError,
|
|
94 |
send_feedback, |
|
95 | 95 |
logout as auth_logout, |
96 |
activate as activate_func, |
|
97 | 96 |
invite as invite_func, |
98 |
send_activation as send_activation_func, |
|
99 |
SendNotificationError, |
|
100 | 97 |
qh_add_pending_app, |
101 | 98 |
accept_membership, reject_membership, remove_membership, cancel_membership, |
102 |
leave_project, join_project, enroll_member, can_join_request, can_leave_request, |
|
99 |
leave_project, join_project, enroll_member, can_join_request, |
|
100 |
can_leave_request, |
|
103 | 101 |
get_related_project_id, get_by_chain_or_404, |
104 | 102 |
approve_application, deny_application, |
105 | 103 |
cancel_application, dismiss_application) |
... | ... | |
110 | 108 |
ACTIVATION_REDIRECT_URL, |
111 | 109 |
MODERATION_ENABLED) |
112 | 110 |
from astakos.im import presentation |
113 |
from astakos.im import settings as astakos_settings
|
|
111 |
from astakos.im import settings |
|
114 | 112 |
from astakos.im import auth_providers as auth |
115 | 113 |
from snf_django.lib.db.transaction import commit_on_success_strict |
116 | 114 |
from astakos.im.ctx import ExceptionHandler |
... | ... | |
315 | 313 |
invite_func(inviter, email, realname) |
316 | 314 |
message = _(astakos_messages.INVITATION_SENT) % locals() |
317 | 315 |
messages.success(request, message) |
318 |
except SendMailError, e: |
|
319 |
message = e.message |
|
320 |
messages.error(request, message) |
|
321 |
transaction.rollback() |
|
322 |
except BaseException, e: |
|
323 |
message = _(astakos_messages.GENERIC_ERROR) |
|
324 |
messages.error(request, message) |
|
325 |
logger.exception(e) |
|
316 |
except Exception, e: |
|
326 | 317 |
transaction.rollback() |
318 |
raise |
|
327 | 319 |
else: |
328 | 320 |
transaction.commit() |
329 | 321 |
else: |
... | ... | |
424 | 416 |
|
425 | 417 |
extra_context['services'] = Service.catalog().values() |
426 | 418 |
return render_response(template_name, |
427 |
profile_form = form,
|
|
428 |
user_providers = user_providers,
|
|
429 |
user_disabled_providers = user_disabled_providers,
|
|
430 |
user_available_providers = user_available_providers,
|
|
431 |
context_instance = get_context(request,
|
|
419 |
profile_form=form,
|
|
420 |
user_providers=user_providers,
|
|
421 |
user_disabled_providers=user_disabled_providers,
|
|
422 |
user_available_providers=user_available_providers,
|
|
423 |
context_instance=get_context(request,
|
|
432 | 424 |
extra_context)) |
433 | 425 |
|
434 | 426 |
|
435 | 427 |
@transaction.commit_manually |
436 | 428 |
@require_http_methods(["GET", "POST"]) |
437 |
def signup(request, template_name='im/signup.html', on_success='index', extra_context=None, backend=None): |
|
429 |
def signup(request, template_name='im/signup.html', on_success='index', |
|
430 |
extra_context=None, activation_backend=None): |
|
438 | 431 |
""" |
439 | 432 |
Allows a user to create a local account. |
440 | 433 |
|
441 | 434 |
In case of GET request renders a form for entering the user information. |
442 | 435 |
In case of POST handles the signup. |
443 | 436 |
|
444 |
The user activation will be delegated to the backend specified by the ``backend`` keyword argument |
|
445 |
if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend`` |
|
446 |
if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not |
|
447 |
(see activation_backends); |
|
437 |
The user activation will be delegated to the backend specified by the |
|
438 |
``activation_backend`` keyword argument if present, otherwise to the |
|
439 |
``astakos.im.activation_backends.InvitationBackend`` if |
|
440 |
settings.ASTAKOS_INVITATIONS_ENABLED is True or |
|
441 |
``astakos.im.activation_backends.SimpleBackend`` if not (see |
|
442 |
activation_backends); |
|
448 | 443 |
|
449 |
Upon successful user creation, if ``next`` url parameter is present the user is redirected there |
|
450 |
otherwise renders the same page with a success message. |
|
444 |
Upon successful user creation, if ``next`` url parameter is present the |
|
445 |
user is redirected there otherwise renders the same page with a success |
|
446 |
message. |
|
451 | 447 |
|
452 | 448 |
On unsuccessful creation, renders ``template_name`` with an error message. |
453 | 449 |
|
... | ... | |
469 | 465 |
""" |
470 | 466 |
extra_context = extra_context or {} |
471 | 467 |
if request.user.is_authenticated(): |
472 |
return HttpResponseRedirect(reverse('edit_profile')) |
|
468 |
logger.info("%s already signed in, redirect to index", |
|
469 |
request.user.log_display) |
|
470 |
return HttpResponseRedirect(reverse('index')) |
|
473 | 471 |
|
474 | 472 |
provider = get_query(request).get('provider', 'local') |
475 | 473 |
if not auth.get_provider(provider).get_create_policy: |
474 |
logger.error("%s provider not available for signup", provider) |
|
476 | 475 |
raise PermissionDenied |
477 | 476 |
|
478 |
id = get_query(request).get('id') |
|
479 |
try: |
|
480 |
instance = AstakosUser.objects.get(id=id) if id else None |
|
481 |
except AstakosUser.DoesNotExist: |
|
482 |
instance = None |
|
477 |
instance = None |
|
483 | 478 |
|
479 |
# user registered using third party provider |
|
484 | 480 |
third_party_token = request.REQUEST.get('third_party_token', None) |
485 | 481 |
unverified = None |
486 | 482 |
if third_party_token: |
483 |
# retreive third party entry. This was created right after the initial |
|
484 |
# third party provider handshake. |
|
487 | 485 |
pending = get_object_or_404(PendingThirdPartyUser, |
488 | 486 |
token=third_party_token) |
489 | 487 |
|
490 | 488 |
provider = pending.provider |
489 |
|
|
490 |
# clone third party instance into the corresponding AstakosUser |
|
491 | 491 |
instance = pending.get_user_instance() |
492 | 492 |
get_unverified = AstakosUserAuthProvider.objects.unverified |
493 |
|
|
494 |
# check existing unverified entries |
|
493 | 495 |
unverified = get_unverified(pending.provider, |
494 | 496 |
identifier=pending.third_party_identifier) |
495 | 497 |
|
496 | 498 |
if unverified and request.method == 'GET': |
497 | 499 |
messages.warning(request, unverified.get_pending_registration_msg) |
498 |
if unverified.user.activation_sent:
|
|
500 |
if unverified.user.moderated:
|
|
499 | 501 |
messages.warning(request, |
500 | 502 |
unverified.get_pending_resend_activation_msg) |
501 | 503 |
else: |
502 | 504 |
messages.warning(request, |
503 | 505 |
unverified.get_pending_moderation_msg) |
504 | 506 |
|
505 |
try: |
|
506 |
if not backend: |
|
507 |
backend = get_backend(request) |
|
508 |
form = backend.get_signup_form(provider, instance) |
|
509 |
except Exception, e: |
|
510 |
form = SimpleBackend(request).get_signup_form(provider) |
|
511 |
messages.error(request, e) |
|
507 |
# prepare activation backend based on current request |
|
508 |
if not activation_backend: |
|
509 |
activation_backend = activation_backends.get_backend() |
|
510 |
|
|
511 |
form_kwargs = {'instance': instance} |
|
512 |
if third_party_token: |
|
513 |
form_kwargs['third_party_token'] = third_party_token |
|
514 |
|
|
515 |
form = activation_backend.get_signup_form( |
|
516 |
provider, None, **form_kwargs) |
|
512 | 517 |
|
513 | 518 |
if request.method == 'POST': |
519 |
form = activation_backend.get_signup_form( |
|
520 |
provider, |
|
521 |
request.POST, |
|
522 |
**form_kwargs) |
|
523 |
|
|
514 | 524 |
if form.is_valid(): |
515 |
user = form.save(commit=False) |
|
525 |
commited = False |
|
526 |
try: |
|
527 |
user = form.save(commit=False) |
|
516 | 528 |
|
517 |
# delete previously unverified accounts |
|
518 |
if AstakosUser.objects.user_exists(user.email): |
|
519 |
AstakosUser.objects.get_by_identifier(user.email).delete() |
|
529 |
# delete previously unverified accounts
|
|
530 |
if AstakosUser.objects.user_exists(user.email):
|
|
531 |
AstakosUser.objects.get_by_identifier(user.email).delete()
|
|
520 | 532 |
|
521 |
try:
|
|
533 |
# store_user so that user auth providers get initialized
|
|
522 | 534 |
form.store_user(user, request) |
523 |
|
|
524 |
result = backend.handle_activation(user) |
|
525 |
status = messages.SUCCESS |
|
535 |
result = activation_backend.handle_registration(user) |
|
536 |
if result.status == \ |
|
537 |
activation_backend.Result.PENDING_MODERATION: |
|
538 |
# user should be warned that his account is not active yet |
|
539 |
status = messages.WARNING |
|
540 |
else: |
|
541 |
status = messages.SUCCESS |
|
526 | 542 |
message = result.message |
543 |
activation_backend.send_result_notifications(result, user) |
|
527 | 544 |
|
528 |
if 'additional_email' in form.cleaned_data: |
|
529 |
additional_email = form.cleaned_data['additional_email'] |
|
530 |
if additional_email != user.email: |
|
531 |
user.additionalmail_set.create(email=additional_email) |
|
532 |
msg = 'Additional email: %s saved for user %s.' % ( |
|
533 |
additional_email, |
|
534 |
user.email |
|
535 |
) |
|
536 |
logger._log(LOGGING_LEVEL, msg, []) |
|
545 |
# commit user entry |
|
546 |
transaction.commit() |
|
547 |
# commited flag |
|
548 |
# in case an exception get raised from this point |
|
549 |
commited = True |
|
537 | 550 |
|
538 | 551 |
if user and user.is_active: |
552 |
# activation backend directly activated the user |
|
553 |
# log him in |
|
539 | 554 |
next = request.POST.get('next', '') |
540 | 555 |
response = prepare_response(request, user, next=next) |
541 |
transaction.commit() |
|
542 | 556 |
return response |
543 | 557 |
|
544 |
transaction.commit() |
|
545 | 558 |
messages.add_message(request, status, message) |
546 | 559 |
return HttpResponseRedirect(reverse(on_success)) |
547 |
|
|
548 |
except SendMailError, e: |
|
549 |
status = messages.ERROR |
|
550 |
message = e.message |
|
551 |
messages.error(request, message) |
|
552 |
transaction.rollback() |
|
553 |
except BaseException, e: |
|
554 |
logger.exception(e) |
|
555 |
message = _(astakos_messages.GENERIC_ERROR) |
|
556 |
messages.error(request, message) |
|
557 |
logger.exception(e) |
|
558 |
transaction.rollback() |
|
560 |
except Exception, e: |
|
561 |
if not commited: |
|
562 |
transaction.rollback() |
|
563 |
raise |
|
559 | 564 |
|
560 | 565 |
return render_response(template_name, |
561 | 566 |
signup_form=form, |
... | ... | |
605 | 610 |
if form.is_valid(): |
606 | 611 |
msg = form.cleaned_data['feedback_msg'] |
607 | 612 |
data = form.cleaned_data['feedback_data'] |
608 |
try: |
|
609 |
send_feedback(msg, data, request.user, email_template_name) |
|
610 |
except SendMailError, e: |
|
611 |
message = e.message |
|
612 |
messages.error(request, message) |
|
613 |
else: |
|
614 |
message = _(astakos_messages.FEEDBACK_SENT) |
|
615 |
messages.success(request, message) |
|
613 |
send_feedback(msg, data, request.user, email_template_name) |
|
614 |
message = _(astakos_messages.FEEDBACK_SENT) |
|
615 |
messages.success(request, message) |
|
616 | 616 |
return HttpResponseRedirect(reverse('feedback')) |
617 |
|
|
617 | 618 |
return render_response(template_name, |
618 | 619 |
feedback_form=form, |
619 |
context_instance=get_context(request, extra_context)) |
|
620 |
context_instance=get_context(request, |
|
621 |
extra_context)) |
|
620 | 622 |
|
621 | 623 |
|
622 | 624 |
@require_http_methods(["GET"]) |
623 |
@signed_terms_required
|
|
624 |
def logout(request, template='registration/logged_out.html', extra_context=None):
|
|
625 |
def logout(request, template='registration/logged_out.html',
|
|
626 |
extra_context=None):
|
|
625 | 627 |
""" |
626 | 628 |
Wraps `django.contrib.auth.logout`. |
627 | 629 |
""" |
... | ... | |
664 | 666 |
def activate(request, greeting_email_template_name='im/welcome_email.txt', |
665 | 667 |
helpdesk_email_template_name='im/helpdesk_notification.txt'): |
666 | 668 |
""" |
667 |
Activates the user identified by the ``auth`` request parameter, sends a welcome email
|
|
668 |
and renews the user token. |
|
669 |
Activates the user identified by the ``auth`` request parameter, sends a |
|
670 |
welcome email and renews the user token.
|
|
669 | 671 |
|
670 |
The view uses commit_manually decorator in order to ensure the user state will be updated
|
|
671 |
only if the email will be send successfully. |
|
672 |
The view uses commit_manually decorator in order to ensure the user state |
|
673 |
will be updated only if the email will be send successfully.
|
|
672 | 674 |
""" |
673 | 675 |
token = request.GET.get('auth') |
674 | 676 |
next = request.GET.get('next') |
675 |
try: |
|
676 |
user = AstakosUser.objects.get(auth_token=token) |
|
677 |
except AstakosUser.DoesNotExist: |
|
678 |
return HttpResponseBadRequest(_(astakos_messages.ACCOUNT_UNKNOWN)) |
|
679 | 677 |
|
680 |
if user.is_active or user.email_verified:
|
|
681 |
message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
|
|
678 |
if request.user.is_authenticated():
|
|
679 |
message = _(astakos_messages.LOGGED_IN_WARNING)
|
|
682 | 680 |
messages.error(request, message) |
683 | 681 |
return HttpResponseRedirect(reverse('index')) |
684 | 682 |
|
685 |
if not user.activation_sent: |
|
686 |
provider = user.get_auth_provider() |
|
687 |
message = user.get_inactive_message(provider.module) |
|
683 |
try: |
|
684 |
user = AstakosUser.objects.get(verification_code=token) |
|
685 |
except AstakosUser.DoesNotExist: |
|
686 |
raise Http404 |
|
687 |
|
|
688 |
if user.email_verified: |
|
689 |
message = _(astakos_messages.ACCOUNT_ALREADY_VERIFIED) |
|
688 | 690 |
messages.error(request, message) |
689 | 691 |
return HttpResponseRedirect(reverse('index')) |
690 | 692 |
|
691 | 693 |
try: |
692 |
activate_func(user, greeting_email_template_name,
|
|
693 |
helpdesk_email_template_name, verify_email=True)
|
|
694 |
messages.success(request, _(astakos_messages.ACCOUNT_ACTIVATED))
|
|
694 |
backend = activation_backends.get_backend()
|
|
695 |
result = backend.handle_verification(user, token)
|
|
696 |
backend.send_result_notifications(result, user)
|
|
695 | 697 |
next = ACTIVATION_REDIRECT_URL or next |
696 |
response = prepare_response(request, user, next, renew=True) |
|
698 |
response = HttpResponseRedirect(reverse('index')) |
|
699 |
if user.is_active: |
|
700 |
response = prepare_response(request, user, next, renew=True) |
|
701 |
messages.success(request, _(result.message)) |
|
702 |
else: |
|
703 |
messages.warning(request, _(result.message)) |
|
704 |
except Exception: |
|
705 |
transaction.rollback() |
|
706 |
raise |
|
707 |
else: |
|
697 | 708 |
transaction.commit() |
698 | 709 |
return response |
699 |
except SendMailError, e: |
|
700 |
message = e.message |
|
701 |
messages.add_message(request, messages.ERROR, message) |
|
702 |
transaction.rollback() |
|
703 |
return index(request) |
|
704 |
except BaseException, e: |
|
705 |
status = messages.ERROR |
|
706 |
message = _(astakos_messages.GENERIC_ERROR) |
|
707 |
messages.add_message(request, messages.ERROR, message) |
|
708 |
logger.exception(e) |
|
709 |
transaction.rollback() |
|
710 |
return index(request) |
|
711 | 710 |
|
712 | 711 |
|
713 | 712 |
@require_http_methods(["GET", "POST"]) |
714 |
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None): |
|
713 |
def approval_terms(request, term_id=None, |
|
714 |
template_name='im/approval_terms.html', extra_context=None): |
|
715 | 715 |
extra_context = extra_context or {} |
716 | 716 |
term = None |
717 | 717 |
terms = None |
... | ... | |
734 | 734 |
except IOError: |
735 | 735 |
messages.error(request, _(astakos_messages.GENERIC_ERROR)) |
736 | 736 |
return render_response( |
737 |
template_name, context_instance=get_context(request, extra_context)) |
|
737 |
template_name, context_instance=get_context(request, |
|
738 |
extra_context)) |
|
738 | 739 |
|
739 | 740 |
terms = f.read() |
740 | 741 |
|
... | ... | |
750 | 751 |
return render_response(template_name, |
751 | 752 |
terms=terms, |
752 | 753 |
approval_terms_form=form, |
753 |
context_instance=get_context(request, extra_context)) |
|
754 |
context_instance=get_context(request, |
|
755 |
extra_context)) |
|
754 | 756 |
user = form.save() |
755 | 757 |
return HttpResponseRedirect(next) |
756 | 758 |
else: |
... | ... | |
760 | 762 |
return render_response(template_name, |
761 | 763 |
terms=terms, |
762 | 764 |
approval_terms_form=form, |
763 |
context_instance=get_context(request, extra_context)) |
|
765 |
context_instance=get_context(request, |
|
766 |
extra_context)) |
|
764 | 767 |
|
765 | 768 |
|
766 | 769 |
@require_http_methods(["GET", "POST"]) |
... | ... | |
772 | 775 |
extra_context=None): |
773 | 776 |
extra_context = extra_context or {} |
774 | 777 |
|
775 |
|
|
776 |
if not astakos_settings.EMAILCHANGE_ENABLED: |
|
778 |
if not settings.EMAILCHANGE_ENABLED: |
|
777 | 779 |
raise PermissionDenied |
778 | 780 |
|
779 | 781 |
if activation_key: |
780 | 782 |
try: |
781 | 783 |
user = EmailChange.objects.change_email(activation_key) |
782 |
if request.user.is_authenticated() and request.user == user or not \ |
|
784 |
if request.user.is_authenticated() and \ |
|
785 |
request.user == user or not \ |
|
783 | 786 |
request.user.is_authenticated(): |
784 | 787 |
msg = _(astakos_messages.EMAIL_CHANGED) |
785 | 788 |
messages.success(request, msg) |
... | ... | |
791 | 794 |
return HttpResponseRedirect(reverse('index')) |
792 | 795 |
|
793 | 796 |
return render_response(confirm_template_name, |
794 |
modified_user=user if 'user' in locals() \
|
|
797 |
modified_user=user if 'user' in locals() |
|
795 | 798 |
else None, context_instance=get_context(request, |
796 |
extra_context))
|
|
799 |
extra_context)) |
|
797 | 800 |
|
798 | 801 |
if not request.user.is_authenticated(): |
799 | 802 |
path = quote(request.get_full_path()) |
... | ... | |
812 | 815 |
if request.method == 'POST' and form.is_valid(): |
813 | 816 |
try: |
814 | 817 |
ec = form.save(request, email_template_name, request) |
815 |
except SendMailError, e: |
|
816 |
msg = e |
|
817 |
messages.error(request, msg) |
|
818 |
except Exception, e: |
|
818 | 819 |
transaction.rollback() |
819 |
return HttpResponseRedirect(reverse('edit_profile'))
|
|
820 |
raise
|
|
820 | 821 |
else: |
821 | 822 |
msg = _(astakos_messages.EMAIL_CHANGE_REGISTERED) |
822 | 823 |
messages.success(request, msg) |
... | ... | |
833 | 834 |
) |
834 | 835 |
|
835 | 836 |
|
836 |
def send_activation(request, user_id, template_name='im/login.html', extra_context=None): |
|
837 |
def send_activation(request, user_id, template_name='im/login.html', |
|
838 |
extra_context=None): |
|
837 | 839 |
|
838 | 840 |
if request.user.is_authenticated(): |
839 |
return HttpResponseRedirect(reverse('edit_profile'))
|
|
841 |
return HttpResponseRedirect(reverse('index'))
|
|
840 | 842 |
|
841 | 843 |
extra_context = extra_context or {} |
842 | 844 |
try: |
... | ... | |
844 | 846 |
except AstakosUser.DoesNotExist: |
845 | 847 |
messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN)) |
846 | 848 |
else: |
847 |
if not u.activation_sent and astakos_settings.MODERATION_ENABLED: |
|
848 |
raise PermissionDenied |
|
849 |
try: |
|
850 |
send_activation_func(u) |
|
851 |
msg = _(astakos_messages.ACTIVATION_SENT) |
|
852 |
messages.success(request, msg) |
|
853 |
except SendMailError, e: |
|
854 |
messages.error(request, e) |
|
849 |
if u.email_verified: |
|
850 |
logger.warning("[resend activation] Account already verified: %s", |
|
851 |
u.log_display) |
|
852 |
|
|
853 |
messages.error(request, |
|
854 |
_(astakos_messages.ACCOUNT_ALREADY_VERIFIED)) |
|
855 |
else: |
|
856 |
activation_backend = activation_backends.get_backend() |
|
857 |
activation_backend.send_user_verification_email(u) |
|
858 |
messages.success(request, astakos_messages.ACTIVATION_SENT) |
|
855 | 859 |
|
856 | 860 |
return HttpResponseRedirect(reverse('index')) |
857 | 861 |
|
... | ... | |
881 | 885 |
resource_groups=resource_groups, |
882 | 886 |
resources_order=resources_order, |
883 | 887 |
current_usage=current_usage, |
884 |
token_cookie_name=astakos_settings.COOKIE_NAME,
|
|
888 |
token_cookie_name=settings.COOKIE_NAME, |
|
885 | 889 |
usage_update_interval= |
886 |
astakos_settings.USAGE_UPDATE_INTERVAL)
|
|
890 |
settings.USAGE_UPDATE_INTERVAL) |
|
887 | 891 |
|
888 | 892 |
|
889 | 893 |
# TODO: action only on POST and user should confirm the removal |
Also available in: Unified diff