Commit 1f5d2254 authored by George Koltsov's avatar George Koltsov

Add AttributesPermitter for Import attributes filtering

- 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
parent 8f56f4b6
......@@ -3,6 +3,8 @@
module Gitlab
module ImportExport
class AttributesFinder
attr_reader :tree, :included_attributes, :excluded_attributes, :methods, :preloads
def initialize(config:)
@tree = config[:tree] || {}
@included_attributes = config[:included_attributes] || {}
......
# 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
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_name, relation_hash)
permitted_attributes = permitted_attributes_for(relation_name)
relation_hash.select do |key, _|
permitted_attributes.include?(key)
end
end
def permitted_attributes_for(relation_name)
@permitted_attributes[relation_name] || []
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(&method(:add_permitted_attributes))
end
def build_methods
@attributes_finder.methods.each(&method(:add_permitted_attributes))
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
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ImportExport::AttributesPermitter do
let(:yml_config) do
<<-EOF
tree:
project:
- labels:
- :priorities
- milestones:
- events:
- :push_event_payload
included_attributes:
labels:
- :title
- :description
methods:
labels:
- :type
EOF
end
let(:file) { Tempfile.new(%w(import_export .yml)) }
let(:config_hash) { Gitlab::ImportExport::Config.new(config: file.path).to_h }
before do
file.write(yml_config)
file.rewind
end
after do
file.close
file.unlink
end
subject { described_class.new(config: config_hash) }
describe '#permitted_attributes' do
it 'builds permitted attributes hash' do
expect(subject.permitted_attributes).to match(
a_hash_including(
project: [:labels, :milestones],
labels: [:priorities, :title, :description, :type],
events: [:push_event_payload],
milestones: [:events],
priorities: [],
push_event_payload: []
)
)
end
end
describe '#permit' do
let(:unfiltered_hash) do
{
title: 'Title',
description: 'Description',
undesired_attribute: 'Undesired Attribute',
another_attribute: 'Another Attribute'
}
end
it 'only allows permitted attributes' do
expect(subject.permit(:labels, unfiltered_hash)).to eq(title: 'Title', description: 'Description')
end
end
describe '#permitted_attributes_for' do
it 'returns an array of permitted attributes for a relation' do
expect(subject.permitted_attributes_for(:labels)).to contain_exactly(:title, :description, :type, :priorities)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment