Class: Users::RefreshAuthorizedProjectsService
- Inherits:
-
Object
- Object
- Users::RefreshAuthorizedProjectsService
- Defined in:
- app/services/users/refresh_authorized_projects_service.rb
Overview
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
Constant Summary collapse
- LEASE_TIMEOUT =
1.minute.to_i
Instance Attribute Summary collapse
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Instance Method Summary collapse
- #current_authorizations ⇒ Object
- #current_authorizations_per_project ⇒ Object
- #execute ⇒ Object
-
#execute_without_lease ⇒ Object
This method returns the updated User object.
- #fresh_access_levels_per_project ⇒ Object
- #fresh_authorizations ⇒ Object
-
#initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil) ⇒ RefreshAuthorizedProjectsService
constructor
user - The User for which to refresh the authorized projects.
-
#update_authorizations(remove = [], add = []) ⇒ Object
Updates the list of authorizations for the current user.
Constructor Details
#initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil) ⇒ RefreshAuthorizedProjectsService
user - The User for which to refresh the authorized projects.
22 23 24 25 26 27 28 29 30 31 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 22 def initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil) @user = user @incorrect_auth_found_callback = incorrect_auth_found_callback @missing_auth_found_callback = missing_auth_found_callback # 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 end |
Instance Attribute Details
#user ⇒ Object (readonly)
Returns the value of attribute user
17 18 19 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 17 def user @user end |
Instance Method Details
#current_authorizations ⇒ Object
114 115 116 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 114 def @current_authorizations ||= user..select(:project_id, :access_level) end |
#current_authorizations_per_project ⇒ Object
110 111 112 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 110 def .index_by(&:project_id) end |
#execute ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 33 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. sleep(0.1) end begin execute_without_lease ensure Gitlab::ExclusiveLease.cancel(lease_key, uuid) end end |
#execute_without_lease ⇒ Object
This method returns the updated User object.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 52 def execute_without_lease current = fresh = fresh_access_levels_per_project # Delete projects that have more than one authorizations associated with # the user. The correct authorization is added to the ``add`` array in the # next stage. remove = projects_with_duplicates current.except!(*projects_with_duplicates) remove |= current.each_with_object([]) do |(project_id, row), array| # rows not in the new list or with a different access level should be # removed. if !fresh[project_id] || fresh[project_id] != row.access_level if incorrect_auth_found_callback incorrect_auth_found_callback.call(project_id, row.access_level) end array << row.project_id end end add = fresh.each_with_object([]) do |(project_id, level), array| # rows not in the old list or with a different access level should be # added. if !current[project_id] || current[project_id].access_level != level if missing_auth_found_callback missing_auth_found_callback.call(project_id, level) end array << [user.id, project_id, level] end end (remove, add) end |
#fresh_access_levels_per_project ⇒ Object
104 105 106 107 108 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 104 def fresh_access_levels_per_project .each_with_object({}) do |row, hash| hash[row.project_id] = row.access_level end end |
#fresh_authorizations ⇒ Object
118 119 120 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 118 def Gitlab::ProjectAuthorizations.new(user).calculate end |
#update_authorizations(remove = [], add = []) ⇒ Object
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]`
93 94 95 96 97 98 99 100 101 102 |
# File 'app/services/users/refresh_authorized_projects_service.rb', line 93 def (remove = [], add = []) User.transaction do user.(remove) unless remove.empty? ProjectAuthorization.(add) unless add.empty? end # Since we batch insert authorization rows, Rails' associations may get # out of sync. As such we force a reload of the User object. user.reset end |