From 0b7520e3b1203cca9b772fe37f8220bac714328c Mon Sep 17 00:00:00 2001 From: colcrunch Date: Thu, 22 Mar 2018 15:23:52 -0400 Subject: [PATCH 1/6] Fix celerybeat task in ts3 config. --- docs/installation/services/teamspeak3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/services/teamspeak3.md b/docs/installation/services/teamspeak3.md index a10c761e..aec183eb 100644 --- a/docs/installation/services/teamspeak3.md +++ b/docs/installation/services/teamspeak3.md @@ -23,7 +23,7 @@ In your auth project's settings file, do the following: TEAMSPEAK3_PUBLIC_URL = '' CELERYBEAT_SCHEDULE['run_ts3_group_update'] = { - 'task': 'services.modules.teamspeak3.tasks.run_ts3_group_update', + 'task': 'allianceauth.services.modules.teamspeak3.tasks.run_ts3_group_update', 'schedule': crontab(minute='*/30'), } From bf1fe99d98ee50555a76cd716c6e90f1be22dd85 Mon Sep 17 00:00:00 2001 From: colcrunch Date: Mon, 4 Jun 2018 01:45:44 -0400 Subject: [PATCH 2/6] Add Audit Log to Group Management --- .../migrations/0009_requestlog.py | 28 +++++++++++++ allianceauth/groupmanagement/models.py | 31 ++++++++++++++ .../templates/groupmanagement/audit.html | 40 +++++++++++++++++++ .../groupmanagement/groupmembership.html | 3 ++ allianceauth/groupmanagement/urls.py | 1 + allianceauth/groupmanagement/views.py | 39 +++++++++++++++++- 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 allianceauth/groupmanagement/migrations/0009_requestlog.py create mode 100644 allianceauth/groupmanagement/templates/groupmanagement/audit.html diff --git a/allianceauth/groupmanagement/migrations/0009_requestlog.py b/allianceauth/groupmanagement/migrations/0009_requestlog.py new file mode 100644 index 00000000..c6537fb9 --- /dev/null +++ b/allianceauth/groupmanagement/migrations/0009_requestlog.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.6 on 2018-06-04 02:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0009_alter_user_last_name_max_length'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('groupmanagement', '0008_remove_authgroup_permissions'), + ] + + operations = [ + migrations.CreateModel( + name='RequestLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('request_type', models.NullBooleanField(default=0)), + ('request_info', models.CharField(max_length=254)), + ('action', models.BooleanField(default=0)), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), + ('request_actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/allianceauth/groupmanagement/models.py b/allianceauth/groupmanagement/models.py index 3e389e1c..4e93a090 100644 --- a/allianceauth/groupmanagement/models.py +++ b/allianceauth/groupmanagement/models.py @@ -23,6 +23,37 @@ class GroupRequest(models.Model): return self.user.username + ":" + self.group.name +class RequestLog(models.Model): + request_type = models.NullBooleanField(default=0) + group = models.ForeignKey(Group, on_delete=models.CASCADE) + request_info = models.CharField(max_length=254) + action = models.BooleanField(default=0) + request_actor = models.ForeignKey(User, on_delete=models.CASCADE) + + def requestor(self): + return self.request_info.split(":")[0] + + def type_to_str(self): + if self.request_type is None: + return "Removed" + elif self.request_type is True: + return "Leave" + elif self.request_type is False: + return "Join" + + def action_to_str(self): + if self.action is True: + return "Accept" + elif self.action is False: + return "Reject" + + def req_char(self): + usr = self.requestor() + user = User.objects.get(username=usr) + return user.profile.main_character + + + class AuthGroup(models.Model): """ Extends Django Group model with a one-to-one field diff --git a/allianceauth/groupmanagement/templates/groupmanagement/audit.html b/allianceauth/groupmanagement/templates/groupmanagement/audit.html new file mode 100644 index 00000000..c5f26892 --- /dev/null +++ b/allianceauth/groupmanagement/templates/groupmanagement/audit.html @@ -0,0 +1,40 @@ +{% extends "allianceauth/base.html" %} +{% load staticfiles %} +{% load i18n %} + +{% block page_title %}{{ group }} {% trans "Audit Log" %}{% endblock page_title %} +{% block extra_css %}{% endblock extra_css %} + +{% block content %} +
+
+ {% include 'groupmanagement/menu.html' %} +
+ {% if entries %} +

{{ group }} Audit Log

+ + + + + + + + + + {% for entry in entries %} + + + + + + + + + {% endfor %} +
{% trans "Requestor" %}{% trans "Main Character" %}{% trans "Group" %}{% trans "Type" %}{% trans "Action" %}{% trans "Actor" %}
{{ entry.requestor }}{{ entry.req_char }}{{ entry.group }}{{ entry.type_to_str }}{{ entry.action_to_str }}{{ entry.request_actor }}
+ {% else %} +
{% trans "No entries found." %}
+ {% endif %} +
+
+{% endblock content %} diff --git a/allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html b/allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html index 7384c23a..5e47a0be 100644 --- a/allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html +++ b/allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html @@ -41,6 +41,9 @@ title="{% trans "View Members" %}"> + + + {% endfor %} diff --git a/allianceauth/groupmanagement/urls.py b/allianceauth/groupmanagement/urls.py index 37de11fe..3533df01 100644 --- a/allianceauth/groupmanagement/urls.py +++ b/allianceauth/groupmanagement/urls.py @@ -12,6 +12,7 @@ urlpatterns = [ name='membership'), url(r'^membership/(\w+)/$', views.group_membership_list, name='membership_list'), + url(r'^membership/(\w+)/audit/', views.group_membership_audit, name="audit_log"), url(r'^membership/(\w+)/remove/(\w+)/$', views.group_membership_remove, name='membership_remove'), url(r'^request_add/(\w+)', views.group_request_add, diff --git a/allianceauth/groupmanagement/views.py b/allianceauth/groupmanagement/views.py index ee564a2d..2ee8438a 100755 --- a/allianceauth/groupmanagement/views.py +++ b/allianceauth/groupmanagement/views.py @@ -10,7 +10,7 @@ from django.http import Http404 from django.shortcuts import render, redirect, get_object_or_404 from django.utils.translation import ugettext_lazy as _ from .managers import GroupManager -from .models import GroupRequest +from .models import GroupRequest, RequestLog from allianceauth.notifications import notify @@ -65,6 +65,32 @@ def group_membership(request): return render(request, 'groupmanagement/groupmembership.html', context=render_items) +@login_required +@user_passes_test(GroupManager.can_manage_groups) +def group_membership_audit(request, group_id): + logger.debug("group_management_audit called by user %s" % request.user) + group = get_object_or_404(Group, id=group_id) + try: + + # 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") + + entries = RequestLog.objects.filter(group=group) + + render_items = {'entries': entries, 'group': group.name} + + return render(request, 'groupmanagement/audit.html', context=render_items) + + + + @login_required @user_passes_test(GroupManager.can_manage_groups) def group_membership_list(request, group_id): @@ -112,6 +138,9 @@ def group_membership_remove(request, group_id, user_id): try: user = group.user_set.get(id=user_id) + request_info = user.username + ":" + group.name + log = RequestLog(request_type=None,group=group,request_info=request_info,action=1,request_actor=request.user) + log.save() # Remove group from user user.groups.remove(group) logger.info("User %s removed user %s from group %s" % (request.user, user, group)) @@ -139,6 +168,8 @@ def group_accept_request(request, group_request_id): group_request.user.groups.add(group) group_request.user.save() + log = RequestLog(request_type=group_request.leave_request,group=group,request_info=group_request.__str__(),action=1,request_actor=request.user) + log.save() group_request.delete() logger.info("User %s accepted group request from user %s to group %s" % ( request.user, group_request.user, group_request.group.name)) @@ -172,6 +203,8 @@ def group_reject_request(request, group_request_id): 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)) + log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user) + log.save() group_request.delete() notify(group_request.user, "Group Application Rejected", level="danger", message="Your application to %s has been rejected." % group_request.group) @@ -204,6 +237,8 @@ def group_leave_accept_request(request, group_request_id): group, created = Group.objects.get_or_create(name=group_request.group.name) group_request.user.groups.remove(group) group_request.user.save() + log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=1,request_actor=request.user) + log.save() group_request.delete() logger.info("User %s accepted group leave request from user %s to group %s" % ( request.user, group_request.user, group_request.group.name)) @@ -236,6 +271,8 @@ def group_leave_reject_request(request, group_request_id): raise PermissionDenied if group_request: + log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user) + log.save() group_request.delete() logger.info("User %s rejected group leave request from user %s for group %s" % ( request.user, group_request.user, group_request.group.name)) From 35cb56d6e98d9dbe41ff0d643c6c1f562a923261 Mon Sep 17 00:00:00 2001 From: colcrunch Date: Wed, 6 Jun 2018 06:41:56 +0000 Subject: [PATCH 3/6] Update 0009_requestlog.py --- allianceauth/groupmanagement/migrations/0009_requestlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/allianceauth/groupmanagement/migrations/0009_requestlog.py b/allianceauth/groupmanagement/migrations/0009_requestlog.py index c6537fb9..3e3bef90 100644 --- a/allianceauth/groupmanagement/migrations/0009_requestlog.py +++ b/allianceauth/groupmanagement/migrations/0009_requestlog.py @@ -8,7 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('auth', '0009_alter_user_last_name_max_length'), + ('auth', '0008_alter_user_last_name_max_length'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('groupmanagement', '0008_remove_authgroup_permissions'), ] From ab98d72022ccc6da714980b04144d1a93e4504a3 Mon Sep 17 00:00:00 2001 From: colcrunch Date: Wed, 6 Jun 2018 07:03:57 +0000 Subject: [PATCH 4/6] Fix migration dependencies. --- allianceauth/groupmanagement/migrations/0009_requestlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/allianceauth/groupmanagement/migrations/0009_requestlog.py b/allianceauth/groupmanagement/migrations/0009_requestlog.py index 3e3bef90..aaff4927 100644 --- a/allianceauth/groupmanagement/migrations/0009_requestlog.py +++ b/allianceauth/groupmanagement/migrations/0009_requestlog.py @@ -8,7 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('auth', '0008_alter_user_last_name_max_length'), + ('auth', '0008_alter_user_username_max_length'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('groupmanagement', '0008_remove_authgroup_permissions'), ] From 1730bc3b98ffa892cb1ddb71153538e0a0bf4c92 Mon Sep 17 00:00:00 2001 From: colcrunch Date: Sun, 22 Jul 2018 19:38:56 -0400 Subject: [PATCH 5/6] Add check for auditable groups. To ensure functionality with other possible changes to group management. --- allianceauth/groupmanagement/managers.py | 9 +++++++++ allianceauth/groupmanagement/views.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/allianceauth/groupmanagement/managers.py b/allianceauth/groupmanagement/managers.py index bdb6dc9d..de5f2497 100644 --- a/allianceauth/groupmanagement/managers.py +++ b/allianceauth/groupmanagement/managers.py @@ -23,6 +23,15 @@ class GroupManager: """ return not group.authgroup.internal + @staticmethod + def auditable_group(group): + """ + Check if a group is auditable, i.e not an internal group + :param group: django.contrib.auth.models.Group object + :return: bool True if it is auditable, false otherwise + """ + return not group.authgroup.internal + @staticmethod def has_management_permission(user): return user.has_perm('auth.group_management') diff --git a/allianceauth/groupmanagement/views.py b/allianceauth/groupmanagement/views.py index 2ee8438a..0baf2085 100755 --- a/allianceauth/groupmanagement/views.py +++ b/allianceauth/groupmanagement/views.py @@ -74,7 +74,7 @@ def group_membership_audit(request, 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): + if not GroupManager.auditable_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 From 5b8983deacafa0e16e44c39779c88749e5598a50 Mon Sep 17 00:00:00 2001 From: colcrunch Date: Sun, 22 Jul 2018 20:08:16 -0400 Subject: [PATCH 6/6] Rename Auditable group. Its better to be explicit. --- allianceauth/groupmanagement/managers.py | 2 +- allianceauth/groupmanagement/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/allianceauth/groupmanagement/managers.py b/allianceauth/groupmanagement/managers.py index de5f2497..0a6ebe24 100644 --- a/allianceauth/groupmanagement/managers.py +++ b/allianceauth/groupmanagement/managers.py @@ -24,7 +24,7 @@ class GroupManager: return not group.authgroup.internal @staticmethod - def auditable_group(group): + def check_internal_group(group): """ Check if a group is auditable, i.e not an internal group :param group: django.contrib.auth.models.Group object diff --git a/allianceauth/groupmanagement/views.py b/allianceauth/groupmanagement/views.py index 0baf2085..39014f20 100755 --- a/allianceauth/groupmanagement/views.py +++ b/allianceauth/groupmanagement/views.py @@ -74,7 +74,7 @@ def group_membership_audit(request, group_id): # Check its a joinable group i.e. not corp or internal # And the user has permission to manage it - if not GroupManager.auditable_group(group) or not GroupManager.can_manage_group(request.user, group): + if not GroupManager.check_internal_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