diff options
Diffstat (limited to 'app/models/concerns')
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 |