diff --git a/datahub/company/admin/adviser.py b/datahub/company/admin/adviser.py index 576fb0e6c..e1676bbf6 100644 --- a/datahub/company/admin/adviser.py +++ b/datahub/company/admin/adviser.py @@ -5,10 +5,11 @@ from datahub.company.admin.adviser_forms import AddAdviserFromSSOForm from datahub.company.models import Advisor +from datahub.core.admin import ExportWinsAdminMixin @admin.register(Advisor) -class AdviserAdmin(VersionAdmin, UserAdmin): +class AdviserAdmin(ExportWinsAdminMixin, VersionAdmin, UserAdmin): """Adviser admin.""" fieldsets = ( diff --git a/datahub/core/admin.py b/datahub/core/admin.py index c22f0ad95..8bb274e92 100644 --- a/datahub/core/admin.py +++ b/datahub/core/admin.py @@ -15,6 +15,8 @@ from django.utils.translation import gettext, gettext_lazy from django.views.decorators.csrf import csrf_exempt, csrf_protect +EXPORT_WIN_GROUP_NAME = 'ExportWinAdmin' + class DisabledOnFilter(admin.SimpleListFilter): """This filter allows us to filter values that have disabled_on value.""" @@ -75,7 +77,44 @@ def has_change_permission(self, request, obj=None): return False -class BaseModelAdminMixin: +class ExportWinsAdminMixin(admin.ModelAdmin): + def has_module_permission(self, request): + return handle_export_wins_admin_permissions( + request.user, + self.opts.app_label, + super().has_module_permission(request), + ) + + def has_view_permission(self, request, obj=None): + return handle_export_wins_admin_permissions( + request.user, + self.opts.app_label, + super().has_view_permission(request, obj), + ) + + def has_add_permission(self, request): + return handle_export_wins_admin_permissions( + request.user, + self.opts.app_label, + super().has_add_permission(request), + ) + + def has_delete_permission(self, request, obj=None): + return handle_export_wins_admin_permissions( + request.user, + self.opts.app_label, + super().has_delete_permission(request, obj), + ) + + def has_change_permission(self, request, obj=None): + return handle_export_wins_admin_permissions( + request.user, + self.opts.app_label, + super().has_change_permission(request, obj), + ) + + +class BaseModelAdminMixin(ExportWinsAdminMixin): """ Mixin for ModelAdmins which adds extra functionalities. Useful when the model extends core.BaseModel @@ -353,10 +392,24 @@ def pretty_source(self, obj): return format_html('
{0}', json.dumps(value, indent=2)) +def handle_export_wins_admin_permissions(user, app_label, function): + if not user.is_superuser and user.groups.filter(name=EXPORT_WIN_GROUP_NAME).exists(): + if app_label == 'export_win': + return True + return False + + return function + + def _make_admin_permission_getter(codename): def _has_permission(self, request, obj=None): + app_label = self.opts.app_label qualified_name = f'{app_label}.{codename}' - return request.user.has_perm(qualified_name) + return handle_export_wins_admin_permissions( + request.user, + app_label, + request.user.has_perm(qualified_name), + ) return _has_permission diff --git a/datahub/core/test/test_admin.py b/datahub/core/test/test_admin.py index 38f4b09d3..6f41dcbed 100644 --- a/datahub/core/test/test_admin.py +++ b/datahub/core/test/test_admin.py @@ -2,6 +2,7 @@ from unittest.mock import Mock import pytest + from django.conf import settings from django.contrib import auth, messages as django_messages from django.contrib.admin.templatetags.admin_urls import admin_urlname @@ -11,6 +12,7 @@ from faker import Faker from rest_framework import status +from datahub.company.test.factories import AdviserFactory from datahub.core.admin import ( custom_add_permission, custom_change_permission, @@ -19,8 +21,10 @@ format_json_as_html, get_change_link, get_change_url, + handle_export_wins_admin_permissions, RawIdWidget, ) +from datahub.core.test.factories import GroupFactory from datahub.core.test.support.factories import BookFactory from datahub.core.test.support.models import Book from datahub.core.test.support.views import MAX_UPLOAD_SIZE @@ -338,3 +342,44 @@ def test_admin_account_lock_out_after_too_many_attempts(self): status.HTTP_403_FORBIDDEN ), (attempt, settings.AXES_FAILURE_LIMIT) assert auth.get_user(client).is_authenticated is False + + +@pytest.mark.django_db +class TestHandleExportWinsAdminPermissions: + def test_user_is_not_superuser_not_in_export_wins_group(self): + permission_group = GroupFactory(name='Group') + user = AdviserFactory(is_superuser=False) + user.groups.add(permission_group) + expected_response = 'ABC' + + result = handle_export_wins_admin_permissions(user, '', function=expected_response) + + assert result == expected_response + + def test_user_is_superuser_in_export_wins_group(self): + permission_group = GroupFactory(name='ExportWinAdmin') + user = AdviserFactory(is_superuser=True) + user.groups.add(permission_group) + expected_response = 'ABC' + + result = handle_export_wins_admin_permissions(user, '', function=expected_response) + + assert result == expected_response + + def test_user_is_not_superuser_in_export_wins_group_accessing_export_win_module(self): + permission_group = GroupFactory(name='ExportWinAdmin') + user = AdviserFactory(is_superuser=False) + user.groups.add(permission_group) + + result = handle_export_wins_admin_permissions(user, 'export_win', function=None) + + assert result is True + + def test_user_is_not_superuser_in_export_wins_group_accessing_company_module(self): + permission_group = GroupFactory(name='ExportWinAdmin') + user = AdviserFactory(is_superuser=False) + user.groups.add(permission_group) + + result = handle_export_wins_admin_permissions(user, 'company', function=None) + + assert result is False diff --git a/datahub/export_win/admin.py b/datahub/export_win/admin.py index c723d642c..67e9d8837 100644 --- a/datahub/export_win/admin.py +++ b/datahub/export_win/admin.py @@ -4,8 +4,7 @@ from django.forms import ModelForm from reversion.admin import VersionAdmin - -from datahub.core.admin import BaseModelAdminMixin +from datahub.core.admin import BaseModelAdminMixin, EXPORT_WIN_GROUP_NAME from datahub.export_win.models import Breakdown, CustomerResponse, DeletedWin, Win, WinAdviser @@ -265,6 +264,9 @@ def has_change_permission(self, request, obj=None): def has_view_permission(self, request, obj=None): """Set the desired user group to access view deleted win""" - if request.user.is_superuser or request.user.groups.filter(name='ExportWinAdmin').exists(): + if ( + request.user.is_superuser + or request.user.groups.filter(name=EXPORT_WIN_GROUP_NAME).exists() + ): return True return False diff --git a/datahub/investment/investor_profile/admin.py b/datahub/investment/investor_profile/admin.py index bcf2a4e46..e3882836f 100644 --- a/datahub/investment/investor_profile/admin.py +++ b/datahub/investment/investor_profile/admin.py @@ -1,10 +1,12 @@ from django.contrib import admin +from datahub.core.admin import ExportWinsAdminMixin + from datahub.investment.investor_profile import models @admin.register(models.LargeCapitalInvestorProfile) -class LargeCapitalInvestorProfileAdmin(admin.ModelAdmin): +class LargeCapitalInvestorProfileAdmin(ExportWinsAdminMixin, admin.ModelAdmin): """Large capital investor profile admin.""" autocomplete_fields = ( diff --git a/datahub/investment/opportunity/admin.py b/datahub/investment/opportunity/admin.py index 84de1a585..47498e48b 100644 --- a/datahub/investment/opportunity/admin.py +++ b/datahub/investment/opportunity/admin.py @@ -1,10 +1,12 @@ from django.contrib import admin +from datahub.core.admin import ExportWinsAdminMixin + from datahub.investment.opportunity import models @admin.register(models.LargeCapitalOpportunity) -class LargeCapitalOpportunityAdmin(admin.ModelAdmin): +class LargeCapitalOpportunityAdmin(ExportWinsAdminMixin, admin.ModelAdmin): """Large capital opportunity admin.""" autocomplete_fields = (