Skip to content

Commit

Permalink
Merge pull request #432 from UW-GAC/feature/permission-rename
Browse files Browse the repository at this point in the history
Give permissions better names
  • Loading branch information
amstilp authored Nov 7, 2023
2 parents e475e2f + c306db6 commit d187048
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 331 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

* Switch to using `pyproject.toml` where possible.
* Use hatch for backend building.
* Rename permissions and associated auth mixins.
- anvil_project_manager_edit -> anvil_consortium_manager_staff_edit
- anvil_project_manager_view -> anvil_consortium_manager_staff_view
- anvil_project_manager_limited_view -> anvil_consortium_manager_view
- `AnVILConsortiumManagerEditRequired` -> `AnVILConsortiumManagerStaffEditRequired`
- `AnVILConsortiumManagerViewRequired` -> `AnVILConsortiumManagerStaffViewRequired`
- `AnVILConsortiumManagerLimitedViewRequired` -> `AnVILConsortiumManagerViewRequired`
* Bugfix: Allow Workspace Data objects to have a second foreign key to `Workspace`.

## 0.19 (2023-10-27)
Expand Down
2 changes: 1 addition & 1 deletion anvil_consortium_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.20.dev3"
__version__ = "0.20.dev4"
20 changes: 10 additions & 10 deletions anvil_consortium_manager/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@
from .models import AnVILProjectManagerAccess


class AnVILConsortiumManagerLimitedViewRequired(UserPassesTestMixin):
"""AnVIL global app limited view permission required mixin.
class AnVILConsortiumManagerViewRequired(UserPassesTestMixin):
"""AnVIL global app view permission required mixin.
This mixin allows anyone with either LIMITED_VIEW or VIEW permission to access a view."""
This mixin allows anyone with either VIEW or STAFF_VIEW permission to access a view."""

def test_func(self):
apm_content_type = ContentType.objects.get_for_model(AnVILProjectManagerAccess)
perm_1 = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.LIMITED_VIEW_PERMISSION_CODENAME}"
perm_2 = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME}"
perm_1 = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME}"
perm_2 = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME}"
has_perms = self.request.user.has_perms((perm_1,)) or self.request.user.has_perms((perm_2,))
return has_perms


class AnVILConsortiumManagerViewRequired(PermissionRequiredMixin):
"""AnVIL global app view permission required mixin"""
class AnVILConsortiumManagerStaffViewRequired(PermissionRequiredMixin):
"""AnVIL global app staff view permission required mixin"""

def get_permission_required(self):
apm_content_type = ContentType.objects.get_for_model(AnVILProjectManagerAccess)
perm_required = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME}"
perm_required = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME}"
return (perm_required,)


class AnVILConsortiumManagerEditRequired(PermissionRequiredMixin):
class AnVILConsortiumManagerStaffEditRequired(PermissionRequiredMixin):
"""AnVIL global app edit permission required mixin"""

def get_permission_required(self):
apm_content_type = ContentType.objects.get_for_model(AnVILProjectManagerAccess)
perm_required = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME}"
perm_required = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME}"
return (perm_required,)


Expand Down
56 changes: 56 additions & 0 deletions anvil_consortium_manager/migrations/0014_rename_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 4.2.7 on 2023-11-06 19:12
# We cannot easily test this migration using django-test-migrations because
# permissions and content types are created via post-migrate signals, and
# django-test-migrations mutes signals.
# So this is only manually tested :(
#
# Some relevant stack overflow posts:
# https://stackoverflow.com/questions/47182012/remove-old-permissions-in-django

from django.db import migrations


def set_up_new_permissions(apps, schema_editor):
# Map between original codename and updated codename.
permissions_codename_map = {
"anvil_project_manager_edit": "anvil_consortium_manager_staff_edit",
"anvil_project_manager_view": "anvil_consortium_manager_staff_view",
"anvil_project_manager_limited_view": "anvil_consortium_manager_view",
"anvil_project_manager_account_link": "anvil_consortium_manager_account_link",
}
# Map between original codename and updated name.
permissions_name_map = {
"anvil_project_manager_edit": "AnVIL Consortium Manager Staff Edit Permission",
"anvil_project_manager_view": "AnVIL Consortium Manager Staff View Permission",
"anvil_project_manager_limited_view": "AnVIL Consortium Manager View Permission",
"anvil_project_manager_account_link": "AnVIL Consortium Manager Account Link Permission",
}
# Rename old permissions if they exist.
Permission = apps.get_model("auth", "Permission")

