Class: Gitlab::Auth::CurrentUserMode

Inherits:
Object
  • Object
show all
Includes:
Utils::StrongMemoize
Defined in:
lib/gitlab/auth/current_user_mode.rb

Overview

Keeps track of the current session user mode

In order to perform administrative tasks over some interfaces, an administrator must have explicitly enabled admin-mode e.g. on web access require re-authentication

Constant Summary collapse

NotRequestedError =
Class.new(StandardError)
NonSidekiqEnvironmentError =
Class.new(StandardError)
CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY =

RequestStore entries

{ res: :current_user_mode, data: :bypass_session_admin_id }.freeze
CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY =
{ res: :current_user_mode, data: :current_admin }.freeze
SESSION_STORE_KEY =

SessionStore entries

:current_user_mode
ADMIN_MODE_START_TIME_KEY =
:admin_mode
ADMIN_MODE_REQUESTED_TIME_KEY =
:admin_mode_requested
MAX_ADMIN_MODE_TIME =
6.hours
ADMIN_MODE_REQUESTED_GRACE_PERIOD =
5.minutes

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user, session = Gitlab::Session.current) ⇒ CurrentUserMode

Returns a new instance of CurrentUserMode.



105
106
107
108
# File 'lib/gitlab/auth/current_user_mode.rb', line 105

def initialize(user, session = Gitlab::Session.current)
  @user = user
  @session = session
end

Class Method Details

.bypass_session!(admin_id) ⇒ Object

Admin mode activation requires storing a flag in the user session. Using this method when scheduling jobs in sessionless environments (e.g. Sidekiq, API) will bypass the session check for a user that was already in admin mode

If passed a block, it will surround the block execution and reset the session bypass at the end; otherwise you must remember to call ‘.reset_bypass_session!’



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/gitlab/auth/current_user_mode.rb', line 34

def bypass_session!(admin_id)
  Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY] = admin_id
  # Bypassing the session invalidates the cached value of admin_mode?
  # Any new calls need to be re-computed.
  uncache_admin_mode_state(admin_id)

  Gitlab::AppLogger.debug("Bypassing session in admin mode for: #{admin_id}")

  return unless block_given?

  begin
    yield
  ensure
    reset_bypass_session!(admin_id)
  end
end

.bypass_session_admin_idObject



57
58
59
# File 'lib/gitlab/auth/current_user_mode.rb', line 57

def bypass_session_admin_id
  Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY]
end

.current_adminObject



86
87
88
# File 'lib/gitlab/auth/current_user_mode.rb', line 86

def current_admin
  Gitlab::SafeRequestStore[CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY]
end

.optionally_run_in_admin_mode(user) ⇒ Object

Execute the given block with admin privileges if the user is an admin and admin mode is enabled. Otherwise, execute the block with regular user permissions.



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/gitlab/auth/current_user_mode.rb', line 92

def optionally_run_in_admin_mode(user)
  raise NonSidekiqEnvironmentError unless Gitlab::Runtime.sidekiq?

  return yield unless Gitlab::CurrentSettings.admin_mode && user.can_access_admin_area?

  bypass_session!(user.id) do
    with_current_admin(user) do
      yield
    end
  end
end

.reset_bypass_session!(admin_id = nil) ⇒ Object



51
52
53
54
55
# File 'lib/gitlab/auth/current_user_mode.rb', line 51

def reset_bypass_session!(admin_id = nil)
  # Restoring the session bypass invalidates the cached value of admin_mode?
  uncache_admin_mode_state(admin_id)
  Gitlab::SafeRequestStore.delete(CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY)
end

.uncache_admin_mode_state(admin_id = nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
# File 'lib/gitlab/auth/current_user_mode.rb', line 61

def uncache_admin_mode_state(admin_id = nil)
  if admin_id
    key = { res: :current_user_mode, user: admin_id, method: :admin_mode? }
    Gitlab::SafeRequestStore.delete(key)
  else
    Gitlab::SafeRequestStore.delete_if do |key|
      key.is_a?(Hash) && key[:res] == :current_user_mode && key[:method] == :admin_mode?
    end
  end
end

.with_current_admin(admin) ⇒ Object

Store in the current request the provided user model (only if in admin mode) and yield



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gitlab/auth/current_user_mode.rb', line 74

def with_current_admin(admin)
  return yield unless new(admin).admin_mode?

  Gitlab::SafeRequestStore[CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY] = admin

  Gitlab::AppLogger.debug("Admin mode active for: #{admin.username}")

  yield
ensure
  Gitlab::SafeRequestStore.delete(CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY)
end

Instance Method Details

#admin_mode?Boolean

Returns:

  • (Boolean)


110
111
112
113
114
115
116
# File 'lib/gitlab/auth/current_user_mode.rb', line 110

def admin_mode?
  return false unless user

  Gitlab::SafeRequestStore.fetch(admin_mode_rs_key) do
    user.can_access_admin_area? && (privileged_runtime? || session_with_admin_mode?)
  end
end

#admin_mode_requested?Boolean

Returns:

  • (Boolean)


118
119
120
121
122
123
124
# File 'lib/gitlab/auth/current_user_mode.rb', line 118

def admin_mode_requested?
  return false unless user

  Gitlab::SafeRequestStore.fetch(admin_mode_requested_rs_key) do
    user.can_access_admin_area? && admin_mode_requested_in_grace_period?
  end
end

#current_session_dataObject



159
160
161
# File 'lib/gitlab/auth/current_user_mode.rb', line 159

def current_session_data
  Gitlab::NamespacedSessionStore.new(SESSION_STORE_KEY, @session)
end

#disable_admin_mode!Object



142
143
144
145
146
147
148
149
# File 'lib/gitlab/auth/current_user_mode.rb', line 142

def disable_admin_mode!
  return unless user&.can_access_admin_area?

  reset_request_store_cache_entries

  current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
  current_session_data[ADMIN_MODE_START_TIME_KEY] = nil
end

#enable_admin_mode!(password: nil, skip_password_validation: false) ⇒ Object

Raises:



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/gitlab/auth/current_user_mode.rb', line 126

def enable_admin_mode!(password: nil, skip_password_validation: false)
  return false unless user&.can_access_admin_area?
  return false unless skip_password_validation || user&.valid_password?(password)

  raise NotRequestedError unless admin_mode_requested?

  reset_request_store_cache_entries

  current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
  current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now

  audit_user_enable_admin_mode

  true
end

#request_admin_mode!Object



151
152
153
154
155
156
157
# File 'lib/gitlab/auth/current_user_mode.rb', line 151

def request_admin_mode!
  return unless user&.can_access_admin_area?

  reset_request_store_cache_entries

  current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = Time.now
end