Skip to content

Commit

Permalink
Added ability to remove signatures from a container(push) repo.
Browse files Browse the repository at this point in the history
closes pulp#548
  • Loading branch information
ipanova committed Feb 8, 2022
1 parent 08b0a06 commit 080e76c
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/548.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ability to remove signatures from a container(push) repo.
52 changes: 51 additions & 1 deletion pulp_container/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ class RemoveImageSerializer(serializers.Serializer):

def validate(self, data):
"""
Validate and extract the latest repository version, manifest, and tags from the passed data.
Validate and extract the latest repository version, manifest, signatures and tags from the passed data.
"""
new_data = {}
new_data.update(self.initial_data)
Expand Down Expand Up @@ -656,6 +656,56 @@ def validate(self, data):
pk__in=new_data["latest_version"].content.all(), tagged_manifest=new_data["manifest"]
).values_list("pk", flat=True)
new_data["tags_pks"] = tags_pks
sigs_pks = models.ManifestSignature.objects.filter(
pk__in=new_data["latest_version"].content.all(), signed_manifest=new_data["manifest"]
).values_list("pk", flat=True)
new_data["sigs_pks"] = sigs_pks

return new_data


class RemoveSignaturesSerializer(serializers.Serializer):
"""
A serializer for parsing and validating data associated with the signatures removal.
"""

signed_with_key_id = serializers.CharField(
help_text="key_id of the key the signatures were produced with"
)

def validate(self, data):
"""
Validate and extract the latest repository version, signatures from the passed data.
"""
new_data = {}
new_data.update(self.initial_data)

latest_version = new_data["repository"].latest_version()
if not latest_version:
raise serializers.ValidationError(
_(
"The latest repository version of '{}' was not found".format(
new_data["repository"]
)
)
)

new_data["latest_version"] = latest_version
sigs_pks = models.ManifestSignature.objects.filter(
pk__in=new_data["latest_version"].content.all(),
key_id=new_data["signed_with_key_id"],
).values_list("pk", flat=True)
if not sigs_pks:
raise serializers.ValidationError(
_(
"There are no signatures in the latest repository version '{}' "
"produced with the specified key_id '{}'".format(
new_data["latest_version"], new_data["signed_with_key_id"]
)
)
)

new_data["sigs_pks"] = sigs_pks

return new_data

Expand Down
4 changes: 3 additions & 1 deletion pulp_container/app/tasks/recursive_remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def recursive_remove_content(repository_pk, content_units):
new_version.remove_content(latest_content)
else:
tags_in_repo = Q(pk__in=latest_content.filter(pulp_type=Tag.get_pulp_type()))
sigs_in_repo = Q(pk__in=latest_content.filter(pulp_type=ManifestSignature.get_pulp_type()))
manifests_in_repo = Q(pk__in=latest_content.filter(pulp_type=Manifest.get_pulp_type()))
user_provided_content = Q(pk__in=content_units)
type_manifest_list = Q(media_type__in=[MEDIA_TYPE.MANIFEST_LIST, MEDIA_TYPE.INDEX_OCI])
Expand Down Expand Up @@ -113,8 +114,9 @@ def recursive_remove_content(repository_pk, content_units):
)

# signatures can't be shared, so no need to calculate which ones to remain
sigs_to_remove_from_manifests = Q(signed_manifest__in=manifests_to_remove)
signatures_to_remove = ManifestSignature.objects.filter(
signed_manifest__in=manifests_to_remove
(user_provided_content & sigs_in_repo) | sigs_to_remove_from_manifests
)

with repository.new_version() as new_version:
Expand Down
28 changes: 27 additions & 1 deletion pulp_container/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ class ContainerPushRepositoryViewSet(
"condition": "has_namespace_or_obj_perms:container.view_containerpushrepository",
},
{
"action": ["tag", "untag", "remove_image", "sign"],
"action": ["tag", "untag", "remove_image", "sign", "remove_signatures"],
"principal": "authenticated",
"effect": "allow",
"condition": [
Expand Down Expand Up @@ -1028,6 +1028,7 @@ def remove_image(self, request, pk):
serializer.is_valid(raise_exception=True)

content_units_to_remove = list(serializer.validated_data["tags_pks"])
content_units_to_remove.extend(list(serializer.validated_data["sigs_pks"]))
content_units_to_remove.append(serializer.validated_data["manifest"].pk)

result = dispatch(
Expand All @@ -1040,6 +1041,31 @@ def remove_image(self, request, pk):
)
return OperationPostponedResponse(result, request)

@action(detail=True, methods=["post"], serializer_class=serializers.RemoveSignaturesSerializer)
def remove_signatures(self, request, pk):
"""
Create a task which deletes signatures by the passed key_id.
"""
repository = self.get_object()
request.data["repository"] = repository

serializer = serializers.RemoveSignaturesSerializer(
data=request.data, context={"request": request}
)
serializer.is_valid(raise_exception=True)

content_units_to_remove = list(serializer.validated_data["sigs_pks"])

result = dispatch(
tasks.recursive_remove_content,
exclusive_resources=[repository],
kwargs={
"repository_pk": str(repository.pk),
"content_units": [str(pk) for pk in content_units_to_remove],
},
)
return OperationPostponedResponse(result, request)

def get_queryset(self):
"""
Returns a queryset by filtering by namespace permission to view distributions and
Expand Down

0 comments on commit 080e76c

Please sign in to comment.