Class: Gitlab::Auth::CurrentUserMode

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

Returns a new instance of CurrentUserMode.



88
89
90
# File 'lib/gitlab/auth/current_user_mode.rb', line 88

def initialize(user)
  @user = user
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!’



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

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



54
55
56
# File 'lib/gitlab/auth/current_user_mode.rb', line 54

def bypass_session_admin_id
  Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY]
end

.current_adminObject



83
84
85
# File 'lib/gitlab/auth/current_user_mode.rb', line 83

def current_admin
  Gitlab::SafeRequestStore[CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY]
end

.reset_bypass_session!(admin_id = nil) ⇒ Object



48
49
50
51
52
# File 'lib/gitlab/auth/current_user_mode.rb', line 48

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



58
59
60
61
62
63
64
65
66
67
# File 'lib/gitlab/auth/current_user_mode.rb', line 58

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



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/gitlab/auth/current_user_mode.rb', line 71

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)


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

def admin_mode?
  return false unless user

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

#admin_mode_requested?Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
106
# File 'lib/gitlab/auth/current_user_mode.rb', line 100

def admin_mode_requested?
  return false unless user

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

#disable_admin_mode!Object



124
125
126
127
128
129
130
131
# File 'lib/gitlab/auth/current_user_mode.rb', line 124

def disable_admin_mode!
  return unless user&.admin?

  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:



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/gitlab/auth/current_user_mode.rb', line 108

def enable_admin_mode!(password: nil, skip_password_validation: false)
  return false unless user&.admin?
  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



133
134
135
136
137
138
139
# File 'lib/gitlab/auth/current_user_mode.rb', line 133

def request_admin_mode!
  return unless user&.admin?

  reset_request_store_cache_entries

  current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = Time.now
end