mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-10 00:56:19 +01:00
Restructure Alliance Auth package (#867)
* Refactor allianceauth into its own package * Add setup * Add missing default_app_config declarations * Fix timerboard namespacing * Remove obsolete future imports * Remove py2 mock support * Remove six * Add experimental 3.7 support and multiple Dj versions * Remove python_2_unicode_compatible * Add navhelper as local package * Update requirements
This commit is contained in:
1
allianceauth/permissions_tool/__init__.py
Normal file
1
allianceauth/permissions_tool/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
default_app_config = 'allianceauth.permissions_tool.apps.PermissionsToolConfig'
|
||||
7
allianceauth/permissions_tool/apps.py
Normal file
7
allianceauth/permissions_tool/apps.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PermissionsToolConfig(AppConfig):
|
||||
name = 'allianceauth.permissions_tool'
|
||||
label = 'permissions_tool'
|
||||
|
||||
29
allianceauth/permissions_tool/auth_hooks.py
Normal file
29
allianceauth/permissions_tool/auth_hooks.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from . import urls
|
||||
|
||||
from allianceauth import hooks
|
||||
from allianceauth.services.hooks import MenuItemHook, UrlHook
|
||||
|
||||
|
||||
class PermissionsTool(MenuItemHook):
|
||||
def __init__(self):
|
||||
MenuItemHook.__init__(self,
|
||||
'Permissions Audit',
|
||||
'fa fa-key fa-id-card grayiconecolor',
|
||||
'permissions_tool:overview',
|
||||
order=400,
|
||||
navactive=['permissions_tool:'])
|
||||
|
||||
def render(self, request):
|
||||
if request.user.has_perm('permissions_tool.audit_permissions'):
|
||||
return MenuItemHook.render(self, request)
|
||||
return ''
|
||||
|
||||
|
||||
@hooks.register('menu_item_hook')
|
||||
def register_menu():
|
||||
return PermissionsTool()
|
||||
|
||||
|
||||
@hooks.register('url_hook')
|
||||
def register_url():
|
||||
return UrlHook(urls, 'permissions_tool', r'^permissions/')
|
||||
26
allianceauth/permissions_tool/migrations/0001_initial.py
Normal file
26
allianceauth/permissions_tool/migrations/0001_initial.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-02-06 08:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PermissionsTool',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
options={
|
||||
'managed': False,
|
||||
'permissions': (('audit_permissions', 'Can audit permissions'),),
|
||||
},
|
||||
),
|
||||
]
|
||||
12
allianceauth/permissions_tool/models.py
Normal file
12
allianceauth/permissions_tool/models.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class PermissionsTool(models.Model):
|
||||
"""
|
||||
Dummy model for holding permissions
|
||||
"""
|
||||
class Meta:
|
||||
managed = False
|
||||
permissions = (
|
||||
('audit_permissions', 'Can audit permissions'),
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
{% extends "registered/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{{ permission.permission.codename }} - {% trans "Permissions Audit" %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1 class="page-header">{% trans "Permissions Audit" %}: {{ permission.permission.codename }}</h1>
|
||||
<a href="{% url 'permissions_tool:overview' %}" class="btn btn-default">
|
||||
<i class="glyphicon glyphicon-chevron-left"></i> {% trans "Back" %}
|
||||
</a>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-3">
|
||||
{% trans "Group" %}
|
||||
</th>
|
||||
<th class="col-md-3">
|
||||
{% trans "User" %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in permission.users %}
|
||||
<tr>
|
||||
{% include 'permissions_tool/audit_row.html' with group="Permission Granted Directly (No Group)" %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for group in permission.groups %}
|
||||
{% for user in group.user_set.all %}
|
||||
{% include 'permissions_tool/audit_row.html' %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for state in permission.states %}
|
||||
{% for profile in state.userprofile_set.all %}
|
||||
{% with profile.user as user %}
|
||||
<tr>
|
||||
{% include 'permissions_tool/audit_state_row.html' %}
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,10 @@
|
||||
<tr>
|
||||
<td>
|
||||
{% if forloop.first %}
|
||||
<b>{{ group }}</b>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ user }}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,11 @@
|
||||
{% load i18n %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if forloop.first %}
|
||||
<b>{% trans 'State' %}: {{ state }}</b>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ user }}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,79 @@
|
||||
{% extends "registered/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% trans "Permissions Overview" %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1 class="page-header">{% trans "Permissions Overview" %}</h1>
|
||||
{% if request.GET.all != 'yes' %}
|
||||
<span class="pull-right">
|
||||
{% blocktrans %}Showing only applied permissions{% endblocktrans %}
|
||||
<a href="{% url 'permissions_tool:overview' %}?all=yes" class="btn btn-primary">{% trans "Show All" %}</a>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="pull-right">
|
||||
{% blocktrans %}Showing all permissions{% endblocktrans %}
|
||||
<a href="{% url 'permissions_tool:overview' %}?all=no" class="btn btn-primary">{% trans "Show Applied" %}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% trans "App" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Model" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Code Name" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Name" %}
|
||||
</th>
|
||||
<th class="col-md-1">
|
||||
{% trans "Users" %}
|
||||
</th>
|
||||
<th class="col-md-1">
|
||||
{% trans "Groups" %}
|
||||
</th>
|
||||
<th class="col-md-1">
|
||||
{% trans "States" %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for perm in permissions %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ perm.permission.content_type.app_label }}
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.permission.content_type.model }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url "permissions_tool:audit" app_label=perm.permission.content_type.app_label model=perm.permission.content_type.model codename=perm.permission.codename %}">
|
||||
{{ perm.permission.codename }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.permission.name }}
|
||||
</td>
|
||||
<td class="{% if perm.users > 0 %}info {% endif %}text-right">
|
||||
{{ perm.users }}
|
||||
</td>
|
||||
<td class="{% if perm.groups > 0 %}info {% endif %}text-right">
|
||||
{{ perm.groups }} ({{ perm.group_users }})
|
||||
</td>
|
||||
<td class="{% if perm.states > 0 %}info {% endif %}text-right">
|
||||
{{ perm.states }} ({{ perm.state_users }})
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
111
allianceauth/permissions_tool/tests.py
Normal file
111
allianceauth/permissions_tool/tests.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase
|
||||
from django import urls
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
|
||||
class PermissionsToolViewsTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.member = AuthUtils.create_member('auth_member')
|
||||
self.member.set_password('password')
|
||||
self.member.email = 'auth_member@example.com'
|
||||
self.member.save()
|
||||
self.none_user = AuthUtils.create_user('none_user', disconnect_signals=True)
|
||||
self.none_user2 = AuthUtils.create_user('none_user2', disconnect_signals=True)
|
||||
self.none_user3 = AuthUtils.create_user('none_user3', disconnect_signals=True)
|
||||
|
||||
self.no_perm_user = AuthUtils.create_user('no_perm_user', disconnect_signals=True)
|
||||
self.no_perm_user.set_password('password')
|
||||
|
||||
AuthUtils.disconnect_signals()
|
||||
self.no_perm_group = Group.objects.create(name="No Permission Group")
|
||||
|
||||
self.test_group = Group.objects.create(name="Test group")
|
||||
|
||||
self.test_group.user_set.add(self.none_user)
|
||||
self.test_group.user_set.add(self.none_user2)
|
||||
self.test_group.user_set.add(self.none_user3)
|
||||
|
||||
self.permission = Permission.objects.get_by_natural_key(codename='audit_permissions',
|
||||
app_label='permissions_tool',
|
||||
model='permissionstool')
|
||||
|
||||
self.test_group.permissions.add(self.permission)
|
||||
self.member.user_permissions.add(self.permission)
|
||||
AuthUtils.connect_signals()
|
||||
|
||||
def test_menu_item(self):
|
||||
self.client.login(username=self.member.username, password='password')
|
||||
response = self.client.get(urls.reverse('permissions_tool:overview'))
|
||||
|
||||
response_content = str(response.content, encoding='utf8')
|
||||
|
||||
self.assertInHTML(
|
||||
'<li><a class="active" href="/permissions/overview/"><i class="fa fa-key fa-id-card grayiconecolor"></i> Permissions Audit</a></li>',
|
||||
response_content)
|
||||
|
||||
def test_permissions_overview(self):
|
||||
self.client.login(username=self.member.username, password='password')
|
||||
|
||||
response = self.client.get(urls.reverse('permissions_tool:overview'))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed('permissions_tool/overview.html')
|
||||
self.assertContains(response, self.permission.codename)
|
||||
self.assertContains(response, self.permission.content_type.app_label)
|
||||
self.assertContains(response, self.permission.content_type.model)
|
||||
|
||||
tested_context = False
|
||||
# Test the context
|
||||
for perm in response.context['permissions']:
|
||||
if perm['permission'] == self.permission:
|
||||
tested_context = True
|
||||
self.assertDictContainsSubset({'users': 1}, perm)
|
||||
self.assertDictContainsSubset({'groups': 1}, perm)
|
||||
self.assertDictContainsSubset({'group_users': 3}, perm)
|
||||
break
|
||||
self.assertTrue(tested_context)
|
||||
|
||||
def test_permissions_overview_perms(self):
|
||||
# Ensure permission effectively denys access
|
||||
self.client.login(username=self.no_perm_user.username, password='password')
|
||||
|
||||
response = self.client.get(urls.reverse('permissions_tool:overview'))
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_permissions_audit(self):
|
||||
self.client.login(username=self.member.username, password='password')
|
||||
|
||||
response = self.client.get(urls.reverse('permissions_tool:audit',
|
||||
kwargs={
|
||||
'app_label': self.permission.content_type.app_label,
|
||||
'model': self.permission.content_type.model,
|
||||
'codename': self.permission.codename,
|
||||
}))
|
||||
|
||||
self.assertTemplateUsed('permissions_tool/audit.html')
|
||||
self.assertTemplateUsed('permissions_tool/audit_row.html')
|
||||
|
||||
self.assertContains(response, self.permission.codename)
|
||||
self.assertContains(response, self.none_user)
|
||||
self.assertContains(response, self.none_user3)
|
||||
self.assertContains(response, self.test_group)
|
||||
|
||||
self.assertNotContains(response, self.no_perm_user)
|
||||
|
||||
def test_permissions_audit_perms(self):
|
||||
# Ensure permission effectively denys access
|
||||
self.client.login(username=self.no_perm_user.username, password='password')
|
||||
|
||||
response = self.client.get(urls.reverse('permissions_tool:audit',
|
||||
kwargs={
|
||||
'app_label': self.permission.content_type.app_label,
|
||||
'model': self.permission.content_type.model,
|
||||
'codename': self.permission.codename,
|
||||
}))
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
9
allianceauth/permissions_tool/urls.py
Normal file
9
allianceauth/permissions_tool/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^overview/$', views.permissions_overview, name='overview'),
|
||||
url(r'^audit/(?P<app_label>[\w\-_]+)/(?P<model>[\w\-_]+)/(?P<codename>[\w\-_]+)/$', views.permissions_audit,
|
||||
name='audit'),
|
||||
]
|
||||
60
allianceauth/permissions_tool/views.py
Normal file
60
allianceauth/permissions_tool/views.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.db.models import Count
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from allianceauth.authentication.models import UserProfile
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('permissions_tool.audit_permissions')
|
||||
def permissions_overview(request):
|
||||
logger.debug("permissions_overview called by user %s" % request.user)
|
||||
perms = Permission.objects.all()
|
||||
|
||||
get_all = True if request.GET.get('all', 'no') == 'yes' else False
|
||||
|
||||
context = {'permissions': []}
|
||||
for perm in perms:
|
||||
this_perm = {
|
||||
'users': perm.user_set.all().count(),
|
||||
'groups': perm.group_set.all().count(),
|
||||
'states': perm.state_set.all().count(),
|
||||
'permission': perm
|
||||
}
|
||||
|
||||
if get_all or this_perm['users'] > 0 or this_perm['groups'] > 0 or this_perm['states'] > 0:
|
||||
# Only add if we're getting everything or one of the objects has this permission
|
||||
# Add group_users separately to improve performance
|
||||
this_perm['group_users'] = sum(group.user_count for group in
|
||||
perm.group_set.annotate(user_count=Count('user')))
|
||||
this_perm['state_users'] = UserProfile.objects.filter(state__in=perm.state_set.all()).count()
|
||||
context['permissions'].append(this_perm)
|
||||
|
||||
return render(request, 'permissions_tool/overview.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('permissions_tool.audit_permissions')
|
||||
def permissions_audit(request, app_label, model, codename):
|
||||
logger.debug("permissions_audit called by user {} on {}:{}:{}".format(request.user, app_label, model, codename))
|
||||
perm = get_object_or_404(Permission,
|
||||
content_type__app_label=app_label,
|
||||
content_type__model=model,
|
||||
codename=codename)
|
||||
|
||||
context = {'permission': {
|
||||
'permission': perm,
|
||||
'users': perm.user_set.all(),
|
||||
'groups': perm.group_set.all(),
|
||||
'states': perm.state_set.all(),
|
||||
'group_users': [group.user_set.all() for group in perm.group_set.all()],
|
||||
'state_users': User.objects.filter(profile__state__in=perm.state_set.all()),
|
||||
}
|
||||
}
|
||||
|
||||
return render(request, 'permissions_tool/audit.html', context=context)
|
||||
Reference in New Issue
Block a user