Commit b334ce67 authored by James Lopez's avatar James Lopez

Merge branch 'feat/user-mode-in-session-for-admins' into 'master'

feat: user mode in session for admins

See merge request gitlab-org/gitlab!16981
parents 38931822 a1fddbb7
# frozen_string_literal: true
class Admin::SessionsController < ApplicationController
include InternalRedirect
before_action :user_is_admin!
def new
# Renders a form in which the admin can enter their password
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)
else
flash.now[:alert] = _('Invalid Login or password')
render :new
end
end
def destroy
current_user_mode.disable_admin_mode!
redirect_to root_path, status: :found, notice: _('Admin mode disabled')
end
private
def user_is_admin!
render_404 unless current_user&.admin?
end
end
......@@ -36,6 +36,7 @@ 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?,
......@@ -533,6 +534,10 @@ class ApplicationController < ActionController::Base
yield
end
end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
end
ApplicationController.prepend_if_ee('EE::ApplicationController')
......@@ -14,6 +14,16 @@ module EnforcesAdminAuthentication
end
def authenticate_admin!
render_404 unless current_user.admin?
return render_404 unless current_user.admin?
return unless Feature.enabled?(:user_mode_in_session)
unless current_user_mode.admin_mode?
store_location_for(:redirect, request.fullpath) if storable_location?
redirect_to(new_admin_session_path, notice: _('Re-authentication required'))
end
end
def storable_location?
request.path != new_admin_session_path
end
end
......@@ -5,6 +5,12 @@
# Controller concern to handle PAT, RSS, and static objects token authentication methods
#
module SessionlessAuthentication
extend ActiveSupport::Concern
included do
before_action :enable_admin_mode!, if: :sessionless_user?
end
# This filter handles personal access tokens, atom requests with rss tokens, and static object tokens
def authenticate_sessionless_user!(request_format)
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
......@@ -25,4 +31,8 @@ module SessionlessAuthentication
sign_in(user, store: false, message: :sessionless_sign_in)
end
end
def enable_admin_mode!
current_user_mode.enable_admin_mode!(skip_password_validation: true) if Feature.enabled?(:user_mode_in_session)
end
end
......@@ -86,6 +86,12 @@ module NavHelper
links << :admin_impersonation
end
if Feature.enabled?(:user_mode_in_session)
if current_user&.admin? && current_user_mode&.admin_mode?
links << :admin_mode
end
end
links
end
end
......
......@@ -5,7 +5,13 @@ require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base
desc "User is an instance admin"
with_options scope: :user, score: 0
condition(:admin) { @user&.admin? }
condition(:admin) do
if Feature.enabled?(:user_mode_in_session)
Gitlab::Auth::CurrentUserMode.new(@user).admin_mode?
else
@user&.admin?
end
end
desc "User is blocked"
with_options scope: :user, score: 0
......
= form_tag(admin_session_path, method: :post, html: { class: 'new_user gl-show-field-errors', 'aria-live': 'assertive'}) do
.form-group
= label_tag :password, _('Password'), class: 'label-bold'
= password_field_tag :password, nil, class: 'form-control', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
.submit-container.move-submit-down
= submit_tag _('Enter admin mode'), class: 'btn btn-success', data: { qa_selector: 'sign_in_button' }
- if form_based_providers.any?
- 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'
%ul.nav-links.new-session-tabs.nav-tabs.nav{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
%a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab', qa_selector: 'sign_in_tab' }, role: 'tab' }= _('Enter admin mode')
- @hide_breadcrumbs = true
- page_title _('Enter admin mode')
.row.justify-content-center
.col-6.new-session-forms-container
.login-page
#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
.prepend-top-default.center
= _('No authentication methods configured.')
......@@ -68,6 +68,15 @@
= nav_link(controller: 'admin/dashboard') do
= link_to admin_root_path, class: 'd-lg-none admin-icon qa-admin-area-link' do
= _('Admin Area')
- if Feature.enabled?(:user_mode_in_session)
- if header_link?(:admin_mode)
= nav_link(controller: 'admin/sessions') do
= link_to destroy_admin_session_path, class: 'd-lg-none lock-open-icon' do
= _('Leave admin mode')
- elsif current_user.admin?
= nav_link(controller: 'admin/sessions') do
= link_to new_admin_session_path, class: 'd-lg-none lock-icon' do
= _('Enter admin mode')
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, class: 'd-lg-none admin-icon' do
......@@ -95,6 +104,17 @@
= nav_link(controller: 'admin/dashboard', html_options: { class: "d-none d-lg-block d-xl-block"}) do
= link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin Area'), aria: { label: _('Admin Area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('admin', size: 18)
- if Feature.enabled?(:user_mode_in_session)
- if header_link?(:admin_mode)
= nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do
= link_to destroy_admin_session_path, title: _('Leave admin mode'), aria: { label: _('Leave admin mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= sprite_icon('lock-open', size: 18)
- elsif current_user.admin?
= nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do
= link_to new_admin_session_path, title: _('Enter admin mode'), aria: { label: _('Enter admin mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= sprite_icon('lock', size: 18)
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, class: 'admin-icon d-none d-lg-block d-xl-block', title: _('Sherlock Transactions'),
......
---
title: Require admins to enter admin-mode by re-authenticating before performing
administrative operations
merge_request: 16981
author: Roger Rüttimann & Diego Louzán
type: added
......@@ -21,6 +21,10 @@ namespace :admin do
end
end
resource :session, only: [:new, :create] do
get 'destroy', action: :destroy, as: :destroy
end
resource :impersonation, only: :destroy
resources :abuse_reports, only: [:index, :destroy]
......
......@@ -4,6 +4,8 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
include ApiHelpers
include ::EE::GeoHelpers
include_context 'custom session'
set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) }
set(:secondary_status) { create(:geo_node_status, :healthy, geo_node: secondary) }
......
......@@ -355,6 +355,8 @@ describe API::Projects do
end
context 'as an admin' do
include_context 'custom session'
let(:admin) { create(:admin) }
it 'returns 500 when repository storage is unknown' do
......
......@@ -17,6 +17,8 @@ module API
request.access_token
end
use AdminModeMiddleware
helpers HelperMethods
install_error_responders(base)
......@@ -52,6 +54,11 @@ module API
forbidden!(api_access_denied_message(user))
end
# 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)
end
user
end
......@@ -141,5 +148,22 @@ module API
end
end
end
class AdminModeMiddleware < ::Grape::Middleware::Base
def initialize(app, **options)
super
end
def call(env)
if Feature.enabled?(:user_mode_in_session)
session = {}
Gitlab::Session.with_session(session) do
app.call(env)
end
else
app.call(env)
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Auth
# 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
class CurrentUserMode
SESSION_STORE_KEY = :current_user_mode
ADMIN_MODE_START_TIME_KEY = 'admin_mode'
MAX_ADMIN_MODE_TIME = 6.hours
def initialize(user)
@user = user
end
def admin_mode?
return false unless user
Gitlab::SafeRequestStore.fetch(request_store_key) do
user&.admin? && any_session_with_admin_mode?
end
end
def enable_admin_mode!(password: nil, skip_password_validation: false)
return unless user&.admin?
return unless skip_password_validation || user&.valid_password?(password)
current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now
end
def disable_admin_mode!
current_session_data[ADMIN_MODE_START_TIME_KEY] = nil
Gitlab::SafeRequestStore.delete(request_store_key)
end
private
attr_reader :user
def request_store_key
@request_store_key ||= { res: :current_user_mode, user: user.id }
end
def current_session_data
@current_session ||= Gitlab::NamespacedSessionStore.new(SESSION_STORE_KEY)
end
def any_session_with_admin_mode?
return true if current_session_data.initiated? && current_session_data[ADMIN_MODE_START_TIME_KEY].to_i > MAX_ADMIN_MODE_TIME.ago.to_i
all_sessions.any? do |session|
session[ADMIN_MODE_START_TIME_KEY].to_i > MAX_ADMIN_MODE_TIME.ago.to_i
end
end
def all_sessions
@all_sessions ||= ActiveSession.list_sessions(user).lazy.map do |session|
Gitlab::NamespacedSessionStore.new(SESSION_STORE_KEY, session.with_indifferent_access )
end
end
end
end
end
......@@ -1031,6 +1031,9 @@ msgstr ""
msgid "Admin Section"
msgstr ""
msgid "Admin mode disabled"
msgstr ""
msgid "Admin notes"
msgstr ""
......@@ -5734,6 +5737,9 @@ msgstr ""
msgid "Enter a number"
msgstr ""
msgid "Enter admin mode"
msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
......@@ -9123,6 +9129,9 @@ msgstr ""
msgid "Leave"
msgstr ""
msgid "Leave admin mode"
msgstr ""
msgid "Leave edit mode? All unsaved changes will be lost."
msgstr ""
......@@ -12739,6 +12748,9 @@ msgstr ""
msgid "Raw blob request rate limit per minute"
msgstr ""
msgid "Re-authentication required"
msgstr ""
msgid "Read more"
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
describe Admin::SessionsController, :do_not_mock_admin_mode do
include_context 'custom session'
let(:user) { create(:user) }
before do
sign_in(user)
end
describe '#new' do
context 'for regular users' do
it 'shows error page' do
get :new
expect(response).to have_gitlab_http_status(:not_found)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
end
end
context 'for admin users' do
let(:user) { create(:admin) }
it 'renders a password form' do
get :new
expect(response).to render_template :new
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
end
end
end
describe '#create' do
context 'for regular users' do
it 'shows error page' do
post :create
expect(response).to have_gitlab_http_status(:not_found)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
end
end
context 'for admin users' 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)
controller.store_location_for(:redirect, admin_root_path)
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)
end
it 'fails with an invalid password' do
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
post :create, params: { password: '' }
expect(response).to render_template :new
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
end
end
end
describe '#destroy' do
context 'for regular users' do
it 'shows error page' do
get :destroy
expect(response).to have_gitlab_http_status(404)
expect(controller.send(:current_user_mode).admin_mode?).to be(false)
end
end
context 'for admin users' 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)
post :create, params: { password: user.password }
expect(controller.send(: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)
end
end
end
end
......@@ -777,4 +777,48 @@ describe ApplicationController do
end
end
end
describe '#current_user_mode', :do_not_mock_admin_mode do
include_context 'custom session'
controller(described_class) do
def index
render html: 'authenticated'
end
end
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session])
sign_in(user)
get :index
end
context 'with a regular user' do
it 'admin mode is not set' do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Auth::CurrentUserMode.new(user).admin_mode?).to be(false)
end
end
context 'with an admin user' do
let(:user) { create(:admin) }
it 'admin mode is not set' do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Auth::CurrentUserMode.new(user).admin_mode?).to be(false)
end
context 'that re-authenticated' do
before do
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
it 'admin mode is set' do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Auth::CurrentUserMode.new(user).admin_mode?).to be(true)
end
end
end
end
end
......@@ -2,7 +2,9 @@
require 'spec_helper'
describe EnforcesAdminAuthentication do
describe EnforcesAdminAuthentication, :do_not_mock_admin_mode do
include AdminModeHelper
let(:user) { create(:user) }
before do
......@@ -10,30 +12,86 @@ describe EnforcesAdminAuthentication do
end
controller(ApplicationController) do
# `described_class` is not available in this context
include EnforcesAdminAuthentication # rubocop:disable RSpec/DescribedClass
include EnforcesAdminAuthentication
def index
head :ok
end
end
describe 'authenticate_admin!' do
context 'as an admin' do
let(:user) { create(:admin) }
context 'feature flag :user_mode_in_session is enabled' do
describe 'authenticate_admin!' do
context 'as an admin' do
let(:user) { create(:admin) }
it 'renders ok' do
get :index
it 'renders redirect for re-authentication and does not set admin mode' do
get :index
expect(response).to redirect_to new_admin_session_path
expect(assigns(:current_user_mode)&.admin_mode?).to be(false)
end
context 'when admin mode is active' do
before do
enable_admin_mode!(user)
end
it 'renders ok' do
get :index
expect(response).to have_gitlab_http_status(200)
end
end
end
context 'as a user' do
it 'renders a 404' do
get :index
expect(response).to have_gitlab_http_status(404)
end
it 'does not set admin mode' do
get :index
expect(response).to have_gitlab_http_status(200)
# check for nil too since on 404, current_user_mode might not be initialized
expect(assigns(:current_user_mode)&.admin_mode?).to be_falsey
end
end
end
end
context 'feature flag :user_mode_in_session is disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
end
context 'as a user' do
it 'renders a 404' do
describe 'authenticate_admin!' do
before do
get :index
end
context 'as an admin' do
let(:user) { create(:admin) }
it 'allows direct access to page' do
expect(response).to have_gitlab_http_status(200)
end
it 'does not set admin mode' do
expect(assigns(:current_user_mode)&.admin_mode?).to be_falsey
end
end
context 'as a user' do
it 'renders a 404' do
expect(response).to have_gitlab_http_status(404)
end
expect(response).to have_gitlab_http_status(404)
it 'does not set admin mode' do
# check for nil too since on 404, current_user_mode might not be initialized
expect(assigns(:current_user_mode)&.admin_mode?).to be_falsey
end
end
end
end
......
......@@ -2,450 +2,529 @@
require 'spec_helper'
describe 'Admin updates settings' do
describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
include StubENV
include TermsHelper
let(:admin) { create(:admin) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
end
context 'General page' do
context 'feature flag :user_mode_in_session is enabled' do
before do
visit general_admin_application_settings_path
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
end
it 'Change visibility settings' do
page.within('.as-visibility-access') do
choose "application_setting_default_project_visibility_20"
click_button 'Save changes'
context 'General page' do
before do
visit general_admin_application_settings_path
end
expect(page).to have_content "Application settings saved successfully"
end
it 'Change visibility settings' do
page.within('.as-visibility-access') do
choose "application_setting_default_project_visibility_20"
click_button 'Save changes'
end
it 'Uncheck all restricted visibility levels' do
page.within('.as-visibility-access') do
find('#application_setting_visibility_level_0').set(false)
find('#application_setting_visibility_level_10').set(false)
find('#application_setting_visibility_level_20').set(false)
click_button 'Save changes'
expect(page).to have_content "Application settings saved successfully"
end
expect(page).to have_content "Application settings saved successfully"
expect(find('#application_setting_visibility_level_0')).not_to be_checked
expect(find('#application_setting_visibility_level_10')).not_to be_checked
expect(find('#application_setting_visibility_level_20')).not_to be_checked
end
it 'Uncheck all restricted visibility levels' do
page.within('.as-visibility-access') do
find('#application_setting_visibility_level_0').set(false)
find('#application_setting_visibility_level_10').set(false)
find('#application_setting_visibility_level_20').set(false)
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(find('#application_setting_visibility_level_0')).not_to be_checked
expect(find('#application_setting_visibility_level_10')).not_to be_checked
expect(find('#application_setting_visibility_level_20')).not_to be_checked
end
it 'Modify import sources' do
expect(current_settings.import_sources).not_to be_empty
it 'Modify import sources' do
expect(current_settings.import_sources).not_to be_empty
page.within('.as-visibility-access') do
Gitlab::ImportSources.options.map do |name, _|
uncheck name
page.within('.as-visibility-access') do
Gitlab::ImportSources.options.map do |name, _|
uncheck name
end
click_button 'Save changes'
end
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.import_sources).to be_empty
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.import_sources).to be_empty
page.within('.as-visibility-access') do
check "Repo by URL"
click_button 'Save changes'
end
page.within('.as-visibility-access') do
check "Repo by URL"
click_button 'Save changes'
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.import_sources).to eq(['git'])
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.import_sources).to eq(['git'])
end
it 'Change Visibility and Access Controls' do
page.within('.as-visibility-access') do
uncheck 'Project export enabled'
click_button 'Save changes'
end
it 'Change Visibility and Access Controls' do
page.within('.as-visibility-access') do
uncheck 'Project export enabled'
click_button 'Save changes'
expect(current_settings.project_export_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
expect(current_settings.project_export_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
it 'Change Keys settings' do
page.within('.as-visibility-access') do
select 'Are forbidden', from: 'RSA SSH keys'
select 'Are allowed', from: 'DSA SSH keys'
select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
select 'Are forbidden', from: 'ED25519 SSH keys'
click_on 'Save changes'
end
it 'Change Keys settings' do
page.within('.as-visibility-access') do
select 'Are forbidden', from: 'RSA SSH keys'
select 'Are allowed', from: 'DSA SSH keys'
select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
select 'Are forbidden', from: 'ED25519 SSH keys'
click_on 'Save changes'
end
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE.to_s
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE.to_s
expect(page).to have_content 'Application settings saved successfully'
expect(find_field('RSA SSH keys').value).to eq(forbidden)
expect(find_field('DSA SSH keys').value).to eq('0')
expect(find_field('ECDSA SSH keys').value).to eq('384')
expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
end
expect(page).to have_content 'Application settings saved successfully'
expect(find_field('RSA SSH keys').value).to eq(forbidden)
expect(find_field('DSA SSH keys').value).to eq('0')
expect(find_field('ECDSA SSH keys').value).to eq('384')
expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
end
it 'Change Account and Limit Settings' do
page.within('.as-account-limit') do
uncheck 'Gravatar enabled'
click_button 'Save changes'
end
it 'Change Account and Limit Settings' do
page.within('.as-account-limit') do
uncheck 'Gravatar enabled'
click_button 'Save changes'
expect(current_settings.gravatar_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
expect(current_settings.gravatar_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
it 'Change New users set to external', :js do
user_internal_regex = find('#application_setting_user_default_internal_regex', visible: :all)
it 'Change New users set to external', :js do
user_internal_regex = find('#application_setting_user_default_internal_regex', visible: :all)
expect(user_internal_regex).to be_readonly
expect(user_internal_regex['placeholder']).to eq 'To define internal users, first enable new users set to external'
expect(user_internal_regex).to be_readonly
expect(user_internal_regex['placeholder']).to eq 'To define internal users, first enable new users set to external'
check 'application_setting_user_default_external'
check 'application_setting_user_default_external'
expect(user_internal_regex).not_to be_readonly
expect(user_internal_regex['placeholder']).to eq 'Regex pattern'
end
expect(user_internal_regex).not_to be_readonly
expect(user_internal_regex['placeholder']).to eq 'Regex pattern'
end
it 'Change Sign-in restrictions' do
page.within('.as-signin') do
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
click_button 'Save changes'
end
it 'Change Sign-in restrictions' do
page.within('.as-signin') do
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
click_button 'Save changes'
expect(current_settings.home_page_url).to eq "https://about.gitlab.com/"
expect(page).to have_content "Application settings saved successfully"
end
expect(current_settings.home_page_url).to eq "https://about.gitlab.com/"
expect(page).to have_content "Application settings saved successfully"
end
it 'Terms of Service' do
# Already have the admin accept terms, so they don't need to accept in this spec.
_existing_terms = create(:term)
accept_terms(admin)
it 'Terms of Service' do
# Already have the admin accept terms, so they don't need to accept in this spec.
_existing_terms = create(:term)
accept_terms(admin)
page.within('.as-terms') do
check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.'
fill_in 'Terms of Service Agreement', with: 'Be nice!'
click_button 'Save changes'
end
page.within('.as-terms') do
check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.'
fill_in 'Terms of Service Agreement', with: 'Be nice!'
click_button 'Save changes'
expect(current_settings.enforce_terms).to be(true)
expect(current_settings.terms).to eq 'Be nice!'
expect(page).to have_content 'Application settings saved successfully'
end
expect(current_settings.enforce_terms).to be(true)
expect(current_settings.terms).to eq 'Be nice!'
expect(page).to have_content 'Application settings saved successfully'
end
it 'Modify oauth providers' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
it 'Modify oauth providers' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
uncheck 'Google'
click_button 'Save changes'
end
page.within('.as-signin') do
uncheck 'Google'
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
page.within('.as-signin') do
check "Google"
click_button 'Save changes'
end
page.within('.as-signin') do
check "Google"
click_button 'Save changes'
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
it 'Oauth providers do not raise validation errors when saving unrelated changes' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
it 'Oauth providers do not raise validation errors when saving unrelated changes' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
uncheck 'Google'
click_button 'Save changes'
end
page.within('.as-signin') do
uncheck 'Google'
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
# Remove google_oauth2 from the Omniauth strategies
allow(Devise).to receive(:omniauth_providers).and_return([])
# Remove google_oauth2 from the Omniauth strategies
allow(Devise).to receive(:omniauth_providers).and_return([])
# Save an unrelated setting
page.within('.as-terms') do
click_button 'Save changes'
end
# Save an unrelated setting
page.within('.as-terms') do
click_button 'Save changes'
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
it 'Configure web terminal' do
page.within('.as-terminal') do
fill_in 'Max session time', with: 15
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.terminal_max_session_time).to eq(15)
end
end
it 'Configure web terminal' do
page.within('.as-terminal') do
fill_in 'Max session time', with: 15
click_button 'Save changes'
context 'Integrations page' do
before do
visit integrations_admin_application_settings_path
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.terminal_max_session_time).to eq(15)
end
end
it 'Enable hiding third party offers' do
page.within('.as-third-party-offers') do
check 'Do not display offers from third parties within GitLab'
click_button 'Save changes'
end
context 'Integrations page' do
before do
visit integrations_admin_application_settings_path
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.hide_third_party_offers).to be true
end
it 'Change Slack Notifications Service template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken pipelines')
page.select 'All branches', from: 'Branches to be notified'
check_all_events
click_on 'Save'
it 'Enable hiding third party offers' do
page.within('.as-third-party-offers') do
check 'Do not display offers from third parties within GitLab'
click_button 'Save changes'
expect(page).to have_content 'Application settings saved successfully'
click_link 'Slack notifications'
page.all('input[type=checkbox]').each do |checkbox|
expect(checkbox).to be_checked
end
expect(find_field('Webhook').value).to eq 'http://localhost'
expect(find_field('Username').value).to eq 'test_user'
expect(find('#service_push_channel').value).to eq '#test_channel'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.hide_third_party_offers).to be true
it 'defaults Deployment events to false for chat notification template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
expect(find_field('Deployment')).not_to be_checked
end
end
it 'Change Slack Notifications Service template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken pipelines')
page.select 'All branches', from: 'Branches to be notified'
context 'CI/CD page' do
it 'Change CI/CD settings' do
visit ci_cd_admin_application_settings_path
page.within('.as-ci-cd') do
check 'Default to Auto DevOps pipeline for all projects'
fill_in 'application_setting_auto_devops_domain', with: 'domain.com'
click_button 'Save changes'
end
check_all_events
click_on 'Save'
expect(current_settings.auto_devops_enabled?).to be true
expect(current_settings.auto_devops_domain).to eq('domain.com')
expect(page).to have_content "Application settings saved successfully"
end
end
expect(page).to have_content 'Application settings saved successfully'
context 'Reporting page' do
it 'Change Spam settings' do
visit reporting_admin_application_settings_path
click_link 'Slack notifications'
page.within('.as-spam') do
check 'Enable reCAPTCHA'
check 'Enable reCAPTCHA for login'
fill_in 'reCAPTCHA Site Key', with: 'key'
fill_in 'reCAPTCHA Private Key', with: 'key'
fill_in 'IPs per user', with: 15
click_button 'Save changes'
end
page.all('input[type=checkbox]').each do |checkbox|
expect(checkbox).to be_checked
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.recaptcha_enabled).to be true
expect(current_settings.login_recaptcha_protection_enabled).to be true
expect(current_settings.unique_ips_limit_per_user).to eq(15)
end
expect(find_field('Webhook').value).to eq 'http://localhost'
expect(find_field('Username').value).to eq 'test_user'
expect(find('#service_push_channel').value).to eq '#test_channel'
end
it 'defaults Deployment events to false for chat notification template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
context 'Metrics and profiling page' do
before do
visit metrics_and_profiling_admin_application_settings_path
end
expect(find_field('Deployment')).not_to be_checked
end
end
it 'Change Influx settings' do
page.within('.as-influx') do
check 'Enable InfluxDB Metrics'
click_button 'Save changes'
end
context 'CI/CD page' do
it 'Change CI/CD settings' do
visit ci_cd_admin_application_settings_path
expect(current_settings.metrics_enabled?).to be true
expect(page).to have_content "Application settings saved successfully"
end
it 'Change Prometheus settings' do
page.within('.as-prometheus') do
check 'Enable Prometheus Metrics'
click_button 'Save changes'
end
page.within('.as-ci-cd') do
check 'Default to Auto DevOps pipeline for all projects'
fill_in 'application_setting_auto_devops_domain', with: 'domain.com'
click_button 'Save changes'
expect(current_settings.prometheus_metrics_enabled?).to be true
expect(page).to have_content "Application settings saved successfully"
end
expect(current_settings.auto_devops_enabled?).to be true
expect(current_settings.auto_devops_domain).to eq('domain.com')
expect(page).to have_content "Application settings saved successfully"
end
end
it 'Change Performance bar settings' do
group = create(:group)
page.within('.as-performance-bar') do
check 'Enable access to the Performance Bar'
fill_in 'Allowed group', with: group.path
click_on 'Save changes'
end
context 'Reporting page' do
it 'Change Spam settings' do
visit reporting_admin_application_settings_path
expect(page).to have_content "Application settings saved successfully"
expect(find_field('Enable access to the Performance Bar')).to be_checked
expect(find_field('Allowed group').value).to eq group.path
page.within('.as-spam') do
check 'Enable reCAPTCHA'
check 'Enable reCAPTCHA for login'
fill_in 'reCAPTCHA Site Key', with: 'key'
fill_in 'reCAPTCHA Private Key', with: 'key'
fill_in 'IPs per user', with: 15
click_button 'Save changes'
page.within('.as-performance-bar') do
uncheck 'Enable access to the Performance Bar'
click_on 'Save changes'
end
expect(page).to have_content 'Application settings saved successfully'
expect(find_field('Enable access to the Performance Bar')).not_to be_checked
expect(find_field('Allowed group').value).to be_nil
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.recaptcha_enabled).to be true
expect(current_settings.login_recaptcha_protection_enabled).to be true
expect(current_settings.unique_ips_limit_per_user).to eq(15)
end
end
it 'loads usage ping payload on click', :js do
expect(page).to have_button 'Preview payload'
context 'Metrics and profiling page' do
before do
visit metrics_and_profiling_admin_application_settings_path
end
find('.js-usage-ping-payload-trigger').click
it 'Change Influx settings' do
page.within('.as-influx') do
check 'Enable InfluxDB Metrics'
click_button 'Save changes'
expect(page).to have_selector '.js-usage-ping-payload'
expect(page).to have_button 'Hide payload'
end
end
context 'Network page' do
it 'Changes Outbound requests settings' do
visit network_admin_application_settings_path
expect(current_settings.metrics_enabled?).to be true
expect(page).to have_content "Application settings saved successfully"
page.within('.as-outbound') do
check 'Allow requests to the local network from web hooks and services'
# Enabled by default
uncheck 'Allow requests to the local network from system hooks'
# Enabled by default
uncheck 'Enforce DNS rebinding attack protection'
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.allow_local_requests_from_web_hooks_and_services).to be true
expect(current_settings.allow_local_requests_from_system_hooks).to be false
expect(current_settings.dns_rebinding_protection_enabled).to be false
end
end
it 'Change Prometheus settings' do
page.within('.as-prometheus') do
check 'Enable Prometheus Metrics'
click_button 'Save changes'
context 'Preferences page' do
before do
visit preferences_admin_application_settings_path
end
expect(current_settings.prometheus_metrics_enabled?).to be true
expect(page).to have_content "Application settings saved successfully"
end
it 'Change Help page' do
new_support_url = 'http://example.com/help'
it 'Change Performance bar settings' do
group = create(:group)
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
fill_in 'Support page URL', with: new_support_url
click_button 'Save changes'
end
page.within('.as-performance-bar') do
check 'Enable access to the Performance Bar'
fill_in 'Allowed group', with: group.path
click_on 'Save changes'
expect(current_settings.help_page_text).to eq "Example text"
expect(current_settings.help_page_hide_commercial_content).to be_truthy
expect(current_settings.help_page_support_url).to eq new_support_url
expect(page).to have_content "Application settings saved successfully"
end
expect(page).to have_content "Application settings saved successfully"
expect(find_field('Enable access to the Performance Bar')).to be_checked
expect(find_field('Allowed group').value).to eq group.path
it 'Change Pages settings' do
page.within('.as-pages') do
fill_in 'Maximum size of pages (MB)', with: 15
check 'Require users to prove ownership of custom domains'
click_button 'Save changes'
end
page.within('.as-performance-bar') do
uncheck 'Enable access to the Performance Bar'
click_on 'Save changes'
expect(current_settings.max_pages_size).to eq 15
expect(current_settings.pages_domain_verification_enabled?).to be_truthy
expect(page).to have_content "Application settings saved successfully"
end
expect(page).to have_content 'Application settings saved successfully'
expect(find_field('Enable access to the Performance Bar')).not_to be_checked
expect(find_field('Allowed group').value).to be_nil
end
it 'Change Real-time features settings' do
page.within('.as-realtime') do
fill_in 'Polling interval multiplier', with: 5.0
click_button 'Save changes'
end
it 'loads usage ping payload on click', :js do
expect(page).to have_button 'Preview payload'
expect(current_settings.polling_interval_multiplier).to eq 5.0
expect(page).to have_content "Application settings saved successfully"
end
find('.js-usage-ping-payload-trigger').click
it 'shows an error when validation fails' do
page.within('.as-realtime') do
fill_in 'Polling interval multiplier', with: -1.0
click_button 'Save changes'
end
expect(page).to have_selector '.js-usage-ping-payload'
expect(page).to have_button 'Hide payload'
end
end
expect(current_settings.polling_interval_multiplier).not_to eq(-1.0)
expect(page)
.to have_content "The form contains the following error: Polling interval multiplier must be greater than or equal to 0"
end
context 'Network page' do
it 'Changes Outbound requests settings' do
visit network_admin_application_settings_path
it "Change Pages Let's Encrypt settings" do
visit preferences_admin_application_settings_path
page.within('.as-pages') do
fill_in 'Email', with: 'my@test.example.com'
check "I have read and agree to the Let's Encrypt Terms of Service"
click_button 'Save changes'
end
page.within('.as-outbound') do
check 'Allow requests to the local network from web hooks and services'
# Enabled by default
uncheck 'Allow requests to the local network from system hooks'
# Enabled by default
uncheck 'Enforce DNS rebinding attack protection'
click_button 'Save changes'
expect(current_settings.lets_encrypt_notification_email).to eq 'my@test.example.com'
expect(current_settings.lets_encrypt_terms_of_service_accepted).to eq true
end
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.allow_local_requests_from_web_hooks_and_services).to be true
expect(current_settings.allow_local_requests_from_system_hooks).to be false
expect(current_settings.dns_rebinding_protection_enabled).to be false
end
end
context 'Preferences page' do
before do
visit preferences_admin_application_settings_path
end
context 'Nav bar' do
it 'Shows default help links in nav' do
default_support_url = 'https://about.gitlab.com/getting-help/'
it 'Change Help page' do
new_support_url = 'http://example.com/help'
visit root_dashboard_path
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
fill_in 'Support page URL', with: new_support_url
click_button 'Save changes'
find('.header-help-dropdown-toggle').click
page.within '.header-help' do
expect(page).to have_link(text: 'Help', href: help_path)
expect(page).to have_link(text: 'Support', href: default_support_url)
end
end
expect(current_settings.help_page_text).to eq "Example text"
expect(current_settings.help_page_hide_commercial_content).to be_truthy
expect(current_settings.help_page_support_url).to eq new_support_url
expect(page).to have_content "Application settings saved successfully"
end
it 'Shows custom support url in nav when set' do
new_support_url = 'http://example.com/help'
stub_application_setting(help_page_support_url: new_support_url)
it 'Change Pages settings' do
page.within('.as-pages') do
fill_in 'Maximum size of pages (MB)', with: 15
check 'Require users to prove ownership of custom domains'
click_button 'Save changes'
end
visit root_dashboard_path
expect(current_settings.max_pages_size).to eq 15
expect(current_settings.pages_domain_verification_enabled?).to be_truthy
expect(page).to have_content "Application settings saved successfully"
find('.header-help-dropdown-toggle').click
page.within '.header-help' do
expect(page).to have_link(text: 'Support', href: new_support_url)
end
end
end
it 'Change Real-time features settings' do
page.within('.as-realtime') do
fill_in 'Polling interval multiplier', with: 5.0
click_button 'Save changes'
context 'when in admin_mode' do
it 'contains link to leave admin mode' do
page.within('.navbar-sub-nav') do
expect(page).to have_link(href: destroy_admin_session_path)
end
end
expect(current_settings.polling_interval_multiplier).to eq 5.0
expect(page).to have_content "Application settings saved successfully"
end
it 'can leave admin mode' do
page.within('.navbar-sub-nav') do
# Select first, link is also included in mobile view list
click_on 'Leave admin mode', match: :first
it 'shows an error when validation fails' do
page.within('.as-realtime') do
fill_in 'Polling interval multiplier', with: -1.0
click_button 'Save changes'
expect(page).to have_link(href: new_admin_session_path)
end
end
expect(current_settings.polling_interval_multiplier).not_to eq(-1.0)
expect(page)
.to have_content "The form contains the following error: Polling interval multiplier must be greater than or equal to 0"
it 'can open pages not in admin scope' do
page.within('.navbar-sub-nav') do
find_all('a', text: 'Projects').first.click
expect(page).to have_current_path(dashboard_projects_path)
end
end
end
it "Change Pages Let's Encrypt settings" do
visit preferences_admin_application_settings_path
page.within('.as-pages') do
fill_in 'Email', with: 'my@test.example.com'
check "I have read and agree to the Let's Encrypt Terms of Service"
click_button 'Save changes'
context 'when not in admin mode' do
before do
page.within('.navbar-sub-nav') do
# Select first, link is also included in mobile view list
click_on 'Leave admin mode', match: :first
end
end
expect(current_settings.lets_encrypt_notification_email).to eq 'my@test.example.com'
expect(current_settings.lets_encrypt_terms_of_service_accepted).to eq true
end
end
it 'has no leave admin mode button' do
page.within('.navbar-sub-nav') do
expect(page).not_to have_link(href: destroy_admin_session_path)
end
end
context 'Nav bar' do
it 'Shows default help links in nav' do
default_support_url = 'https://about.gitlab.com/getting-help/'
it 'is necessary to provide credentials again before opening admin settings' do
visit admin_application_settings_path # admin logged out because not in admin_mode
visit root_dashboard_path
expect(page).to have_current_path(new_admin_session_path)
end
find('.header-help-dropdown-toggle').click
it 'can open pages not in admin scope' do
page.within('.navbar-sub-nav') do
find_all('a', text: 'Projects').first.click
end
page.within '.header-help' do
expect(page).to have_link(text: 'Help', href: help_path)
expect(page).to have_link(text: 'Support', href: default_support_url)
expect(page).to have_current_path(dashboard_projects_path)
end
end
end
it 'Shows custom support url in nav when set' do
new_support_url = 'http://example.com/help'
stub_application_setting(help_page_support_url: new_support_url)
context 'feature flag :user_mode_in_session is disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
visit root_dashboard_path
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
find('.header-help-dropdown-toggle').click
sign_in(admin)
visit admin_application_settings_path
end
it 'loads admin settings page without redirect for reauthentication' do
expect(current_path).to eq admin_application_settings_path
end
page.within '.header-help' do
expect(page).to have_link(text: 'Support', href: new_support_url)
it 'shows no admin mode buttons in navbar' do
page.within('.navbar-sub-nav') do
expect(page).not_to have_link(href: new_admin_session_path)
expect(page).not_to have_link(href: destroy_admin_session_path)
end
end
end
......
require 'spec_helper'
describe NavHelper do
describe NavHelper, :do_not_mock_admin_mode do
describe '#header_links' do
include_context 'custom session'
before do
allow(helper).to receive(:session) { {} }
allow(helper).to receive(:session).and_return(session)
end
context 'when the user is logged in' do
let(:user) { build(:user) }
let(:user) { create(:user) }
let(:current_user_mode) { Gitlab::Auth::CurrentUserMode.new(user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:current_user_mode).and_return(current_user_mode)
allow(helper).to receive(:can?) { true }
end
......@@ -26,6 +30,46 @@ describe NavHelper do
expect(helper.header_links).to include(:admin_impersonation)
end
context 'as admin' do
let(:user) { create(:user, :admin) }
context 'feature flag :user_mode_in_session is enabled' do
it 'does not contain the admin mode link by default' do
expect(helper.header_links).not_to include(:admin_mode)
end
context 'with admin mode enabled' do
before do
current_user_mode.enable_admin_mode!(password: user.password)
end
it 'contains the admin mode link' do
expect(helper.header_links).to include(:admin_mode)
end
end
end
context 'feature flag :user_mode_in_session is disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
end
it 'does not contain the admin mode link' do
expect(helper.header_links).not_to include(:admin_mode)
end
context 'with admin mode enabled' do
before do
current_user_mode.enable_admin_mode!(password: user.password)
end
it 'has no effect on header links' do
expect(helper.header_links).not_to include(:admin_mode)
end
end
end
end
context 'when the user cannot read cross project' do
before do
allow(helper).to receive(:can?).with(user, :read_cross_project) { false }
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
include_context 'custom session'
let(:user) { build(:user) }
subject { described_class.new(user) }
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session])
end
describe '#admin_mode?', :request_store do
context 'when the user is a regular user' do
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
it 'cannot be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password)
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
it 'cannot be enabled with empty params' do
subject.enable_admin_mode!
expect(subject.admin_mode?).to be(false)
end
it 'disable has no effect' do
subject.enable_admin_mode!
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(false)
end
context 'skipping password validation' do
it 'cannot be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
expect(subject.admin_mode?).to be(false)
end
it 'cannot be enabled with an invalid password' do
subject.enable_admin_mode!(skip_password_validation: true)
expect(subject.admin_mode?).to be(false)
end
end
end
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
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
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
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'
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
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
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.enable_admin_mode!(password: user.password)
end
expect(subject.admin_mode?).to be(true)
end
end
end
end
describe '#enable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'creates a timestamp in the session' do
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of Time.now))
end
end
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'sets the session timestamp to nil' do
subject.disable_admin_mode!
expect(session).to include(expected_session_entry(be_nil))
end
end
def expected_session_entry(value_matcher)
{
Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher)
}
end
end
......@@ -343,6 +343,8 @@ describe API::Helpers do
end
context 'sudo' do
include_context 'custom session'
shared_examples 'successful sudo' do
it 'sets current_user' do
expect(current_user).to eq(user)
......
......@@ -3,9 +3,14 @@ require 'spec_helper'
describe BuildActionEntity do
let(:job) { create(:ci_build, name: 'test_job') }
let(:request) { double('request') }
let(:user) { create(:user) }
let(:entity) do
described_class.new(job, request: spy('request'))
described_class.new(job, request: request)
end
before do
allow(request).to receive(:current_user).and_return(user)
end
describe '#as_json' do
......
......@@ -160,6 +160,25 @@ RSpec.configure do |config|
allow(Gitlab::Git::KeepAround).to receive(:execute)
Gitlab::ThreadMemoryCache.cache_backend.clear
# Temporary patch to force admin mode to be active by default in tests when
# using the feature flag :user_mode_in_session, since this will require
# modifying a significant number of specs to test both states for admin
# mode enabled / disabled.
#
# See https://gitlab.com/gitlab-org/gitlab/issues/31511
# See gitlab/spec/support/helpers/admin_mode_helpers.rb
#
# If it is required to have the real behaviour that an admin is signed in
# with normal user mode and needs to switch to admin mode, it is possible to
# mark such tests with the `do_not_mock_admin_mode` metadata tag, e.g:
#
# context 'some test with normal user mode', :do_not_mock_admin_mode do ... end
unless example.metadata[:do_not_mock_admin_mode]
allow_any_instance_of(Gitlab::Auth::CurrentUserMode).to receive(:admin_mode?) do |current_user_mode|
current_user_mode.send(:user)&.admin?
end
end
end
config.around(:example, :quarantine) do |example|
......
# frozen_string_literal: true
# Helper for enabling admin mode in tests
module AdminModeHelper
# Users 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)
def enable_admin_mode!(user)
fake_user_mode = instance_double(Gitlab::Auth::CurrentUserMode)
allow(Gitlab::Auth::CurrentUserMode).to receive(:new).with(user).and_return(fake_user_mode)
allow(fake_user_mode).to receive(:admin_mode?).and_return(user&.admin?)
end
end
......@@ -48,6 +48,14 @@ module LoginHelpers
@current_user = user
end
def gitlab_enable_admin_mode_sign_in(user)
visit new_admin_session_path
fill_in 'password', with: user.password
click_button 'Enter admin mode'
end
def gitlab_sign_in_via(provider, user, uid, saml_response = nil)
mock_auth_hash_with_saml_xml(provider, uid, user.email, saml_response)
visit new_user_session_path
......
# frozen_string_literal: true
# the session is empty by default; you can overwrite it by defining your own
# let(:session) variable
# we do not use a parameter such as |session| because it does not play nice
# with let variables
shared_context 'custom session' do
let!(:session) { {} }
around do |example|
Gitlab::Session.with_session(session) do
example.run
end
end
end
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
it "shows enter password form" do
render
expect(rendered).to have_css('#login-pane.active')
expect(rendered).to have_selector('input[name="password"]')
end
end
context 'admin has no password set' do
before do
allow(view).to receive(:password_authentication_enabled_for_web?).and_return(false)
end
it "warns authentication not possible" do
render
expect(rendered).not_to have_css('#login-pane')
expect(rendered).to have_content 'No authentication methods configured'
end
end
end
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