Commit 7bfb6111 authored by Diego Louzán's avatar Diego Louzán Committed by Bob Van Landuyt

feat: support omniauth for admin mode

- Extend admin mode support to OmniAuth authentication
- Improve admin mode flow by adding a request step with a grace period
parent bc4ad61f
......@@ -6,17 +6,23 @@ class Admin::SessionsController < ApplicationController
before_action :user_is_admin!
def new
# Renders a form in which the admin can enter their password
if current_user_mode.admin_mode?
redirect_to redirect_path, notice: _('Admin mode already enabled')
else
current_user_mode.request_admin_mode! unless current_user_mode.admin_mode_requested?
store_location_for(:redirect, redirect_path)
end
end
def create
if current_user_mode.enable_admin_mode!(password: params[:password])
redirect_location = stored_location_for(:redirect) || admin_root_path
redirect_to safe_redirect_path(redirect_location)
redirect_to redirect_path, notice: _('Admin mode enabled')
else
flash.now[:alert] = _('Invalid Login or password')
flash.now[:alert] = _('Invalid login or password')
render :new
end
rescue Gitlab::Auth::CurrentUserMode::NotRequestedError
redirect_to new_admin_session_path, alert: _('Re-authentication period expired or never requested. Please try again')
end
def destroy
......@@ -30,4 +36,19 @@ class Admin::SessionsController < ApplicationController
def user_is_admin!
render_404 unless current_user&.admin?
end
def redirect_path
redirect_to_path = safe_redirect_path(stored_location_for(:redirect)) || safe_redirect_path_for_url(request.referer)
if redirect_to_path &&
excluded_redirect_paths.none? { |excluded| redirect_to_path.include?(excluded) }
redirect_to_path
else
admin_root_path
end
end
def excluded_redirect_paths
[new_admin_session_path, admin_session_path]
end
end
......@@ -16,6 +16,7 @@ class ApplicationController < ActionController::Base
include ConfirmEmailWarning
include Gitlab::Tracking::ControllerConcern
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
before_action :authenticate_user!, except: [:route_not_found]
before_action :enforce_terms!, if: :should_enforce_terms?
......@@ -41,7 +42,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
helper_method :can?
helper_method :current_user_mode
helper_method :import_sources_enabled?, :github_import_enabled?,
:gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?,
......@@ -546,10 +546,6 @@ class ApplicationController < ActionController::Base
end
end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
# A user requires a role and have the setup_for_company attribute set when they are part of the experimental signup
# flow (executed by the Growth team). Users are redirected to the welcome page when their role is required and the
# experiment is enabled for the current user.
......
......@@ -18,6 +18,7 @@ module EnforcesAdminAuthentication
return unless Feature.enabled?(:user_mode_in_session)
unless current_user_mode.admin_mode?
current_user_mode.request_admin_mode!
store_location_for(:redirect, request.fullpath) if storable_location?
redirect_to(new_admin_session_path, notice: _('Re-authentication required'))
end
......
# frozen_string_literal: true
module InitializesCurrentUserMode
extend ActiveSupport::Concern
included do
helper_method :current_user_mode
end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
end
......@@ -33,6 +33,8 @@ module SessionlessAuthentication
end
def enable_admin_mode!
current_user_mode.enable_admin_mode!(skip_password_validation: true) if Feature.enabled?(:user_mode_in_session)
return unless Feature.enabled?(:user_mode_in_session)
current_user_mode.enable_sessionless_admin_mode!
end
end
......@@ -6,6 +6,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include PageLayoutHelper
include OauthApplications
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
before_action :verify_user_oauth_applications_enabled, except: :index
before_action :authenticate_user!
......
......@@ -2,6 +2,8 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
layout 'profile'
# Overridden from Doorkeeper::AuthorizationsController to
......
......@@ -4,6 +4,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
include AuthHelper
include InitializesCurrentUserMode
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
......@@ -94,8 +95,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
return render_403 unless link_provider_allowed?(oauth['provider'])
log_audit_event(current_user, with: oauth['provider'])
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
if Feature.enabled?(:user_mode_in_session)
return admin_mode_flow if current_user_mode.admin_mode_requested?
end
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
link_identity(identity_linker)
if identity_linker.changed?
......@@ -239,6 +244,24 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
store_location_for(:user, uri.to_s)
end
end
def admin_mode_flow
if omniauth_identity_matches_current_user?
current_user_mode.enable_admin_mode!(skip_password_validation: true)
redirect_to stored_location_for(:redirect) || admin_root_path, notice: _('Admin mode enabled')
else
fail_admin_mode_invalid_credentials
end
end
def omniauth_identity_matches_current_user?
current_user.matches_identity?(oauth['provider'], oauth['uid'])
end
def fail_admin_mode_invalid_credentials
redirect_to new_admin_session_path, alert: _('Invalid login or password')
end
end
OmniauthCallbacksController.prepend_if_ee('EE::OmniauthCallbacksController')
......@@ -87,7 +87,7 @@ module NavHelper
end
if Feature.enabled?(:user_mode_in_session)
if current_user&.admin? && current_user_mode&.admin_mode?
if current_user_mode.admin_mode?
links << :admin_mode
end
end
......
......@@ -996,6 +996,10 @@ class User < ApplicationRecord
@ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def matches_identity?(provider, extern_uid)
identities.where(provider: provider, extern_uid: extern_uid).exists?
end
def project_deploy_keys
DeployKey.in_projects(authorized_projects.select(:id)).distinct(:id)
end
......
- if any_form_based_providers_enabled?
- if password_authentication_enabled_for_web?
.login-box.tab-pane{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
- elsif password_authentication_enabled_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
......@@ -7,9 +7,16 @@
#signin-container
= render 'admin/sessions/tabs_normal'
.tab-content
- if password_authentication_enabled_for_web?
= render 'admin/sessions/signin_box'
- else
-# Show a message if none of the mechanisms above are enabled
- if !current_user.require_password_creation_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
- if omniauth_enabled? && button_based_providers_enabled?
.clearfix
= render 'devise/shared/omniauth_box'
-# Show a message if none of the mechanisms above are enabled
- if current_user.require_password_creation_for_web? && !omniauth_enabled?
.prepend-top-default.center
= _('No authentication methods configured.')
---
title: Add OmniAuth authentication support to admin mode feature
merge_request: 18214
author: Diego Louzán
type: added
......@@ -67,6 +67,7 @@ describe Gitlab::Elastic::SnippetSearchResults, :elastic, :sidekiq_might_not_nee
context 'admin mode enabled' do
before do
Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
......
......@@ -56,7 +56,9 @@ module API
# Set admin mode for API requests (if admin)
if Feature.enabled?(:user_mode_in_session)
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(skip_password_validation: true)
current_user_mode = Gitlab::Auth::CurrentUserMode.new(user)
current_user_mode.enable_sessionless_admin_mode!
end
user
......
......@@ -8,9 +8,13 @@ module Gitlab
# an administrator must have explicitly enabled admin-mode
# e.g. on web access require re-authentication
class CurrentUserMode
NotRequestedError = Class.new(StandardError)
SESSION_STORE_KEY = :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
def initialize(user)
@user = user
......@@ -19,8 +23,16 @@ module Gitlab
def admin_mode?
return false unless user
Gitlab::SafeRequestStore.fetch(request_store_key) do
user&.admin? && any_session_with_admin_mode?
Gitlab::SafeRequestStore.fetch(admin_mode_rs_key) do
user.admin? && any_session_with_admin_mode?
end
end
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
......@@ -28,20 +40,45 @@ module Gitlab
return unless user&.admin?
return unless skip_password_validation || user&.valid_password?(password)
raise NotRequestedError unless admin_mode_requested?
reset_request_store
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now
end
def enable_sessionless_admin_mode!
request_admin_mode! && enable_admin_mode!(skip_password_validation: true)
end
def disable_admin_mode!
return unless user&.admin?
reset_request_store
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
current_session_data[ADMIN_MODE_START_TIME_KEY] = nil
Gitlab::SafeRequestStore.delete(request_store_key)
end
def request_admin_mode!
return unless user&.admin?
reset_request_store
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = Time.now
end
private
attr_reader :user
def request_store_key
@request_store_key ||= { res: :current_user_mode, user: user.id }
def admin_mode_rs_key
@admin_mode_rs_key ||= { res: :current_user_mode, user: user.id, method: :admin_mode? }
end
def admin_mode_requested_rs_key
@admin_mode_requested_rs_key ||= { res: :current_user_mode, user: user.id, method: :admin_mode_requested? }
end
def current_session_data
......@@ -61,6 +98,15 @@ module Gitlab
Gitlab::NamespacedSessionStore.new(SESSION_STORE_KEY, session.with_indifferent_access )
end
end
def admin_mode_requested_in_grace_period?
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY].to_i > ADMIN_MODE_REQUESTED_GRACE_PERIOD.ago.to_i
end
def reset_request_store
Gitlab::SafeRequestStore.delete(admin_mode_rs_key)
Gitlab::SafeRequestStore.delete(admin_mode_requested_rs_key)
end
end
end
end
......@@ -1134,9 +1134,15 @@ msgstr ""
msgid "Admin Section"
msgstr ""
msgid "Admin mode already enabled"
msgstr ""
msgid "Admin mode disabled"
msgstr ""
msgid "Admin mode enabled"
msgstr ""
msgid "Admin notes"
msgstr ""
......@@ -9678,6 +9684,9 @@ msgstr ""
msgid "Invalid input, please avoid emojis"
msgstr ""
msgid "Invalid login or password"
msgstr ""
msgid "Invalid pin code"
msgstr ""
......@@ -14397,6 +14406,9 @@ msgstr ""
msgid "Raw blob request rate limit per minute"
msgstr ""
msgid "Re-authentication period expired or never requested. Please try again"
msgstr ""
msgid "Re-authentication required"
msgstr ""
......
......@@ -17,7 +17,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :new
expect(response).to have_gitlab_http_status(:not_found)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
......@@ -28,7 +28,21 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :new
expect(response).to render_template :new
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
context 'already in admin mode' do
before do
controller.current_user_mode.request_admin_mode!
controller.current_user_mode.enable_admin_mode!(password: user.password)
end
it 'redirects to original location' do
get :new
expect(response).to redirect_to(admin_root_path)
expect(controller.current_user_mode.admin_mode?).to be(true)
end
end
end
end
......@@ -39,7 +53,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
post :create
expect(response).to have_gitlab_http_status(:not_found)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
......@@ -47,24 +61,60 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
let(:user) { create(:admin) }
it 'sets admin mode with a valid password' do
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
# triggering the auth form will request admin mode
get :new
post :create, params: { password: user.password }
expect(response).to redirect_to admin_root_path
expect(controller.send(:current_user_mode).admin_mode?).to be(true)
expect(controller.current_user_mode.admin_mode?).to be(true)
end
it 'fails with an invalid password' do
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
# triggering the auth form will request admin mode
get :new
post :create, params: { password: '' }
expect(response).to render_template :new
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
it 'fails if not requested first' do
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
# do not trigger the auth form
post :create, params: { password: user.password }
expect(response).to redirect_to(new_admin_session_path)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
it 'fails if request period expired' do
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
# triggering the auth form will request admin mode
get :new
Timecop.freeze(Gitlab::Auth::CurrentUserMode::ADMIN_MODE_REQUESTED_GRACE_PERIOD.from_now) do
post :create, params: { password: user.password }
expect(response).to redirect_to(new_admin_session_path)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
end
end
......@@ -75,7 +125,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :destroy
expect(response).to have_gitlab_http_status(404)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
......@@ -83,15 +133,17 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
let(:user) { create(:admin) }
it 'disables admin mode and redirects to main page' do
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
get :new
post :create, params: { password: user.password }
expect(controller.send(:current_user_mode).admin_mode?).to be(true)
expect(controller.current_user_mode.admin_mode?).to be(true)
get :destroy
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(root_path)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
end
......
......@@ -814,6 +814,7 @@ describe ApplicationController do
context 'that re-authenticated' do
before do
Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
......
......@@ -62,6 +62,12 @@ describe Oauth::ApplicationsController do
end
end
context 'Helpers' do
it 'current_user_mode available' do
expect(subject.current_user_mode).not_to be_nil
end
end
def disable_user_oauth
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false)
end
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe OmniauthCallbacksController, type: :controller do
describe OmniauthCallbacksController, type: :controller, do_not_mock_admin_mode: true do
include LoginHelpers
describe 'omniauth' do
......@@ -336,4 +336,109 @@ describe OmniauthCallbacksController, type: :controller do
end
end
end
describe 'enable admin mode' do
include_context 'custom session'
let(:provider) { :auth0 }
let(:extern_uid) { 'my-uid' }
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
def reauthenticate_and_check_admin_mode(expected_admin_mode:)
# Initially admin mode disabled
expect(subject.current_user_mode.admin_mode?).to be(false)
# Trigger OmniAuth admin mode flow and expect admin mode status
post provider
expect(request.env['warden']).to be_authenticated
expect(subject.current_user_mode.admin_mode?).to be(expected_admin_mode)
end
context 'user and admin mode requested by the same user' do
before do
sign_in user
mock_auth_hash(provider.to_s, extern_uid, user.email, additional_info: {})
stub_omniauth_provider(provider, context: request)
end
context 'with a regular user' do
it 'cannot be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: false)
expect(response).to redirect_to(root_path)
end
end
context 'with an admin user' do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider, access_level: :admin) }
context 'when requested first' do
before do
subject.current_user_mode.request_admin_mode!
end
it 'can be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: true)
expect(response).to redirect_to(admin_root_path)
end
end
context 'when not requested first' do
it 'cannot be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: false)
expect(response).to redirect_to(root_path)
end
end
end
end
context 'user and admin mode requested by different users' do
let(:reauth_extern_uid) { 'another_uid' }
let(:reauth_user) { create(:omniauth_user, extern_uid: reauth_extern_uid, provider: provider) }
before do
sign_in user
mock_auth_hash(provider.to_s, reauth_extern_uid, reauth_user.email, additional_info: {})
stub_omniauth_provider(provider, context: request)
end
context 'with a regular user' do
it 'cannot be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: false)
expect(response).to redirect_to(profile_account_path)
end
end
context 'with an admin user' do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider, access_level: :admin) }
let(:reauth_user) { create(:omniauth_user, extern_uid: reauth_extern_uid, provider: provider, access_level: :admin) }
context 'when requested first' do
before do
subject.current_user_mode.request_admin_mode!
end
it 'cannot be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: false)
expect(response).to redirect_to(new_admin_session_path)
end
end
context 'when not requested first' do
it 'cannot be enabled' do
reauthenticate_and_check_admin_mode(expected_admin_mode: false)
expect(response).to redirect_to(profile_account_path)
end
end
end
end
end
end
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe 'Gcp Cluster', :js do
describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
......
......@@ -42,6 +42,7 @@ describe NavHelper, :do_not_mock_admin_mode do
context 'with admin mode enabled' do
before do
current_user_mode.request_admin_mode!
current_user_mode.enable_admin_mode!(password: user.password)
end
......@@ -62,6 +63,7 @@ describe NavHelper, :do_not_mock_admin_mode do
context 'with admin mode enabled' do
before do
current_user_mode.request_admin_mode!
current_user_mode.enable_admin_mode!(password: user.password)
end
......@@ -89,11 +91,18 @@ describe NavHelper, :do_not_mock_admin_mode do
end
end
it 'returns only the sign in and search when the user is not logged in' do
allow(helper).to receive(:current_user).and_return(nil)
allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
context 'when the user is not logged in' do
let(:current_user_mode) { Gitlab::Auth::CurrentUserMode.new(nil) }
expect(helper.header_links).to contain_exactly(:sign_in, :search)
before do
allow(helper).to receive(:current_user).and_return(nil)
allow(helper).to receive(:current_user_mode).and_return(current_user_mode)
allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
end
it 'returns only the sign in and search when the user is not logged in' do
expect(helper.header_links).to contain_exactly(:sign_in, :search)
end
end
end
......
......@@ -62,69 +62,90 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
context 'when the user is an admin' do
let(:user) { build(:user, :admin) }
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
it 'cannot be enabled with an invalid password' do
subject.enable_admin_mode!(password: nil)
expect(subject.admin_mode?).to be(false)
end
context 'when admin mode not requested' do
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password)
it 'raises exception if we try to enable it' do
expect do
subject.enable_admin_mode!(password: user.password)
end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
expect(subject.admin_mode?).to be(true)
expect(subject.admin_mode?).to be(false)
end
end
it 'can be disabled' do
subject.enable_admin_mode!(password: user.password)
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(false)
end
context 'when admin mode requested first' do
before do
subject.request_admin_mode!
end
it 'will expire in the future' do
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
# in the future this will be a new request, simulate by clearing the RequestStore
Gitlab::SafeRequestStore.clear!
it 'cannot be enabled with an invalid password' do
subject.enable_admin_mode!(password: nil)
expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
expect(subject.admin_mode?).to be(false)
end
end
context 'skipping password validation' do
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
end
it 'can be enabled with an invalid password' do
subject.enable_admin_mode!(skip_password_validation: true)
it 'can be disabled' do
subject.enable_admin_mode!(password: user.password)
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(true)
expect(subject.admin_mode?).to be(false)
end
end
context 'with two independent sessions' do
let(:another_session) { {} }
let(:another_subject) { described_class.new(user) }
it 'will expire in the future' do
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
# in the future this will be a new request, simulate by clearing the RequestStore
Gitlab::SafeRequestStore.clear!
expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
end
end
it 'can be enabled in one and seen in the other' do
Gitlab::Session.with_session(another_session) do
another_subject.enable_admin_mode!(password: user.password)
context 'skipping password validation' do
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
expect(subject.admin_mode?).to be(true)
end
expect(subject.admin_mode?).to be(true)
it 'can be enabled with an invalid password' do
subject.enable_admin_mode!(skip_password_validation: true)
expect(subject.admin_mode?).to be(true)
end
end
context 'with two independent sessions' do
let(:another_session) { {} }
let(:another_subject) { described_class.new(user) }
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
end
it 'can be enabled in one and seen in the other' do
Gitlab::Session.with_session(another_session) do
another_subject.request_admin_mode!
another_subject.enable_admin_mode!(password: user.password)
end
expect(subject.admin_mode?).to be(true)
end
end
end
end
......@@ -134,16 +155,28 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
let(:user) { build(:user, :admin) }
it 'creates a timestamp in the session' do
subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of Time.now))
end
end
describe '#enable_sessionless_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'enabled admin mode without password' do
subject.enable_sessionless_admin_mode!
expect(subject.admin_mode?).to be(true)
end
end
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'sets the session timestamp to nil' do
subject.request_admin_mode!
subject.disable_admin_mode!
expect(session).to include(expected_session_entry(be_nil))
......
......@@ -2839,6 +2839,7 @@ describe User, :do_not_mock_admin_mode do
context 'when admin mode is enabled' do
before do
Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
......
......@@ -3,7 +3,7 @@
# Helper for enabling admin mode in tests
module AdminModeHelper
# Users are logged in by default in user mode and have to switch to admin
# Administrators are logged in by default in user mode and have to switch to admin
# mode for accessing any administrative functionality. This helper lets a user
# be in admin mode without requiring a second authentication step (provided
# the user is an admin)
......
......@@ -3,29 +3,44 @@
require 'spec_helper'
describe 'admin/sessions/new.html.haml' do
context 'admin has password set' do
before do
allow(view).to receive(:password_authentication_enabled_for_web?).and_return(true)
end
let(:user) { create(:admin) }
before do
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:omniauth_enabled?).and_return(false)
end
it "shows enter password form" do
context 'internal admin user' do
it 'shows enter password form' do
render
expect(rendered).to have_css('#login-pane.active')
expect(rendered).to have_selector('input[name="password"]')
end
it 'warns authentication not possible if password not set' do
allow(user).to receive(:require_password_creation_for_web?).and_return(true)
render
expect(rendered).not_to have_css('#login-pane')
expect(rendered).to have_content _('No authentication methods configured.')
end
end
context 'admin has no password set' do
context 'omniauth authentication enabled' do
before do
allow(view).to receive(:password_authentication_enabled_for_web?).and_return(false)
allow(view).to receive(:omniauth_enabled?).and_return(true)
allow(view).to receive(:button_based_providers_enabled?).and_return(true)
end
it "warns authentication not possible" do
it 'shows omniauth form' do
render
expect(rendered).not_to have_css('#login-pane')
expect(rendered).to have_content 'No authentication methods configured'
expect(rendered).to have_css('.omniauth-container')
expect(rendered).to have_content _('Sign in with')
expect(rendered).not_to have_content _('No authentication methods configured.')
end
end
end
......@@ -11,6 +11,7 @@ describe 'layouts/application' do
allow(view).to receive(:session).and_return({})
allow(view).to receive(:user_signed_in?).and_return(true)
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user))
end
context 'body data elements for pageview context' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment