summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/models/concerns
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
downloadgitlab-ce-8c890596f5d0792c467fe12805ab1b39f93bf140.tar.gz
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/analytics/cycle_analytics/stage.rb18
-rw-r--r--app/models/concerns/any_field_validation.rb25
-rw-r--r--app/models/concerns/cache_markdown_field.rb25
-rw-r--r--app/models/concerns/cascading_namespace_setting_attribute.rb2
-rw-r--r--app/models/concerns/ci/has_status.rb1
-rw-r--r--app/models/concerns/ci/metadatable.rb10
-rw-r--r--app/models/concerns/ci/namespaced_model_name.rb13
-rw-r--r--app/models/concerns/counter_attribute.rb3
-rw-r--r--app/models/concerns/each_batch.rb6
-rw-r--r--app/models/concerns/enums/ci/pipeline.rb8
-rw-r--r--app/models/concerns/expirable.rb3
-rw-r--r--app/models/concerns/has_integrations.rb12
-rw-r--r--app/models/concerns/incident_management/escalatable.rb104
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/concerns/limitable.rb2
-rw-r--r--app/models/concerns/mentionable.rb19
-rw-r--r--app/models/concerns/packages/debian/distribution.rb19
-rw-r--r--app/models/concerns/project_features_compatibility.rb7
-rw-r--r--app/models/concerns/restricted_signup.rb52
-rw-r--r--app/models/concerns/select_for_project_authorization.rb2
-rw-r--r--app/models/concerns/sha256_attribute.rb2
-rw-r--r--app/models/concerns/sha_attribute.rb2
-rw-r--r--app/models/concerns/spammable.rb2
-rw-r--r--app/models/concerns/strip_attribute.rb8
-rw-r--r--app/models/concerns/time_trackable.rb8
-rw-r--r--app/models/concerns/timebox.rb2
-rw-r--r--app/models/concerns/vulnerability_finding_helpers.rb33
-rw-r--r--app/models/concerns/vulnerability_finding_signature_helpers.rb28
-rw-r--r--app/models/concerns/x509_serial_number_attribute.rb2
29 files changed, 343 insertions, 79 deletions
diff --git a/app/models/concerns/analytics/cycle_analytics/stage.rb b/app/models/concerns/analytics/cycle_analytics/stage.rb
index 2a0274f5706..7bb6004ca83 100644
--- a/app/models/concerns/analytics/cycle_analytics/stage.rb
+++ b/app/models/concerns/analytics/cycle_analytics/stage.rb
@@ -10,6 +10,7 @@ module Analytics
included do
belongs_to :start_event_label, class_name: 'GroupLabel', optional: true
belongs_to :end_event_label, class_name: 'GroupLabel', optional: true
+ belongs_to :stage_event_hash, class_name: 'Analytics::CycleAnalytics::StageEventHash', foreign_key: :stage_event_hash_id, optional: true
validates :name, presence: true
validates :name, exclusion: { in: Gitlab::Analytics::CycleAnalytics::DefaultStages.names }, if: :custom?
@@ -28,6 +29,9 @@ module Analytics
scope :ordered, -> { order(:relative_position, :id) }
scope :for_list, -> { includes(:start_event_label, :end_event_label).ordered }
scope :by_value_stream, -> (value_stream) { where(value_stream_id: value_stream.id) }
+
+ before_save :ensure_stage_event_hash_id
+ after_commit :cleanup_old_stage_event_hash
end
def parent=(_)
@@ -133,6 +137,20 @@ module Analytics
.id_in(label_id)
.exists?
end
+
+ def ensure_stage_event_hash_id
+ previous_stage_event_hash = stage_event_hash&.hash_sha256
+
+ if previous_stage_event_hash.blank? || events_hash_code != previous_stage_event_hash
+ self.stage_event_hash_id = Analytics::CycleAnalytics::StageEventHash.record_id_by_hash_sha256(events_hash_code)
+ end
+ end
+
+ def cleanup_old_stage_event_hash
+ if stage_event_hash_id_previously_changed? && stage_event_hash_id_previously_was
+ Analytics::CycleAnalytics::StageEventHash.cleanup_if_unused(stage_event_hash_id_previously_was)
+ end
+ end
end
end
end
diff --git a/app/models/concerns/any_field_validation.rb b/app/models/concerns/any_field_validation.rb
deleted file mode 100644
index 987c4e7800e..00000000000
--- a/app/models/concerns/any_field_validation.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-# This module enables a record to be valid if any field is present
-#
-# Overwrite one_of_required_fields to set one of which fields must be present
-module AnyFieldValidation
- extend ActiveSupport::Concern
-
- included do
- validate :any_field_present
- end
-
- private
-
- def any_field_present
- return unless one_of_required_fields.all? { |field| self[field].blank? }
-
- errors.add(:base, _("At least one field of %{one_of_required_fields} must be present") %
- { one_of_required_fields: one_of_required_fields })
- end
-
- def one_of_required_fields
- raise NotImplementedError
- end
-end
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 79b622c8dad..44d9beff27e 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -160,6 +160,8 @@ module CacheMarkdownField
# We can only store mentions if the mentionable is a database object
return unless self.is_a?(ApplicationRecord)
+ return store_mentions_without_subtransaction! if Feature.enabled?(:store_mentions_without_subtransaction, default_enabled: :yaml)
+
refs = all_references(self.author)
references = {}
@@ -190,6 +192,29 @@ module CacheMarkdownField
true
end
+ def store_mentions_without_subtransaction!
+ identifier = user_mention_identifier
+
+ # this may happen due to notes polymorphism, so noteable_id may point to a record
+ # that no longer exists as we cannot have FK on noteable_id
+ return if identifier.blank?
+
+ refs = all_references(self.author)
+
+ references = {}
+ references[:mentioned_users_ids] = refs.mentioned_user_ids.presence
+ references[:mentioned_groups_ids] = refs.mentioned_group_ids.presence
+ references[:mentioned_projects_ids] = refs.mentioned_project_ids.presence
+
+ if references.compact.any?
+ user_mention_class.upsert(references.merge(identifier), unique_by: identifier.compact.keys)
+ else
+ user_mention_class.delete_by(identifier)
+ end
+
+ true
+ end
+
def mentionable_attributes_changed?(changes = saved_changes)
return false unless is_a?(Mentionable)
diff --git a/app/models/concerns/cascading_namespace_setting_attribute.rb b/app/models/concerns/cascading_namespace_setting_attribute.rb
index 5d24e15d518..e58e5ddc966 100644
--- a/app/models/concerns/cascading_namespace_setting_attribute.rb
+++ b/app/models/concerns/cascading_namespace_setting_attribute.rb
@@ -127,7 +127,7 @@ module CascadingNamespaceSettingAttribute
end
def alias_boolean(attribute)
- return unless Gitlab::Database.exists? && type_for_attribute(attribute).type == :boolean
+ return unless Gitlab::Database.main.exists? && type_for_attribute(attribute).type == :boolean
alias_method :"#{attribute}?", attribute
end
diff --git a/app/models/concerns/ci/has_status.rb b/app/models/concerns/ci/has_status.rb
index f3c254053b5..c1299e3d468 100644
--- a/app/models/concerns/ci/has_status.rb
+++ b/app/models/concerns/ci/has_status.rb
@@ -93,6 +93,7 @@ module Ci
scope :running_or_pending, -> { with_status(:running, :pending) }
scope :finished, -> { with_status(:success, :failed, :canceled) }
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
+ scope :complete, -> { with_status(completed_statuses) }
scope :incomplete, -> { without_statuses(completed_statuses) }
scope :cancelable, -> do
diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb
index 114435d5a21..ec86746ae54 100644
--- a/app/models/concerns/ci/metadatable.rb
+++ b/app/models/concerns/ci/metadatable.rb
@@ -76,14 +76,8 @@ module Ci
end
def write_metadata_attribute(legacy_key, metadata_key, value)
- # save to metadata or this model depending on the state of feature flag
- if Feature.enabled?(:ci_build_metadata_config, project, default_enabled: :yaml)
- ensure_metadata.write_attribute(metadata_key, value)
- write_attribute(legacy_key, nil)
- else
- write_attribute(legacy_key, value)
- metadata&.write_attribute(metadata_key, nil)
- end
+ ensure_metadata.write_attribute(metadata_key, value)
+ write_attribute(legacy_key, nil)
end
end
end
diff --git a/app/models/concerns/ci/namespaced_model_name.rb b/app/models/concerns/ci/namespaced_model_name.rb
new file mode 100644
index 00000000000..e941a3a7a0c
--- /dev/null
+++ b/app/models/concerns/ci/namespaced_model_name.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ module NamespacedModelName
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def model_name
+ @model_name ||= ActiveModel::Name.new(self, Ci)
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb
index 829b2a6ef21..4bfeba338d2 100644
--- a/app/models/concerns/counter_attribute.rb
+++ b/app/models/concerns/counter_attribute.rb
@@ -128,8 +128,7 @@ module CounterAttribute
end
def counter_attribute_enabled?(attribute)
- Feature.enabled?(:efficient_counter_attribute, project) &&
- self.class.counter_attributes.include?(attribute)
+ self.class.counter_attributes.include?(attribute)
end
private
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index a59f00d73ec..443e1ab53b4 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -91,7 +91,11 @@ module EachBatch
# Any ORDER BYs are useless for this relation and can lead to less
# efficient UPDATE queries, hence we get rid of it.
- yield relation.except(:order), index
+ relation = relation.except(:order)
+
+ # Using unscoped is necessary to prevent leaking the current scope used by
+ # ActiveRecord to chain `each_batch` method.
+ unscoped { yield relation, index }
break unless stop
end
diff --git a/app/models/concerns/enums/ci/pipeline.rb b/app/models/concerns/enums/ci/pipeline.rb
index c42b046592f..94d11c871ca 100644
--- a/app/models/concerns/enums/ci/pipeline.rb
+++ b/app/models/concerns/enums/ci/pipeline.rb
@@ -37,7 +37,9 @@ module Enums
merge_request_event: 10,
external_pull_request_event: 11,
parent_pipeline: 12,
- ondemand_dast_scan: 13
+ ondemand_dast_scan: 13,
+ ondemand_dast_validation: 14,
+ security_orchestration_policy: 15
}
end
@@ -48,8 +50,10 @@ module Enums
# parent pipeline. It's up to the parent to affect the ref CI status
# - when an ondemand_dast_scan pipeline runs it is for testing purpose and should
# not affect the ref CI status.
+ # - when an ondemand_dast_validation pipeline runs it is for validating a DAST site
+ # profile and should not affect the ref CI status.
def self.dangling_sources
- sources.slice(:webide, :parent_pipeline, :ondemand_dast_scan)
+ sources.slice(:webide, :parent_pipeline, :ondemand_dast_scan, :ondemand_dast_validation, :security_orchestration_policy)
end
# CI sources are those pipeline events that affect the CI status of the ref
diff --git a/app/models/concerns/expirable.rb b/app/models/concerns/expirable.rb
index 512822089ba..e029ada84f0 100644
--- a/app/models/concerns/expirable.rb
+++ b/app/models/concerns/expirable.rb
@@ -13,6 +13,9 @@ module Expirable
expires? && expires_at <= Time.current
end
+ # Used in subclasses that override expired?
+ alias_method :expired_original?, :expired?
+
def expires?
expires_at.present?
end
diff --git a/app/models/concerns/has_integrations.rb b/app/models/concerns/has_integrations.rb
index 25650ae56ad..76e03d68600 100644
--- a/app/models/concerns/has_integrations.rb
+++ b/app/models/concerns/has_integrations.rb
@@ -4,18 +4,6 @@ module HasIntegrations
extend ActiveSupport::Concern
class_methods do
- def with_custom_integration_for(integration, page = nil, per = nil)
- custom_integration_project_ids = Integration
- .select(:project_id)
- .where(type: integration.type)
- .where(inherit_from_id: nil)
- .where.not(project_id: nil)
- .page(page)
- .per(per)
-
- Project.where(id: custom_integration_project_ids)
- end
-
def without_integration(integration)
integrations = Integration
.select('1')
diff --git a/app/models/concerns/incident_management/escalatable.rb b/app/models/concerns/incident_management/escalatable.rb
new file mode 100644
index 00000000000..78dce63f59e
--- /dev/null
+++ b/app/models/concerns/incident_management/escalatable.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ # Shared functionality for a `#status` field, representing
+ # whether action is required. In EE, this corresponds
+ # to paging functionality with EscalationPolicies.
+ #
+ # This module is only responsible for setting the status and
+ # possible status-related timestamps (EX triggered_at/resolved_at)
+ # for the implementing class. The relationships between these
+ # values and other related timestamps/logic should be managed from
+ # the object class itself. (EX Alert#ended_at = Alert#resolved_at)
+ module Escalatable
+ extend ActiveSupport::Concern
+
+ STATUSES = {
+ triggered: 0,
+ acknowledged: 1,
+ resolved: 2,
+ ignored: 3
+ }.freeze
+
+ STATUS_DESCRIPTIONS = {
+ triggered: 'Investigation has not started',
+ acknowledged: 'Someone is actively investigating the problem',
+ resolved: 'The problem has been addressed',
+ ignored: 'No action will be taken'
+ }.freeze
+
+ included do
+ validates :status, presence: true
+
+ # Ascending sort order sorts statuses: Ignored > Resolved > Acknowledged > Triggered
+ # Descending sort order sorts statuses: Triggered > Acknowledged > Resolved > Ignored
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
+ scope :order_status, -> (sort_order) { order(status: sort_order == :asc ? :desc : :asc) }
+
+ state_machine :status, initial: :triggered do
+ state :triggered, value: STATUSES[:triggered]
+
+ state :acknowledged, value: STATUSES[:acknowledged]
+
+ state :resolved, value: STATUSES[:resolved] do
+ validates :resolved_at, presence: true
+ end
+
+ state :ignored, value: STATUSES[:ignored]
+
+ state :triggered, :acknowledged, :ignored do
+ validates :resolved_at, absence: true
+ end
+
+ event :trigger do
+ transition any => :triggered
+ end
+
+ event :acknowledge do
+ transition any => :acknowledged
+ end
+
+ event :resolve do
+ transition any => :resolved
+ end
+
+ event :ignore do
+ transition any => :ignored
+ end
+
+ before_transition to: [:triggered, :acknowledged, :ignored] do |escalatable, _transition|
+ escalatable.resolved_at = nil
+ end
+
+ before_transition to: :resolved do |escalatable, transition|
+ resolved_at = transition.args.first
+ escalatable.resolved_at = resolved_at || Time.current
+ end
+ end
+
+ class << self
+ def status_value(name)
+ state_machine_statuses[name]
+ end
+
+ def status_name(raw_status)
+ state_machine_statuses.key(raw_status)
+ end
+
+ def status_names
+ @status_names ||= state_machine_statuses.keys
+ end
+
+ private
+
+ def state_machine_statuses
+ @state_machine_statuses ||= state_machines[:status].states.to_h { |s| [s.name, s.value] }
+ end
+ end
+
+ def status_event_for(status)
+ self.class.state_machines[:status].events.transitions_for(self, to: status.to_s.to_sym).first&.event
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index d5e2e63402f..8d0f8b01d64 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -152,7 +152,7 @@ module Issuable
participant :notes_with_associations
participant :assignees
- strip_attributes :title
+ strip_attributes! :title
class << self
def labels_hash
@@ -374,6 +374,8 @@ module Issuable
grouping_columns << milestone_table[:due_date]
elsif %w(merged_at_desc merged_at_asc).include?(sort)
grouping_columns << MergeRequest::Metrics.arel_table[:merged_at]
+ elsif %w(closed_at_desc closed_at_asc).include?(sort)
+ grouping_columns << MergeRequest::Metrics.arel_table[:closed_at]
end
grouping_columns
diff --git a/app/models/concerns/limitable.rb b/app/models/concerns/limitable.rb
index 41efea65c5a..fab1aa21634 100644
--- a/app/models/concerns/limitable.rb
+++ b/app/models/concerns/limitable.rb
@@ -9,6 +9,7 @@ module Limitable
class_attribute :limit_relation
class_attribute :limit_name
class_attribute :limit_feature_flag
+ class_attribute :limit_feature_flag_for_override # Allows selectively disabling by actor (as per https://docs.gitlab.com/ee/development/feature_flags/#selectively-disable-by-actor)
self.limit_name = self.name.demodulize.tableize
validate :validate_plan_limit_not_exceeded, on: :create
@@ -28,6 +29,7 @@ module Limitable
scope_relation = self.public_send(limit_scope) # rubocop:disable GitlabSecurity/PublicSend
return unless scope_relation
return if limit_feature_flag && ::Feature.disabled?(limit_feature_flag, scope_relation, default_enabled: :yaml)
+ return if limit_feature_flag_for_override && ::Feature.enabled?(limit_feature_flag_for_override, scope_relation, default_enabled: :yaml)
relation = limit_relation ? self.public_send(limit_relation) : self.class.where(limit_scope => scope_relation) # rubocop:disable GitlabSecurity/PublicSend
limits = scope_relation.actual_limits
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index f1baa923ec5..4df9e32d8ec 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -161,6 +161,21 @@ module Mentionable
create_cross_references!(author)
end
+ def user_mention_class
+ user_mention_association.klass
+ end
+
+ # Identifier for the user mention that is parsed from model description rather then its related notes.
+ # Models that have a description attribute like Issue, MergeRequest, Epic, Snippet may have such a user mention.
+ # Other mentionable models like DesignManagement::Design, will never have such record as those do not have
+ # a description attribute.
+ def user_mention_identifier
+ {
+ user_mention_association.foreign_key => id,
+ note_id: nil
+ }
+ end
+
private
def extracted_mentionables(refs)
@@ -199,6 +214,10 @@ module Mentionable
{}
end
+ def user_mention_association
+ association(:user_mentions).reflection
+ end
+
# User mention that is parsed from model description rather then its related notes.
# Models that have a description attribute like Issue, MergeRequest, Epic, Snippet may have such a user mention.
# Other mentionable models like Commit, DesignManagement::Design, will never have such record as those do not have
diff --git a/app/models/concerns/packages/debian/distribution.rb b/app/models/concerns/packages/debian/distribution.rb
index 159f0044c82..196bec04be6 100644
--- a/app/models/concerns/packages/debian/distribution.rb
+++ b/app/models/concerns/packages/debian/distribution.rb
@@ -77,23 +77,16 @@ module Packages
validates container_type, presence: true
validates :file_store, presence: true
-
- validates :file_signature, absence: true
- validates :signing_keys, absence: true
+ validates :signed_file_store, presence: true
scope :with_container, ->(subject) { where(container_type => subject) }
scope :with_codename, ->(codename) { where(codename: codename) }
scope :with_suite, ->(suite) { where(suite: suite) }
scope :with_codename_or_suite, ->(codename_or_suite) { with_codename(codename_or_suite).or(with_suite(codename_or_suite)) }
- attr_encrypted :signing_keys,
- mode: :per_attribute_iv,
- key: Settings.attr_encrypted_db_key_base_32,
- algorithm: 'aes-256-gcm',
- encode: false,
- encode_iv: false
-
mount_file_store_uploader Packages::Debian::DistributionReleaseFileUploader
+ mount_uploader :signed_file, Packages::Debian::DistributionReleaseFileUploader
+ after_save :update_signed_file_store, if: :saved_change_to_signed_file?
def component_names
components.pluck(:name).sort
@@ -131,6 +124,12 @@ module Packages
self.class.with_container(container).with_codename(suite).exists?
end
+
+ def update_signed_file_store
+ # The signed_file.object_store is set during `uploader.store!`
+ # which happens after object is inserted/updated
+ self.update_column(:signed_file_store, signed_file.object_store)
+ end
end
end
end
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 484c91e0833..0cab874a240 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -90,6 +90,13 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:container_registry_access_level, value)
end
+ # TODO: Remove this method after we drop support for project create/edit APIs to set the
+ # container_registry_enabled attribute. They can instead set the container_registry_access_level
+ # attribute.
+ def container_registry_enabled=(value)
+ write_feature_attribute_boolean(:container_registry_access_level, value)
+ end
+
private
def write_feature_attribute_boolean(field, value)
diff --git a/app/models/concerns/restricted_signup.rb b/app/models/concerns/restricted_signup.rb
new file mode 100644
index 00000000000..587f8c35ff7
--- /dev/null
+++ b/app/models/concerns/restricted_signup.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+module RestrictedSignup
+ extend ActiveSupport::Concern
+
+ private
+
+ def validate_admin_signup_restrictions(email)
+ return if allowed_domain?(email)
+
+ if allowlist_present?
+ return _('domain is not authorized for sign-up.')
+ elsif denied_domain?(email)
+ return _('is not from an allowed domain.')
+ elsif restricted_email?(email)
+ return _('is not allowed. Try again with a different email address, or contact your GitLab admin.')
+ end
+
+ nil
+ end
+
+ def denied_domain?(email)
+ return false unless Gitlab::CurrentSettings.domain_denylist_enabled?
+
+ denied_domains = Gitlab::CurrentSettings.domain_denylist
+ denied_domains.present? && domain_matches?(denied_domains, email)
+ end
+
+ def allowlist_present?
+ Gitlab::CurrentSettings.domain_allowlist.present?
+ end
+
+ def allowed_domain?(email)
+ allowed_domains = Gitlab::CurrentSettings.domain_allowlist
+ allowlist_present? && domain_matches?(allowed_domains, email)
+ end
+
+ def restricted_email?(email)
+ return false unless Gitlab::CurrentSettings.email_restrictions_enabled?
+
+ restrictions = Gitlab::CurrentSettings.email_restrictions
+ restrictions.present? && Gitlab::UntrustedRegexp.new(restrictions).match?(email)
+ end
+
+ def domain_matches?(email_domains, email)
+ signup_domain = Mail::Address.new(email).domain
+ email_domains.any? do |domain|
+ escaped = Regexp.escape(domain).gsub('\*', '.*?')
+ regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
+ signup_domain =~ regexp
+ end
+ end
+end
diff --git a/app/models/concerns/select_for_project_authorization.rb b/app/models/concerns/select_for_project_authorization.rb
index 4fae36f7b8d..49342e30db6 100644
--- a/app/models/concerns/select_for_project_authorization.rb
+++ b/app/models/concerns/select_for_project_authorization.rb
@@ -5,7 +5,7 @@ module SelectForProjectAuthorization
class_methods do
def select_for_project_authorization
- select("projects.id AS project_id, members.access_level")
+ select("projects.id AS project_id", "members.access_level")
end
def select_as_maintainer_for_project_authorization
diff --git a/app/models/concerns/sha256_attribute.rb b/app/models/concerns/sha256_attribute.rb
index 4921f7f1a7e..17fda6c806c 100644
--- a/app/models/concerns/sha256_attribute.rb
+++ b/app/models/concerns/sha256_attribute.rb
@@ -39,7 +39,7 @@ module Sha256Attribute
end
def database_exists?
- Gitlab::Database.exists?
+ Gitlab::Database.main.exists?
end
end
end
diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb
index f6f5dbce4b6..27277bc5296 100644
--- a/app/models/concerns/sha_attribute.rb
+++ b/app/models/concerns/sha_attribute.rb
@@ -32,7 +32,7 @@ module ShaAttribute
end
def database_exists?
- Gitlab::Database.exists?
+ Gitlab::Database.main.exists?
end
end
end
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 2daea388939..4901cd832ff 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -111,7 +111,7 @@ module Spammable
end
# Override in Spammable if further checks are necessary
- def check_for_spam?
+ def check_for_spam?(user:)
true
end
diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb
index 8f6a6244dd3..1c433a3275e 100644
--- a/app/models/concerns/strip_attribute.rb
+++ b/app/models/concerns/strip_attribute.rb
@@ -7,7 +7,7 @@
# Usage:
#
# class Milestone < ApplicationRecord
-# strip_attributes :title
+# strip_attributes! :title
# end
#
#
@@ -15,7 +15,7 @@ module StripAttribute
extend ActiveSupport::Concern
class_methods do
- def strip_attributes(*attrs)
+ def strip_attributes!(*attrs)
strip_attrs.concat(attrs)
end
@@ -25,10 +25,10 @@ module StripAttribute
end
included do
- before_validation :strip_attributes
+ before_validation :strip_attributes!
end
- def strip_attributes
+ def strip_attributes!
self.class.strip_attrs.each do |attr|
self[attr].strip! if self[attr] && self[attr].respond_to?(:strip!)
end
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index 89b42eec727..54fe9eac2bc 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -11,7 +11,7 @@ module TimeTrackable
extend ActiveSupport::Concern
included do
- attr_reader :time_spent, :time_spent_user, :spent_at
+ attr_reader :time_spent, :time_spent_user, :spent_at, :summary
alias_method :time_spent?, :time_spent
@@ -20,7 +20,7 @@ module TimeTrackable
validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false
validate :check_negative_time_spent
- has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :timelogs, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
@@ -29,6 +29,7 @@ module TimeTrackable
@time_spent_note_id = options[:note_id]
@time_spent_user = User.find(options[:user_id])
@spent_at = options[:spent_at]
+ @summary = options[:summary]
@original_total_time_spent = nil
return if @time_spent == 0
@@ -78,7 +79,8 @@ module TimeTrackable
time_spent: time_spent,
note_id: @time_spent_note_id,
user: @time_spent_user,
- spent_at: @spent_at
+ spent_at: @spent_at,
+ summary: @summary
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/app/models/concerns/timebox.rb b/app/models/concerns/timebox.rb
index 8dc58f8dca1..79cbe225e5a 100644
--- a/app/models/concerns/timebox.rb
+++ b/app/models/concerns/timebox.rb
@@ -106,7 +106,7 @@ module Timebox
.where('due_date is NULL or due_date >= ?', start_date)
end
- strip_attributes :title
+ strip_attributes! :title
alias_attribute :name, :title
end
diff --git a/app/models/concerns/vulnerability_finding_helpers.rb b/app/models/concerns/vulnerability_finding_helpers.rb
index f0e5e010e70..a656856487d 100644
--- a/app/models/concerns/vulnerability_finding_helpers.rb
+++ b/app/models/concerns/vulnerability_finding_helpers.rb
@@ -2,6 +2,35 @@
module VulnerabilityFindingHelpers
extend ActiveSupport::Concern
-end
+ def matches_signatures(other_signatures, other_uuid)
+ other_signature_types = other_signatures.index_by(&:algorithm_type)
+
+ # highest first
+ match_result = nil
+ signatures.sort_by(&:priority).reverse_each do |signature|
+ matching_other_signature = other_signature_types[signature.algorithm_type]
+ next if matching_other_signature.nil?
+
+ match_result = matching_other_signature == signature
+ break
+ end
-VulnerabilityFindingHelpers.prepend_mod_with('VulnerabilityFindingHelpers')
+ if match_result.nil?
+ [uuid, *signature_uuids].include?(other_uuid)
+ else
+ match_result
+ end
+ end
+
+ def signature_uuids
+ signatures.map do |signature|
+ hex_sha = signature.signature_hex
+ ::Security::VulnerabilityUUID.generate(
+ report_type: report_type,
+ location_fingerprint: hex_sha,
+ primary_identifier_fingerprint: primary_identifier&.fingerprint,
+ project_id: project_id
+ )
+ end
+ end
+end
diff --git a/app/models/concerns/vulnerability_finding_signature_helpers.rb b/app/models/concerns/vulnerability_finding_signature_helpers.rb
index f98c1e93aaf..71a12b4077b 100644
--- a/app/models/concerns/vulnerability_finding_signature_helpers.rb
+++ b/app/models/concerns/vulnerability_finding_signature_helpers.rb
@@ -2,6 +2,30 @@
module VulnerabilityFindingSignatureHelpers
extend ActiveSupport::Concern
-end
+ # If the location object describes a physical location within a file
+ # (filename + line numbers), the 'location' algorithm_type should be used
+ # If the location object describes arbitrary data, then the 'hash'
+ # algorithm_type should be used.
+
+ ALGORITHM_TYPES = { hash: 1, location: 2, scope_offset: 3 }.with_indifferent_access.freeze
+
+ class_methods do
+ def priority(algorithm_type)
+ raise ArgumentError, "No priority for #{algorithm_type.inspect}" unless ALGORITHM_TYPES.key?(algorithm_type)
+
+ ALGORITHM_TYPES[algorithm_type]
+ end
-VulnerabilityFindingSignatureHelpers.prepend_mod_with('VulnerabilityFindingSignatureHelpers')
+ def algorithm_types
+ ALGORITHM_TYPES
+ end
+ end
+
+ def priority
+ self.class.priority(algorithm_type)
+ end
+
+ def algorithm_types
+ self.class.algorithm_types
+ end
+end
diff --git a/app/models/concerns/x509_serial_number_attribute.rb b/app/models/concerns/x509_serial_number_attribute.rb
index dbba80eff53..dfb1e151b41 100644
--- a/app/models/concerns/x509_serial_number_attribute.rb
+++ b/app/models/concerns/x509_serial_number_attribute.rb
@@ -39,7 +39,7 @@ module X509SerialNumberAttribute
end
def database_exists?
- Gitlab::Database.exists?
+ Gitlab::Database.main.exists?
end
end
end