ContentType = apps.get_model("contenttypes", "ContentType")
try:
model = ContentType.objects.get(app_label="anvil_consortium_manager", model="anvilprojectmanageraccess")
for original_codename in permissions_codename_map.keys():
permission = Permission.objects.get(content_type=model, codename=original_codename)
# Update codename and name.
permission.codename = permissions_codename_map[original_codename]
permission.name = permissions_name_map[original_codename]
# Save the new permission
permission.save()
except ContentType.DoesNotExist:
# Permissions and ContentTypes are created after all migrations are completed using
# post-migrate signals. If the ContentType for this app does not exist, then this is
# the first time that "migrate" has been run, so no permissions need to be renamed.
pass


class Migration(migrations.Migration):
dependencies = [
("anvil_consortium_manager", "0013_alter_anvilprojectmanageraccess_options"),
]

operations = [
# I could not get the reverse code to work - see comments above.
migrations.RunPython(set_up_new_permissions, reverse_code=migrations.RunPython.noop),
]
25 changes: 25 additions & 0 deletions anvil_consortium_manager/migrations/0015_add_new_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.7 on 2023-11-06 22:55

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("anvil_consortium_manager", "0014_rename_permissions"),
]

operations = [
migrations.AlterModelOptions(
name="anvilprojectmanageraccess",
options={
"default_permissions": (),
"managed": False,
"permissions": [
("anvil_consortium_manager_staff_edit", "AnVIL Consortium Manager Staff Edit Permission"),
("anvil_consortium_manager_staff_view", "AnVIL Consortium Manager Staff View Permission"),
("anvil_consortium_manager_account_link", "AnVIL Consortium Manager Account Link Permission"),
("anvil_consortium_manager_view", "AnVIL Consortium Manager View Permission"),
],
},
),
]
20 changes: 10 additions & 10 deletions anvil_consortium_manager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
class AnVILProjectManagerAccess(models.Model):
"""A meta model used to define app level permissions"""

EDIT_PERMISSION_CODENAME = "anvil_project_manager_edit"
VIEW_PERMISSION_CODENAME = "anvil_project_manager_view"
LIMITED_VIEW_PERMISSION_CODENAME = "anvil_project_manager_limited_view"
ACCOUNT_LINK_PERMISSION_CODENAME = "anvil_project_manager_account_link"
STAFF_EDIT_PERMISSION_CODENAME = "anvil_consortium_manager_staff_edit"
STAFF_VIEW_PERMISSION_CODENAME = "anvil_consortium_manager_staff_view"
VIEW_PERMISSION_CODENAME = "anvil_consortium_manager_view"
ACCOUNT_LINK_PERMISSION_CODENAME = "anvil_consortium_manager_account_link"

class Meta:
"""Not a concrete model."""
Expand All @@ -34,15 +34,15 @@ class Meta:
default_permissions = ()

permissions = [
("anvil_project_manager_edit", "AnVIL Project Manager Edit Permission"),
("anvil_project_manager_view", "AnVIL Project Manager View Permission"),
("anvil_consortium_manager_staff_edit", "AnVIL Consortium Manager Staff Edit Permission"),
("anvil_consortium_manager_staff_view", "AnVIL Consortium Manager Staff View Permission"),
(
"anvil_project_manager_account_link",
"AnVIL Project Manager Account Link Permission",
"anvil_consortium_manager_account_link",
"AnVIL Consortium Manager Account Link Permission",
),
(
"anvil_project_manager_limited_view",
"AnVIL Project Manager Limited View Permission",
"anvil_consortium_manager_view",
"AnVIL Consortium Manager View Permission",
),
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ <h5 class="card-header"><span class="fa-solid fa-dollar-sign mx-2"></span>Billin
<a href="{% url 'anvil_consortium_manager:billing_projects:list' %}" class="icon-link">List billing projects</a>
<span class="fa-solid fa-angle-right"></span>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li class ="list-group-item">
<a href="{% url 'anvil_consortium_manager:billing_projects:import' %}" class="icon-link">Import a billing project</a>
<span class="fa-solid fa-angle-right"></span>
Expand All @@ -58,15 +58,15 @@ <h5 class="card-header"><span class="fa-solid fa-dollar-sign mx-2"></span>Billin
<h5 class="card-header"><span class="fa-solid fa-user mx-2"></span>Accounts</h5>
<div class="card-body">
<p>
View {% if perms.anvil_consortium_manager.anvil_project_manager_edit %}or import{% endif %} accounts from AnVIL.
View {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}or import{% endif %} accounts from AnVIL.
</p>
</div>
<ul class="list-group list-group-flush">
<li class ="list-group-item">
<a href="{% url 'anvil_consortium_manager:accounts:list' %}" class="icon-link">List accounts</a>
<span class="fa-solid fa-angle-right"></span>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li class ="list-group-item">
<a href="{% url 'anvil_consortium_manager:accounts:import' %}" class="icon-link">Import an account</a>
<span class="fa-solid fa-angle-right"></span>
Expand All @@ -86,7 +86,7 @@ <h5 class="card-header"><span class="fa-solid fa-user-group mx-2"></span>Managed
<div class="card-body">
<p>
Interact with Managed Groups on AnVIL.
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}You can add accounts to a group so a the access for set of related accounts can be managed together.{% endif %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}You can add accounts to a group so a the access for set of related accounts can be managed together.{% endif %}
</p>
</div>
<ul class="list-group list-group-flush">
Expand All @@ -98,7 +98,7 @@ <h5 class="card-header"><span class="fa-solid fa-user-group mx-2"></span>Managed
<a href="{% url 'anvil_consortium_manager:managed_groups:visualization' %}" class="icon-link">Group visualization</a>
<span class="fa-solid fa-angle-right"></span>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li class ="list-group-item">
<a href="{% url 'anvil_consortium_manager:managed_groups:new' %}" class="icon-link">Create a new group</a>
<span class="fa-solid fa-angle-right"></span>
Expand Down Expand Up @@ -127,7 +127,7 @@ <h5 class="card-header"><span class="fa-solid fa-computer mx-2"></span>Workspace
<div class="card-body">
<p>
Interact with workspaces on AnVIL.
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}You can create or import workspaces and then share them with a group.{% endif%}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}You can create or import workspaces and then share them with a group.{% endif%}
</p>
</div>

