Commit 98b64105 authored by Kostas Papadimitriou's avatar Kostas Papadimitriou Committed by Olga Brani
Browse files

Required auth providers functionality

if one of auth providers is set to be required, user with no such
provider can only view his profile page and is prompted to add a
new login method.
parent 32e951c4
......@@ -47,6 +47,7 @@ logger = logging.getLogger(__name__)
# providers registry
class AuthProviderBase(type):
......@@ -62,6 +63,8 @@ class AuthProviderBase(type):
newcls = super(AuthProviderBase, cls).__new__(cls, name, bases, dct)
if include:
PROVIDERS[type_id] = newcls
if newcls().is_required():
REQUIRED_PROVIDERS[type_id] = newcls
return newcls
......@@ -122,6 +125,10 @@ class AuthProvider(object):
return self.is_active() and self.get_setting('CAN_ADD',
def is_required(self):
"""Provider required (user cannot remove the last one)"""
return self.is_active() and self.get_setting('REQUIRED', False)
def is_active(self):
return self.module in astakos_settings.IM_MODULES
......@@ -46,6 +46,7 @@ ACCOUNT_PENDING_ACTIVATION_HELP = 'If you haven\'t received activation
ACCOUNT_ACTIVATED = 'Congratulations. Your account has' + \
' been activated and you have been' + \
' automatically signed in to your account.'
ALREADY_LOGGED_IN = 'You are already signed in to your account.'
PASSWORD_RESET_DONE = 'A mail with details on how to change your password was sent.'
PASSWORD_RESET_CONFIRM_DONE = 'Password changed. You can now login using your new password.'
......@@ -180,6 +181,7 @@ AUTH_PROVIDER_ADD_FAILED = "Failed to add new login method
AUTH_PROVIDER_ADD_EXISTS = "Account already assigned to another user."
AUTH_PROVIDER_LOGIN_TO_ADD = "The new login method will be assigned once you login to your account."
AUTH_PROVIDER_INVALID_LOGIN = "No account exists."
AUTH_PROVIDER_REQUIRED = "%(provider)s login method is required. Add one from your profile page."
messages = locals().keys()
......@@ -188,4 +190,4 @@ for msg in messages:
attr = "ASTAKOS_%s_MESSAGE" % msg
settings_value = getattr(settings, attr, None)
if settings_value:
locals()[msg] = settings_value
\ No newline at end of file
locals()[msg] = settings_value
......@@ -661,14 +661,29 @@ class AstakosUser(User):
return True
def can_remove_auth_provider(self, provider):
if len(self.get_active_auth_providers()) <= 1:
def can_remove_auth_provider(self, module):
provider = auth_providers.get_provider(module)
existing = self.get_active_auth_providers()
existing_for_provider = self.get_active_auth_providers(module=module)
if len(existing) <= 1:
return False
if len(existing_for_provider) == 1 and provider.is_required():
return False
return True
def can_change_password(self):
return self.has_auth_provider('local', auth_backend='astakos')
def has_required_auth_providers(self):
required = auth_providers.REQUIRED_PROVIDERS
for provider in required:
if not self.has_auth_provider(provider):
return False
return True
def has_auth_provider(self, provider, **kwargs):
return bool(self.auth_providers.filter(module=provider,
......@@ -743,9 +758,9 @@ class AstakosUser(User):
return providers
def get_active_auth_providers(self):
def get_active_auth_providers(self, **filters):
providers = []
for provider in
for provider in**filters):
if auth_providers.get_provider(provider.module).is_available_for_login():
return providers
......@@ -784,8 +799,8 @@ class AstakosUser(User):
class AstakosUserAuthProviderManager(models.Manager):
def active(self):
return self.filter(active=True)
def active(self, **filters):
return self.filter(active=True, **filters)
class AstakosUserAuthProvider(models.Model):
......@@ -46,8 +46,9 @@ from django.shortcuts import get_object_or_404
from urlparse import urlunsplit, urlsplit
from import prepare_response, get_context
from import (
requires_anonymous, render_response, requires_auth_provider)
from import requires_anonymous, render_response, \
requires_auth_provider, required_auth_methods_assigned
from import AstakosUser, PendingThirdPartyUser
from import LoginForm
......@@ -166,7 +166,7 @@ def requires_anonymous(func):
def signed_terms_required(func):
Decorator checkes whether the request.user is Anonymous and in that case
Decorator checks whether the request.user is Anonymous and in that case
redirects to `logout`.
......@@ -180,6 +180,38 @@ def signed_terms_required(func):
return wrapper
def required_auth_methods_assigned(only_warn=False):
Decorator that checks whether the request.user has all required auth providers
required_providers = auth_providers.REQUIRED_PROVIDERS.keys()
def decorator(func):
if not required_providers:
return func
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated():
for required in required_providers:
if not request.user.has_auth_provider(required):
provider = auth_providers.get_provider(required)
if only_warn:
_(astakos_messages.AUTH_PROVIDER_REQUIRED % {
'provider': provider.get_title_display}))
return HttpResponseRedirect(reverse('edit_profile'))
return func(request, *args, **kwargs)
return wrapper
return decorator
def valid_astakos_user_required(func):
return signed_terms_required(required_auth_methods_assigned()(login_required(func)))
@require_http_methods(["GET", "POST"])
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context=None):
......@@ -217,8 +249,7 @@ def index(request, login_template_name='im/login.html', profile_template_name='i
@require_http_methods(["GET", "POST"])
def invite(request, template_name='im/invitations.html', extra_context=None):
......@@ -296,6 +327,7 @@ def invite(request, template_name='im/invitations.html', extra_context=None):
@require_http_methods(["GET", "POST"])
def edit_profile(request, template_name='im/profile.html', extra_context=None):
......@@ -493,6 +525,7 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
@require_http_methods(["GET", "POST"])
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
......@@ -668,8 +701,7 @@ def approval_terms(request, term_id=None, template_name='im/approval_terms.html'
@require_http_methods(["GET", "POST"])
def change_email(request, activation_key=None,
......@@ -723,6 +755,10 @@ def change_email(request, activation_key=None,
def send_activation(request, user_id, template_name='im/login.html', extra_context=None):
if request.user.is_authenticated():
messages.error(request, _(astakos_messages.ALREADY_LOGGED_IN))
return HttpResponseRedirect(reverse('edit_profile'))
raise PermissionDenied
......@@ -1307,8 +1343,7 @@ def send_activation(request, user_id, template_name='im/login.html', extra_conte
@require_http_methods(["POST", "GET"])
def resource_usage(request):
def with_class(entry):
entry['load_class'] = 'red'
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment