diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index fa6a5aa3..b9dc87fd 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/alliance_auth/urls.py b/alliance_auth/urls.py index bbea8f80..056d4579 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -190,6 +190,12 @@ urlpatterns += i18n_patterns( url(_(r'^groups/'), groupmanagement.views.groups_view, name='auth_groups'), url(_(r'^group/management/'), groupmanagement.views.group_management, name='auth_group_management'), + url(_(r'^group/membership/$'), groupmanagement.views.group_membership, + name='auth_group_membership'), + url(_(r'^group/membership/(\w+)/$'), groupmanagement.views.group_membership_list, + name='auth_group_membership_list'), + url(_(r'^group/membership/(\w+)/remove/(\w+)/$'), groupmanagement.views.group_membership_remove, + name='auth_group_membership_remove'), url(_(r'^group/request_add/(\w+)'), groupmanagement.views.group_request_add, name='auth_group_request_add'), url(_(r'^group/request/accept/(\w+)'), groupmanagement.views.group_accept_request, diff --git a/authentication/context_processors.py b/authentication/context_processors.py index d403df83..7f1c39f1 100644 --- a/authentication/context_processors.py +++ b/authentication/context_processors.py @@ -1,14 +1,11 @@ from __future__ import unicode_literals -from authentication.models import AuthServicesInfo from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE +from authentication.managers import UserState from django.conf import settings def membership_state(request): - if request.user.is_authenticated: - auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0] - return {'STATE': auth.state} - return {'STATE': NONE_STATE} + return UserState.get_membership_state(request) def states(request): @@ -19,6 +16,7 @@ def states(request): 'MEMBER_BLUE_STATE': [MEMBER_STATE, BLUE_STATE], } + def sso(request): return { 'EVE_SSO_CALLBACK_URL': settings.EVE_SSO_CALLBACK_URL, diff --git a/authentication/decorators.py b/authentication/decorators.py index 1d3167cb..c08fc8cb 100644 --- a/authentication/decorators.py +++ b/authentication/decorators.py @@ -1,33 +1,23 @@ from __future__ import unicode_literals from django.contrib.auth.decorators import user_passes_test -from authentication.models import AuthServicesInfo -from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE -from django.conf import settings +from authentication.managers import UserState -def _state_required(states, *args, **kwargs): - def test_func(user): - if user.is_superuser and settings.SUPERUSER_STATE_BYPASS: - return True - if user.is_authenticated: - auth = AuthServicesInfo.objects.get_or_create(user=user)[0] - return auth.state in states - return False - - return user_passes_test(test_func, *args, **kwargs) +def _state_required(state_test, *args, **kwargs): + return user_passes_test(state_test, *args, **kwargs) def members(*args, **kwargs): - return _state_required([MEMBER_STATE], *args, **kwargs) + return _state_required(UserState.member_state, *args, **kwargs) def blues(*args, **kwargs): - return _state_required([BLUE_STATE], *args, **kwargs) + return _state_required(UserState.blue_state, *args, **kwargs) def members_and_blues(*args, **kwargs): - return _state_required([MEMBER_STATE, BLUE_STATE], *args, **kwargs) + return _state_required(UserState.member_or_blue_state, *args, **kwargs) def none_state(*args, **kwargs): - return _state_required([NONE_STATE], *args, **kwargs) + return _state_required(UserState.none_state, *args, **kwargs) diff --git a/authentication/managers.py b/authentication/managers.py index 6471880b..a577a05a 100755 --- a/authentication/managers.py +++ b/authentication/managers.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals from django.contrib.auth.models import User - +from django.conf import settings +from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE from authentication.models import AuthServicesInfo import logging @@ -143,3 +144,44 @@ class AuthServicesInfoManager: logger.info("Updated user %s market info in authservicesinfo model." % user) else: logger.error("Failed to update user %s market info: user does not exist." % user) + + +class UserState: + def __init__(self): + pass + + MEMBER_STATE = MEMBER_STATE + BLUE_STATE = BLUE_STATE + NONE_STATE = NONE_STATE + + @classmethod + def member_state(cls, user): + return cls.state_required(user, [cls.MEMBER_STATE]) + + @classmethod + def member_or_blue_state(cls, user): + return cls.state_required(user, [cls.MEMBER_STATE, cls.BLUE_STATE]) + + @classmethod + def blue_state(cls, user): + return cls.state_required(user, [cls.BLUE_STATE]) + + @classmethod + def none_state(cls, user): + return cls.state_required(user, [cls.NONE_STATE]) + + @classmethod + def get_membership_state(cls, request): + if request.user.is_authenticated: + auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0] + return {'STATE': auth.state} + return {'STATE': cls.NONE_STATE} + + @staticmethod + def state_required(user, states): + if user.is_superuser and settings.SUPERUSER_STATE_BYPASS: + return True + if user.is_authenticated: + auth = AuthServicesInfo.objects.get_or_create(user=user)[0] + return auth.state in states + return False diff --git a/groupmanagement/admin.py b/groupmanagement/admin.py index 4d21a00f..aa7ea35a 100644 --- a/groupmanagement/admin.py +++ b/groupmanagement/admin.py @@ -1,13 +1,15 @@ from __future__ import unicode_literals from django.contrib import admin -from groupmanagement.models import GroupDescription from groupmanagement.models import GroupRequest -from groupmanagement.models import HiddenGroup -from groupmanagement.models import OpenGroup +from groupmanagement.models import AuthGroup -admin.site.register(GroupDescription) +class AuthGroupAdmin(admin.ModelAdmin): + """ + Admin model for AuthGroup + """ + filter_horizontal = ('group_leaders',) + admin.site.register(GroupRequest) -admin.site.register(HiddenGroup) -admin.site.register(OpenGroup) +admin.site.register(AuthGroup, AuthGroupAdmin) 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 new file mode 100644 index 00000000..41188257 --- /dev/null +++ b/groupmanagement/managers.py @@ -0,0 +1,56 @@ +from django.contrib.auth.models import Group +from django.conf import settings +from authentication.managers import UserState + +class GroupManager: + def __init__(self): + pass + + @staticmethod + 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): + """ + Check if a group is a user joinable group, i.e. + not an internal group for Corp, Alliance, Members etc + :param group: django.contrib.auth.models.Group object + :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/migrations/0004_authgroup.py b/groupmanagement/migrations/0004_authgroup.py new file mode 100644 index 00000000..a7bbda98 --- /dev/null +++ b/groupmanagement/migrations/0004_authgroup.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-12-04 10:25 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +from django.core.exceptions import ObjectDoesNotExist +import django.db.models.deletion + + +def internal_group(group): + return ( + "Corp_" in group.name or + "Alliance_" in group.name or + settings.DEFAULT_AUTH_GROUP in group.name or + settings.DEFAULT_BLUE_GROUP in group.name + ) + + +def combine_group_models(apps, schema_editor): + Group = apps.get_model("auth", "Group") + AuthGroup = apps.get_model("groupmanagement", "AuthGroup") + + for group in Group.objects.all(): + authgroup = AuthGroup(group=group) + if not hasattr(group, 'hiddengroup'): + authgroup.hidden = False + if hasattr(group, 'opengroup'): + authgroup.open = True + + if hasattr(group, 'groupdescription'): + authgroup.description = group.groupdescription.description + + authgroup.internal = internal_group(group) + authgroup.save() + + +def reverse_group_models(apps, schema_editor): + Group = apps.get_model("auth", "Group") + GroupDescription = apps.get_model("groupmanagement", "GroupDescription") + OpenGroup = apps.get_model("groupmanagement", "OpenGroup") + HiddenGroup = apps.get_model("groupmanagement", "HiddenGroup") + + for group in Group.objects.all(): + if not hasattr(group, 'authgroup') or group.authgroup is None: + continue + if group.authgroup.open: + OpenGroup.objects.get_or_create(group=group) + else: + try: + OpenGroup.objects.get(group=group).delete() + except ObjectDoesNotExist: + pass + + if group.authgroup.hidden: + HiddenGroup.objects.get_or_create(group=group) + else: + try: + HiddenGroup.objects.get(group=group).delete() + except ObjectDoesNotExist: + pass + + if len(group.authgroup.description): + GroupDescription.objects.update_or_create(group=group, + defaults={'description': group.authgroup.description}) + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0008_alter_user_username_max_length'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('groupmanagement', '0003_default_groups'), + ] + + operations = [ + migrations.CreateModel( + name='AuthGroup', + fields=[ + ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, + serialize=False, to='auth.Group')), + ('internal', models.BooleanField(default=True, help_text='Internal group, users cannot see, join or request to join this group.
Used for groups such as Members, Corp_*, Alliance_* etc.
Overrides Hidden and Open options when selected.')), + ('hidden', models.BooleanField(default=True, help_text='Group is hidden from users but can still join with the correct link.')), + ('open', models.BooleanField(default=False, help_text='Group is open and users will be automatically added upon request.
If the group is not open users will need their request manually approved.')), + ('description', models.CharField(max_length=512, blank=True, help_text='Description of the group shown to users.', )), + + ('group_leaders', models.ManyToManyField(related_name='leads_groups', to=settings.AUTH_USER_MODEL, blank=True, help_text='Group leaders can process group requests for this group specifically. Use the auth.group_management permission to allow a user to manage all groups.',)), + ], + ), + migrations.RunPython(combine_group_models, reverse_group_models), + migrations.RemoveField( + model_name='groupdescription', + name='group', + ), + migrations.RemoveField( + model_name='hiddengroup', + name='group', + ), + migrations.RemoveField( + model_name='opengroup', + name='group', + ), + migrations.DeleteModel( + name='GroupDescription', + ), + migrations.DeleteModel( + name='HiddenGroup', + ), + migrations.DeleteModel( + name='OpenGroup', + ), + ] diff --git a/groupmanagement/models.py b/groupmanagement/models.py index ffad61aa..52d98343 100644 --- a/groupmanagement/models.py +++ b/groupmanagement/models.py @@ -3,19 +3,12 @@ from django.utils.encoding import python_2_unicode_compatible from django.db import models from django.contrib.auth.models import User from django.contrib.auth.models import Group +from django.db.models.signals import post_save +from django.dispatch import receiver from eveonline.models import EveCharacter -@python_2_unicode_compatible -class GroupDescription(models.Model): - description = models.CharField(max_length=512) - group = models.OneToOneField(Group) - - def __str__(self): - return self.group.name + " - Description" - - @python_2_unicode_compatible class GroupRequest(models.Model): status = models.CharField(max_length=254) @@ -29,16 +22,57 @@ class GroupRequest(models.Model): @python_2_unicode_compatible -class HiddenGroup(models.Model): - group = models.OneToOneField(Group) +class AuthGroup(models.Model): + """ + Extends Django Group model with a one-to-one field + Attributes are accessible via group as if they were in the model + e.g. group.authgroup.internal + + Logic: + Internal - not requestable by users, at all. Covers Corp_, Alliance_, Members etc groups. + Groups are internal by default + + Not Internal and: + Hidden - users cannot view, can request if they have the direct link. + Not Hidden - Users can view and request the group + Open - Users are automatically accepted into the group + Not Open - Users requests must be approved before they are added to the group + """ + group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True) + + internal = models.BooleanField(default=True, + help_text="Internal group, users cannot see, join or request to join this group.
" + "Used for groups such as Members, Corp_*, Alliance_* etc.
" + "Overrides Hidden and Open options when selected.") + hidden = models.BooleanField(default=True, + help_text="Group is hidden from users but can still join with the correct link.") + open = models.BooleanField(default=False, + help_text="Group is open and users will be automatically added upon request.
" + "If the group is not open users will need their request manually approved.") + # Group leaders have management access to this group + group_leaders = models.ManyToManyField(User, related_name='leads_groups', blank=True, + help_text="Group leaders can process group requests for this group " + "specifically. Use the auth.group_management permission to allow " + "a user to manage all groups.") + + description = models.CharField(max_length=512, blank=True, help_text="Description of the group shown to users.") def __str__(self): - return self.group.name + " - Hidden" + return self.group.name -@python_2_unicode_compatible -class OpenGroup(models.Model): - group = models.OneToOneField(Group) +@receiver(post_save, sender=Group) +def create_auth_group(sender, instance, created, **kwargs): + """ + Creates the AuthGroup model when a group is created + """ + if created: + AuthGroup.objects.create(group=instance) - def __str__(self): - return self.group.name + " - Open" + +@receiver(post_save, sender=Group) +def save_auth_group(sender, instance, **kwargs): + """ + Ensures AuthGroup model is saved automatically + """ + instance.authgroup.save() diff --git a/groupmanagement/views.py b/groupmanagement/views.py index 9a9eaf91..d0a00b4d 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.conf import settings 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.models import GroupDescription +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 groupmanagement.models import HiddenGroup -from groupmanagement.models import OpenGroup from authentication.models import AuthServicesInfo from eveonline.managers import EveManager -from django.utils.translation import ugettext_lazy as _ 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,12 +48,98 @@ 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 + 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} + + return render(request, 'registered/groupmembership.html', context=render_items) + + +@login_required +@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 + # 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: + raise Http404("Group does not exist") + + members = list() + + for member in group.user_set.all().order_by('username'): + authinfo = AuthServicesInfo.objects.get_or_create(user=member)[0] + + members.append({ + 'user': member, + 'main_char': EveManager.get_character_by_id(authinfo.main_char_id) + }) + + render_items = {'group': group, 'members': members} + + return render(request, 'registered/groupmembers.html', context=render_items) + + +@login_required +@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 + user.groups.remove(group) + logger.info("User %s removed user %s from group %s" % (request.user, user, group)) + messages.success(request, "Removed user %s from group %s" % (user, group)) + except ObjectDoesNotExist: + messages.warning(request, "User does not exist in that group") + + except ObjectDoesNotExist: + messages.warning(request, "Group does not exist") + + return redirect('auth_group_membership_list', group_id) + + +@login_required +@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() @@ -55,6 +149,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)) @@ -66,12 +165,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)) @@ -80,6 +182,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)) @@ -91,12 +198,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() @@ -107,6 +218,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)) @@ -118,13 +233,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" % ( @@ -133,6 +251,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)) @@ -146,37 +268,16 @@ def group_leave_reject_request(request, group_request_id): @login_required def groups_view(request): logger.debug("groups_view called by user %s" % request.user) - paired_list = [] + groups = [] - for group in Group.objects.all(): - # Check if group is a corp - if "Corp_" in group.name: - pass - elif "Alliance_" in group.name: - pass - elif settings.DEFAULT_AUTH_GROUP in group.name: - pass - elif settings.DEFAULT_BLUE_GROUP in group.name: - pass - elif HiddenGroup.objects.filter(group=group).exists(): - pass - else: - # Get the descriptionn - group_desc = GroupDescription.objects.filter(group=group) + for group in GroupManager.get_joinable_groups(): + # Exclude hidden + if not group.authgroup.hidden: group_request = GroupRequest.objects.filter(user=request.user).filter(group=group) - if group_desc: - if group_request: - paired_list.append((group, group_desc[0], group_request[0])) - else: - paired_list.append((group, group_desc[0], "")) - else: - if group_request: - paired_list.append((group, "", group_request[0])) - else: - paired_list.append((group, "", "")) + groups.append({'group': group, 'request': group_request[0] if group_request else None}) - render_items = {'pairs': paired_list} + render_items = {'groups': groups} return render(request, 'registered/groups.html', context=render_items) @@ -184,7 +285,12 @@ def groups_view(request): def group_request_add(request, group_id): logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id)) group = Group.objects.get(id=group_id) - if OpenGroup.objects.filter(group=group).exists(): + if not GroupManager.joinable_group(group): + logger.warning("User %s attempted to join group id %s but it is not a joinable group" % + (request.user, group_id)) + messages.warning(request, "You cannot join that group") + return redirect('auth_groups') + if group.authgroup.open: logger.info("%s joining %s as is an open group" % (request.user, group)) request.user.groups.add(group) return redirect("auth_groups") @@ -205,7 +311,17 @@ def group_request_add(request, group_id): def group_request_leave(request, group_id): logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id)) group = Group.objects.get(id=group_id) - if OpenGroup.objects.filter(group=group).exists(): + if not GroupManager.joinable_group(group): + logger.warning("User %s attempted to leave group id %s but it is not a joinable group" % + (request.user, group_id)) + messages.warning(request, "You cannot leave that group") + return redirect('auth_groups') + if group not in request.user.groups.all(): + logger.debug("User %s attempted to leave group id %s but they are not a member" % + (request.user, group_id)) + messages.warning(request, "You are not a member of that group") + return redirect('auth_groups') + if group.authgroup.open: logger.info("%s leaving %s as is an open group" % (request.user, group)) request.user.groups.remove(group) return redirect("auth_groups") diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index 595dbfc5..7e6a6ce1 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -153,7 +153,7 @@ {% endif %} - {% if perms.auth.group_management %} + {% if can_manage_groups %}
  • {% trans " Group Management" %} diff --git a/stock/templates/registered/groupmanagement.html b/stock/templates/registered/groupmanagement.html index 9f48722d..767d7e75 100644 --- a/stock/templates/registered/groupmanagement.html +++ b/stock/templates/registered/groupmanagement.html @@ -9,7 +9,7 @@ {% block content %}
    - + {% include 'registered/groupmanagementmenu.html' %}