from django.contrib.auth import get_user as _get_user from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import AnonymousUser as DjangoAnonymousUser from django.contrib.auth.models import Permission from django.db.models import Q from django.utils.functional import SimpleLazyObject from rest_framework.authentication import BaseAuthentication from ..core.config import config # Registered users class CustomizedModelBackend(ModelBackend): """ Customized backend for authentication. Ensures that registered users have all permissions of the group 'Registered' (pk=2). See AUTHENTICATION_BACKENDS settings. """ def get_group_permissions(self, user_obj, obj=None): """ Returns a set of permission strings that this user has through his/her groups. """ # TODO: Refactor this after Django 1.8 is minimum requirement. Add # also anonymous permission check to this backend. if user_obj.is_anonymous() or obj is not None: return set() if not hasattr(user_obj, '_group_perm_cache'): if user_obj.is_superuser: perms = Permission.objects.all() else: user_groups_field = get_user_model()._meta.get_field('groups') user_groups_query = 'group__%s' % user_groups_field.related_query_name() # The next two lines are the customization. query = Q(**{user_groups_query: user_obj}) | Q(group__pk=2) perms = Permission.objects.filter(query) perms = perms.values_list('content_type__app_label', 'codename').order_by() user_obj._group_perm_cache = set("%s.%s" % (ct, name) for ct, name in perms) return user_obj._group_perm_cache # Anonymous users class AnonymousUser(DjangoAnonymousUser): """ Class for anonymous user instances which have the permissions from the group 'Anonymous' (pk=1). """ def get_all_permissions(self, obj=None): """ Returns the permissions a user is granted by his group membership(s). Try to return the permissions for the 'Anonymous' group (pk=1). """ perms = Permission.objects.filter(group__pk=1) if perms is None: return set() # TODO: Test without order_by() perms = perms.values_list('content_type__app_label', 'codename').order_by() return set(['%s.%s' % (content_type, codename) for content_type, codename in perms]) def has_perm(self, perm, obj=None): """ Checks if the user has a specific permission. """ return (perm in self.get_all_permissions()) def has_module_perms(self, app_label): """ Checks if the user has permissions on the module app_label. """ for perm in self.get_all_permissions(): if perm[:perm.index('.')] == app_label: return True return False class RESTFrameworkAnonymousAuthentication(BaseAuthentication): """ Authentication class for the Django REST framework. Sets the user to the our AnonymousUser but only if general_system_enable_anonymous is set to True in the config. """ def authenticate(self, request): if config['general_system_enable_anonymous']: return (AnonymousUser(), None) return None class AuthenticationMiddleware: """ Middleware to get the logged in user in users. Uses AnonymousUser instead of Django's anonymous user. """ def process_request(self, request): """ Like django.contrib.auth.middleware.AuthenticationMiddleware, but uses our own get_user function. """ assert hasattr(request, 'session'), ( "The authentication middleware requires session middleware " "to be installed. Edit your MIDDLEWARE_CLASSES setting to insert " "'django.contrib.sessions.middleware.SessionMiddleware' before " "'openslides.users.auth.AuthenticationMiddleware'." ) request.user = SimpleLazyObject(lambda: get_user(request)) def get_user(request): """ Gets the user from the request. This is a mix of django.contrib.auth.get_user and django.contrib.auth.middleware.get_user which uses our anonymous user. """ try: return_user = request._cached_user except AttributeError: # Get the user. If it is a DjangoAnonymousUser, then use our AnonymousUser return_user = _get_user(request) if config['general_system_enable_anonymous'] and isinstance(return_user, DjangoAnonymousUser): return_user = AnonymousUser() request._cached_user = return_user return return_user