Skip to content

Commit

Permalink
Add permissions to all models and two viewsets
Browse files Browse the repository at this point in the history
[noissue]
  • Loading branch information
lubosmj committed Feb 2, 2024
1 parent 9861308 commit 65725e5
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 8 deletions.
25 changes: 25 additions & 0 deletions pulp_ostree/app/migrations/0007_add_permissions_to_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.9 on 2024-02-02 11:26

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('ostree', '0006_alter_pointers_to_related_models_globally'),
]

operations = [
migrations.AlterModelOptions(
name='ostreedistribution',
options={'default_related_name': '%(app_label)s_%(model_name)s', 'permissions': [('manage_roles_ostreedistribution', 'Can manage roles on ostree distributions')]},
),
migrations.AlterModelOptions(
name='ostreeremote',
options={'default_related_name': '%(app_label)s_%(model_name)s', 'permissions': [('manage_roles_ostreeremote', 'Can manage roles on ostree remotes')]},
),
migrations.AlterModelOptions(
name='ostreerepository',
options={'default_related_name': '%(app_label)s_%(model_name)s', 'permissions': [('sync_ostreerepository', 'Can start a sync task'), ('modify_ostreerepository', 'Can modify content of the repository'), ('manage_roles_ostreerepository', 'Can manage roles on ostree repositories'), ('repair_ostreerepository', 'Can repair repository versions')]},
),
]
19 changes: 16 additions & 3 deletions pulp_ostree/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.contrib.postgres.fields import ArrayField

from pulpcore.plugin.models import (
AutoAddObjPermsMixin,
Content,
Remote,
Repository,
Expand Down Expand Up @@ -123,7 +124,7 @@ class Meta:
unique_together = [["sha256", "relative_path"]]


class OstreeRemote(Remote):
class OstreeRemote(Remote, AutoAddObjPermsMixin):
"""A remote model for OSTree content."""

TYPE = "ostree"
Expand All @@ -134,9 +135,12 @@ class OstreeRemote(Remote):

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
permissions = [
("manage_roles_ostreeremote", "Can manage roles on ostree remotes"),
]


class OstreeRepository(Repository):
class OstreeRepository(Repository, AutoAddObjPermsMixin):
"""A repository model for OSTree content."""

TYPE = "ostree"
Expand All @@ -155,17 +159,26 @@ class OstreeRepository(Repository):

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
permissions = [
("sync_ostreerepository", "Can start a sync task"),
("modify_ostreerepository", "Can modify content of the repository"),
("manage_roles_ostreerepository", "Can manage roles on ostree repositories"),
("repair_ostreerepository", "Can repair repository versions"),
]

def finalize_new_version(self, new_version):
"""Handle repository duplicates."""
remove_duplicates(new_version)
validate_duplicate_content(new_version)


class OstreeDistribution(Distribution):
class OstreeDistribution(Distribution, AutoAddObjPermsMixin):
"""A distribution model for OSTree content."""

TYPE = "ostree"

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
permissions = [
("manage_roles_ostreedistribution", "Can manage roles on ostree distributions"),
]
212 changes: 207 additions & 5 deletions pulp_ostree/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,177 @@
RepositorySyncURLSerializer,
)
from pulpcore.plugin.tasking import dispatch
from pulpcore.plugin.util import get_objects_for_user

from . import models, serializers, tasks


class OstreeRemoteViewSet(core.RemoteViewSet):
REPO_VIEW_PERM = "ostree.view_ostreerepository"


class OstreeRemoteViewSet(core.RemoteViewSet, core.RolesMixin):
"""A ViewSet class for OSTree remote repositories."""

endpoint_name = "ostree"
queryset = models.OstreeRemote.objects.all()
serializer_class = serializers.OstreeRemoteSerializer


class OstreeRepositoryViewSet(core.RepositoryViewSet, ModifyRepositoryActionMixin):
queryset_filtering_required_permission = "ostree.view_ostreeremote"

DEFAULT_ACCESS_POLICY = {
"statements": [
{
"action": ["list", "my_permissions"],
"principal": "authenticated",
"effect": "allow",
},
{
"action": ["create"],
"principal": "authenticated",
"effect": "allow",
"condition": "has_model_perms:ostree.add_ostreeremote",
},
{
"action": ["retrieve"],
"principal": "authenticated",
"effect": "allow",
"condition": "has_model_or_obj_perms:ostree.view_ostreeremote",
},
{
"action": ["update", "partial_update", "set_label", "unset_label"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_obj_perms:ostree.change_ostreeremote",
],
},
{
"action": ["destroy"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_or_obj_perms:ostree.delete_ostreeremote",
],
},
{
"action": ["list_roles", "add_role", "remove_role"],
"principal": "authenticated",
"effect": "allow",
"condition": ["has_model_or_or_obj_perms:ostree.manage_roles_ostreeremote"],
},
],
"creation_hooks": [
{
"function": "add_roles_for_object_creator",
"parameters": {"roles": "ostree.ostreeremote_owner"},
},
],
"queryset_scoping": {"function": "scope_queryset"},
}
LOCKED_ROLES = {
"ostree.ostreeremote_creator": ["ostree.add_ostreeremote"],
"ostree.ostreeremote_owner": [
"ostree.view_ostreeremote",
"ostree.change_ostreeremote",
"ostree.delete_ostreeremote",
"ostree.manage_roles_ostreeremote",
],
"ostree.ostreeremote_viewer": ["ostree.view_ostreeremote"],
}


