Update tag policy to take into account protection rules
Context
We have an existing permission type for ContainerRepositoryTag and we need to override this to take into account tag protection rules.
Ideally, we want to throw in an additional validation (protection rules) on top of the permissions system. I have a sense that this should be possible with some override functions, similar to overriding an object function from a resolver to add more logic.
One usage of this is for displaying / hiding the checkbox for deleting tags: https://gitlab.com/gitlab-org/gitlab/-/issues/499872/designs/protected-tag-identifier.png.
What does this MR do?
- Add a new rule to
ContainerRegistry::TagPolicyto preventdestroy_container_imageability if the tag is protected for delete. - Centralize the logic of
Tag#protection_rulein theContainerRegistry::Tagclass, so that it can be called fromTypes::ContainerRegistry::ContainerRepositoryTagType&ContainerRegistry::TagPolicy. - Add the needed specs.
References
Please include cross links to any resources that are relevant to this MR. This will give reviewers and future readers helpful context to give an efficient review of the changes introduced.
- https://gitlab.com/gitlab-org/gitlab/-/issues/512367+
- Add protection to ContainerRepositoryTagType (!177039 - merged)
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
N/A
How to set up and validate locally
- Prepare a project with several container registry tags on it
- Create several tag protection rules for the project. Create some rule that matches the registry tags and some that don't.
project = Project.find(id)
# will not match a tag, unless that tag has the name `thiswillnotmatch`
project.container_registry_protection_tag_rules.create(tag_name_pattern: "thiswillnotmatch", minimum_access_level_for_push: "maintainer", minimum_access_level_for_delete: "maintainer")
# will always match a tag
project.container_registry_protection_tag_rules.create(tag_name_pattern: ".*", minimum_access_level_for_push: "maintainer", minimum_access_level_for_delete: "owner")
# another rule that matches the tag, update `name` to the tag name to make sure it matches
project.container_registry_protection_tag_rules.create(tag_name_pattern: "name", minimum_access_level_for_push: "owner", minimum_access_level_for_delete: "maintainer")
- Enable the
container_registry_protected_tagsfeature flag:
Feature.enable(:container_registry_protected_tags)
- Login as a
rootuser and visithttp://gdk.test:3000/-/graphql-explorerand send a GraphQL query. The response should containuserPermissionsfield for each tag.
{
containerRepository(id: "gid://gitlab/ContainerRepository/<Container Repository ID>") {
id
tagsCount
tags(first: 5) {
nodes {
userPermissions {
destroyContainerRepositoryTag
}
}
}
}
}
Since the root user is admin, destroyContainerRepositoryTag should be true.
-
Impersonate a different member in the project with a
maintainerrole and visithttp://gdk.test:3000/-/graphql-exploreragain and repeat the same query.This time, the value of
destroyContainerRepositoryTagshould befalse, because the protection rule that matches the tag hasminimum_access_level_for_delete: "owner". -
You can play around by trying different users with different roles and by updating
minimum_access_level_for_deletefor the matching tag's rule.
Related to #512367