Skip to content

Commit

Permalink
Add Django permission for package deprecation
Browse files Browse the repository at this point in the history
Add a new Django permission which can be used to grant package
deprecation ability without granting full change permissions.

This makes it possible to grant access to the package management panel
for users without full edit rights to the Package model, as it's
currently based on the deprecation permission.
  • Loading branch information
MythicManiac committed Dec 3, 2023
1 parent bdff92b commit 488375d
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.1.7 on 2023-12-03 03:02

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("repository", "0043_add_package_index_task_schedule"),
]

operations = [
migrations.AlterModelOptions(
name="package",
options={
"permissions": (
("deprecate_package", "Can manage package deprecation status"),
)
},
),
]
6 changes: 5 additions & 1 deletion django/thunderstore/repository/models/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class Package(models.Model):
)

class Meta:
permissions = (("deprecate_package", "Can manage package deprecation status"),)
constraints = [
models.UniqueConstraint(
fields=("owner", "name"), name="unique_name_per_namespace"
Expand Down Expand Up @@ -300,7 +301,10 @@ def ensure_user_can_manage_deprecation(self, user: Optional[UserType]) -> None:
raise ValidationError("Must be authenticated")
if not user.is_active:
raise ValidationError("User has been deactivated")
if user.is_staff and user.has_perm("repository.change_package"):
if user.is_staff and (
user.has_perm("repository.change_package")
or user.has_perm("repository.deprecate_package")
):
return
self.owner.ensure_user_can_manage_packages(user)

Expand Down
25 changes: 25 additions & 0 deletions django/thunderstore/repository/tests/test_package.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from typing import Any

import pytest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError

from conftest import TestUserTypes
from thunderstore.community.factories import SiteFactory
from thunderstore.community.models.package_listing import PackageListing
from thunderstore.core.types import UserType
from thunderstore.repository.factories import PackageFactory
from thunderstore.repository.models import (
Namespace,
Expand All @@ -16,6 +20,8 @@
)
from thunderstore.wiki.factories import WikiPageFactory

User = get_user_model()


@pytest.mark.django_db
def test_package_get_page_url(
Expand Down Expand Up @@ -112,6 +118,25 @@ def test_package_ensure_user_can_manage_deprecation(
assert package.ensure_user_can_manage_deprecation(user) is None


@pytest.mark.django_db
def test_package_ensure_user_can_manage_deprecation_deprecate_package_perm(
namespace: Namespace,
user: UserType,
):
team = namespace.team
package = PackageFactory(owner=team, namespace=namespace)
user.is_staff = True
user.save()
assert package.can_user_manage_deprecation(user) is False
content_type = ContentType.objects.get_for_model(Package)
perm = Permission.objects.get(
content_type=content_type, codename="deprecate_package"
)
user.user_permissions.add(perm)
user = User.objects.get(pk=user.pk)
assert package.can_user_manage_deprecation(user) is True


@pytest.mark.django_db
@pytest.mark.parametrize("user_type", TestUserTypes.options())
@pytest.mark.parametrize("role", TeamMemberRole.options() + [None])
Expand Down
9 changes: 8 additions & 1 deletion django/thunderstore/repository/views/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
PackageListingSection,
)
from thunderstore.core.types import UserType
from thunderstore.core.utils import check_validity
from thunderstore.frontend.api.experimental.serializers.views import CommunitySerializer
from thunderstore.frontend.url_reverse import get_community_url_reverse_args
from thunderstore.repository.mixins import CommunityMixin
Expand Down Expand Up @@ -470,6 +471,12 @@ def get_object(self, *args, **kwargs) -> PackageListing:
def can_manage(self):
return self.object.package.can_user_manage_deprecation(self.request.user)

@property
def can_manage_categories(self) -> bool:
return self.can_manage and check_validity(
lambda: self.object.ensure_update_categories_permission(self.request.user)
)

@property
def can_deprecate(self):
return self.can_manage and self.object.package.is_deprecated is False
Expand Down Expand Up @@ -510,7 +517,7 @@ def format_category(cat: PackageCategory):
"canDeprecate": self.can_deprecate,
"canUndeprecate": self.can_undeprecate,
"canUnlist": self.can_unlist,
"canUpdateCategories": self.can_manage,
"canUpdateCategories": self.can_manage_categories,
"csrfToken": csrf.get_token(self.request),
"currentCategories": [
format_category(x) for x in package_listing.categories.all()
Expand Down

0 comments on commit 488375d

Please sign in to comment.