summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/pipeline/chain/build/associations.rb
blob: b5d63691849d59d6f704175614efab1b93432a32 (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
# frozen_string_literal: true

module Gitlab
  module Ci
    module Pipeline
      module Chain
        class Build
          class Associations < Chain::Base
            include Gitlab::Allowable
            include Chain::Helpers

            def perform!
              assign_pipeline_variables
              assign_source_pipeline
            end

            def break?
              @pipeline.errors.any?
            end

            private

            def assign_pipeline_variables
              @pipeline.variables_attributes = variables_attributes
            end

            def assign_source_pipeline
              return unless @command.bridge

              @pipeline.build_source_pipeline(
                source_pipeline: @command.bridge.pipeline,
                source_project: @command.bridge.project,
                source_bridge: @command.bridge,
                project: @command.project
              )
            end

            def variables_attributes
              variables = Array(@command.variables_attributes)
              variables = apply_permissions(variables)
              validate_uniqueness(variables)
            end

            def apply_permissions(variables)
              # We allow parent pipelines to pass variables to child pipelines since
              # these variables are coming from internal configurations. We will check
              # permissions to :set_pipeline_variables when those are injected upstream,
              # to the parent pipeline.
              # In other scenarios (e.g. multi-project pipelines or run pipeline via UI)
              # the variables are provided from the outside and those should be guarded.
              return variables if @command.creates_child_pipeline?

              if variables.present? && !can?(@command.current_user, :set_pipeline_variables, @command.project)
                error("Insufficient permissions to set pipeline variables")
                variables = []
              end

              variables
            end

            def validate_uniqueness(variables)
              duplicated_keys = variables
                .map { |var| var[:key] }
                .tally
                .filter_map { |key, count| key if count > 1 }

              if duplicated_keys.empty?
                variables
              else
                error(duplicate_variables_message(duplicated_keys), config_error: true)
                []
              end
            end

            def duplicate_variables_message(keys)
              "Duplicate variable #{'name'.pluralize(keys.size)}: #{keys.join(', ')}"
            end
          end
        end
      end
    end
  end
end