Expand All @@ -140,7 +140,7 @@ <h5 class="card-header"><span class="fa-solid fa-computer mx-2"></span>Workspace
<a href="{% url 'anvil_consortium_manager:workspaces:list_all' %}" class="icon-link">List all workspaces</a>
<span class="fa-solid fa-angle-right"></span>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li class ="list-group-item">
<a href="{% url 'anvil_consortium_manager:workspace_group_sharing:new' %}" class="icon-link">Share a workspace with a group</a>
<span class="fa-solid fa-angle-right"></span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- Only include the ACM navbar if the user has view permission.-->
{% if perms.anvil_consortium_manager.anvil_project_manager_view %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_view %}

<nav id="nav-acm" class="navbar navbar-expand-lg navbar-light" aria-label="AnVIL Consortium Manager navbar">
<div class="container">
Expand Down Expand Up @@ -30,7 +30,7 @@
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:billing_projects:list' %}">List billing projects</a>
</li>

{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:billing_projects:import' %}">Import a billing project</a>
</li>
Expand All @@ -53,7 +53,7 @@
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:accounts:list' %}">List accounts</a>
</li>

{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:accounts:import' %}">Import an account</a>
</li>
Expand All @@ -76,7 +76,7 @@
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:managed_groups:visualization' %}">Group visualization</a>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:managed_groups:new' %}">Create a new group</a>
</li>
Expand Down Expand Up @@ -106,7 +106,7 @@
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:workspaces:list_all' %}">List all workspaces</a>
</li>
{% if perms.anvil_consortium_manager.anvil_project_manager_edit %}
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
<li>
<a class="dropdown-item" href="{% url 'anvil_consortium_manager:workspace_group_sharing:new' %}">Share a workspace with a group</a>
</li>
Expand Down
16 changes: 8 additions & 8 deletions anvil_consortium_manager/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .. import auth, models


class AnVILConsortiumManagerLimitedViewRequiredTest(TestCase):
class AnVILConsortiumManagerViewRequiredTest(TestCase):
"""(Temporary) class to test the AnVILConsortiumManagerLimitedViewRequired mixin."""

def setUp(self):
Expand All @@ -14,34 +14,34 @@ def setUp(self):
self.user = User.objects.create_user(username="test", password="test")

def get_view_class(self):
return auth.AnVILConsortiumManagerLimitedViewRequired
return auth.AnVILConsortiumManagerViewRequired

def test_user_with_limited_view_perms(self):
def test_user_with_view_perms(self):
"""test_func returns True for a user with limited view permission."""
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.LIMITED_VIEW_PERMISSION_CODENAME)
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
inst = self.get_view_class()()
request = self.factory.get("")
request.user = self.user
inst.request = request
self.assertTrue(inst.test_func())

def test_user_with_view_perms(self):
def test_user_with_staff_view_perms(self):
"""test_func returns True for a user with view permission."""
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
)
inst = self.get_view_class()()
request = self.factory.get("")
request.user = self.user
inst.request = request
self.assertTrue(inst.test_func())

def test_user_with_edit_perms(self):
def test_user_with_staff_edit_perms(self):
"""test_func returns False for a user with edit permission."""
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME)
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME)
)
inst = self.get_view_class()()
request = self.factory.get("")
Expand Down
Loading

0 comments on commit d187048

Please sign in to comment.