summaryrefslogtreecommitdiff
path: root/app/services/groups/update_service.rb
blob: 84385f5da25eb926aa1f000a3181f6ddabef3f6f (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
# 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

      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, params[:visibility_level])

      return false unless valid_share_with_group_lock_change?

      return false unless valid_path_change_with_npm_packages?

      return false unless update_shared_runners

      handle_changes

      before_assignment_hook(group, params)

      handle_namespace_settings

      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_namespace_settings
      settings_params = params.slice(*::NamespaceSetting::NAMESPACE_SETTINGS_PARAMS)

      return if settings_params.empty?

      ::NamespaceSetting::NAMESPACE_SETTINGS_PARAMS.each do |nsp|
        params.delete(nsp)
      end

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

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

      npm_packages = ::Packages::GroupPackagesFinder.new(current_user, group, package_type: :npm).execute
      if npm_packages.exists?
        group.errors.add(:path, s_('GroupSettings|cannot change when group contains projects with NPM packages'))
        return
      end

      true
    end

    def before_assignment_hook(group, params)
      # overridden in EE
    end

    def renaming_group_with_container_registry_images?
      new_path = params[:path]

      new_path &&
        new_path != group.path &&
        group.has_container_repository_including_subgroups?
    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?(:visibility_level) && 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
    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)
      params.delete(:default_branch_protection) unless can?(current_user, :update_default_branch_protection, group)
    end

    def handle_changes
      handle_settings_update
    end

    def handle_settings_update
      settings_params = params.slice(*allowed_settings_params)
      allowed_settings_params.each { |param| params.delete(param) }

      ::NamespaceSettings::UpdateService.new(current_user, group, settings_params).execute
    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
  end
end

Groups::UpdateService.prepend_if_ee('EE::Groups::UpdateService')