summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/bulk_insert_safe.rb6
-rw-r--r--app/models/concerns/cache_markdown_field.rb9
-rw-r--r--app/models/concerns/cron_schedulable.rb21
-rw-r--r--app/models/concerns/deployment_platform.rb8
-rw-r--r--app/models/concerns/enum_with_nil.rb8
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb1
-rw-r--r--app/models/concerns/enums/vulnerability.rb12
-rw-r--r--app/models/concerns/has_timelogs_report.rb20
-rw-r--r--app/models/concerns/has_user_type.rb5
-rw-r--r--app/models/concerns/integrations/base_data_fields.rb (renamed from app/models/concerns/services/data_fields.rb)8
-rw-r--r--app/models/concerns/integrations/has_data_fields.rb61
-rw-r--r--app/models/concerns/integrations/slack_mattermost_notifier.rb24
-rw-r--r--app/models/concerns/issuable.rb23
-rw-r--r--app/models/concerns/issue_available_features.rb3
-rw-r--r--app/models/concerns/limitable.rb3
-rw-r--r--app/models/concerns/mentionable/reference_regexes.rb2
-rw-r--r--app/models/concerns/notification_branch_selection.rb2
-rw-r--r--app/models/concerns/packages/debian/component_file.rb4
-rw-r--r--app/models/concerns/packages/debian/distribution.rb12
-rw-r--r--app/models/concerns/packages/debian/distribution_key.rb45
-rw-r--r--app/models/concerns/prometheus_adapter.rb2
-rw-r--r--app/models/concerns/service_push_data_validations.rb4
-rw-r--r--app/models/concerns/taggable_queries.rb16
-rw-r--r--app/models/concerns/time_trackable.rb18
-rw-r--r--app/models/concerns/timebox.rb13
-rw-r--r--app/models/concerns/token_authenticatable_strategies/encryption_helper.rb12
26 files changed, 251 insertions, 91 deletions
diff --git a/app/models/concerns/bulk_insert_safe.rb b/app/models/concerns/bulk_insert_safe.rb
index 3748e77e933..908f0b6a7e2 100644
--- a/app/models/concerns/bulk_insert_safe.rb
+++ b/app/models/concerns/bulk_insert_safe.rb
@@ -141,6 +141,12 @@ module BulkInsertSafe
raise ArgumentError, "returns needs to be :ids or nil"
end
+ # Handle insertions for tables with a composite primary key
+ primary_keys = connection.schema_cache.primary_keys(table_name)
+ if unique_by.blank? && primary_key != primary_keys
+ unique_by = primary_keys
+ end
+
transaction do
items.each_slice(batch_size).flat_map do |item_batch|
attributes = _bulk_insert_item_attributes(
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index a5cf947ba07..101bff32dfe 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -27,7 +27,7 @@ module CacheMarkdownField
# Returns the default Banzai render context for the cached markdown field.
def banzai_render_context(field)
raise ArgumentError, "Unknown field: #{field.inspect}" unless
- cached_markdown_fields.markdown_fields.include?(field)
+ cached_markdown_fields.key?(field)
# Always include a project key, or Banzai complains
project = self.project if self.respond_to?(:project)
@@ -100,7 +100,7 @@ module CacheMarkdownField
def cached_html_for(markdown_field)
raise ArgumentError, "Unknown field: #{markdown_field}" unless
- cached_markdown_fields.markdown_fields.include?(markdown_field)
+ cached_markdown_fields.key?(markdown_field)
__send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -108,7 +108,7 @@ module CacheMarkdownField
# Updates the markdown cache if necessary, then returns the field
# Unlike `cached_html_for` it returns `nil` if the field does not exist
def updated_cached_html_for(markdown_field)
- return unless cached_markdown_fields.markdown_fields.include?(markdown_field)
+ return unless cached_markdown_fields.key?(markdown_field)
if attribute_invalidated?(cached_markdown_fields.html_field(markdown_field))
# Invalidated due to Markdown content change
@@ -157,6 +157,9 @@ module CacheMarkdownField
end
def store_mentions!
+ # We can only store mentions if the mentionable is a database object
+ return unless self.is_a?(ApplicationRecord)
+
refs = all_references(self.author)
references = {}
diff --git a/app/models/concerns/cron_schedulable.rb b/app/models/concerns/cron_schedulable.rb
index beb3a09c119..48605ecc3d7 100644
--- a/app/models/concerns/cron_schedulable.rb
+++ b/app/models/concerns/cron_schedulable.rb
@@ -4,23 +4,28 @@ module CronSchedulable
extend ActiveSupport::Concern
include Schedulable
+ def set_next_run_at
+ self.next_run_at = calculate_next_run_at
+ end
+
+ private
+
##
# The `next_run_at` column is set to the actual execution date of worker that
# triggers the schedule. This way, a schedule like `*/1 * * * *` won't be triggered
# in a short interval when the worker runs irregularly by Sidekiq Memory Killer.
- def set_next_run_at
+ def calculate_next_run_at
now = Time.zone.now
+
ideal_next_run = ideal_next_run_from(now)
- self.next_run_at = if ideal_next_run == cron_worker_next_run_from(now)
- ideal_next_run
- else
- cron_worker_next_run_from(ideal_next_run)
- end
+ if ideal_next_run == cron_worker_next_run_from(now)
+ ideal_next_run
+ else
+ cron_worker_next_run_from(ideal_next_run)
+ end
end
- private
-
def ideal_next_run_from(start_time)
next_time_from(start_time, cron, cron_timezone)
end
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 02f7711e927..b6245e29746 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -10,10 +10,6 @@ module DeploymentPlatform
private
- def cluster_management_project_enabled?
- Feature.enabled?(:cluster_management_project, self, default_enabled: true)
- end
-
def find_deployment_platform(environment)
find_platform_kubernetes_with_cte(environment) ||
find_instance_cluster_platform_kubernetes(environment: environment)
@@ -21,13 +17,13 @@ module DeploymentPlatform
def find_platform_kubernetes_with_cte(environment)
if environment
- ::Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?)
+ ::Clusters::ClustersHierarchy.new(self)
.base_and_ancestors
.enabled
.on_environment(environment, relevant_only: true)
.first&.platform_kubernetes
else
- Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors
+ Clusters::ClustersHierarchy.new(self).base_and_ancestors
.enabled.default_environment
.first&.platform_kubernetes
end
diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb
index 6d0a21cf070..c66942025d7 100644
--- a/app/models/concerns/enum_with_nil.rb
+++ b/app/models/concerns/enum_with_nil.rb
@@ -11,14 +11,6 @@ module EnumWithNil
# override auto-defined methods only for the
# key which uses nil value
definitions.each do |name, values|
- next unless key_with_nil = values.key(nil)
-
- # E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
- # this overrides auto-generated method `unknown_failure?`
- define_method("#{key_with_nil}?") do
- self[name].nil?
- end
-
# E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
# this overrides auto-generated method `failure_reason`
define_method(name) do
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index 2e368b12cb7..72788d15c0a 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -24,6 +24,7 @@ module Enums
project_deleted: 15,
ci_quota_exceeded: 16,
pipeline_loop_detected: 17,
+ no_matching_runner: 18, # not used anymore, but cannot be deleted because of old data
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
diff --git a/app/models/concerns/enums/vulnerability.rb b/app/models/concerns/enums/vulnerability.rb
index 55360eb92e6..749d1ad65cd 100644
--- a/app/models/concerns/enums/vulnerability.rb
+++ b/app/models/concerns/enums/vulnerability.rb
@@ -29,6 +29,14 @@ module Enums
critical: 7
}.with_indifferent_access.freeze
+ DETECTION_METHODS = {
+ gitlab_security_report: 0,
+ external_security_report: 1,
+ bug_bounty: 2,
+ code_review: 3,
+ security_audit: 4
+ }.with_indifferent_access.freeze
+
def self.confidence_levels
CONFIDENCE_LEVELS
end
@@ -40,6 +48,10 @@ module Enums
def self.severity_levels
SEVERITY_LEVELS
end
+
+ def self.detection_methods
+ DETECTION_METHODS
+ end
end
end
diff --git a/app/models/concerns/has_timelogs_report.rb b/app/models/concerns/has_timelogs_report.rb
deleted file mode 100644
index 3af063438bf..00000000000
--- a/app/models/concerns/has_timelogs_report.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-module HasTimelogsReport
- extend ActiveSupport::Concern
- include Gitlab::Utils::StrongMemoize
-
- def timelogs(start_time, end_time)
- strong_memoize(:timelogs) { timelogs_for(start_time, end_time) }
- end
-
- def user_can_access_group_timelogs?(current_user)
- Ability.allowed?(current_user, :read_group_timelogs, self)
- end
-
- private
-
- def timelogs_for(start_time, end_time)
- Timelog.between_times(start_time, end_time).in_group(self)
- end
-end
diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb
index 468387115e5..4b4f9c0df84 100644
--- a/app/models/concerns/has_user_type.rb
+++ b/app/models/concerns/has_user_type.rb
@@ -12,10 +12,11 @@ module HasUserType
ghost: 5,
project_bot: 6,
migration_bot: 7,
- security_bot: 8
+ security_bot: 8,
+ automation_bot: 9
}.with_indifferent_access.freeze
- BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot].freeze
+ BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot].freeze
NON_INTERNAL_USER_TYPES = %w[human project_bot service_user].freeze
INTERNAL_USER_TYPES = (USER_TYPES.keys - NON_INTERNAL_USER_TYPES).freeze
diff --git a/app/models/concerns/services/data_fields.rb b/app/models/concerns/integrations/base_data_fields.rb
index fd56af449bc..3cedb90756f 100644
--- a/app/models/concerns/services/data_fields.rb
+++ b/app/models/concerns/integrations/base_data_fields.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
-module Services
- module DataFields
+module Integrations
+ module BaseDataFields
extend ActiveSupport::Concern
included do
- belongs_to :integration, inverse_of: self.name.underscore.to_sym, foreign_key: :service_id
+ # TODO: Once we rename the tables we can't rely on `table_name` anymore.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/331953
+ belongs_to :integration, inverse_of: self.table_name.to_sym, foreign_key: :service_id
delegate :activated?, to: :integration, allow_nil: true
diff --git a/app/models/concerns/integrations/has_data_fields.rb b/app/models/concerns/integrations/has_data_fields.rb
new file mode 100644
index 00000000000..e9aaaac8226
--- /dev/null
+++ b/app/models/concerns/integrations/has_data_fields.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Integrations
+ module HasDataFields
+ extend ActiveSupport::Concern
+
+ class_methods do
+ # Provide convenient accessor methods for data fields.
+ # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
+ def data_field(*args)
+ args.each do |arg|
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ unless method_defined?(arg)
+ def #{arg}
+ data_fields.send('#{arg}') || (properties && properties['#{arg}'])
+ end
+ end
+
+ def #{arg}=(value)
+ @old_data_fields ||= {}
+ @old_data_fields['#{arg}'] ||= #{arg} # set only on the first assignment, IOW we remember the original value only
+ data_fields.send('#{arg}=', value)
+ end
+
+ def #{arg}_touched?
+ @old_data_fields ||= {}
+ @old_data_fields.has_key?('#{arg}')
+ end
+
+ def #{arg}_changed?
+ #{arg}_touched? && @old_data_fields['#{arg}'] != #{arg}
+ end
+
+ def #{arg}_was
+ return unless #{arg}_touched?
+ return if data_fields.persisted? # arg_was does not work for attr_encrypted
+
+ legacy_properties_data['#{arg}']
+ end
+ RUBY
+ end
+ end
+ end
+
+ included do
+ has_one :issue_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::IssueTrackerData'
+ has_one :jira_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::JiraTrackerData'
+ has_one :open_project_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::OpenProjectTrackerData'
+
+ def data_fields
+ raise NotImplementedError
+ end
+
+ def data_fields_present?
+ data_fields.present?
+ rescue NotImplementedError
+ false
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/integrations/slack_mattermost_notifier.rb b/app/models/concerns/integrations/slack_mattermost_notifier.rb
new file mode 100644
index 00000000000..a919fc840fd
--- /dev/null
+++ b/app/models/concerns/integrations/slack_mattermost_notifier.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Integrations
+ module SlackMattermostNotifier
+ private
+
+ def notify(message, opts)
+ # See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
+ notifier = ::Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
+ notifier.ping(
+ message.pretext,
+ attachments: message.attachments,
+ fallback: message.fallback
+ )
+ end
+
+ class HTTPClient
+ def self.post(uri, params = {})
+ params.delete(:http_options) # these are internal to the client and we do not want them
+ Gitlab::HTTP.post(uri, body: params)
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index f5c70f10dc5..2d06247a486 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -101,20 +101,19 @@ module Issuable
scope :unassigned, -> do
where("NOT EXISTS (SELECT TRUE FROM #{to_ability_name}_assignees WHERE #{to_ability_name}_id = #{to_ability_name}s.id)")
end
- scope :assigned_to, ->(u) do
- assignees_table = Arel::Table.new("#{to_ability_name}_assignees")
- sql = assignees_table.project('true').where(assignees_table[:user_id].in(u.id)).where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
- where("EXISTS (#{sql.to_sql})")
- end
- # rubocop:enable GitlabSecurity/SqlInjection
+ scope :assigned_to, ->(users) do
+ assignees_class = self.reflect_on_association("#{to_ability_name}_assignees").klass
+ condition = assignees_class.where(user_id: users).where(Arel.sql("#{to_ability_name}_id = #{to_ability_name}s.id"))
+ where(condition.arel.exists)
+ end
scope :not_assigned_to, ->(users) do
- assignees_table = Arel::Table.new("#{to_ability_name}_assignees")
- sql = assignees_table.project('true')
- .where(assignees_table[:user_id].in(users))
- .where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
- where(sql.exists.not)
+ assignees_class = self.reflect_on_association("#{to_ability_name}_assignees").klass
+
+ condition = assignees_class.where(user_id: users).where(Arel.sql("#{to_ability_name}_id = #{to_ability_name}s.id"))
+ where(condition.arel.exists.not)
end
+ # rubocop:enable GitlabSecurity/SqlInjection
scope :without_particular_labels, ->(label_names) do
labels_table = Label.arel_table
@@ -469,9 +468,11 @@ module Issuable
if self.respond_to?(:total_time_spent)
old_total_time_spent = old_associations.fetch(:total_time_spent, total_time_spent)
+ old_time_change = old_associations.fetch(:time_change, time_change)
if old_total_time_spent != total_time_spent
changes[:total_time_spent] = [old_total_time_spent, total_time_spent]
+ changes[:time_change] = [old_time_change, time_change]
end
end
end
diff --git a/app/models/concerns/issue_available_features.rb b/app/models/concerns/issue_available_features.rb
index 28d12a033a6..933e8b5f687 100644
--- a/app/models/concerns/issue_available_features.rb
+++ b/app/models/concerns/issue_available_features.rb
@@ -11,7 +11,8 @@ module IssueAvailableFeatures
def available_features_for_issue_types
{
assignee: %w(issue incident),
- confidentiality: %(issue incident)
+ confidentiality: %w(issue incident),
+ time_tracking: %w(issue incident)
}.with_indifferent_access
end
end
diff --git a/app/models/concerns/limitable.rb b/app/models/concerns/limitable.rb
index 672bcdbbb1b..41efea65c5a 100644
--- a/app/models/concerns/limitable.rb
+++ b/app/models/concerns/limitable.rb
@@ -6,6 +6,7 @@ module Limitable
included do
class_attribute :limit_scope
+ class_attribute :limit_relation
class_attribute :limit_name
class_attribute :limit_feature_flag
self.limit_name = self.name.demodulize.tableize
@@ -28,7 +29,7 @@ module Limitable
return unless scope_relation
return if limit_feature_flag && ::Feature.disabled?(limit_feature_flag, scope_relation, default_enabled: :yaml)
- relation = self.class.where(limit_scope => scope_relation)
+ relation = limit_relation ? self.public_send(limit_relation) : self.class.where(limit_scope => scope_relation) # rubocop:disable GitlabSecurity/PublicSend
limits = scope_relation.actual_limits
check_plan_limit_not_exceeded(limits, relation)
diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb
index e33b6db0103..b05beb6c764 100644
--- a/app/models/concerns/mentionable/reference_regexes.rb
+++ b/app/models/concerns/mentionable/reference_regexes.rb
@@ -29,7 +29,7 @@ module Mentionable
def self.external_pattern
strong_memoize(:external_pattern) do
- issue_pattern = IssueTrackerService.reference_pattern
+ issue_pattern = Integrations::BaseIssueTracker.reference_pattern
link_patterns = URI::DEFAULT_PARSER.make_regexp(%w(http https))
reference_pattern(link_patterns, issue_pattern)
end
diff --git a/app/models/concerns/notification_branch_selection.rb b/app/models/concerns/notification_branch_selection.rb
index 2354335469a..18ec996c3df 100644
--- a/app/models/concerns/notification_branch_selection.rb
+++ b/app/models/concerns/notification_branch_selection.rb
@@ -2,7 +2,7 @@
# Concern handling functionality around deciding whether to send notification
# for activities on a specified branch or not. Will be included in
-# ChatNotificationService and PipelinesEmailService classes.
+# Integrations::BaseChatNotification and PipelinesEmailService classes.
module NotificationBranchSelection
extend ActiveSupport::Concern
diff --git a/app/models/concerns/packages/debian/component_file.rb b/app/models/concerns/packages/debian/component_file.rb
index c41635a0d16..9cf66c756a0 100644
--- a/app/models/concerns/packages/debian/component_file.rb
+++ b/app/models/concerns/packages/debian/component_file.rb
@@ -50,6 +50,8 @@ module Packages
scope :with_file_type, ->(file_type) { where(file_type: file_type) }
+ scope :with_architecture, ->(architecture) { where(architecture: architecture) }
+
scope :with_architecture_name, ->(architecture_name) do
left_outer_joins(:architecture)
.where("packages_debian_#{container_type}_architectures" => { name: architecture_name })
@@ -60,7 +62,7 @@ module Packages
scope :preload_distribution, -> { includes(component: :distribution) }
- scope :created_before, ->(reference) { where("#{table_name}.created_at < ?", reference) }
+ scope :updated_before, ->(reference) { where("#{table_name}.updated_at < ?", reference) }
mount_file_store_uploader Packages::Debian::ComponentFileUploader
diff --git a/app/models/concerns/packages/debian/distribution.rb b/app/models/concerns/packages/debian/distribution.rb
index 267c7a4d201..159f0044c82 100644
--- a/app/models/concerns/packages/debian/distribution.rb
+++ b/app/models/concerns/packages/debian/distribution.rb
@@ -18,6 +18,10 @@ module Packages
belongs_to container_type
belongs_to :creator, class_name: 'User'
+ has_one :key,
+ class_name: "Packages::Debian::#{container_type.capitalize}DistributionKey",
+ foreign_key: :distribution_id,
+ inverse_of: :distribution
# component_files must be destroyed by ruby code in order to properly remove carrierwave uploads
has_many :components,
class_name: "Packages::Debian::#{container_type.capitalize}Component",
@@ -91,6 +95,14 @@ module Packages
mount_file_store_uploader Packages::Debian::DistributionReleaseFileUploader
+ def component_names
+ components.pluck(:name).sort
+ end
+
+ def architecture_names
+ architectures.pluck(:name).sort
+ end
+
def needs_update?
!file.exists? || time_duration_expired?
end
diff --git a/app/models/concerns/packages/debian/distribution_key.rb b/app/models/concerns/packages/debian/distribution_key.rb
new file mode 100644
index 00000000000..7023e2dcd37
--- /dev/null
+++ b/app/models/concerns/packages/debian/distribution_key.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ module DistributionKey
+ extend ActiveSupport::Concern
+
+ included do
+ belongs_to :distribution, class_name: "Packages::Debian::#{container_type.capitalize}Distribution", inverse_of: :key
+ validates :distribution,
+ presence: true
+
+ validates :private_key, presence: true, length: { maximum: 512.kilobytes }
+ validates :passphrase, presence: true, length: { maximum: 255 }
+ validates :public_key, presence: true, length: { maximum: 512.kilobytes }
+ validates :fingerprint, presence: true, length: { maximum: 255 }
+
+ validate :private_key_armored, :public_key_armored
+
+ attr_encrypted :private_key,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_32,
+ algorithm: 'aes-256-gcm'
+ attr_encrypted :passphrase,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_32,
+ algorithm: 'aes-256-gcm'
+
+ private
+
+ def private_key_armored
+ if private_key.present? && !private_key.start_with?('-----BEGIN PGP PRIVATE KEY BLOCK-----')
+ errors.add(:private_key, 'must be ASCII armored')
+ end
+ end
+
+ def public_key_armored
+ if public_key.present? && !public_key.start_with?('-----BEGIN PGP PUBLIC KEY BLOCK-----')
+ errors.add(:public_key, 'must be ASCII armored')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index afebc426762..86280097d19 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -38,7 +38,7 @@ module PrometheusAdapter
# This is a heavy-weight check if a prometheus is properly configured and accessible from GitLab.
# This actually sends a request to an external service and often it could take a long time,
- # Please consider using `configured?` instead if the process is running on unicorn/puma threads.
+ # Please consider using `configured?` instead if the process is running on Puma threads.
def can_query?
prometheus_client.present?
end
diff --git a/app/models/concerns/service_push_data_validations.rb b/app/models/concerns/service_push_data_validations.rb
index defc5794142..451804a2c56 100644
--- a/app/models/concerns/service_push_data_validations.rb
+++ b/app/models/concerns/service_push_data_validations.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-# This concern is used by registerd services such as TeamCityService and
-# DroneCiService and add methods to perform validations on the received
+# This concern is used by registered integrations such as Integrations::TeamCity and
+# Integrations::DroneCi and adds methods to perform validations on the received
# data.
module ServicePushDataValidations
diff --git a/app/models/concerns/taggable_queries.rb b/app/models/concerns/taggable_queries.rb
new file mode 100644
index 00000000000..2897e5e6420
--- /dev/null
+++ b/app/models/concerns/taggable_queries.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module TaggableQueries
+ extend ActiveSupport::Concern
+
+ class_methods do
+ # context is a name `acts_as_taggable context`
+ def arel_tag_names_array(context = :tags)
+ ActsAsTaggableOn::Tagging
+ .joins(:tag)
+ .where("taggings.taggable_id=#{quoted_table_name}.id") # rubocop:disable GitlabSecurity/SqlInjection
+ .where(taggings: { context: context, taggable_type: polymorphic_name })
+ .select('COALESCE(array_agg(tags.name ORDER BY name), ARRAY[]::text[])')
+ end
+ end
+end
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index a1e7d06b1c1..89b42eec727 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -33,11 +33,11 @@ module TimeTrackable
return if @time_spent == 0
- if @time_spent == :reset
- reset_spent_time
- else
- add_or_subtract_spent_time
- end
+ @timelog = if @time_spent == :reset
+ reset_spent_time
+ else
+ add_or_subtract_spent_time
+ end
end
alias_method :spend_time=, :spend_time
# rubocop:enable Gitlab/ModuleWithInstanceVariables
@@ -50,6 +50,14 @@ module TimeTrackable
Gitlab::TimeTrackingFormatter.output(total_time_spent)
end
+ def time_change
+ @timelog&.time_spent.to_i # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+
+ def human_time_change
+ Gitlab::TimeTrackingFormatter.output(time_change)
+ end
+
def human_time_estimate
Gitlab::TimeTrackingFormatter.output(time_estimate)
end
diff --git a/app/models/concerns/timebox.rb b/app/models/concerns/timebox.rb
index fb9a8cd312d..8dc58f8dca1 100644
--- a/app/models/concerns/timebox.rb
+++ b/app/models/concerns/timebox.rb
@@ -44,7 +44,6 @@ module Timebox
validates :project, presence: true, unless: :group
validates :title, presence: true
- validate :uniqueness_of_title, if: :title_changed?
validate :timebox_type_check
validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? }
validate :dates_within_4_digits
@@ -243,18 +242,6 @@ module Timebox
end
end
- # Timebox titles must be unique across project and group timeboxes
- def uniqueness_of_title
- if project
- relation = self.class.for_projects_and_groups([project_id], [project.group&.id])
- elsif group
- relation = self.class.for_projects_and_groups(group.projects.select(:id), [group.id])
- end
-
- title_exists = relation.find_by_title(title)
- errors.add(:title, _("already being used for another group or project %{timebox_name}.") % { timebox_name: timebox_name }) if title_exists
- end
-
# Timebox should be either a project timebox or a group timebox
def timebox_type_check
if group_id && project_id
diff --git a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb
index 25c050820d6..3be82ed72d3 100644
--- a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb
+++ b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb
@@ -5,10 +5,6 @@ module TokenAuthenticatableStrategies
DYNAMIC_NONCE_IDENTIFIER = "|"
NONCE_SIZE = 12
- def self.encrypt_token(plaintext_token)
- Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext_token)
- end
-
def self.decrypt_token(token)
return unless token
@@ -22,5 +18,13 @@ module TokenAuthenticatableStrategies
Gitlab::CryptoHelper.aes256_gcm_decrypt(token)
end
end
+
+ def self.encrypt_token(plaintext_token)
+ return Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext_token) unless Feature.enabled?(:dynamic_nonce, type: :ops)
+
+ iv = ::Digest::SHA256.hexdigest(plaintext_token).bytes.take(NONCE_SIZE).pack('c*')
+ token = Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext_token, nonce: iv)
+ "#{DYNAMIC_NONCE_IDENTIFIER}#{token}#{iv}"
+ end
end
end