Commit 5707f305 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 759cd6c2
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
image: ruby:2.6-alpine image: ruby:2.6-alpine
stage: qa stage: qa
dependencies: [] dependencies: []
variables:
GIT_DEPTH: "1"
retry: 0 retry: 0
script: script:
- source scripts/utils.sh - source scripts/utils.sh
......
...@@ -87,9 +87,9 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors' ...@@ -87,9 +87,9 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
# GraphQL API # GraphQL API
gem 'graphql', '~> 1.9.11' gem 'graphql', '~> 1.9.11'
# NOTE: graphiql-rails v1.5+ doesn't work: https://gitlab.com/gitlab-org/gitlab-ce/issues/67293 # NOTE: graphiql-rails v1.5+ doesn't work: https://gitlab.com/gitlab-org/gitlab/issues/31771
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released: # TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/67263 # https://gitlab.com/gitlab-org/gitlab/issues/31747
gem 'graphiql-rails', '~> 1.4.10' gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.0.beta3' gem 'apollo_upload_server', '~> 2.0.0.beta3'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test] gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
......
...@@ -328,7 +328,7 @@ Thanks for the issue report. This issue has already been fixed in newer versions ...@@ -328,7 +328,7 @@ Thanks for the issue report. This issue has already been fixed in newer versions
Due to the size of this project and our limited resources we are only able to support the Due to the size of this project and our limited resources we are only able to support the
latest stable release as outlined in our [contributing guidelines](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html). latest stable release as outlined in our [contributing guidelines](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html).
In order to get this bug fix and enjoy many new features please In order to get this bug fix and enjoy many new features please
[upgrade](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). [upgrade](https://gitlab.com/gitlab-org/gitlab/tree/master/doc/update).
If you still experience issues at that time please open a new issue following our issue If you still experience issues at that time please open a new issue following our issue
tracker guidelines found in the [contributing guidelines](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#issue-tracker-guidelines). tracker guidelines found in the [contributing guidelines](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#issue-tracker-guidelines).
``` ```
...@@ -337,14 +337,14 @@ tracker guidelines found in the [contributing guidelines](https://docs.gitlab.co ...@@ -337,14 +337,14 @@ tracker guidelines found in the [contributing guidelines](https://docs.gitlab.co
``` ```
Thanks for your interest in improving the GitLab codebase! Thanks for your interest in improving the GitLab codebase!
Please update your merge request according to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/contributing/merge_request_workflow.md#merge-request-guidelines). Please update your merge request according to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/contributing/merge_request_workflow.md#merge-request-guidelines).
``` ```
### Accepting merge requests ### Accepting merge requests
``` ```
Is there an issue on the Is there an issue on the
[issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) that is [issue tracker](https://gitlab.com/gitlab-org/gitlab/issues) that is
similar to this? Could you please link it here? similar to this? Could you please link it here?
Please be aware that new functionality that is not marked Please be aware that new functionality that is not marked
[`Accepting merge requests`](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#label-for-community-contributors) [`Accepting merge requests`](https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#label-for-community-contributors)
......
...@@ -23,6 +23,46 @@ export const parseHeaderLine = (line = {}, lineNumber) => ({ ...@@ -23,6 +23,46 @@ export const parseHeaderLine = (line = {}, lineNumber) => ({
lines: [], lines: [],
}); });
/**
* Finds the matching header section
* for the section_duration object and adds it to it
*
* {
* isHeader: true,
* line: {
* content: [],
* lineNumber: 0,
* section_duration: "",
* },
* lines: []
* }
*
* @param Array data
* @param Object durationLine
*/
export function addDurationToHeader(data, durationLine) {
data.forEach(el => {
if (el.line && el.line.section === durationLine.section) {
el.line.section_duration = durationLine.section_duration;
}
});
}
/**
* Check is the current section belongs to a collapsible section
*
* @param Array acc
* @param Object last
* @param Object section
*
* @returns Boolean
*/
export const isCollapsibleSection = (acc = [], last = {}, section = {}) =>
acc.length > 0 &&
last.isHeader === true &&
!section.section_duration &&
section.section === last.line.section;
/** /**
* Parses the job log content into a structure usable by the template * Parses the job log content into a structure usable by the template
* *
...@@ -32,28 +72,35 @@ export const parseHeaderLine = (line = {}, lineNumber) => ({ ...@@ -32,28 +72,35 @@ export const parseHeaderLine = (line = {}, lineNumber) => ({
* - adds a isHeader property to handle template logic * - adds a isHeader property to handle template logic
* - adds the section_duration * - adds the section_duration
* For each line: * For each line:
* - adds the index as lineNumber * - adds the index as lineNumber
* *
* @param {Array} lines * @param Array lines
* @returns {Array} * @param Number lineNumberStart
* @param Array accumulator
* @returns Array parsed log lines
*/ */
export const logLinesParser = (lines = [], lineNumberStart) => export const logLinesParser = (lines = [], lineNumberStart, accumulator = []) =>
lines.reduce((acc, line, index) => { lines.reduce((acc, line, index) => {
const lineNumber = lineNumberStart ? lineNumberStart + index : index; const lineNumber = lineNumberStart ? lineNumberStart + index : index;
const last = acc[acc.length - 1]; const last = acc[acc.length - 1];
// If the object is an header, we parse it into another structure
if (line.section_header) { if (line.section_header) {
acc.push(parseHeaderLine(line, lineNumber)); acc.push(parseHeaderLine(line, lineNumber));
} else if (acc.length && last.isHeader && !line.section_duration && line.content.length) { } else if (isCollapsibleSection(acc, last, line)) {
// if the object belongs to a nested section, we append it to the new `lines` array of the
// previously formated header
last.lines.push(parseLine(line, lineNumber)); last.lines.push(parseLine(line, lineNumber));
} else if (acc.length && last.isHeader && line.section_duration) { } else if (line.section_duration) {
last.section_duration = line.section_duration; // if the line has section_duration, we look for the correct header to add it
} else if (line.content.length) { addDurationToHeader(acc, line);
} else {
// otherwise it's a regular line
acc.push(parseLine(line, lineNumber)); acc.push(parseLine(line, lineNumber));
} }
return acc; return acc;
}, []); }, accumulator);
/** /**
* Finds the repeated offset, removes the old one * Finds the repeated offset, removes the old one
......
.card-header { .card-header {
&:first-child { &:first-child {
// intended use case: card with only a header (for example empty related issues) // intended use case: card with only a header (for example empty related issues)
&.border-0, &:last-child {
&.border-bottom-0 {
@include border-radius($card-inner-border-radius); @include border-radius($card-inner-border-radius);
} }
} }
......
# 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 ...@@ -36,6 +36,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true protect_from_forgery with: :exception, prepend: true
helper_method :can? helper_method :can?
helper_method :current_user_mode
helper_method :import_sources_enabled?, :github_import_enabled?, helper_method :import_sources_enabled?, :github_import_enabled?,
:gitea_import_enabled?, :github_import_configured?, :gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?,
...@@ -533,6 +534,10 @@ class ApplicationController < ActionController::Base ...@@ -533,6 +534,10 @@ class ApplicationController < ActionController::Base
yield yield
end end
end end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
end end
ApplicationController.prepend_if_ee('EE::ApplicationController') ApplicationController.prepend_if_ee('EE::ApplicationController')
...@@ -14,6 +14,16 @@ module EnforcesAdminAuthentication ...@@ -14,6 +14,16 @@ module EnforcesAdminAuthentication
end end
def authenticate_admin! 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
end end
...@@ -5,6 +5,12 @@ ...@@ -5,6 +5,12 @@
# Controller concern to handle PAT, RSS, and static objects token authentication methods # Controller concern to handle PAT, RSS, and static objects token authentication methods
# #
module SessionlessAuthentication 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 # This filter handles personal access tokens, atom requests with rss tokens, and static object tokens
def authenticate_sessionless_user!(request_format) def authenticate_sessionless_user!(request_format)
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format) user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
...@@ -25,4 +31,8 @@ module SessionlessAuthentication ...@@ -25,4 +31,8 @@ module SessionlessAuthentication
sign_in(user, store: false, message: :sessionless_sign_in) sign_in(user, store: false, message: :sessionless_sign_in)
end end
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 end
...@@ -20,9 +20,7 @@ class HealthController < ActionController::Base ...@@ -20,9 +20,7 @@ class HealthController < ActionController::Base
end end
def liveness def liveness
results = CHECKS.map { |check| [check.name, check.liveness] } render json: { status: 'ok' }, status: :ok
render_check_results(results)
end end
private private
......
...@@ -86,6 +86,12 @@ module NavHelper ...@@ -86,6 +86,12 @@ module NavHelper
links << :admin_impersonation links << :admin_impersonation
end end
if Feature.enabled?(:user_mode_in_session)
if current_user&.admin? && current_user_mode&.admin_mode?
links << :admin_mode
end
end
links links
end end
end end
......
...@@ -5,7 +5,13 @@ require_dependency 'declarative_policy' ...@@ -5,7 +5,13 @@ require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base class BasePolicy < DeclarativePolicy::Base
desc "User is an instance admin" desc "User is an instance admin"
with_options scope: :user, score: 0 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" desc "User is blocked"
with_options scope: :user, score: 0 with_options scope: :user, score: 0
......
...@@ -32,7 +32,7 @@ module Projects ...@@ -32,7 +32,7 @@ module Projects
# This is a hack as the registry doesn't support deleting individual # This is a hack as the registry doesn't support deleting individual
# tags. This code effectively pushes a dummy image and assigns the tag to it. # tags. This code effectively pushes a dummy image and assigns the tag to it.
# This way when the tag is deleted only the dummy image is affected. # This way when the tag is deleted only the dummy image is affected.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/21405 for a discussion # See https://gitlab.com/gitlab-org/gitlab/issues/15737 for a discussion
def smart_delete(container_repository, tag_names) def smart_delete(container_repository, tag_names)
# generates the blobs for the dummy image # generates the blobs for the dummy image
dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path) dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path)
......
= 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 @@ ...@@ -68,6 +68,15 @@
= nav_link(controller: 'admin/dashboard') do = nav_link(controller: 'admin/dashboard') do
= link_to admin_root_path, class: 'd-lg-none admin-icon qa-admin-area-link' do = link_to admin_root_path, class: 'd-lg-none admin-icon qa-admin-area-link' do
= _('Admin Area') = _('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? - if Gitlab::Sherlock.enabled?
%li %li
= link_to sherlock_transactions_path, class: 'd-lg-none admin-icon' do = link_to sherlock_transactions_path, class: 'd-lg-none admin-icon' do
...@@ -95,6 +104,17 @@ ...@@ -95,6 +104,17 @@
= nav_link(controller: 'admin/dashboard', html_options: { class: "d-none d-lg-block d-xl-block"}) do = 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 = 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) = 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? - if Gitlab::Sherlock.enabled?
%li %li
= link_to sherlock_transactions_path, class: 'admin-icon d-none d-lg-block d-xl-block', title: _('Sherlock Transactions'), = link_to sherlock_transactions_path, class: 'admin-icon d-none d-lg-block d-xl-block', title: _('Sherlock Transactions'),
......
...@@ -44,7 +44,7 @@ if (isJest) { ...@@ -44,7 +44,7 @@ if (isJest) {
plugins.push('@babel/plugin-transform-modules-commonjs'); plugins.push('@babel/plugin-transform-modules-commonjs');
/* /*
without the following, babel-plugin-istanbul throws an error: without the following, babel-plugin-istanbul throws an error:
https://gitlab.com/gitlab-org/gitlab-ce/issues/58390 https://gitlab.com/gitlab-org/gitlab-foss/issues/58390
*/ */
plugins.push('babel-plugin-dynamic-import-node'); plugins.push('babel-plugin-dynamic-import-node');
} }
......
---
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
---
title: Changes response body of liveness check to be more accurate
merge_request: 17655
author:
type: changed
--- ---
title: Redo fix for related issues border radius title: Redo fix for related issues border radius
merge_request: 17172 merge_request: 17480
author: author:
type: fixed type: fixed
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
# as a workaround until this is resolved. # as a workaround until this is resolved.
# #
# This can be removed once fog-google and google-api-client can be upgraded. # This can be removed once fog-google and google-api-client can be upgraded.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/66630 for more details. # See https://gitlab.com/gitlab-org/gitlab/issues/31280 for more details.
# #
require 'google/apis/container_v1beta1' require 'google/apis/container_v1beta1'
......
...@@ -21,6 +21,10 @@ namespace :admin do ...@@ -21,6 +21,10 @@ namespace :admin do
end end
end end
resource :session, only: [:new, :create] do
get 'destroy', action: :destroy, as: :destroy
end
resource :impersonation, only: :destroy resource :impersonation, only: :destroy
resources :abuse_reports, only: [:index, :destroy] resources :abuse_reports, only: [:index, :destroy]
......
...@@ -125,7 +125,7 @@ CAUTION: **Warning:** ...@@ -125,7 +125,7 @@ CAUTION: **Warning:**
**Extended downtime is required** so no new files are created in object storage during **Extended downtime is required** so no new files are created in object storage during
the migration. A configuration setting will be added soon to allow migrating the migration. A configuration setting will be added soon to allow migrating
from object storage to local files with only a brief moment of downtime for configuration changes. from object storage to local files with only a brief moment of downtime for configuration changes.
See issue [gitlab-org/gitlab-ce#66144](https://gitlab.com/gitlab-org/gitlab-ce/issues/66144) See issue [gitlab-org/gitlab#30979](https://gitlab.com/gitlab-org/gitlab/issues/30979)
### All-in-one rake task ### All-in-one rake task
......
...@@ -116,28 +116,11 @@ curl 'https://gitlab.example.com/-/liveness' ...@@ -116,28 +116,11 @@ curl 'https://gitlab.example.com/-/liveness'
Example response: Example response:
On success, the endpoint will return a valid successful HTTP status code, and a response like below. On success, the endpoint will return a `200` HTTP status code, and a response like below.
```json ```json
{ {
"db_check":{ "status": "ok"
"status":"ok"
},
"redis_check":{
"status":"ok"
},
"cache_check":{
"status":"ok"
},
"queues_check":{
"status":"ok"
},
"shared_state_check":{
"status":"ok"
},
"gitaly_check":{
"status":"ok"
}
} }
``` ```
......
...@@ -4,7 +4,7 @@ type: reference, howto, concepts ...@@ -4,7 +4,7 @@ type: reference, howto, concepts
# Subgroups # Subgroups
>[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2772) in GitLab 9.0. >[Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/2772) in GitLab 9.0.
Subgroups, also known as nested groups or hierarchical groups, allow you to have up to 20 Subgroups, also known as nested groups or hierarchical groups, allow you to have up to 20
levels of groups. levels of groups.
......
...@@ -3,7 +3,7 @@ const IS_EE = require('./config/helpers/is_ee_env'); ...@@ -3,7 +3,7 @@ const IS_EE = require('./config/helpers/is_ee_env');
const reporters = ['default']; const reporters = ['default'];
// To have consistent date time parsing both in local and CI environments we set // To have consistent date time parsing both in local and CI environments we set
// the timezone of the Node process. https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27738 // the timezone of the Node process. https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/27738
process.env.TZ = 'GMT'; process.env.TZ = 'GMT';
if (process.env.CI) { if (process.env.CI) {
......
...@@ -17,6 +17,8 @@ module API ...@@ -17,6 +17,8 @@ module API
request.access_token request.access_token
end end
use AdminModeMiddleware
helpers HelperMethods helpers HelperMethods
install_error_responders(base) install_error_responders(base)
...@@ -52,6 +54,11 @@ module API ...@@ -52,6 +54,11 @@ module API
forbidden!(api_access_denied_message(user)) forbidden!(api_access_denied_message(user))
end 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 user
end end
...@@ -141,5 +148,22 @@ module API ...@@ -141,5 +148,22 @@ module API
end end
end 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
end end
...@@ -1052,7 +1052,7 @@ module API ...@@ -1052,7 +1052,7 @@ module API
expose :job_events expose :job_events
# Expose serialized properties # Expose serialized properties
expose :properties do |service, options| expose :properties do |service, options|
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
if service.data_fields_present? if service.data_fields_present?
service.data_fields.as_json.slice(*service.api_field_names) service.data_fields.as_json.slice(*service.api_field_names)
else else
......
# 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
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# We use Workhorse to detect the real extension when we serve files with # We use Workhorse to detect the real extension when we serve files with
# the `SendsBlob` helper methods, and ask Workhorse to set the content # the `SendsBlob` helper methods, and ask Workhorse to set the content
# type when it serves the file: # type when it serves the file:
# https://gitlab.com/gitlab-org/gitlab-ce/blob/33e5955/app/helpers/workhorse_helper.rb#L48. # https://gitlab.com/gitlab-org/gitlab/blob/33e5955/app/helpers/workhorse_helper.rb#L48.
# #
# Because Workhorse has access to the content when it is downloaded, if # Because Workhorse has access to the content when it is downloaded, if
# the type/extension doesn't match the real type, we adjust the # the type/extension doesn't match the real type, we adjust the
......
...@@ -15,10 +15,6 @@ module Gitlab ...@@ -15,10 +15,6 @@ module Gitlab
raise NotImplementedError raise NotImplementedError
end end
def liveness
HealthChecks::Result.new(true)
end
def metrics def metrics
[] []
end end
......
...@@ -187,7 +187,7 @@ module Gitlab ...@@ -187,7 +187,7 @@ module Gitlab
.find_in_batches(batch_size: BATCH_SIZE) do |services| .find_in_batches(batch_size: BATCH_SIZE) do |services|
counts = services.group_by do |service| counts = services.group_by do |service|
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
service_url = service.data_fields&.url || (service.properties && service.properties['url']) service_url = service.data_fields&.url || (service.properties && service.properties['url'])
service_url&.include?('.atlassian.net') ? :cloud : :server service_url&.include?('.atlassian.net') ? :cloud : :server
end end
......
...@@ -1031,6 +1031,9 @@ msgstr "" ...@@ -1031,6 +1031,9 @@ msgstr ""
msgid "Admin Section" msgid "Admin Section"
msgstr "" msgstr ""
msgid "Admin mode disabled"
msgstr ""
msgid "Admin notes" msgid "Admin notes"
msgstr "" msgstr ""
...@@ -5734,6 +5737,9 @@ msgstr "" ...@@ -5734,6 +5737,9 @@ msgstr ""
msgid "Enter a number" msgid "Enter a number"
msgstr "" msgstr ""
msgid "Enter admin mode"
msgstr ""
msgid "Enter at least three characters to search" msgid "Enter at least three characters to search"
msgstr "" msgstr ""
...@@ -9123,6 +9129,9 @@ msgstr "" ...@@ -9123,6 +9129,9 @@ msgstr ""
msgid "Leave" msgid "Leave"
msgstr "" msgstr ""
msgid "Leave admin mode"
msgstr ""
msgid "Leave edit mode? All unsaved changes will be lost." msgid "Leave edit mode? All unsaved changes will be lost."
msgstr "" msgstr ""
...@@ -12739,6 +12748,9 @@ msgstr "" ...@@ -12739,6 +12748,9 @@ msgstr ""
msgid "Raw blob request rate limit per minute" msgid "Raw blob request rate limit per minute"
msgstr "" msgstr ""
msgid "Re-authentication required"
msgstr ""
msgid "Read more" msgid "Read more"
msgstr "" msgstr ""
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# Only some crawlers respect this setting, e.g. Googlebot does not # Only some crawlers respect this setting, e.g. Googlebot does not
# Crawl-delay: 1 # Crawl-delay: 1
# Based on details in https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/routes.rb, https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/routing, and using application # Based on details in https://gitlab.com/gitlab-org/gitlab/blob/master/config/routes.rb, https://gitlab.com/gitlab-org/gitlab/blob/master/spec/routing, and using application
User-Agent: * User-Agent: *
Disallow: /autocomplete/users Disallow: /autocomplete/users
Disallow: /search Disallow: /search
......
# 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 ...@@ -777,4 +777,48 @@ describe ApplicationController do
end end
end 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 end
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
require 'spec_helper' require 'spec_helper'
describe EnforcesAdminAuthentication do describe EnforcesAdminAuthentication, :do_not_mock_admin_mode do
include AdminModeHelper
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -10,30 +12,86 @@ describe EnforcesAdminAuthentication do ...@@ -10,30 +12,86 @@ describe EnforcesAdminAuthentication do
end end
controller(ApplicationController) do controller(ApplicationController) do
# `described_class` is not available in this context include EnforcesAdminAuthentication
include EnforcesAdminAuthentication # rubocop:disable RSpec/DescribedClass
def index def index
head :ok head :ok
end end
end end
describe 'authenticate_admin!' do context 'feature flag :user_mode_in_session is enabled' do
context 'as an admin' do describe 'authenticate_admin!' do
let(:user) { create(:admin) } context 'as an admin' do
let(:user) { create(:admin) }
it 'renders ok' do it 'renders redirect for re-authentication and does not set admin mode' do
get :index 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 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 describe 'authenticate_admin!' do
it 'renders a 404' do before do
get :index 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 end
end end
......
...@@ -76,10 +76,7 @@ describe HealthController do ...@@ -76,10 +76,7 @@ describe HealthController do
it 'responds with liveness checks data' do it 'responds with liveness checks data' do
subject subject
expect(json_response['db_check']['status']).to eq('ok') expect(json_response['status']).to eq('ok')
expect(json_response['cache_check']['status']).to eq('ok')
expect(json_response['queues_check']['status']).to eq('ok')
expect(json_response['shared_state_check']['status']).to eq('ok')
end end
end end
......
...@@ -117,7 +117,7 @@ FactoryBot.define do ...@@ -117,7 +117,7 @@ FactoryBot.define do
end end
# this is for testing storing values inside properties, which is deprecated and will be removed in # this is for testing storing values inside properties, which is deprecated and will be removed in
# https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 # https://gitlab.com/gitlab-org/gitlab/issues/29404
trait :without_properties_callback do trait :without_properties_callback do
jira_tracker_data nil jira_tracker_data nil
issue_tracker_data nil issue_tracker_data nil
......
This diff is collapsed.
...@@ -3,6 +3,8 @@ import { ...@@ -3,6 +3,8 @@ import {
updateIncrementalTrace, updateIncrementalTrace,
parseHeaderLine, parseHeaderLine,
parseLine, parseLine,
addDurationToHeader,
isCollapsibleSection,
findOffsetAndRemove, findOffsetAndRemove,
} from '~/jobs/store/utils'; } from '~/jobs/store/utils';
import { import {
...@@ -43,6 +45,127 @@ describe('Jobs Store Utils', () => { ...@@ -43,6 +45,127 @@ describe('Jobs Store Utils', () => {
}); });
}); });
describe('addDurationToHeader', () => {
const duration = {
offset: 106,
content: [],
section: 'prepare-script',
section_duration: '00:03',
};
it('adds the section duration to the correct header', () => {
const parsed = [
{
isClosed: true,
isHeader: true,
line: {
section: 'prepare-script',
content: [{ text: 'foo' }],
},
lines: [],
},
{
isClosed: true,
isHeader: true,
line: {
section: 'foo-bar',
content: [{ text: 'foo' }],
},
lines: [],
},
];
addDurationToHeader(parsed, duration);
expect(parsed[0].line.section_duration).toEqual(duration.section_duration);
expect(parsed[1].line.section_duration).toEqual(undefined);
});
it('does not add the section duration when the headers do not match', () => {
const parsed = [
{
isClosed: true,
isHeader: true,
line: {
section: 'bar-foo',
content: [{ text: 'foo' }],
},
lines: [],
},
{
isClosed: true,
isHeader: true,
line: {
section: 'foo-bar',
content: [{ text: 'foo' }],
},
lines: [],
},
];
addDurationToHeader(parsed, duration);
expect(parsed[0].line.section_duration).toEqual(undefined);
expect(parsed[1].line.section_duration).toEqual(undefined);
});
it('does not add when content has no headers', () => {
const parsed = [
{
section: 'bar-foo',
content: [{ text: 'foo' }],
lineNumber: 1,
},
{
section: 'foo-bar',
content: [{ text: 'foo' }],
lineNumber: 2,
},
];
addDurationToHeader(parsed, duration);
expect(parsed[0].line).toEqual(undefined);
expect(parsed[1].line).toEqual(undefined);
});
});
describe('isCollapsibleSection', () => {
const header = {
isHeader: true,
line: {
section: 'foo',
},
};
const line = {
lineNumber: 1,
section: 'foo',
content: [],
};
it('returns true when line belongs to the last section', () => {
expect(isCollapsibleSection([header], header, { section: 'foo', content: [] })).toEqual(true);
});
it('returns false when last line was not an header', () => {
expect(isCollapsibleSection([line], line, { section: 'bar' })).toEqual(false);
});
it('returns false when accumulator is empty', () => {
expect(isCollapsibleSection([], { isHeader: true }, { section: 'bar' })).toEqual(false);
});
it('returns false when section_duration is defined', () => {
expect(isCollapsibleSection([header], header, { section_duration: '10:00' })).toEqual(false);
});
it('returns false when `section` is not a match', () => {
expect(isCollapsibleSection([header], header, { section: 'bar' })).toEqual(false);
});
it('returns false when no parameters are provided', () => {
expect(isCollapsibleSection()).toEqual(false);
});
});
describe('logLinesParser', () => { describe('logLinesParser', () => {
let result; let result;
...@@ -75,7 +198,7 @@ describe('Jobs Store Utils', () => { ...@@ -75,7 +198,7 @@ describe('Jobs Store Utils', () => {
describe('section duration', () => { describe('section duration', () => {
it('adds the section information to the header section', () => { it('adds the section information to the header section', () => {
expect(result[1].section_duration).toEqual(utilsMockData[4].section_duration); expect(result[1].line.section_duration).toEqual(utilsMockData[4].section_duration);
}); });
it('does not add section duration as a line', () => { it('does not add section duration as a line', () => {
......
...@@ -89,7 +89,7 @@ export const release = { ...@@ -89,7 +89,7 @@ export const release = {
id: 2, id: 2,
name: 'my second link', name: 'my second link',
url: url:
'https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50', 'https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
external: false, external: false,
}, },
], ],
......
require 'spec_helper' require 'spec_helper'
describe NavHelper do describe NavHelper, :do_not_mock_admin_mode do
describe '#header_links' do describe '#header_links' do
include_context 'custom session'
before do before do
allow(helper).to receive(:session) { {} } allow(helper).to receive(:session).and_return(session)
end end
context 'when the user is logged in' do 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 before do
allow(helper).to receive(:current_user).and_return(user) 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 } allow(helper).to receive(:can?) { true }
end end
...@@ -26,6 +30,46 @@ describe NavHelper do ...@@ -26,6 +30,46 @@ describe NavHelper do
expect(helper.header_links).to include(:admin_impersonation) expect(helper.header_links).to include(:admin_impersonation)
end 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 context 'when the user cannot read cross project' do
before do before do
allow(helper).to receive(:can?).with(user, :read_cross_project) { false } 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
...@@ -58,9 +58,4 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| ...@@ -58,9 +58,4 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to have_attributes(success: false, message: "#{described_class.human_name} check timed out") } it { is_expected.to have_attributes(success: false, message: "#{described_class.human_name} check timed out") }
end end
end end
describe '#liveness' do
subject { described_class.readiness }
it { is_expected.to eq(Gitlab::HealthChecks::Result.new(true)) }
end
end end
...@@ -343,6 +343,8 @@ describe API::Helpers do ...@@ -343,6 +343,8 @@ describe API::Helpers do
end end
context 'sudo' do context 'sudo' do
include_context 'custom session'
shared_examples 'successful sudo' do shared_examples 'successful sudo' do
it 'sets current_user' do it 'sets current_user' do
expect(current_user).to eq(user) expect(current_user).to eq(user)
......
...@@ -3,9 +3,14 @@ require 'spec_helper' ...@@ -3,9 +3,14 @@ require 'spec_helper'
describe BuildActionEntity do describe BuildActionEntity do
let(:job) { create(:ci_build, name: 'test_job') } let(:job) { create(:ci_build, name: 'test_job') }
let(:request) { double('request') } let(:request) { double('request') }
let(:user) { create(:user) }
let(:entity) do 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 end
describe '#as_json' do describe '#as_json' do
......
...@@ -160,6 +160,25 @@ RSpec.configure do |config| ...@@ -160,6 +160,25 @@ RSpec.configure do |config|
allow(Gitlab::Git::KeepAround).to receive(:execute) allow(Gitlab::Git::KeepAround).to receive(:execute)
Gitlab::ThreadMemoryCache.cache_backend.clear 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 end
config.around(:example, :quarantine) do |example| 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 ...@@ -48,6 +48,14 @@ module LoginHelpers
@current_user = user @current_user = user
end 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) def gitlab_sign_in_via(provider, user, uid, saml_response = nil)
mock_auth_hash_with_saml_xml(provider, uid, user.email, saml_response) mock_auth_hash_with_saml_xml(provider, uid, user.email, saml_response)
visit new_user_session_path 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