2018-08-03 13:22:24 -04:00
# frozen_string_literal: true
2017-04-03 10:17:24 -04:00
module ProtectedRef
extend ActiveSupport :: Concern
included do
2021-03-17 11:09:03 -04:00
belongs_to :project , touch : true
2017-04-05 13:59:46 -04:00
2017-04-03 10:17:24 -04:00
validates :name , presence : true
validates :project , presence : true
2017-04-03 14:48:00 -04:00
delegate :matching , :matches? , :wildcard? , to : :ref_matcher
2020-01-15 13:08:34 -05:00
scope :for_project , - > ( project ) { where ( project : project ) }
2020-12-07 13:10:36 -05:00
def allow_multiple? ( type )
false
end
2017-05-05 11:59:31 -04:00
end
def commit
project . commit ( self . name )
end
class_methods do
def protected_ref_access_levels ( * types )
types . each do | type |
2017-07-02 11:02:59 -04:00
# We need to set `inverse_of` to make sure the `belongs_to`-object is set
# when creating children using `accepts_nested_attributes_for`.
#
# If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations
# to fail.
2018-07-02 06:43:06 -04:00
has_many :" #{ type } _access_levels " , inverse_of : self . model_name . singular
2017-05-05 11:59:31 -04:00
2020-12-07 13:10:36 -05:00
validates :" #{ type } _access_levels " , length : { is : 1 , message : " are restricted to a single instance per #{ self . model_name . human } . " } , unless : - > { allow_multiple? ( type ) }
2017-04-03 14:48:00 -04:00
2017-05-05 11:59:31 -04:00
accepts_nested_attributes_for :" #{ type } _access_levels " , allow_destroy : true
end
end
2018-04-18 09:52:55 -04:00
def protected_ref_accessible_to? ( ref , user , project : , action : , protected_refs : nil )
2021-02-17 10:09:21 -05:00
access_levels_for_ref ( ref , action : action , protected_refs : protected_refs ) . any? do | access_level |
2017-04-03 10:17:24 -04:00
access_level . check_access ( user )
end
end
2019-11-12 04:06:14 -05:00
def developers_can? ( action , ref , protected_refs : nil )
2021-02-17 10:09:21 -05:00
access_levels_for_ref ( ref , action : action , protected_refs : protected_refs ) . any? do | access_level |
2017-04-03 20:39:34 -04:00
access_level . access_level == Gitlab :: Access :: DEVELOPER
end
end
2021-02-17 10:09:21 -05:00
def access_levels_for_ref ( ref , action : , protected_refs : nil )
self . matching ( ref , protected_refs : protected_refs )
. flat_map ( & :" #{ action } _access_levels " )
2017-04-03 10:17:24 -04:00
end
2018-07-25 07:01:10 -04:00
# Returns all protected refs that match the given ref name.
# This checks all records from the scope built up so far, and does
# _not_ return a relation.
#
# This method optionally takes in a list of `protected_refs` to search
# through, to avoid calling out to the database.
2017-05-05 11:59:31 -04:00
def matching ( ref_name , protected_refs : nil )
2018-07-25 07:01:10 -04:00
( protected_refs || self . all ) . select { | protected_ref | protected_ref . matches? ( ref_name ) }
2017-04-03 10:17:24 -04:00
end
end
private
def ref_matcher
2018-07-25 07:01:10 -04:00
@ref_matcher || = RefMatcher . new ( self . name )
2017-04-03 10:17:24 -04:00
end
end
2019-09-13 09:26:31 -04:00
# Prepending a module into a concern doesn't work very well for class methods,
# since these are defined in a ClassMethods constant. As such, we prepend the
# module directly into ProtectedRef::ClassMethods, instead of prepending it into
# ProtectedRef.
2021-05-11 17:10:21 -04:00
ProtectedRef :: ClassMethods . prepend_mod_with ( 'ProtectedRef' )