Class: Users::RefreshAuthorizedProjectsService

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initialize(user, source: nil, 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
# File 'app/services/users/refresh_authorized_projects_service.rb', line 22

def initialize(user, source: nil, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
  @user = user
  @source = source
  @incorrect_auth_found_callback = incorrect_auth_found_callback
  @missing_auth_found_callback = missing_auth_found_callback
end

Instance Attribute Details

#sourceObject (readonly)

Returns the value of attribute source.



17
18
19
# File 'app/services/users/refresh_authorized_projects_service.rb', line 17

def source
  @source
end

#userObject (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

#executeObject



29
30
31
32
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 29

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
    # 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
    execute_without_lease
  ensure
    Gitlab::ExclusiveLease.cancel(lease_key, uuid)
  end
end

#execute_without_leaseObject

This method returns the updated User object.



52
53
54
55
56
57
58
59
60
61
# File 'app/services/users/refresh_authorized_projects_service.rb', line 52

def execute_without_lease
  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

  update_authorizations(remove, add)
end

#update_authorizations(remove = [], add = []) ⇒ Object

Updates the list of authorizations for the current user.

remove - The project IDs of the authorization rows to remove. add - Rows to insert in the form ‘[{ user_id: user_id, project_id: project_id, access_level: access_level}, …]`



67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/services/users/refresh_authorized_projects_service.rb', line 67

def update_authorizations(remove = [], add = [])
  log_refresh_details(remove, add)

  ProjectAuthorizations::Changes.new do |changes|
    changes.add(add)
    changes.remove_projects_for_user(user, remove)
  end.apply!

  # 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