diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 271eace0..bef0b271 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -111,6 +111,7 @@ TEMPLATES = [ 'authentication.context_processors.states', 'authentication.context_processors.membership_state', 'authentication.context_processors.sso', + 'groupmanagement.context_processors.can_manage_groups', ], }, }, diff --git a/groupmanagement/context_processors.py b/groupmanagement/context_processors.py new file mode 100644 index 00000000..0d60be3f --- /dev/null +++ b/groupmanagement/context_processors.py @@ -0,0 +1,5 @@ +from groupmanagement.managers import GroupManager + + +def can_manage_groups(request): + return {'can_manage_groups': GroupManager.can_manage_groups(request.user)} diff --git a/groupmanagement/managers.py b/groupmanagement/managers.py index cb0d0486..41188257 100644 --- a/groupmanagement/managers.py +++ b/groupmanagement/managers.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import Group from django.conf import settings +from authentication.managers import UserState class GroupManager: def __init__(self): @@ -9,6 +10,10 @@ class GroupManager: def get_joinable_groups(): return Group.objects.exclude(authgroup__internal=True) + @staticmethod + def get_group_leaders_groups(user): + return Group.objects.filter(authgroup__group_leaders__in=[user]) + @staticmethod def joinable_group(group): """ @@ -18,3 +23,34 @@ class GroupManager: :return: bool True if its joinable, False otherwise """ return not group.authgroup.internal + + @staticmethod + def has_management_permission(user): + return user.has_perm('auth.group_management') + + @classmethod + def can_manage_groups(cls, user): + """ + For use with user_passes_test decorator. + Check if the user can manage groups. Either has the + auth.group_management permission or is a leader of at least one group + and is also a Member. + :param user: django.contrib.auth.models.User for the request + :return: bool True if user can manage groups, False otherwise + """ + if user.is_authenticated: + return cls.has_management_permission(user) or (user.leads_groups.all() and UserState.member_state(user)) + return False + + @classmethod + def can_manage_group(cls, user, group): + """ + Check user has permission to manage the given group + :param user: User object to test permission of + :param group: Group object the user is attempting to manage + :return: True if the user can manage the group + """ + if user.is_authenticated: + return cls.has_management_permission(user) or ( + user.leads_groups.filter(group=group).exists() and UserState.member_state(user)) + return False diff --git a/groupmanagement/views.py b/groupmanagement/views.py index 0e11cdb5..c36de8a5 100755 --- a/groupmanagement/views.py +++ b/groupmanagement/views.py @@ -1,18 +1,18 @@ from __future__ import unicode_literals from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required -from django.contrib.auth.decorators import permission_required +from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.models import Group from django.contrib import messages from notifications import notify -from groupmanagement.managers import GroupManager -from groupmanagement.models import GroupRequest -from authentication.models import AuthServicesInfo -from eveonline.managers import EveManager from django.utils.translation import ugettext_lazy as _ from django.db.models import Count from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.http import Http404 +from groupmanagement.managers import GroupManager +from groupmanagement.models import GroupRequest +from authentication.models import AuthServicesInfo +from eveonline.managers import EveManager import logging @@ -20,17 +20,25 @@ logger = logging.getLogger(__name__) @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_management(request): logger.debug("group_management called by user %s" % request.user) acceptrequests = [] leaverequests = [] - for grouprequest in GroupRequest.objects.all(): + if GroupManager.has_management_permission(request.user): + # Full access + group_requests = GroupRequest.objects.all() + else: + # Group specific leader + group_requests = GroupRequest.objects.filter(group__authgroup__group_leaders__in=[request.user]) + + for grouprequest in group_requests: if grouprequest.leave_request: leaverequests.append(grouprequest) else: acceptrequests.append(grouprequest) + logger.debug("Providing user %s with %s acceptrequests and %s leaverequests." % ( request.user, len(acceptrequests), len(leaverequests))) @@ -40,11 +48,18 @@ def group_management(request): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_membership(request): logger.debug("group_membership called by user %s" % request.user) # Get all open and closed groups - groups = Group.objects.exclude(authgroup__internal=True).annotate(num_members=Count('user')).order_by('name') + if GroupManager.has_management_permission(request.user): + # Full access + groups = GroupManager.get_joinable_groups() + else: + # Group leader specific + groups = GroupManager.get_group_leaders_groups(request.user) + + groups = groups.exclude(authgroup__internal=True).annotate(num_members=Count('user')).order_by('name') render_items = {'groups': groups} @@ -52,14 +67,17 @@ def group_membership(request): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_membership_list(request, group_id): logger.debug("group_membership_list called by user %s for group id %s" % (request.user, group_id)) try: group = Group.objects.get(id=group_id) # Check its a joinable group i.e. not corp or internal - if not GroupManager.joinable_group(group): + # And the user has permission to manage it + if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group): + logger.warning("User %s attempted to view the membership of group %s but permission was denied" % + (request.user, group_id)) raise PermissionDenied except ObjectDoesNotExist: @@ -81,13 +99,20 @@ def group_membership_list(request, group_id): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_membership_remove(request, group_id, user_id): logger.debug("group_membership_remove called by user %s for group id %s on user id %s" % (request.user, group_id, user_id)) try: group = Group.objects.get(id=group_id) + # Check its a joinable group i.e. not corp or internal + # And the user has permission to manage it + if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group): + logger.warning("User %s attempted to remove a user from group %s but permission was denied" % (request.user, + group_id)) + raise PermissionDenied + try: user = group.user_set.get(id=user_id) # Remove group from user @@ -103,12 +128,17 @@ def group_membership_remove(request, group_id, user_id): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_accept_request(request, group_request_id): logger.debug("group_accept_request called by user %s for grouprequest id %s" % (request.user, group_request_id)) try: group_request = GroupRequest.objects.get(id=group_request_id) group, created = Group.objects.get_or_create(name=group_request.group.name) + + if not GroupManager.joinable_group(group_request.group) or \ + not GroupManager.can_manage_group(request.user, group_request.group): + raise PermissionDenied + group_request.user.groups.add(group) group_request.user.save() group_request.delete() @@ -118,6 +148,11 @@ def group_accept_request(request, group_request_id): message="Your application to %s has been accepted." % group_request.group) messages.success(request, 'Accepted application from %s to %s.' % (group_request.main_char, group_request.group)) + + except PermissionDenied as p: + logger.warning("User %s attempted to accept group join request %s but permission was denied" % + (request.user, group_request_id)) + raise p except: messages.error(request, 'An unhandled error occurred while processing the application from %s to %s.' % ( group_request.main_char, group_request.group)) @@ -129,12 +164,15 @@ def group_accept_request(request, group_request_id): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_reject_request(request, group_request_id): logger.debug("group_reject_request called by user %s for group request id %s" % (request.user, group_request_id)) try: group_request = GroupRequest.objects.get(id=group_request_id) + if not GroupManager.can_manage_group(request.user, group_request.group): + raise PermissionDenied + if group_request: logger.info("User %s rejected group request from user %s to group %s" % ( request.user, group_request.user, group_request.group.name)) @@ -143,6 +181,11 @@ def group_reject_request(request, group_request_id): message="Your application to %s has been rejected." % group_request.group) messages.success(request, 'Rejected application from %s to %s.' % (group_request.main_char, group_request.group)) + + except PermissionDenied as p: + logger.warning("User %s attempted to reject group join request %s but permission was denied" % + (request.user, group_request_id)) + raise p except: messages.error(request, 'An unhandled error occured while processing the application from %s to %s.' % ( group_request.main_char, group_request.group)) @@ -154,12 +197,16 @@ def group_reject_request(request, group_request_id): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_leave_accept_request(request, group_request_id): logger.debug( "group_leave_accept_request called by user %s for group request id %s" % (request.user, group_request_id)) try: group_request = GroupRequest.objects.get(id=group_request_id) + + if not GroupManager.can_manage_group(request.user, group_request.group): + raise PermissionDenied + group, created = Group.objects.get_or_create(name=group_request.group.name) group_request.user.groups.remove(group) group_request.user.save() @@ -170,6 +217,10 @@ def group_leave_accept_request(request, group_request_id): message="Your request to leave %s has been accepted." % group_request.group) messages.success(request, 'Accepted application from %s to leave %s.' % (group_request.main_char, group_request.group)) + except PermissionDenied as p: + logger.warning("User %s attempted to accept group leave request %s but permission was denied" % + (request.user, group_request_id)) + raise p except: messages.error(request, 'An unhandled error occured while processing the application from %s to leave %s.' % ( group_request.main_char, group_request.group)) @@ -181,13 +232,16 @@ def group_leave_accept_request(request, group_request_id): @login_required -@permission_required('auth.group_management') +@user_passes_test(GroupManager.can_manage_groups) def group_leave_reject_request(request, group_request_id): logger.debug( "group_leave_reject_request called by user %s for group request id %s" % (request.user, group_request_id)) try: group_request = GroupRequest.objects.get(id=group_request_id) + if not GroupManager.can_manage_group(request.user, group_request.group): + raise PermissionDenied + if group_request: group_request.delete() logger.info("User %s rejected group leave request from user %s for group %s" % ( @@ -196,6 +250,10 @@ def group_leave_reject_request(request, group_request_id): message="Your request to leave %s has been rejected." % group_request.group) messages.success(request, 'Rejected application from %s to leave %s.' % ( group_request.main_char, group_request.group)) + except PermissionDenied as p: + logger.warning("User %s attempted to reject group leave request %s but permission was denied" % + (request.user, group_request_id)) + raise p except: messages.error(request, 'An unhandled error occured while processing the application from %s to leave %s.' % ( group_request.main_char, group_request.group)) diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index 650d843a..d44a80c9 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -165,7 +165,7 @@ {% endif %} - {% if perms.auth.group_management %} + {% if can_manage_groups %}
  • {% trans " Group Management" %}