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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/integration.rb')
-rw-r--r--app/models/integration.rb98
1 files changed, 61 insertions, 37 deletions
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 274c16507b7..c0e244e38b6 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -10,9 +10,11 @@ class Integration < ApplicationRecord
include FromUnion
include EachBatch
include IgnorableColumns
+ extend ::Gitlab::Utils::Override
ignore_column :template, remove_with: '15.0', remove_after: '2022-04-22'
ignore_column :type, remove_with: '15.0', remove_after: '2022-04-22'
+ ignore_column :properties, remove_with: '15.1', remove_after: '2022-05-22'
UnknownType = Class.new(StandardError)
@@ -47,10 +49,7 @@ class Integration < ApplicationRecord
SECTION_TYPE_CONNECTION = 'connection'
- serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
-
- attr_encrypted :encrypted_properties_tmp,
- attribute: :encrypted_properties,
+ attr_encrypted :properties,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_32,
algorithm: 'aes-256-gcm',
@@ -59,6 +58,15 @@ class Integration < ApplicationRecord
encode: false,
encode_iv: false
+ # Handle assignment of props with symbol keys.
+ # To do this correctly, we need to call the method generated by attr_encrypted.
+ alias_method :attr_encrypted_props=, :properties=
+ private :attr_encrypted_props=
+
+ def properties=(props)
+ self.attr_encrypted_props = props&.with_indifferent_access&.freeze
+ end
+
alias_attribute :type, :type_new
default_value_for :active, false
@@ -77,8 +85,6 @@ class Integration < ApplicationRecord
default_value_for :wiki_page_events, true
after_initialize :initialize_properties
- after_initialize :copy_properties_to_encrypted_properties
- before_save :copy_properties_to_encrypted_properties
after_commit :reset_updated_properties
@@ -96,6 +102,9 @@ class Integration < ApplicationRecord
validate :validate_belongs_to_project_or_group
scope :external_issue_trackers, -> { where(category: 'issue_tracker').active }
+ # TODO: Will be modified in 15.0
+ # Details: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74501#note_744393645
+ scope :third_party_wikis, -> { where(type: %w[Integrations::Confluence Integrations::Shimo]).active }
scope :by_name, ->(name) { by_type(integration_name_to_type(name)) }
scope :external_wikis, -> { by_name(:external_wiki).active }
scope :active, -> { where(active: true) }
@@ -162,16 +171,14 @@ class Integration < ApplicationRecord
class_eval <<~RUBY, __FILE__, __LINE__ + 1
unless method_defined?(arg)
def #{arg}
- properties['#{arg}']
+ properties['#{arg}'] if properties.present?
end
end
def #{arg}=(value)
self.properties ||= {}
- self.encrypted_properties_tmp = properties
updated_properties['#{arg}'] = #{arg} unless #{arg}_changed?
- self.properties['#{arg}'] = value
- self.encrypted_properties_tmp['#{arg}'] = value
+ self.properties = self.properties.merge('#{arg}' => value)
end
def #{arg}_changed?
@@ -192,11 +199,13 @@ class Integration < ApplicationRecord
# Provide convenient boolean accessor methods for each serialized property.
# Also keep track of updated properties in a similar way as ActiveModel::Dirty
def self.boolean_accessor(*args)
- self.prop_accessor(*args)
+ prop_accessor(*args)
args.each do |arg|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{arg}
+ return if properties.blank?
+
Gitlab::Utils.to_boolean(properties['#{arg}'])
end
@@ -315,18 +324,31 @@ class Integration < ApplicationRecord
def self.build_from_integration(integration, project_id: nil, group_id: nil)
new_integration = integration.dup
- if integration.supports_data_fields?
- data_fields = integration.data_fields.dup
- data_fields.integration = new_integration
- end
-
new_integration.instance = false
new_integration.project_id = project_id
new_integration.group_id = group_id
- new_integration.inherit_from_id = integration.id if integration.instance_level? || integration.group_level?
+ new_integration.inherit_from_id = integration.id if integration.inheritable?
new_integration
end
+ # Duplicating an integration also duplicates the data fields. Duped records have different ciphertexts.
+ override :dup
+ def dup
+ new_integration = super
+ new_integration.assign_attributes(reencrypt_properties)
+
+ if supports_data_fields?
+ fields = data_fields.dup
+ fields.integration = new_integration
+ end
+
+ new_integration
+ end
+
+ def inheritable?
+ instance_level? || group_level?
+ end
+
def self.instance_exists_for?(type)
exists?(instance: true, type: type)
end
@@ -350,16 +372,17 @@ class Integration < ApplicationRecord
end
private_class_method :instance_level_integration
- def self.create_from_active_default_integrations(scope, association)
- group_ids = sorted_ancestors(scope).select(:id)
+ # Returns the number of successfully saved integrations
+ # Duplicate integrations are excluded from this count by their validations.
+ def self.create_from_active_default_integrations(owner, association)
+ group_ids = sorted_ancestors(owner).select(:id)
array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]'
+ order = Arel.sql("type_new ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")
- from_union([
- active.where(instance: true),
- active.where(group_id: group_ids, inherit_from_id: nil)
- ]).order(Arel.sql("type_new ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")).group_by(&:type).each do |type, records|
- build_from_integration(records.first, association => scope.id).save
- end
+ from_union([active.where(instance: true), active.where(group_id: group_ids, inherit_from_id: nil)])
+ .order(order)
+ .group_by(&:type)
+ .count { |type, parents| build_from_integration(parents.first, association => owner.id).save }
end
def self.inherited_descendants_from_self_or_ancestors_from(integration)
@@ -398,13 +421,7 @@ class Integration < ApplicationRecord
end
def initialize_properties
- self.properties = {} if has_attribute?(:properties) && properties.nil?
- end
-
- def copy_properties_to_encrypted_properties
- self.encrypted_properties_tmp = properties
- rescue ActiveModel::MissingAttributeError
- # ignore - in a record built from using a restricted select list
+ self.properties = {} if has_attribute?(:encrypted_properties) && encrypted_properties.nil?
end
def title
@@ -428,7 +445,9 @@ class Integration < ApplicationRecord
[]
end
- def password_fields
+ # TODO: Once all integrations use `Integrations::Field` we can
+ # use `#secret?` here.
+ def secret_fields
fields.select { |f| f[:type] == 'password' }.pluck(:name)
end
@@ -439,21 +458,26 @@ class Integration < ApplicationRecord
%w[active]
end
+ # properties is always nil - ignore it.
+ override :attributes
+ def attributes
+ super.except('properties')
+ end
+
# return a hash of columns => values suitable for passing to insert_all
def to_integration_hash
column = self.class.attribute_aliases.fetch('type', 'type')
- copy_properties_to_encrypted_properties
- as_json(except: %w[id instance project_id group_id encrypted_properties_tmp])
+ as_json(except: %w[id instance project_id group_id])
.merge(column => type)
.merge(reencrypt_properties)
end
def reencrypt_properties
unless properties.nil? || properties.empty?
- alg = self.class.encrypted_attributes[:encrypted_properties_tmp][:algorithm]
+ alg = self.class.encrypted_attributes[:properties][:algorithm]
iv = generate_iv(alg)
- ep = self.class.encrypt(:encrypted_properties_tmp, properties, { iv: iv })
+ ep = self.class.encrypt(:properties, properties, { iv: iv })
end
{ 'encrypted_properties' => ep, 'encrypted_properties_iv' => iv }