Protected packages: Adding new scope for_package_name
What does this MR do and why?
- This MR adds the following scopes to the model
Packages::Protection::Rule
:-
.for_package_name
=> the scope filtersPackages::Protection::Rule
for a given package name (with optional wildcard characters) -
.for_package_type
=> the scope filters protection rules based on a given package type -
.push_protection_applicable_for_access_level
=> filter the protection rules based on a given access level -
.protected_against_push
=> combination of the three scopes as a utility
-
- Additionally, the method
push_protected_from?(user)
inPackages::Package
constructs the complete DB query to check if a package protection rule for this package and user exists - Note: These scopes are needed for the implementation of the feature
protected packages
, see &5574 - The implementation was already discussed in a previous MR. This MR intends to isolate the implemenation and other discussions related to this necessary scope.
Implementation thoughts
We want package protection rules to allow wildcard character (*
) as used in the feature protected branches. This needs to be considered for the implementation of this scope.
As discussed in a previous MR and the initial concept, @10io sees the possibility and the need for performing the evaluation of the packages protection rules in the database in order to have an efficient way of evaluating different package protection rules at once.
In terms of implementation, @10io proposes to use the psql operator ILIKE
to perform the evaluation in the database. As the the field package_name_pattern
accepts wildcard characters (only *
), the value of package_name_pattern
has to be preprocessed before being applied to the ILIKE
query, e.g. convert the character *
to %
. For this preprocessing, @10io also proposes to introduce a new column that contains the preprocessed value for package_name_pattern
that can be easioly applied to the ILIKE
query.
This MR is intended to finalize the discussion and the implementation on this matter.
DB Query changes
Old query
New query
The following queries are used to find and evaluate the package protection rules for a given package, e.g. package.push_protected_from?(project_owner)
ProjectAuthorization Maximum (0.9ms) SELECT MAX("project_authorizations"."access_level") AS "maximum_access_level", "project_authorizations"."project_id" AS "project_authorizations_project_id" FROM "project_authorizations" WHERE "project_authorizations"."user_id" = 1 AND "project_authorizations"."project_id" = 7 GROUP BY "project_authorizations"."project_id" /*application:console,db_config_name:main,console_hostname:Gerardos-MacBook-Pro.local,console_username:client-siemens,line:/app/models/user.rb:2038:in `block in max_member_access_for_project_ids'*/
Packages::Protection::Rule Exists? (0.4ms) SELECT 1 AS one FROM "packages_protection_rules" WHERE "packages_protection_rules"."project_id" = 7 AND ('@gitlab-org/npm-package-1' ILIKE package_name_pattern_ilike_query) AND "packages_protection_rules"."package_type" = 2 AND "packages_protection_rules"."push_protected_up_to_access_level" >= 50 LIMIT 1 /*application:console,db_config_name:main,console_hostname:Gerardos-MacBook-Pro.local,console_username:client-siemens,line:/app/models/packages/package.rb:416:in `push_protected_from?'*/
Screenshots or screen recordings
How to set up and validate locally
- Create one
Packages::Protection::Rule
Packages::Protection::Rule.create(project: Project.find(7), package_type: :npm, push_protected_up_to_access_level: Gitlab::Access::DEVELOPER, package_name_pattern: "@gitlab-org/npm-package-*")
- Build a package object and check if a package protection rule exists for a project developer
package = Project.find(7).packages.with_package_type(:npm).with_name("@gitlab-org/npm-package-1").build()
project_developer = Project.find(7).team.developers.first
package.push_protected_from?(project_developer)
# => Returns true because developers are not allowed to push based on the created protection rule, see step 1.
- Build a package object and check if a package protection rule exists for a project owner (SQL query displayed above)
package = Project.find(7).packages.with_package_type(:npm).with_name("@gitlab-org/npm-package-1").build()
project_owner = Project.find(7).team.owners.first
package.push_protected_from?(project_owner)
# => Returns false because owners are still allowed to push because there is no protection rule, see step 1.
Todos
-
Finalize discussion on this implementation -
Implement the approach proposed by @10io, see (!124776 (comment 1546621819))
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR. -
Changelog entry added, if necessary -
Documentation created/updated via this MR -
Documentation reviewed by technical writer or follow-up review issue created -
Tests added for this feature/bug -
Tested in all supported browsers -
Conforms to the code review guidelines -
Conforms to the merge request performance guidelines -
Conforms to the style guides -
Conforms to the javascript style guides -
Conforms to the database guides
-
Related to #416382