summaryrefslogtreecommitdiff
path: root/lib/gitlab/import_export/attributes_permitter.rb
blob: acd03d9ec2062c68f5818dbb6da52b2a25eea150 (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
# frozen_string_literal: true

# AttributesPermitter builds a hash of permitted attributes for
# every model defined in import_export.yml that is used to validate and
# filter out any attributes that are not permitted when doing Project/Group Import
#
# Each model's list includes:
#   - attributes defined under included_attributes section
#   - associations defined under project/group tree
#   - methods defined under methods section
#
# Given the following import_export.yml example:
# ```
#   tree:
#     project:
#       - labels:
#         - :priorities
#   included_attributes:
#     labels:
#       - :title
#       - :description
#    methods:
#      labels:
#        - :type
# ```
#
# Produces a list of permitted attributes:
# ```
#   Gitlab::ImportExport::AttributesPermitter.new.permitted_attributes
#
#   => { labels: [:priorities, :title, :description, :type] }
# ```
#
# Filters out any other attributes from specific relation hash:
# ```
#   Gitlab::ImportExport::AttributesPermitter.new.permit(:labels, {id: 5, type: 'opened', description: 'test', sensitive_attribute: 'my_sensitive_attribute'})
#
#   => {:type=>"opened", :description=>"test"}
# ```
module Gitlab
  module ImportExport
    class AttributesPermitter
      attr_reader :permitted_attributes

      # We want to use AttributesCleaner for these relations instead, in the future this should be removed to make sure
      # we are using AttributesPermitter for every imported relation.
      DISABLED_RELATION_NAMES = %i[user author ci_cd_settings issuable_sla push_rule].freeze

      def initialize(config: ImportExport::Config.new.to_h)
        @config = config
        @attributes_finder = Gitlab::ImportExport::AttributesFinder.new(config: @config)
        @permitted_attributes = {}

        build_permitted_attributes
      end

      def permit(relation_sym, relation_hash)
        permitted_attributes = permitted_attributes_for(relation_sym)

        relation_hash.select do |key, _|
          permitted_attributes.include?(key.to_sym)
        end
      end

      def permitted_attributes_for(relation_sym)
        @permitted_attributes[relation_sym] || []
      end

      def permitted_attributes_defined?(relation_sym)
        !DISABLED_RELATION_NAMES.include?(relation_sym) && @attributes_finder.included_attributes.key?(relation_sym)
      end

      private

      def build_permitted_attributes
        build_associations
        build_attributes
        build_methods
      end

      # Deep traverse relations tree to build a list of allowed model relations
      def build_associations
        stack = @attributes_finder.tree.to_a

        while stack.any?
          model_name, relations = stack.pop

          if relations.is_a?(Hash)
            add_permitted_attributes(model_name, relations.keys)

            stack.concat(relations.to_a)
          end
        end

        @permitted_attributes
      end

      def build_attributes
        @attributes_finder.included_attributes.each do |model_name, attributes|
          add_permitted_attributes(model_name, attributes)
        end
      end

      def build_methods
        @attributes_finder.methods.each do |model_name, attributes|
          add_permitted_attributes(model_name, attributes)
        end
      end

      def add_permitted_attributes(model_name, attributes)
        @permitted_attributes[model_name] ||= []

        @permitted_attributes[model_name].concat(attributes) if attributes.any?
      end
    end
  end
end