debian-mirror-gitlab/app/services/users/refresh_authorized_projects_service.rb

95 lines
3.6 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2017-08-17 22:00:37 +05:30
module Users
# Service for refreshing the authorized projects of a user.
#
# This particular service class can not be used to update data for the same
# user concurrently. Doing so could lead to an incorrect state. To ensure this
# doesn't happen a caller must synchronize access (e.g. using
# `Gitlab::ExclusiveLease`).
#
# Usage:
#
# user = User.find_by(username: 'alice')
# service = Users::RefreshAuthorizedProjectsService.new(some_user)
# service.execute
class RefreshAuthorizedProjectsService
2021-03-11 19:13:27 +05:30
attr_reader :user, :source
2017-08-17 22:00:37 +05:30
LEASE_TIMEOUT = 1.minute.to_i
# user - The User for which to refresh the authorized projects.
2021-03-11 19:13:27 +05:30
def initialize(user, source: nil, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
2017-08-17 22:00:37 +05:30
@user = user
2021-03-11 19:13:27 +05:30
@source = source
2020-02-15 18:32:13 +05:30
@incorrect_auth_found_callback = incorrect_auth_found_callback
@missing_auth_found_callback = missing_auth_found_callback
2017-08-17 22:00:37 +05:30
end
def execute
lease_key = "refresh_authorized_projects:#{user.id}"
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
until uuid = lease.try_obtain
# Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries.
2017-09-10 17:25:29 +05:30
sleep(0.1)
2017-08-17 22:00:37 +05:30
end
begin
2021-09-04 01:27:46 +05:30
# We need an up to date User object that has access to all relations that
# may have been created earlier. The only way to ensure this is to reload
# the User object.
user.reset
2017-08-17 22:00:37 +05:30
execute_without_lease
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
end
# This method returns the updated User object.
def execute_without_lease
2021-04-29 21:17:54 +05:30
remove, add = AuthorizedProjectUpdate::FindRecordsDueForRefreshService.new(
user,
source: source,
incorrect_auth_found_callback: incorrect_auth_found_callback,
missing_auth_found_callback: missing_auth_found_callback
).execute
2017-08-17 22:00:37 +05:30
update_authorizations(remove, add)
end
# Updates the list of authorizations for the current user.
#
# remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = [])
2021-04-17 20:07:23 +05:30
log_refresh_details(remove, add)
2021-03-11 19:13:27 +05:30
2021-12-11 22:18:48 +05:30
user.remove_project_authorizations(remove) unless remove.empty?
ProjectAuthorization.insert_authorizations(add) unless add.empty?
2017-08-17 22:00:37 +05:30
# Since we batch insert authorization rows, Rails' associations may get
# out of sync. As such we force a reload of the User object.
2019-07-31 22:56:46 +05:30
user.reset
2017-08-17 22:00:37 +05:30
end
2021-04-29 21:17:54 +05:30
private
attr_reader :incorrect_auth_found_callback, :missing_auth_found_callback
2021-04-17 20:07:23 +05:30
def log_refresh_details(remove, add)
2021-03-11 19:13:27 +05:30
Gitlab::AppJsonLogger.info(event: 'authorized_projects_refresh',
2021-04-17 20:07:23 +05:30
user_id: user.id,
2021-03-11 19:13:27 +05:30
'authorized_projects_refresh.source': source,
2021-04-17 20:07:23 +05:30
'authorized_projects_refresh.rows_deleted_count': remove.length,
'authorized_projects_refresh.rows_added_count': add.length,
# most often there's only a few entries in remove and add, but limit it to the first 5
# entries to avoid flooding the logs
'authorized_projects_refresh.rows_deleted_slice': remove.first(5),
'authorized_projects_refresh.rows_added_slice': add.first(5))
2021-03-11 19:13:27 +05:30
end
2017-08-17 22:00:37 +05:30
end
end