class OstreeRepositoryViewSet(core.RepositoryViewSet, ModifyRepositoryActionMixin, core.RolesMixin):
"""A ViewSet class for OSTree repositories."""

endpoint_name = "ostree"
queryset = models.OstreeRepository.objects.all()
serializer_class = serializers.OstreeRepositorySerializer
queryset_filtering_required_permission = "ostree.view_ostreerepository"

DEFAULT_ACCESS_POLICY = {
"statements": [
{
"action": ["list", "my_permissions"],
"principal": "authenticated",
"effect": "allow",
},
{
"action": ["create"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_perms:ostre.add_ostreerepository",
# required when creating a repository with a remote
"has_remote_param_model_or_obj_perms:ostree.view_ostreeremote",
],
},
{
"action": ["retrieve"],
"principal": "authenticated",
"effect": "allow",
"condition": "has_model_or_obj_perms:ostree.view_ostreerepository",
},
{
"action": ["destroy"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_obj_perms:ostree.delete_ostreerepository",
],
},
{
"action": ["update", "partial_update", "set_label", "unset_label"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_obj_perms:ostree.change_ostreerepository",
"has_remote_param_model_or_obj_perms:ostree.view_ostreeremote",
],
},
{
"action": ["sync"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_obj_perms:ostree.sync_ostreerepository",
"has_remote_param_model_or_obj_perms:ostree.view_ostreeremote",
],
},
{
"action": ["modify"],
"principal": "authenticated",
"effect": "allow",
"condition": [
"has_model_or_domain_or_obj_perms:ostree.modify_ostreerepository",
],
},
{
"action": ["list_roles", "add_role", "remove_role"],
"principal": "authenticated",
"effect": "allow",
"condition": ["has_model_or_obj_perms:ostree.manage_roles_ostreerepository"],
},
],
"creation_hooks": [
{
"function": "add_roles_for_object_creator",
"parameters": {"roles": "ostree.ostreerepository_owner"},
},
],
"queryset_scoping": {"function": "scope_queryset"},
}
LOCKED_ROLES = {
"ostre.ostreerepository_creator": ["ostree.add_ostreerepository"],
"ostre.ostreerepository_owner": [
"ostree.view_ostreerepository",
"ostree.change_ostreerepository",
"ostree.delete_ostreerepository",
"ostree.modify_ostreerepository",
"ostree.sync_ostreerepository",
"ostree.manage_roles_ostreerepository",
"ostree.repair_ostreerepository",
],
"ostree.ostreerepository_viewer": ["ostree.view_ostreerepository"],
}

@extend_schema(
description="Trigger an asynchronous task to sync content.",
Expand Down Expand Up @@ -196,14 +349,63 @@ class Meta:
fields = {"name": NAME_FILTER_OPTIONS}


class OstreeRefViewSet(ReadOnlyContentViewSet):
class OstreeContentQuerySetMixin:
"""
A mixin that filters content units based on their object-level permissions.
"""

def _scope_repos_by_repo_version(self, repo_version_href):
repo_version = core.NamedModelViewSet.get_resource(repo_version_href, RepositoryVersion)
repo = repo_version.repository.cast()

has_model_perm = self.request.user.has_perm(REPO_VIEW_PERM)
has_object_perm = self.request.user.has_perm(REPO_VIEW_PERM, repo)

if has_model_perm or has_object_perm:
return [repo]
else:
return []

def get_content_qs(self, qs):
"""
Get a filtered QuerySet based on the current request's scope.
This method returns only content units a user is allowed to preview. The user with the
global import and mirror permissions (i.e., having the "ostree.view_ostreerepository")
can see orphaned content too.
"""
if self.request.user.has_perm(REPO_VIEW_PERM):
return qs

if repo_version_href := self.request.query_params.get("repository_version"):
allowed_repos = self._scope_repos_by_repo_version(repo_version_href)
else:
allowed_repos = get_objects_for_user(
self.request.user, REPO_VIEW_PERM, models.OstreeRepository.objects.all()
).only("pk")

return qs.model.objects.filter(repositories__in=allowed_repos)


class OstreeRefViewSet(OstreeContentQuerySetMixin, ReadOnlyContentViewSet):
"""A ViewSet class for OSTree head commits."""

endpoint_name = "refs"
queryset = models.OstreeRef.objects.all()
serializer_class = serializers.OstreeRefSerializer
filterset_class = OstreeRefFilter

DEFAULT_ACCESS_POLICY = {
"statements": [
{
"action": ["list", "retrieve"],
"principal": "authenticated",
"effect": "allow",
},
],
"queryset_scoping": {"function": "get_content_qs"},
}


class OstreeCommitFilter(ContentFilter):
"""A filterset class for commits."""
Expand Down

0 comments on commit 65725e5

Please sign in to comment.