Welcome to mirror list, hosted at ThFree Co, Russian Federation.

update_service.rb « groups « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a6ef8c8743b4e5c9fdc9f11a44837ceb8dc20a3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# frozen_string_literal: true

module Groups
  class UpdateService < Groups::BaseService
    include UpdateVisibilityLevel

    SETTINGS_PARAMS = [:allow_mfa_for_subgroups].freeze

    def execute
      reject_parent_id!
      remove_unallowed_params

      before_assignment_hook(group, params)

      if renaming_group_with_container_registry_images?
        group.errors.add(:base, container_images_error)
        return false
      end

      return false unless valid_visibility_level_change?(group, group.visibility_attribute_value(params))

      return false unless valid_share_with_group_lock_change?

      return false unless valid_path_change?

      return false unless update_shared_runners

      handle_changes

      handle_namespace_settings

      handle_hierarchy_cache_update

      group.assign_attributes(params)

      begin
        success = group.save

        after_update if success

        success
      rescue Gitlab::UpdatePathError => e
        group.errors.add(:base, e.message)

        false
      end
    end

    private

    def handle_hierarchy_cache_update
      return unless params.key?(:enable_namespace_descendants_cache)

      enabled = Gitlab::Utils.to_boolean(params.delete(:enable_namespace_descendants_cache))

      return unless Feature.enabled?(:group_hierarchy_optimization, group, type: :beta)

      if enabled
        return if group.namespace_descendants

        params[:namespace_descendants_attributes] = {
          traversal_ids: group.traversal_ids,
          all_project_ids: [],
          self_and_descendant_group_ids: []
        }
      else
        return unless group.namespace_descendants

        params[:namespace_descendants_attributes] = { id: group.id, _destroy: true }
      end
    end

    def valid_path_change?
      return true unless group.packages_feature_enabled?
      return true if params[:path].blank?
      return true if group.has_parent?
      return true if !group.has_parent? && group.path == params[:path]

      # we have a path change on a root group:
      # check that we don't have any npm package with a scope set to the group path
      npm_packages = ::Packages::GroupPackagesFinder.new(current_user, group, package_type: :npm, preload_pipelines: false)
                       .execute
                       .with_npm_scope(group.path)

      return true unless npm_packages.exists?

      group.errors.add(:path, s_('GroupSettings|cannot change when group contains projects with NPM packages'))
      false
    end

    def before_assignment_hook(group, params)
      @full_path_before = group.full_path
      @path_before = group.path
    end

    def renaming_group_with_container_registry_images?
      renaming? && group.has_container_repository_including_subgroups?
    end

    def renaming?
      new_path = params[:path]

      new_path && new_path != @path_before
    end

    def container_images_error
      s_("GroupSettings|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.")
    end

    def after_update
      if group.previous_changes.include?(group.visibility_level_field) && group.private?
        # don't enqueue immediately to prevent todos removal in case of a mistake
        TodosDestroyer::GroupPrivateWorker.perform_in(Todo::WAIT_FOR_DELETE, group.id)
      end

      update_two_factor_requirement_for_subgroups

      publish_event
    end

    def update_two_factor_requirement_for_subgroups
      settings = group.namespace_settings
      return if settings.allow_mfa_for_subgroups

      if settings.previous_changes.include?(:allow_mfa_for_subgroups)
        # enque in batches members update
        DisallowTwoFactorForSubgroupsWorker.perform_async(group.id)
      end
    end

    def reject_parent_id!
      params.delete(:parent_id)
    end

    # overridden in EE
    def remove_unallowed_params
      params.delete(:emails_disabled) unless can?(current_user, :set_emails_disabled, group)

      unless can?(current_user, :update_default_branch_protection, group)
        params.delete(:default_branch_protection)
        params.delete(:default_branch_protection_defaults)
      end
    end

    def handle_changes
      handle_settings_update
      handle_crm_settings_update unless params[:crm_enabled].nil?
    end

    def handle_settings_update
      settings_params = params.slice(*allowed_settings_params)
      settings_params.merge!({ default_branch_protection: params[:default_branch_protection] }.compact)
      allowed_settings_params.each { |param| params.delete(param) }

      ::NamespaceSettings::UpdateService.new(current_user, group, settings_params).execute
    end

    def handle_crm_settings_update
      crm_enabled = params.delete(:crm_enabled)
      return if group.crm_enabled? == crm_enabled

      crm_settings = group.crm_settings || group.build_crm_settings
      crm_settings.enabled = crm_enabled
      crm_settings.save
    end

    def allowed_settings_params
      SETTINGS_PARAMS
    end

    def valid_share_with_group_lock_change?
      return true unless changing_share_with_group_lock?
      return true if can?(current_user, :change_share_with_group_lock, group)

      group.errors.add(:share_with_group_lock, s_('GroupSettings|cannot be disabled when the parent group "Share with group lock" is enabled, except by the owner of the parent group'))
      false
    end

    def changing_share_with_group_lock?
      return false if params[:share_with_group_lock].nil?

      params[:share_with_group_lock] != group.share_with_group_lock
    end

    def update_shared_runners
      return true if params[:shared_runners_setting].nil?

      result = Groups::UpdateSharedRunnersService.new(group, current_user, shared_runners_setting: params.delete(:shared_runners_setting)).execute

      return true if result[:status] == :success

      group.errors.add(:update_shared_runners, result[:message])
      false
    end

    def publish_event
      return unless renaming?

      event = Groups::GroupPathChangedEvent.new(
        data: {
          group_id: group.id,
          root_namespace_id: group.root_ancestor.id,
          old_path: @full_path_before,
          new_path: group.full_path
        }
      )

      Gitlab::EventStore.publish(event)
    end
  end
end

Groups::UpdateService.prepend_mod_with('Groups::UpdateService')