summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb67
-rw-r--r--app/models/analytics/cycle_analytics/project_level.rb1
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/clusters/platforms/kubernetes.rb6
-rw-r--r--app/models/container_expiration_policy.rb10
-rw-r--r--app/models/integration.rb4
-rw-r--r--app/models/issue.rb1
-rw-r--r--app/models/merge_request.rb3
-rw-r--r--app/models/packages/package.rb2
-rw-r--r--app/models/project.rb27
-rw-r--r--app/models/remote_mirror.rb3
-rw-r--r--app/models/user.rb15
12 files changed, 116 insertions, 25 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index c18bd21d754..6a63a8d46ba 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -54,7 +54,7 @@ class Ability
end
end
- def allowed?(user, action, subject = :global, opts = {})
+ def allowed?(user, ability, subject = :global, opts = {})
if subject.is_a?(Hash)
opts = subject
subject = :global
@@ -64,21 +64,76 @@ class Ability
case opts[:scope]
when :user
- DeclarativePolicy.user_scope { policy.can?(action) }
+ DeclarativePolicy.user_scope { policy.allowed?(ability) }
when :subject
- DeclarativePolicy.subject_scope { policy.can?(action) }
+ DeclarativePolicy.subject_scope { policy.allowed?(ability) }
else
- policy.can?(action)
+ policy.allowed?(ability)
end
+ ensure
+ # TODO: replace with runner invalidation:
+ # See: https://gitlab.com/gitlab-org/declarative-policy/-/merge_requests/24
+ # See: https://gitlab.com/gitlab-org/declarative-policy/-/merge_requests/25
+ forget_runner_result(policy.runner(ability)) if policy && ability_forgetting?
end
def policy_for(user, subject = :global)
- cache = Gitlab::SafeRequestStore.active? ? Gitlab::SafeRequestStore : {}
- DeclarativePolicy.policy_for(user, subject, cache: cache)
+ DeclarativePolicy.policy_for(user, subject, cache: ::Gitlab::SafeRequestStore.storage)
+ end
+
+ # This method is something of a band-aid over the problem. The problem is
+ # that some conditions may not be re-entrant, if facts change.
+ # (`BasePolicy#admin?` is a known offender, due to the effects of
+ # `admin_mode`)
+ #
+ # To deal with this we need to clear two elements of state: the offending
+ # conditions (selected by 'pattern') and the cached ability checks (cached
+ # on the `policy#runner(ability)`).
+ #
+ # Clearing the conditions (see `forget_all_but`) is fairly robust, provided
+ # the pattern is not _under_-selective. Clearing the runners is harder,
+ # since there is not good way to know which abilities any given condition
+ # may affect. The approach taken here (see `forget_runner_result`) is to
+ # discard all runner results generated during a `forgetting` block. This may
+ # be _under_-selective if a runner prior to this block cached a state value
+ # that might now be invalid.
+ #
+ # TODO: add some kind of reverse-dependency mapping in DeclarativePolicy
+ # See: https://gitlab.com/gitlab-org/declarative-policy/-/issues/14
+ def forgetting(pattern, &block)
+ was_forgetting = ability_forgetting?
+ ::Gitlab::SafeRequestStore[:ability_forgetting] = true
+ keys_before = ::Gitlab::SafeRequestStore.storage.keys
+
+ yield
+ ensure
+ ::Gitlab::SafeRequestStore[:ability_forgetting] = was_forgetting
+ forget_all_but(keys_before, matching: pattern)
end
private
+ def ability_forgetting?
+ ::Gitlab::SafeRequestStore[:ability_forgetting]
+ end
+
+ def forget_all_but(keys_before, matching:)
+ keys_after = ::Gitlab::SafeRequestStore.storage.keys
+
+ added_keys = keys_after - keys_before
+ added_keys.each do |key|
+ if key.is_a?(String) && key.start_with?('/dp') && key =~ matching
+ ::Gitlab::SafeRequestStore.delete(key)
+ end
+ end
+ end
+
+ def forget_runner_result(runner)
+ # TODO: add support in DP for this
+ # See: https://gitlab.com/gitlab-org/declarative-policy/-/issues/15
+ runner.instance_variable_set(:@state, nil)
+ end
+
def apply_filters_if_needed(elements, user, filters)
filters.each do |ability, filter|
elements = filter.call(elements) unless allowed?(user, ability)
diff --git a/app/models/analytics/cycle_analytics/project_level.rb b/app/models/analytics/cycle_analytics/project_level.rb
index 7a73bc75ed6..d43793f60c9 100644
--- a/app/models/analytics/cycle_analytics/project_level.rb
+++ b/app/models/analytics/cycle_analytics/project_level.rb
@@ -47,3 +47,4 @@ module Analytics
end
end
end
+Analytics::CycleAnalytics::ProjectLevel.prepend_mod_with('Analytics::CycleAnalytics::ProjectLevel')
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index ae06bea5a02..159d9d10878 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1257,7 +1257,7 @@ module Ci
end
def build_matchers
- self.builds.build_matchers(project)
+ self.builds.latest.build_matchers(project)
end
private
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index da5f4cc1862..7f5f87e3e36 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -98,11 +98,7 @@ module Clusters
pods = read_pods(environment.deployment_namespace)
deployments = read_deployments(environment.deployment_namespace)
- ingresses = if ::Feature.enabled?(:canary_ingress_weight_control, environment.project, default_enabled: true)
- read_ingresses(environment.deployment_namespace)
- else
- []
- end
+ ingresses = read_ingresses(environment.deployment_namespace)
# extract only the data required for display to avoid unnecessary caching
{
diff --git a/app/models/container_expiration_policy.rb b/app/models/container_expiration_policy.rb
index 0441a5f0f5b..9bacd9a0edf 100644
--- a/app/models/container_expiration_policy.rb
+++ b/app/models/container_expiration_policy.rb
@@ -38,6 +38,16 @@ class ContainerExpirationPolicy < ApplicationRecord
)
end
+ def self.without_container_repositories
+ where.not(
+ 'EXISTS(?)',
+ ContainerRepository.select(1)
+ .where(
+ 'container_repositories.project_id = container_expiration_policies.project_id'
+ )
+ )
+ end
+
def self.keep_n_options
{
1 => _('%{tags} tag per image name') % { tags: 1 },
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 238ecbbf209..2fbcdc7f1cb 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -44,6 +44,10 @@ class Integration < ApplicationRecord
bamboo bugzilla buildkite
campfire confluence custom_issue_tracker
datadog discord drone_ci
+ emails_on_push ewm emails_on_push external_wiki
+ flowdock
+ hangouts_chat
+ irker
].to_set.freeze
def self.renamed?(name)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index b0a126c4442..48f388ea48d 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -23,6 +23,7 @@ class Issue < ApplicationRecord
include IssueAvailableFeatures
include Todoable
include FromUnion
+ include EachBatch
extend ::Gitlab::Utils::Override
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 15f112690d5..68fb957759d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -263,8 +263,9 @@ class MergeRequest < ApplicationRecord
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
scope :from_project, ->(project) { where(source_project_id: project.id) }
+ scope :from_fork, -> { where('source_project_id <> target_project_id') }
scope :from_and_to_forks, ->(project) do
- where('source_project_id <> target_project_id AND (source_project_id = ? OR target_project_id = ?)', project.id, project.id)
+ from_fork.where('source_project_id = ? OR target_project_id = ?', project.id, project.id)
end
scope :merged, -> { with_state(:merged) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 7b0bb72940e..b040c98ef09 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -158,7 +158,7 @@ class Packages::Package < ApplicationRecord
joins(:project).reorder(keyset_order)
end
- after_commit :update_composer_cache, on: :destroy, if: -> { composer? }
+ after_commit :update_composer_cache, on: :destroy, if: -> { composer? && Feature.disabled?(:disable_composer_callback) }
def self.only_maven_packages_with_path(path, use_cte: false)
if use_cte
diff --git a/app/models/project.rb b/app/models/project.rb
index 735dc185575..1f8e8b81015 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -166,12 +166,12 @@ class Project < ApplicationRecord
has_one :datadog_integration, class_name: 'Integrations::Datadog'
has_one :discord_integration, class_name: 'Integrations::Discord'
has_one :drone_ci_integration, class_name: 'Integrations::DroneCi'
- has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
- has_one :ewm_service, class_name: 'Integrations::Ewm'
- has_one :external_wiki_service, class_name: 'Integrations::ExternalWiki'
- has_one :flowdock_service, class_name: 'Integrations::Flowdock'
- has_one :hangouts_chat_service, class_name: 'Integrations::HangoutsChat'
- has_one :irker_service, class_name: 'Integrations::Irker'
+ has_one :emails_on_push_integration, class_name: 'Integrations::EmailsOnPush'
+ has_one :ewm_integration, class_name: 'Integrations::Ewm'
+ has_one :external_wiki_integration, class_name: 'Integrations::ExternalWiki'
+ has_one :flowdock_integration, class_name: 'Integrations::Flowdock'
+ has_one :hangouts_chat_integration, class_name: 'Integrations::HangoutsChat'
+ has_one :irker_integration, class_name: 'Integrations::Irker'
has_one :jenkins_service, class_name: 'Integrations::Jenkins'
has_one :jira_service, class_name: 'Integrations::Jira'
has_one :mattermost_service, class_name: 'Integrations::Mattermost'
@@ -825,6 +825,21 @@ class Project < ApplicationRecord
from_union([with_issues_enabled, with_merge_requests_enabled]).select(:id)
end
+
+ def find_by_url(url)
+ uri = URI(url)
+
+ return unless uri.host == Gitlab.config.gitlab.host
+
+ match = Rails.application.routes.recognize_path(url)
+
+ return if match[:unmatched_route].present?
+ return if match[:namespace_id].blank? || match[:id].blank?
+
+ find_by_full_path(match.values_at(:namespace_id, :id).join("/"))
+ rescue ActionController::RoutingError, URI::InvalidURIError
+ nil
+ end
end
def initialize(attributes = nil)
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index c3ca90ca0ad..a700f104150 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -100,10 +100,11 @@ class RemoteMirror < ApplicationRecord
update_status == 'started'
end
- def update_repository
+ def update_repository(inmemory_remote:)
Gitlab::Git::RemoteMirror.new(
project.repository.raw,
remote_name,
+ inmemory_remote ? remote_url : nil,
**options_for_update
).update
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 8ee0421e45f..5fbd6271589 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -84,10 +84,11 @@ class User < ApplicationRecord
update_tracked_fields(request)
- lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i)
- return unless lease.try_obtain
-
- Users::UpdateService.new(self, user: self).execute(validate: false)
+ Gitlab::ExclusiveLease.throttle(id) do
+ ::Ability.forgetting(/admin/) do
+ Users::UpdateService.new(self, user: self).execute(validate: false)
+ end
+ end
end
# rubocop: enable CodeReuse/ServiceClass
@@ -1868,6 +1869,12 @@ class User < ApplicationRecord
!!(password_expires_at && password_expires_at < Time.current)
end
+ def password_expired_if_applicable?
+ return false unless allow_password_authentication?
+
+ password_expired?
+ end
+
def can_be_deactivated?
active? && no_recent_activity? && !internal?
end