Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-14 18:10:34 +00:00
parent 7172fb1031
commit 7d4b2ed7bf
94 changed files with 1828 additions and 340 deletions

View File

@ -53,6 +53,8 @@ workflow:
variables:
RAILS_ENV: "test"
NODE_ENV: "test"
BUNDLE_WITHOUT: "production:development"
BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
SIMPLECOV: "true"

View File

@ -4,11 +4,13 @@
- .qa-cache
stage: test
needs: []
variables:
USE_BUNDLE_INSTALL: "false"
SETUP_DB: "false"
before_script:
- '[ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb'
- !reference [.default-before_script, before_script]
- cd qa/
- bundle install --clean --jobs=$(nproc) --path=vendor --retry=3 --without=development --quiet
- bundle check
- bundle_install_script
qa:internal:
extends:

View File

@ -8,7 +8,8 @@
.minimal-bundle-install:
script:
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
- export BUNDLE_WITHOUT="${BUNDLE_WITHOUT}:default:test:puma:unicorn:kerberos:metrics:omnibus:ed25519"
- bundle_install_script
.base-script:
script:

View File

@ -218,8 +218,8 @@ danger-review:
stage: test
needs: []
before_script:
- source ./scripts/utils.sh
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --with danger"
- source scripts/utils.sh
- bundle_install_script "--with danger"
- run_timed_command "retry yarn install --frozen-lockfile"
script:
- >

View File

@ -35,8 +35,9 @@ export default {
inject: {
newUserListPath: { default: '' },
newFeatureFlagPath: { default: '' },
canUserConfigure: { required: true },
featureFlagsLimitExceeded: { required: true },
canUserConfigure: {},
featureFlagsLimitExceeded: {},
featureFlagsLimit: {},
},
data() {
const scope = getParameterByName('scope') || SCOPES.FEATURE_FLAG_SCOPE;

View File

@ -24,6 +24,7 @@ export default () => {
newFeatureFlagPath,
newUserListPath,
featureFlagsLimitExceeded,
featureFlagsLimit,
} = el.dataset;
return new Vue({
@ -40,7 +41,8 @@ export default () => {
canUserConfigure: canUserAdminFeatureFlag !== undefined,
newFeatureFlagPath,
newUserListPath,
featureFlagsLimitExceeded,
featureFlagsLimitExceeded: featureFlagsLimitExceeded !== undefined,
featureFlagsLimit,
},
render(createElement) {
return createElement(FeatureFlagsComponent);

View File

@ -7,9 +7,11 @@ class Admin::RunnersController < Admin::ApplicationController
feature_category :continuous_integration
NUMBER_OF_RUNNERS_PER_PAGE = 30
def index
finder = Ci::RunnersFinder.new(current_user: current_user, params: params)
@runners = finder.execute
@runners = finder.execute.page(params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
@active_runners_count = Ci::Runner.online.count
@sort = finder.sort_key
end

View File

@ -7,6 +7,7 @@ class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :cohorts, :new, :create]
before_action :check_impersonation_availability, only: :impersonate
before_action :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :check_ban_user_feature_flag, only: [:ban]
feature_category :users
@ -130,6 +131,24 @@ class Admin::UsersController < Admin::ApplicationController
end
end
def ban
result = Users::BanService.new(current_user).execute(user)
if result[:status] == :success
redirect_back_or_admin_user(notice: _("Successfully banned"))
else
redirect_back_or_admin_user(alert: _("Error occurred. User was not banned"))
end
end
def unban
if update_user { |user| user.activate }
redirect_back_or_admin_user(notice: _("Successfully unbanned"))
else
redirect_back_or_admin_user(alert: _("Error occurred. User was not unbanned"))
end
end
def unlock
if update_user { |user| user.unlock_access! }
redirect_back_or_admin_user(alert: _("Successfully unlocked"))
@ -325,6 +344,10 @@ class Admin::UsersController < Admin::ApplicationController
access_denied! unless Gitlab.config.gitlab.impersonation_enabled
end
def check_ban_user_feature_flag
access_denied! unless Feature.enabled?(:ban_user_feature_flag)
end
def log_impersonation_event
Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username })
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
class Projects::Analytics::CycleAnalytics::StagesController < Projects::ApplicationController
respond_to :json
feature_category :planning_analytics
before_action :authorize_read_cycle_analytics!
before_action :only_default_value_stream_is_allowed!
def index
result = list_service.execute
if result.success?
render json: cycle_analytics_configuration(result.payload[:stages])
else
render json: { message: result.message }, status: result.http_status
end
end
private
def only_default_value_stream_is_allowed!
render_404 if params[:value_stream_id] != Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME
end
def value_stream
Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project)
end
def list_params
{ value_stream: value_stream }
end
def list_service
Analytics::CycleAnalytics::Stages::ListService.new(parent: @project, current_user: current_user, params: list_params)
end
def cycle_analytics_configuration(stages)
stage_presenters = stages.map { |s| ::Analytics::CycleAnalytics::StagePresenter.new(s) }
Analytics::CycleAnalytics::ConfigurationEntity.new(stages: stage_presenters)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class Projects::Analytics::CycleAnalytics::ValueStreamsController < Projects::ApplicationController
respond_to :json
feature_category :planning_analytics
before_action :authorize_read_cycle_analytics!
def index
# FOSS users can only see the default value stream
value_streams = [Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project)]
render json: Analytics::CycleAnalytics::ValueStreamSerializer.new.represent(value_streams)
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class StageFinder
def initialize(parent:, stage_id:)
@parent = parent
@stage_id = stage_id
end
def execute
build_in_memory_stage_by_name
end
private
attr_reader :parent, :stage_id
def build_in_memory_stage_by_name
parent.cycle_analytics_stages.build(find_in_memory_stage)
end
def find_in_memory_stage
# raise ActiveRecord::RecordNotFound, so it will behave similarly to AR models and produce 404 response in the controller
raw_stage = Gitlab::Analytics::CycleAnalytics::DefaultStages.all.find do |hash|
hash[:name].eql?(stage_id)
end
raise(ActiveRecord::RecordNotFound, "Stage with id '#{stage_id}' could not be found") unless raw_stage
raw_stage
end
end
end
end
Analytics::CycleAnalytics::StageFinder.prepend_mod_with('Analytics::CycleAnalytics::StageFinder')

View File

@ -4,8 +4,6 @@ module Ci
class RunnersFinder < UnionFinder
include Gitlab::Allowable
NUMBER_OF_RUNNERS_PER_PAGE = 30
def initialize(current_user:, group: nil, params:)
@params = params
@group = group
@ -18,7 +16,6 @@ module Ci
filter_by_runner_type!
filter_by_tag_list!
sort!
paginate!
@runners.with_tags
@ -77,10 +74,6 @@ module Ci
@runners = @runners.order_by(sort_key)
end
def paginate!
@runners = @runners.page(@params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
end
def filter_by!(scope_name, available_scopes)
scope = @params[scope_name]

View File

@ -162,6 +162,49 @@ module UsersHelper
header + list
end
def user_ban_data(user)
{
path: ban_admin_user_path(user),
method: 'put',
modal_attributes: {
title: s_('AdminUsers|Ban user %{username}?') % { username: sanitize_name(user.name) },
message: s_('AdminUsers|You can unban their account in the future. Their data remains intact.'),
okVariant: 'warning',
okTitle: s_('AdminUsers|Ban')
}.to_json
}
end
def user_unban_data(user)
{
path: unban_admin_user_path(user),
method: 'put',
modal_attributes: {
title: s_('AdminUsers|Unban %{username}?') % { username: sanitize_name(user.name) },
message: s_('AdminUsers|You ban their account in the future if necessary.'),
okVariant: 'info',
okTitle: s_('AdminUsers|Unban')
}.to_json
}
end
def user_ban_effects
header = tag.p s_('AdminUsers|Banning the user has the following effects:')
list = tag.ul do
concat tag.li s_('AdminUsers|User will be blocked')
end
link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") }
info = tag.p s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
header + list + info
end
def ban_feature_available?
Feature.enabled?(:ban_user_feature_flag)
end
def user_deactivation_data(user, message)
{
path: deactivate_admin_user_path(user),
@ -235,6 +278,9 @@ module UsersHelper
pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' }
return pending_approval_badge if user.blocked_pending_approval?
banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' }
return banned_badge if user.banned?
{ text: s_('AdminUsers|Blocked'), variant: 'danger' }
end

View File

@ -7,10 +7,13 @@ module Analytics
validates :project, presence: true
belongs_to :project
belongs_to :value_stream, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', foreign_key: :project_value_stream_id
alias_attribute :parent, :project
alias_attribute :parent_id, :project_id
alias_attribute :value_stream_id, :project_value_stream_id
delegate :group, to: :project
validate :validate_project_group_for_label_events, if: -> { start_event_label_based? || end_event_label_based? }

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class Analytics::CycleAnalytics::ProjectValueStream < ApplicationRecord
belongs_to :project
has_many :stages, class_name: 'Analytics::CycleAnalytics::ProjectStage'
validates :project, :name, presence: true
validates :name, length: { minimum: 3, maximum: 100, allow_nil: false }, uniqueness: { scope: :project_id }
def custom?
false
end
def stages
[]
end
def self.build_default_value_stream(project)
new(name: Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME, project: project)
end
end

View File

@ -27,6 +27,7 @@ module Analytics
scope :default_stages, -> { where(custom: false) }
scope :ordered, -> { order(:relative_position, :id) }
scope :for_list, -> { includes(:start_event_label, :end_event_label).ordered }
scope :by_value_stream, -> (value_stream) { where(value_stream_id: value_stream.id) }
end
def parent=(_)

View File

@ -335,7 +335,8 @@ class Project < ApplicationRecord
has_one :ci_cd_settings, class_name: 'ProjectCiCdSetting', inverse_of: :project, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :remote_mirrors, inverse_of: :project
has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage'
has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage', inverse_of: :project
has_many :value_streams, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', inverse_of: :project
has_many :external_pull_requests, inverse_of: :project

View File

@ -326,6 +326,7 @@ class User < ApplicationRecord
transition deactivated: :blocked
transition ldap_blocked: :blocked
transition blocked_pending_approval: :blocked
transition banned: :blocked
end
event :ldap_block do
@ -338,19 +339,24 @@ class User < ApplicationRecord
transition blocked: :active
transition ldap_blocked: :active
transition blocked_pending_approval: :active
transition banned: :active
end
event :block_pending_approval do
transition active: :blocked_pending_approval
end
event :ban do
transition active: :banned
end
event :deactivate do
# Any additional changes to this event should be also
# reflected in app/workers/users/deactivate_dormant_users_worker.rb
transition active: :deactivated
end
state :blocked, :ldap_blocked, :blocked_pending_approval do
state :blocked, :ldap_blocked, :blocked_pending_approval, :banned do
def blocked?
true
end
@ -377,6 +383,7 @@ class User < ApplicationRecord
scope :instance_access_request_approvers_to_be_notified, -> { admins.active.order_recent_sign_in.limit(INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) }
scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
scope :blocked_pending_approval, -> { with_states(:blocked_pending_approval) }
scope :banned, -> { with_states(:banned) }
scope :external, -> { where(external: true) }
scope :non_external, -> { where(external: false) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
@ -598,6 +605,8 @@ class User < ApplicationRecord
blocked
when 'blocked_pending_approval'
blocked_pending_approval
when 'banned'
banned
when 'two_factor_disabled'
without_two_factor
when 'two_factor_enabled'

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class ConfigurationEntity < Grape::Entity
include RequestAwareEntity
expose :events, using: Analytics::CycleAnalytics::EventEntity
expose :stages, using: Analytics::CycleAnalytics::StageEntity
private
def events
(stage_events.events - stage_events.internal_events).sort_by(&:name)
end
def stage_events
Gitlab::Analytics::CycleAnalytics::StageEvents
end
end
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class EventEntity < Grape::Entity
expose :name
expose :identifier
expose :type
expose :can_be_start_event?, as: :can_be_start_event
expose :allowed_end_events
private
def type
object.label_based? ? 'label' : 'simple'
end
def can_be_start_event?
pairing_rules.has_key?(object)
end
def allowed_end_events
pairing_rules.fetch(object, []).map do |event|
event.identifier unless stage_events.internal_events.include?(event)
end.compact
end
def pairing_rules
stage_events.pairing_rules
end
def stage_events
Gitlab::Analytics::CycleAnalytics::StageEvents
end
end
end
end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class StageEntity < Grape::Entity
expose :title
expose :hidden
expose :legend
expose :description
expose :id
expose :custom
expose :start_event_identifier, if: -> (s) { s.custom? }
expose :end_event_identifier, if: -> (s) { s.custom? }
expose :start_event_label, using: LabelEntity, if: -> (s) { s.start_event_label_based? }
expose :end_event_label, using: LabelEntity, if: -> (s) { s.end_event_label_based? }
expose :start_event_html_description
expose :end_event_html_description
def id
object.id || object.name
end
def start_event_html_description
html_description(object.start_event)
end
def end_event_html_description
html_description(object.end_event)
end
private
def html_description(event)
Banzai::Renderer.render(event.markdown_description, { group: object.group, project: nil })
end
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class ValueStreamEntity < Grape::Entity
expose :name
expose :id
expose :is_custom do |object|
object.custom?
end
expose :stages, using: Analytics::CycleAnalytics::StageEntity
private
def id
object.id || object.name # use the name `default` if the record is not persisted
end
def stages
object.stages.map { |s| ::Analytics::CycleAnalytics::StagePresenter.new(s) } # rubocop: disable CodeReuse/Presenter
end
end
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
class ValueStreamSerializer < BaseSerializer
entity ::Analytics::CycleAnalytics::ValueStreamEntity
end
end
end

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
module Stages
class BaseService
include Gitlab::Allowable
DEFAULT_VALUE_STREAM_NAME = 'default'
def initialize(parent:, current_user:, params: {})
@parent = parent
@current_user = current_user
@params = params
end
def execute
raise NotImplementedError
end
private
attr_reader :parent, :current_user, :params
def success(stage, http_status = :created)
ServiceResponse.success(payload: { stage: stage }, http_status: http_status)
end
def forbidden
ServiceResponse.error(message: 'Forbidden', payload: {}, http_status: :forbidden)
end
def build_default_stages
Gitlab::Analytics::CycleAnalytics::DefaultStages.all.map do |stage_params|
parent.cycle_analytics_stages.build(stage_params.merge(value_stream: value_stream))
end
end
def value_stream
@value_stream ||= params[:value_stream]
end
end
end
end
end
Analytics::CycleAnalytics::Stages::BaseService.prepend_mod_with('Analytics::CycleAnalytics::Stages::BaseService')

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Analytics
module CycleAnalytics
module Stages
class ListService < Analytics::CycleAnalytics::Stages::BaseService
def execute
return forbidden unless allowed?
success(build_default_stages)
end
private
def allowed?
can?(current_user, :read_cycle_analytics, parent)
end
def success(stages)
ServiceResponse.success(payload: { stages: stages })
end
end
end
end
end
Analytics::CycleAnalytics::Stages::ListService.prepend_mod_with('Analytics::CycleAnalytics::Stages::ListService')

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Users
class BanService < BaseService
def initialize(current_user)
@current_user = current_user
end
def execute(user)
if user.ban
log_event(user)
success
else
messages = user.errors.full_messages
error(messages.uniq.join('. '))
end
end
private
def log_event(user)
Gitlab::AppLogger.info(message: "User banned", user: "#{user.username}", email: "#{user.email}", banned_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
end

View File

@ -0,0 +1,9 @@
- if ban_feature_available?
.card.border-warning
.card-header.bg-warning.gl-text-white
= s_('AdminUsers|Ban user')
.card-body
= user_ban_effects
%br
%button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_ban_data(user) }
= s_('AdminUsers|Ban user')

View File

@ -3,6 +3,9 @@
- if @user.blocked_pending_approval?
%span.cred
= s_('AdminUsers|(Pending approval)')
- elsif @user.banned?
%span.cred
= s_('AdminUsers|(Banned)')
- elsif @user.blocked?
%span.cred
= s_('AdminUsers|(Blocked)')

View File

@ -28,6 +28,11 @@
= link_to admin_users_path(filter: "blocked") do
= s_('AdminUsers|Blocked')
%small.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(User.blocked)
- if ban_feature_available?
= nav_link(html_options: { class: active_when(params[:filter] == 'banned') }) do
= link_to admin_users_path(filter: "banned") do
= s_('AdminUsers|Banned')
%small.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(User.banned)
= nav_link(html_options: { class: "#{active_when(params[:filter] == 'blocked_pending_approval')} filter-blocked-pending-approval" }) do
= link_to admin_users_path(filter: "blocked_pending_approval"), data: { qa_selector: 'pending_approval_tab' } do
= s_('AdminUsers|Pending approval')

View File

@ -176,6 +176,20 @@
- if @user.blocked_pending_approval?
= render 'admin/users/approve_user', user: @user
= render 'admin/users/reject_pending_user', user: @user
- elsif @user.banned?
.gl-card.border-info.gl-mb-5
.gl-card-header.gl-bg-blue-500.gl-text-white
= _('This user is banned')
.gl-card-body
%p= _('A banned user cannot:')
%ul
%li= _('Log in')
%li= _('Access Git repositories')
- link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") }
= s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
%p
%button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unban_data(@user) }
= s_('AdminUsers|Unban user')
- else
.gl-card.border-info.gl-mb-5
.gl-card-header.gl-bg-blue-500.gl-text-white
@ -190,6 +204,7 @@
= s_('AdminUsers|Unblock user')
- elsif !@user.internal?
= render 'admin/users/block_user', user: @user
= render 'admin/users/ban_user', user: @user
- if @user.access_locked?
.card.border-info.gl-mb-5

View File

@ -17,8 +17,7 @@ module Packages
::Packages::Nuget::UpdatePackageFromMetadataService.new(package_file).execute
rescue ::Packages::Nuget::MetadataExtractionService::ExtractionError,
::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError => e
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e, project_id: package_file.project_id)
package_file.package.update_column(:status, :error)
end

View File

@ -19,7 +19,7 @@ module Packages
::Packages::Rubygems::ProcessGemService.new(package_file).execute
rescue ::Packages::Rubygems::ProcessGemService::ExtractionError => e
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e, project_id: package_file.project_id)
package_file.package.update_column(:status, :error)
end

View File

@ -0,0 +1,5 @@
---
title: Log additional package extraction errors
merge_request: 61745
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Create database structure to support project value streams
merge_request: 60925
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Inject Feature Flags Limit Value
merge_request: 61621
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Ban user state and UI
merge_request: 61292
author:
type: added

View File

@ -0,0 +1,8 @@
---
name: ban_user_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61292
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330667
milestone: '13.12'
type: development
group: group::access
default_enabled: false

View File

@ -21,6 +21,8 @@ namespace :admin do
get :keys
put :block
put :unblock
put :ban
put :unban
put :deactivate
put :activate
put :unlock

View File

@ -267,6 +267,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
get '/cycle_analytics', to: redirect('%{namespace_id}/%{project_id}/-/value_stream_analytics')
namespace :analytics do
resource :cycle_analytics, only: :show, path: 'value_stream_analytics'
scope module: :cycle_analytics, as: 'cycle_analytics', path: 'value_stream_analytics' do
resources :value_streams, only: [:index] do
resources :stages, only: [:index]
end
end
end
concerns :clusterable
namespace :serverless do

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class CreateProjectValueStreams < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
INDEX_NAME = 'index_analytics_ca_project_value_streams_on_project_id_and_name'
def up
create_table_with_constraints :analytics_cycle_analytics_project_value_streams do |t|
t.timestamps_with_timezone
t.references(:project,
null: false,
index: false,
foreign_key: { to_table: :projects, on_delete: :cascade }
)
t.text :name, null: false
t.index [:project_id, :name], unique: true, name: INDEX_NAME
t.text_limit :name, 100
end
end
def down
with_lock_retries do
drop_table :analytics_cycle_analytics_project_value_streams
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
class AddProjectValueStreamIdToProjectStages < ActiveRecord::Migration[6.0]
disable_ddl_transaction!
INDEX_NAME = 'index_analytics_ca_project_stages_on_value_stream_id'
class ProjectValueStream < ActiveRecord::Base
self.table_name = 'analytics_cycle_analytics_project_stages'
include EachBatch
end
def up
ProjectValueStream.reset_column_information
# The table was never used, there is no user-facing code that modifies the table, it should be empty.
# Since there is no functionality present that depends on this data, it's safe to delete the rows.
ProjectValueStream.each_batch(of: 100) do |relation|
relation.delete_all
end
transaction do
add_reference :analytics_cycle_analytics_project_stages, :project_value_stream, null: false, index: { name: INDEX_NAME }, foreign_key: { on_delete: :cascade, to_table: :analytics_cycle_analytics_project_value_streams }, type: :bigint # rubocop: disable Migration/AddReference, Rails/NotNullColumn
end
end
def down
remove_reference :analytics_cycle_analytics_project_stages, :project_value_stream
end
end

View File

@ -0,0 +1 @@
de8bf6c02589bf308914d43e5cd44dae91d3bbabcdaafcebdb96fba0a09b20bc

View File

@ -0,0 +1 @@
2fdcb66e511d8322ea8fc4de66ecce859f8e91b2a9da22336281a1e784d9b4a5

View File

@ -9051,7 +9051,8 @@ CREATE TABLE analytics_cycle_analytics_project_stages (
end_event_label_id bigint,
hidden boolean DEFAULT false NOT NULL,
custom boolean DEFAULT true NOT NULL,
name character varying(255) NOT NULL
name character varying(255) NOT NULL,
project_value_stream_id bigint NOT NULL
);
CREATE SEQUENCE analytics_cycle_analytics_project_stages_id_seq
@ -9063,6 +9064,24 @@ CREATE SEQUENCE analytics_cycle_analytics_project_stages_id_seq
ALTER SEQUENCE analytics_cycle_analytics_project_stages_id_seq OWNED BY analytics_cycle_analytics_project_stages.id;
CREATE TABLE analytics_cycle_analytics_project_value_streams (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
project_id bigint NOT NULL,
name text NOT NULL,
CONSTRAINT check_9b1970a898 CHECK ((char_length(name) <= 100))
);
CREATE SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id;
CREATE TABLE analytics_devops_adoption_segment_selections (
id bigint NOT NULL,
segment_id bigint NOT NULL,
@ -19369,6 +19388,8 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_value_streams ALTER COLUMN id S
ALTER TABLE ONLY analytics_cycle_analytics_project_stages ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_stages_id_seq'::regclass);
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass);
ALTER TABLE ONLY analytics_devops_adoption_segment_selections ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segment_selections_id_seq'::regclass);
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
@ -20449,6 +20470,9 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_value_streams
ALTER TABLE ONLY analytics_cycle_analytics_project_stages
ADD CONSTRAINT analytics_cycle_analytics_project_stages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id);
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ADD CONSTRAINT analytics_devops_adoption_segment_selections_pkey PRIMARY KEY (id);
@ -22262,6 +22286,10 @@ CREATE INDEX index_analytics_ca_project_stages_on_relative_position ON analytics
CREATE INDEX index_analytics_ca_project_stages_on_start_event_label_id ON analytics_cycle_analytics_project_stages USING btree (start_event_label_id);
CREATE INDEX index_analytics_ca_project_stages_on_value_stream_id ON analytics_cycle_analytics_project_stages USING btree (project_value_stream_id);
CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_name ON analytics_cycle_analytics_project_value_streams USING btree (project_id, name);
CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true);
CREATE UNIQUE INDEX index_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
@ -26519,6 +26547,9 @@ ALTER TABLE ONLY namespace_admin_notes
ALTER TABLE ONLY web_hook_logs_archived
ADD CONSTRAINT fk_rails_666826e111 FOREIGN KEY (web_hook_id) REFERENCES web_hooks(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
ADD CONSTRAINT fk_rails_669f4ba293 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY jira_imports
ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL;
@ -26621,6 +26652,9 @@ ALTER TABLE ONLY ci_subscriptions_projects
ALTER TABLE ONLY terraform_states
ADD CONSTRAINT fk_rails_78f54ca485 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_cycle_analytics_project_stages
ADD CONSTRAINT fk_rails_796a7dbc9c FOREIGN KEY (project_value_stream_id) REFERENCES analytics_cycle_analytics_project_value_streams(id) ON DELETE CASCADE;
ALTER TABLE ONLY software_license_policies
ADD CONSTRAINT fk_rails_7a7a2a92de FOREIGN KEY (software_license_id) REFERENCES software_licenses(id) ON DELETE CASCADE;

View File

@ -196,6 +196,65 @@ NOTE:
The maximum import file size can be set by the Administrator, default is `0` (unlimited)..
As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin UI](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8.
## Import a file from a remote object storage
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/282503) in GitLab 13.12 in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
This endpoint is behind a feature flag that is disabled by default.
To enable this endpoint:
```ruby
Feature.enable(:import_project_from_remote_file)
```
To disable this endpoint:
```ruby
Feature.disable(:import_project_from_remote_file)
```
```plaintext
POST /projects/remote-import
```
| Attribute | Type | Required | Description |
| ----------------- | -------------- | -------- | ---------------------------------------- |
| `namespace` | integer/string | no | The ID or path of the namespace to import the project to. Defaults to the current user's namespace. |
| `name` | string | no | The name of the project to import. If not provided, defaults to the path of the project. |
| `url` | string | yes | URL for the file to import. |
| `path` | string | yes | Name and path for the new project. |
| `overwrite` | boolean | no | Whether to overwrite a project with the same path when importing. Defaults to `false`. |
| `override_params` | Hash | no | Supports all fields defined in the [Project API](projects.md). |
The passed override parameters take precedence over all values defined in the export file.
```shell
curl --request POST \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/remote-import" \
--data '{"url":"https://remoteobject/file?token=123123","path":"remote-project"}'
```
```json
{
"id": 1,
"description": null,
"name": "remote-project",
"name_with_namespace": "Administrator / remote-project",
"path": "remote-project",
"path_with_namespace": "root/remote-project",
"created_at": "2018-02-13T09:05:58.023Z",
"import_status": "scheduled",
"correlation_id": "mezklWso3Za",
"failed_relations": [],
"import_error": null
}
```
The `ContentType` header must return a valid number. The maximum file size is 10 gigabytes.
The `ContentLength` header must be `application/gzip`.
## Import status
Get the status of an import.

View File

@ -795,6 +795,10 @@ aren't in the message with ID `1 pipeline`.
## Adding a new language
A new language should only be added as an option in User Preferences once at least 10% of the
strings have been translated and approved. Even though a larger number of strings may have been
translated, only the approved translations display in the GitLab UI.
NOTE:
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221012) in GitLab 13.3:
Languages with less than 2% of translations are not available in the UI.

View File

@ -48,8 +48,8 @@ using [Seat Link](#seat-link).
A _billable user_ counts against the number of subscription seats. Every user is considered a
billable user, with the following exceptions:
- [Deactivated users](../../user/admin_area/activating_deactivating_users.md#deactivating-a-user) and
[blocked users](../../user/admin_area/blocking_unblocking_users.md) don't count as billable users in the current subscription. When they are either deactivated or blocked they release a _billable user_ seat. However, they may
- [Deactivated users](../../user/admin_area/moderate_users.md#deactivating-a-user) and
[blocked users](../../user/admin_area/moderate_users.md#blocking-a-user) don't count as billable users in the current subscription. When they are either deactivated or blocked they release a _billable user_ seat. However, they may
count toward overages in the subscribed seat count.
- Users who are [pending approval](../../user/admin_area/approving_users.md).
- Members with Guest permissions on an Ultimate subscription.
@ -183,7 +183,7 @@ Starting 30 days before a subscription expires, GitLab notifies administrators o
We recommend following these steps during renewal:
1. Prune any inactive or unwanted users by [blocking them](../../user/admin_area/blocking_unblocking_users.md#blocking-a-user).
1. Prune any inactive or unwanted users by [blocking them](../../user/admin_area/moderate_users.md#blocking-a-user).
1. Determine if you have a need for user growth in the upcoming subscription.
1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription.

View File

@ -1,69 +1,8 @@
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: howto
redirect_to: 'moderate_users.md'
---
# Activating and deactivating users
This document was moved to [another location](moderate_users.md).
GitLab administrators can deactivate and activate users.
## Deactivating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
In order to temporarily prevent access by a GitLab user that has no recent activity, administrators
can choose to deactivate the user.
Deactivating a user is functionally identical to [blocking a user](blocking_unblocking_users.md),
with the following differences:
- It does not prohibit the user from logging back in via the UI.
- Once a deactivated user logs back into the GitLab UI, their account is set to active.
A deactivated user:
- Cannot access Git repositories or the API.
- Will not receive any notifications from GitLab.
- Will not be able to use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the deactivated user will be left intact.
A user can be deactivated from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Deactivate user**.
Please note that for the deactivation option to be visible to an admin, the user:
- Must be currently active.
- Must not have signed in, or have any activity, in the last 90 days.
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
A deactivated user can be activated from the Admin Area.
To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Deactivated** tab.
1. Select a user.
1. Under the **Account** tab, click **Activate user**.
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
NOTE:
Activating a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
NOTE:
A deactivated user can also activate their account themselves by logging back in via the UI.
<!-- This redirect file can be deleted after <2021-08-12>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View File

@ -21,7 +21,7 @@ When a user registers for an account while this setting is enabled:
A user pending approval:
- Is functionally identical to a [blocked](blocking_unblocking_users.md) user.
- Is functionally identical to a [blocked](moderate_users.md#blocking-a-user) user.
- Cannot sign in.
- Cannot access Git repositories or the GitLab API.
- Does not receive any notifications from GitLab.

View File

@ -1,51 +1,8 @@
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: howto
redirect_to: 'moderate_users.md'
---
# Blocking and unblocking users
This document was moved to [another location](moderate_users.md).
GitLab administrators block and unblock users.
## Blocking a user
In order to completely prevent access of a user to the GitLab instance, administrators can choose to
block the user.
Users can be blocked [via an abuse report](review_abuse_reports.md#blocking-users),
or directly from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Block user**.
A blocked user:
- Cannot log in.
- Cannot access Git repositories or the API.
- Does not receive any notifications from GitLab.
- Cannot use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the blocked user are left intact.
Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
NOTE:
A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Unblocking a user
A blocked user can be unblocked from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Blocked** tab.
1. Select a user.
1. Under the **Account** tab, click **Unblock user**.
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
NOTE:
Unblocking a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
<!-- This redirect file can be deleted after <2021-08-12>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View File

@ -117,8 +117,8 @@ To list users matching a specific criteria, click on one of the following tabs o
- **2FA Enabled**
- **2FA Disabled**
- **External**
- **[Blocked](blocking_unblocking_users.md)**
- **[Deactivated](activating_deactivating_users.md)**
- **[Blocked](moderate_users.md#blocking-a-user)**
- **[Deactivated](moderate_users.md#deactivating-a-user)**
- **Without projects**
For each user, the following are listed:

View File

@ -0,0 +1,157 @@
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: howto
---
# Moderate users
GitLab administrators can moderate user access by blocking, banning, or deactivating users.
## Blocking and unblocking users
GitLab administrators can block and unblock users.
### Blocking a user
In order to completely prevent access of a user to the GitLab instance,
administrators can choose to block the user.
Users can be blocked [via an abuse report](review_abuse_reports.md#blocking-users),
or directly from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Block user**.
A blocked user:
- Cannot log in.
- Cannot access Git repositories or the API.
- Does not receive any notifications from GitLab.
- Cannot use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the blocked user are left intact.
Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
NOTE:
A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
### Unblocking a user
A blocked user can be unblocked from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Blocked** tab.
1. Select a user.
1. Under the **Account** tab, click **Unblock user**.
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
NOTE:
Unblocking a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating and deactivating users
GitLab administrators can deactivate and activate users.
### Deactivating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
In order to temporarily prevent access by a GitLab user that has no recent activity,
administrators can choose to deactivate the user.
Deactivating a user is functionally identical to [blocking a user](#blocking-and-unblocking-users),
with the following differences:
- It does not prohibit the user from logging back in via the UI.
- Once a deactivated user logs back into the GitLab UI, their account is set to active.
A deactivated user:
- Cannot access Git repositories or the API.
- Will not receive any notifications from GitLab.
- Will not be able to use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the deactivated user are left intact.
A user can be deactivated from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Deactivate user**.
Please note that for the deactivation option to be visible to an admin, the user:
- Must be currently active.
- Must not have signed in, or have any activity, in the last 90 days.
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
### Activating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
A deactivated user can be activated from the Admin Area.
To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Deactivated** tab.
1. Select a user.
1. Under the **Account** tab, click **Activate user**.
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
NOTE:
Activating a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
NOTE:
A deactivated user can also activate their account themselves by logging back in via the UI.
## Ban and unban users
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327353) in GitLab 13.12.
GitLab administrators can ban users.
NOTE:
This feature is behind a feature flag that is disabled by default. GitLab administrators
with access to the GitLab Rails console can [enable](../../administration/feature_flags.md)
this feature for your GitLab instance.
### Ban a user
To completely block a user, administrators can choose to ban the user.
Users can be banned using the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Ban user**.
NOTE:
This feature is a work in progress. Currently, banning a user
only blocks them and does not hide their comments or issues.
This functionality will be implemented in follow up issues.
### Unban a user
A banned user can be unbanned using the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Banned** tab.
1. Select a user.
1. Under the **Account** tab, click **Unban user**.
NOTE:
Unbanning a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).

View File

@ -501,29 +501,38 @@ For details on saving and transporting Docker images as a file, see Docker's doc
#### Automating container scanning vulnerability database updates with a pipeline
For those using Clair, it can be worthwhile to set up a [scheduled pipeline](../../../ci/pipelines/schedules.md)
to build a new version of the vulnerabilities database on a preset schedule. Automating
this with a pipeline means you do not have to do it manually each time. You can use the following
`.gitlab-yml.ci` as a template:
We recommend that you set up a [scheduled pipeline](../../../ci/pipelines/schedules.md)
to fetch the latest vulnerabilities database on a preset schedule. Because the Clair scanner is
deprecated, the latest vulnerabilities are currently only available for the Trivy scanner.
Automating this with a pipeline means you do not have to do it manually each time. You can use the
following `.gitlab-yml.ci` example as a template.
```yaml
variables:
# If using Clair, uncomment the following 2 lines and comment the Trivy lines below
# SOURCE_IMAGE: arminc/clair-db:latest
# TARGET_IMAGE: $CI_REGISTRY/$CI_PROJECT_PATH/clair-vulnerabilities-db
# If using Trivy, uncomment the following 3 lines and comment the Clair lines above
CS_MAJOR_VERSION: 4 # ensure that this value matches the one you use in your scanning jobs
SOURCE_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/container-scanning:$CS_MAJOR_VERSION
TARGET_IMAGE: $CI_REGISTRY/$CI_PROJECT_PATH/gitlab-container-scanning
image: docker:stable
stages:
- build
build_latest_vulnerabilities:
stage: build
update-vulnerabilities-db:
services:
- docker:19.03.12-dind
- docker:19-dind
script:
- docker pull arminc/clair-db:latest
- docker tag arminc/clair-db:latest $CI_REGISTRY/namespace/clair-vulnerabilities-db
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker push $CI_REGISTRY/namespace/clair-vulnerabilities-db
- docker pull $SOURCE_IMAGE
- docker tag $SOURCE_IMAGE $TARGET_IMAGE
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY --username $CI_REGISTRY_USER --password-stdin
- docker push $TARGET_IMAGE
```
The above template works for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry.
The above template works for a GitLab Docker registry running on a local installation. However, if
you're using a non-GitLab Docker registry, you must change the `$CI_REGISTRY` value and the
`docker login` credentials to match your local registry's details.
## Running the standalone container scanning tool

View File

@ -182,7 +182,7 @@ The following vulnerability scanners and their databases are regularly updated:
| Secure scanning tool | Vulnerabilities database updates |
|:----------------------------------------------------------------|----------------------------------|
| [Container Scanning](../container_scanning/index.md) | Uses `clair`. The latest `clair-db` version is used for each job by running the [`latest` Docker image tag](https://gitlab.com/gitlab-org/gitlab/blob/438a0a56dc0882f22bdd82e700554525f552d91b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L37). The `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). |
| [Container Scanning](../container_scanning/index.md) | Uses either `trivy` or `clair`. For the `trivy` scanner, a job runs on a daily basis to build a new image with the latest vulnerability database updates from the [upstream `trivy-db`](https://github.com/aquasecurity/trivy-db). For the `clair` scanner, the latest `clair-db` version is used; `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). |
| [Dependency Scanning](../dependency_scanning/index.md) | Relies on `bundler-audit` (for Ruby gems), `retire.js` (for npm packages), and `gemnasium` (the GitLab tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. See our [current measurement of time from CVE being issued to our product being updated](https://about.gitlab.com/handbook/engineering/development/performance-indicators/#cve-issue-to-update). |
| [Dynamic Application Security Testing (DAST)](../dast/index.md) | The scanning engine is updated on a periodic basis. See the [version of the underlying tool `zaproxy`](https://gitlab.com/gitlab-org/security-products/dast/blob/master/Dockerfile#L1). The scanning rules are downloaded at scan runtime. |
| [Static Application Security Testing (SAST)](../sast/index.md) | Relies exclusively on [the tools GitLab wraps](../sast/index.md#supported-languages-and-frameworks). The underlying analyzers are updated at least once per month if a relevant update is available. The vulnerabilities database is updated by the upstream tools. |

View File

@ -698,6 +698,13 @@ You can, however, remove the Container Registry for a project:
The **Packages & Registries > Container Registry** entry is removed from the project's sidebar.
## Manifest lists and garbage collection
Manifest lists are commonly used for creating multi-architecture images. If you rely on manifest
lists, you should tag all the individual manifests referenced by a list in their respective
repositories, and not just the manifest list itself. This ensures that those manifests aren't
garbage collected, as long as they have at least one tag pointing to them.
## Troubleshooting the GitLab Container Registry
### Docker connection error

View File

@ -69,7 +69,7 @@ username of the original user.
When using the **Delete user and contributions** option, **all** associated records
are removed. This includes all of the items mentioned above including issues,
merge requests, notes/comments, and more. Consider
[blocking a user](../../admin_area/blocking_unblocking_users.md)
[blocking a user](../../admin_area/moderate_users.md#blocking-a-user)
or using the **Delete user** option instead.
When a user is deleted from an [abuse report](../../admin_area/review_abuse_reports.md)

View File

@ -73,7 +73,7 @@ Your account has been blocked. Fatal: Could not read from remote repository
Your primary email address is not confirmed.
```
You can assure your users that they have not been [Blocked](admin_area/blocking_unblocking_users.md) by an administrator.
You can assure your users that they have not been [Blocked](admin_area/moderate_users.md#blocking-and-unblocking-users) by an administrator.
When affected users see this message, they must confirm their email address before they can commit code.
## What do I need to know as an administrator of a GitLab self-managed Instance?

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
module Sidebars
module Projects
module Menus
class DeploymentsMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
return false if Feature.disabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml)
add_item(feature_flags_menu_item)
add_item(environments_menu_item)
add_item(releases_menu_item)
true
end
override :link
def link
renderable_items.first.link
end
override :extra_container_html_options
def extra_container_html_options
{
class: 'shortcuts-deployments'
}
end
override :title
def title
_('Deployments')
end
override :sprite_icon
def sprite_icon
'environment'
end
private
def feature_flags_menu_item
unless can?(context.current_user, :read_feature_flag, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :feature_flags)
end
::Sidebars::MenuItem.new(
title: _('Feature Flags'),
link: project_feature_flags_path(context.project),
active_routes: { controller: :feature_flags },
container_html_options: { class: 'shortcuts-feature-flags' },
item_id: :feature_flags
)
end
def environments_menu_item
unless can?(context.current_user, :read_environment, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :environments)
end
::Sidebars::MenuItem.new(
title: _('Environments'),
link: project_environments_path(context.project),
active_routes: { controller: :environments },
container_html_options: { class: 'shortcuts-environments' },
item_id: :environments
)
end
def releases_menu_item
if !can?(context.current_user, :read_release, context.project) ||
context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :releases)
end
::Sidebars::MenuItem.new(
title: _('Releases'),
link: project_releases_path(context.project),
item_id: :releases,
active_routes: { controller: :releases },
container_html_options: { class: 'shortcuts-deployments-releases' }
)
end
end
end
end
end

View File

@ -196,7 +196,8 @@ module Sidebars
end
def environments_menu_item
unless can?(context.current_user, :read_environment, context.project)
if Feature.enabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) ||
!can?(context.current_user, :read_environment, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :environments)
end
@ -210,7 +211,8 @@ module Sidebars
end
def feature_flags_menu_item
unless can?(context.current_user, :read_feature_flag, context.project)
if Feature.enabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) ||
!can?(context.current_user, :read_feature_flag, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :feature_flags)
end

View File

@ -84,9 +84,7 @@ module Sidebars
end
def releases_menu_item
if !can?(context.current_user, :read_release, context.project) || context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :releases)
end
return ::Sidebars::NilMenuItem.new(item_id: :releases) unless show_releases?
::Sidebars::MenuItem.new(
title: _('Releases'),
@ -97,6 +95,12 @@ module Sidebars
)
end
def show_releases?
Feature.disabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) &&
can?(context.current_user, :read_release, context.project) &&
!context.project.empty_repo?
end
def labels_menu_item
if Feature.disabled?(:sidebar_refactor, context.current_user)
return ::Sidebars::NilMenuItem.new(item_id: :labels)

View File

@ -7,7 +7,17 @@ module Sidebars
def configure_menus
set_scope_menu(Sidebars::Projects::Menus::ScopeMenu.new(context))
set_hidden_menu(Sidebars::Projects::Menus::HiddenMenu.new(context))
add_menus
end
override :aria_label
def aria_label
_('Project navigation')
end
private
def add_menus
add_menu(Sidebars::Projects::Menus::ProjectInformationMenu.new(context))
add_menu(Sidebars::Projects::Menus::LearnGitlabMenu.new(context))
add_menu(Sidebars::Projects::Menus::RepositoryMenu.new(context))
@ -17,6 +27,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context))
add_menu(Sidebars::Projects::Menus::CiCdMenu.new(context))
add_menu(Sidebars::Projects::Menus::SecurityComplianceMenu.new(context))
add_menu(Sidebars::Projects::Menus::DeploymentsMenu.new(context))
add_menu(Sidebars::Projects::Menus::OperationsMenu.new(context))
add_menu(Sidebars::Projects::Menus::InfrastructureMenu.new(context))
add_menu(Sidebars::Projects::Menus::PackagesRegistriesMenu.new(context))
@ -28,13 +39,6 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::SettingsMenu.new(context))
end
override :aria_label
def aria_label
_('Project navigation')
end
private
def confluence_or_wiki_menu
confluence_menu = ::Sidebars::Projects::Menus::ConfluenceMenu.new(context)

View File

@ -1375,6 +1375,9 @@ msgstr ""
msgid "A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates."
msgstr ""
msgid "A banned user cannot:"
msgstr ""
msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages"
msgstr ""
@ -2437,6 +2440,9 @@ msgstr ""
msgid "AdminUsers|(Admin)"
msgstr ""
msgid "AdminUsers|(Banned)"
msgstr ""
msgid "AdminUsers|(Blocked)"
msgstr ""
@ -2503,6 +2509,21 @@ msgstr ""
msgid "AdminUsers|Automatically marked as default internal user"
msgstr ""
msgid "AdminUsers|Ban"
msgstr ""
msgid "AdminUsers|Ban user"
msgstr ""
msgid "AdminUsers|Ban user %{username}?"
msgstr ""
msgid "AdminUsers|Banned"
msgstr ""
msgid "AdminUsers|Banning the user has the following effects:"
msgstr ""
msgid "AdminUsers|Be added to groups and projects"
msgstr ""
@ -2590,6 +2611,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
msgid "AdminUsers|Log in"
msgstr ""
@ -2671,6 +2695,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
msgid "AdminUsers|Unban"
msgstr ""
msgid "AdminUsers|Unban %{username}?"
msgstr ""
msgid "AdminUsers|Unban user"
msgstr ""
msgid "AdminUsers|Unblock"
msgstr ""
@ -2686,6 +2719,9 @@ msgstr ""
msgid "AdminUsers|Unlock user %{username}?"
msgstr ""
msgid "AdminUsers|User will be blocked"
msgstr ""
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@ -2722,6 +2758,9 @@ msgstr ""
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr ""
msgid "AdminUsers|You ban their account in the future if necessary."
msgstr ""
msgid "AdminUsers|You can always block their account again if needed."
msgstr ""
@ -2734,6 +2773,9 @@ msgstr ""
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
msgid "AdminUsers|You can unban their account in the future. Their data remains intact."
msgstr ""
msgid "AdminUsers|You cannot remove your own admin rights."
msgstr ""
@ -4985,6 +5027,9 @@ msgstr ""
msgid "BillingPlans|@%{user_name} you are currently using the %{plan_name}."
msgstr ""
msgid "BillingPlans|Compare all plans"
msgstr ""
msgid "BillingPlans|Congratulations, your free trial is activated."
msgstr ""
@ -5018,6 +5063,9 @@ msgstr ""
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
msgstr ""
msgid "BillingPlans|Upgrade to GitLab %{planNameForUpgrade}"
msgstr ""
msgid "BillingPlans|While GitLab is ending availability of the Bronze plan, you can still renew your Bronze subscription one additional time before %{eoa_bronze_plan_end_date}. We are also offering a limited time free upgrade to our Premium Plan (up to 25 users)! Learn more about the changes and offers in our %{announcement_link}."
msgstr ""
@ -12898,12 +12946,18 @@ msgstr ""
msgid "Error occurred. A blocked user must be unblocked to be activated"
msgstr ""
msgid "Error occurred. User was not banned"
msgstr ""
msgid "Error occurred. User was not blocked"
msgstr ""
msgid "Error occurred. User was not confirmed"
msgstr ""
msgid "Error occurred. User was not unbanned"
msgstr ""
msgid "Error occurred. User was not unblocked"
msgstr ""
@ -31249,6 +31303,9 @@ msgstr ""
msgid "Successfully approved"
msgstr ""
msgid "Successfully banned"
msgstr ""
msgid "Successfully blocked"
msgstr ""
@ -31273,6 +31330,9 @@ msgstr ""
msgid "Successfully synced %{synced_timeago}."
msgstr ""
msgid "Successfully unbanned"
msgstr ""
msgid "Successfully unblocked"
msgstr ""
@ -33382,6 +33442,9 @@ msgstr ""
msgid "This user has the %{access} role in the %{name} project."
msgstr ""
msgid "This user is banned"
msgstr ""
msgid "This user is blocked"
msgstr ""

View File

@ -2,17 +2,9 @@
export SETUP_DB=${SETUP_DB:-true}
export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
export BUNDLE_INSTALL_FLAGS=${BUNDLE_INSTALL_FLAGS:-"--without=production development --jobs=$(nproc) --path=vendor --retry=3 --quiet"}
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle --version
bundle config set clean 'true'
run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS}"
run_timed_command "bundle check"
# When we test multiple versions of PG in the same pipeline, we have a single `setup-test-env`
# job but the `pg` gem needs to be rebuilt since it includes extensions (https://guides.rubygems.org/gems-with-extensions).
# Uncomment the following line if multiple versions of PG are tested in the same pipeline.
run_timed_command "bundle pristine pg"
bundle_install_script
fi
cp config/gitlab.yml.example config/gitlab.yml

View File

@ -13,6 +13,32 @@ function retry() {
return 1
}
function bundle_install_script() {
local extra_install_args="${1}"
if [[ "${extra_install_args}" =~ "--without" ]]; then
echoerr "The '--without' flag shouldn't be passed as it would replace the default \${BUNDLE_WITHOUT} (currently set to '${BUNDLE_WITHOUT}')."
echoerr "Set the 'BUNDLE_WITHOUT' variable instead, e.g. '- export BUNDLE_WITHOUT=\"\${BUNDLE_WITHOUT}:any:other:group:not:to:install\"'."
exit 1;
fi;
bundle --version
bundle config set path 'vendor'
bundle config set clean 'true'
echo $BUNDLE_WITHOUT
bundle config
run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS} ${extra_install_args} && bundle check"
if [[ $(bundle info pg) ]]; then
# When we test multiple versions of PG in the same pipeline, we have a single `setup-test-env`
# job but the `pg` gem needs to be rebuilt since it includes extensions (https://guides.rubygems.org/gems-with-extensions).
# Uncomment the following line if multiple versions of PG are tested in the same pipeline.
run_timed_command "bundle pristine pg"
fi
}
function setup_db_user_only() {
source scripts/create_postgres_user.sh
}

View File

@ -34,6 +34,17 @@ RSpec.describe Admin::RunnersController do
expect(response.body).to have_content('tag1')
expect(response.body).to have_content('tag2')
end
it 'paginates runners' do
stub_const("Admin::RunnersController::NUMBER_OF_RUNNERS_PER_PAGE", 1)
create(:ci_runner)
get :index
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:runners).count).to be(1)
end
end
describe '#show' do

View File

@ -365,6 +365,56 @@ RSpec.describe Admin::UsersController do
end
end
describe 'PUT ban/:id' do
context 'when ban_user_feature_flag is enabled' do
it 'bans user' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_truthy
expect(flash[:notice]).to eq _('Successfully banned')
end
context 'when unsuccessful' do
let(:user) { create(:user, :blocked) }
it 'does not ban user' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_falsey
expect(flash[:alert]).to eq _('Error occurred. User was not banned')
end
end
end
context 'when ban_user_feature_flag is not enabled' do
before do
stub_feature_flags(ban_user_feature_flag: false)
end
it 'does not ban user, renders 404' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_falsey
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'PUT unban/:id' do
let(:banned_user) { create(:user, :banned) }
it 'unbans user' do
put :unban, params: { id: banned_user.username }
banned_user.reload
expect(banned_user.banned?).to be_falsey
expect(flash[:notice]).to eq _('Successfully unbanned')
end
end
describe 'PUT unlock/:id' do
before do
request.env["HTTP_REFERER"] = "/"

View File

@ -32,6 +32,17 @@ RSpec.describe Groups::Settings::CiCdController do
expect(response).to render_template(:show)
expect(assigns(:group_runners)).to match_array([runner_group, runner_project_1, runner_project_2, runner_project_3])
end
it 'paginates runners' do
stub_const("Groups::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE", 1)
create(:ci_runner)
get :show, params: { group_id: group }
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:group_runners).count).to be(1)
end
end
context 'when user is not owner' do

View File

@ -0,0 +1,67 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Analytics::CycleAnalytics::StagesController do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let(:params) { { namespace_id: group, project_id: project, value_stream_id: 'default' } }
before do
sign_in(user)
end
describe 'GET index' do
context 'when user is member of the project' do
before do
project.add_developer(user)
end
it 'succeeds' do
get :index, params: params
expect(response).to have_gitlab_http_status(:ok)
end
it 'exposes the default stages' do
get :index, params: params
expect(json_response['stages'].size).to eq(Gitlab::Analytics::CycleAnalytics::DefaultStages.all.size)
end
context 'when list service fails' do
it 'renders 403' do
expect_next_instance_of(Analytics::CycleAnalytics::Stages::ListService) do |list_service|
expect(list_service).to receive(:allowed?).and_return(false)
end
get :index, params: params
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
context 'when invalid value stream id is given' do
before do
params[:value_stream_id] = 1
end
it 'renders 404' do
get :index, params: params
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when user is not member of the project' do
it 'renders 404' do
get :index, params: params
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Analytics::CycleAnalytics::ValueStreamsController do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let(:params) { { namespace_id: group, project_id: project } }
before do
sign_in(user)
end
describe 'GET index' do
context 'when user is member of the project' do
before do
project.add_developer(user)
end
it 'succeeds' do
get :index, params: params
expect(response).to have_gitlab_http_status(:ok)
end
it 'exposes the default value stream' do
get :index, params: params
expect(json_response.first['name']).to eq('default')
end
end
context 'when user is not member of the project' do
it 'renders 404' do
get :index, params: params
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end

View File

@ -6,6 +6,7 @@ FactoryBot.define do
sequence(:name) { |n| "Stage ##{n}" }
hidden { false }
issue_stage
value_stream { association(:cycle_analytics_project_value_stream, project: project) }
trait :issue_stage do
start_event_identifier { Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated.identifier }

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :cycle_analytics_project_value_stream, class: 'Analytics::CycleAnalytics::ProjectValueStream' do
sequence(:name) { |n| "Value Stream ##{n}" }
project
end
end

View File

@ -27,6 +27,10 @@ FactoryBot.define do
after(:build) { |user, _| user.block_pending_approval! }
end
trait :banned do
after(:build) { |user, _| user.ban! }
end
trait :ldap_blocked do
after(:build) { |user, _| user.ldap_block! }
end

View File

@ -85,6 +85,7 @@ RSpec.describe 'Admin::Users' do
expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
expect(page).to have_link('Banned', href: admin_users_path(filter: 'banned'))
expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
end

View File

@ -91,8 +91,6 @@ RSpec.describe 'Project navbar' do
_('Error Tracking'),
_('Alerts'),
_('Incidents'),
_('Environments'),
_('Feature Flags'),
_('Product Analytics')
]
end
@ -102,7 +100,6 @@ RSpec.describe 'Project navbar' do
nav_item: _('Project information'),
nav_sub_items: [
_('Activity'),
_('Releases'),
_('Labels')
]
}
@ -133,6 +130,18 @@ RSpec.describe 'Project navbar' do
}
)
insert_after_nav_item(
_('Security & Compliance'),
new_nav_item: {
nav_item: _('Deployments'),
nav_sub_items: [
_('Feature Flags'),
_('Environments'),
_('Releases')
]
}
)
visit project_path(project)
end

View File

@ -178,6 +178,16 @@ RSpec.describe 'User uses shortcuts', :js do
end
end
context 'when navigating to the Deployments page' do
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
expect(page).to have_active_navigation('Deployments')
expect(page).to have_active_sub_navigation('Environments')
end
end
context 'when navigating to the Operations pages' do
it 'redirects to the Metrics page' do
find('body').native.send_key('g')
@ -187,24 +197,26 @@ RSpec.describe 'User uses shortcuts', :js do
expect(page).to have_active_sub_navigation('Metrics')
end
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Environments')
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'redirects to the Kubernetes page with active Operations' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'redirects to the Kubernetes page with active Operations' do
find('body').native.send_key('g')
find('body').native.send_key('k')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Kubernetes')
end
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Environments')
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::StageFinder do
let(:project) { build(:project) }
let(:stage_id) { { id: Gitlab::Analytics::CycleAnalytics::DefaultStages.names.first } }
subject { described_class.new(parent: project, stage_id: stage_id[:id]).execute }
context 'when looking up in-memory default stage by name exists' do
it { expect(subject).not_to be_persisted }
it { expect(subject.name).to eq(stage_id[:id]) }
end
context 'when in-memory default stage cannot be found' do
before do
stage_id[:id] = 'unknown_default_stage'
end
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
end

View File

@ -72,17 +72,6 @@ RSpec.describe Ci::RunnersFinder do
end
end
context 'paginate' do
it 'returns the runners for the specified page' do
stub_const('Ci::RunnersFinder::NUMBER_OF_RUNNERS_PER_PAGE', 1)
runner1 = create :ci_runner, created_at: '2018-07-12 07:00'
runner2 = create :ci_runner, created_at: '2018-07-12 08:00'
expect(described_class.new(current_user: admin, params: { page: 1 }).execute).to eq [runner2]
expect(described_class.new(current_user: admin, params: { page: 2 }).execute).to eq [runner1]
end
end
context 'non admin user' do
it 'returns no runners' do
user = create :user
@ -172,38 +161,6 @@ RSpec.describe Ci::RunnersFinder do
end
end
context 'paginate' do
using RSpec::Parameterized::TableSyntax
let(:runners) do
[[runner_project_7, runner_project_6, runner_project_5],
[runner_project_4, runner_project_3, runner_project_2],
[runner_project_1, runner_sub_group_4, runner_sub_group_3],
[runner_sub_group_2, runner_sub_group_1, runner_group]]
end
where(:page, :index) do
1 | 0
2 | 1
3 | 2
4 | 3
end
before do
stub_const('Ci::RunnersFinder::NUMBER_OF_RUNNERS_PER_PAGE', 3)
group.add_owner(user)
end
with_them do
let(:params) { { page: page } }
it 'returns the runners for the specified page' do
expect(subject).to eq(runners[index])
end
end
end
context 'filter by search term' do
let(:params) { { search: 'runner_project_search' } }

View File

@ -136,6 +136,16 @@ RSpec.describe UsersHelper do
end
end
context 'with a banned user' do
it 'returns the banned badge' do
banned_user = create(:user, :banned)
badges = helper.user_badges_in_admin_section(banned_user)
expect(filter_ee_badges(badges)).to eq([text: 'Banned', variant: 'danger'])
end
end
context 'with an admin user' do
it "returns the admin badge" do
admin_user = create(:admin)

View File

@ -352,6 +352,7 @@ project:
- cluster_project
- creator
- cycle_analytics_stages
- value_streams
- group
- namespace
- management_clusters

View File

@ -0,0 +1,71 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe '#render?' do
subject { described_class.new(context) }
context 'when menu does not have any menu items' do
it 'returns false' do
allow(subject).to receive(:has_renderable_items?).and_return(false)
expect(subject.render?).to be false
end
end
context 'when menu has menu items' do
it 'returns true' do
expect(subject.render?).to be true
end
end
end
describe 'Menu Items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
shared_examples 'access rights checks' do
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
shared_examples 'feature flag :sidebar_refactor disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
end
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
describe 'Environments' do
let(:item_id) { :environments }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
describe 'Releases' do
let(:item_id) { :releases }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
end
end

View File

@ -56,9 +56,7 @@ RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
context 'Menu items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
describe 'Metrics Dashboard' do
let(:item_id) { :metrics }
shared_examples 'access rights checks' do
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
@ -68,153 +66,109 @@ RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
end
end
describe 'Metrics Dashboard' do
let(:item_id) { :metrics }
it_behaves_like 'access rights checks'
end
describe 'Logs' do
let(:item_id) { :logs }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Tracing' do
let(:item_id) { :tracing }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Error Tracking' do
let(:item_id) { :error_tracking }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Alert Management' do
let(:item_id) { :alert_management }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Incidents' do
let(:item_id) { :incidents }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Serverless' do
let(:item_id) { :serverless }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Terraform' do
let(:item_id) { :terraform }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Kubernetes' do
let(:item_id) { :kubernetes }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Environments' do
let(:item_id) { :environments }
specify { is_expected.not_to be_nil }
specify { is_expected.to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
it_behaves_like 'access rights checks'
end
end
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
specify { is_expected.not_to be_nil }
specify { is_expected.to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
it_behaves_like 'access rights checks'
end
end

View File

@ -14,26 +14,30 @@ RSpec.describe Sidebars::Projects::Menus::ProjectInformationMenu do
describe 'Releases' do
let(:item_id) { :releases }
context 'when project repository is empty' do
it 'does not include releases menu item' do
allow(project).to receive(:empty_repo?).and_return(true)
specify { is_expected.to be_nil }
is_expected.to be_nil
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
end
context 'when project repository is not empty' do
context 'when user can download code' do
it 'includes releases menu item' do
is_expected.to be_present
context 'when project repository is empty' do
it 'does not include releases menu item' do
allow(project).to receive(:empty_repo?).and_return(true)
is_expected.to be_nil
end
end
context 'when user cannot download code' do
let(:user) { nil }
context 'when project repository is not empty' do
context 'when user can download code' do
specify { is_expected.not_to be_nil }
end
it 'does not include releases menu item' do
is_expected.to be_nil
context 'when user cannot download code' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20210503105845_add_project_value_stream_id_to_project_stages.rb')
RSpec.describe AddProjectValueStreamIdToProjectStages, schema: 20210503105022 do
let(:stages) { table(:analytics_cycle_analytics_project_stages) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:namespace) { table(:namespaces).create!(name: 'ns1', path: 'nsq1') }
before do
project = projects.create!(name: 'p1', namespace_id: namespace.id)
stages.create!(
project_id: project.id,
created_at: Time.now,
updated_at: Time.now,
start_event_identifier: 1,
end_event_identifier: 2,
name: 'stage 1'
)
stages.create!(
project_id: project.id,
created_at: Time.now,
updated_at: Time.now,
start_event_identifier: 3,
end_event_identifier: 4,
name: 'stage 2'
)
end
it 'deletes the existing rows' do
migrate!
expect(stages.count).to eq(0)
end
end

View File

@ -17,6 +17,7 @@ RSpec.describe Analytics::CycleAnalytics::ProjectStage do
end
it_behaves_like 'value stream analytics stage' do
let(:factory) { :cycle_analytics_project_stage }
let(:parent) { build(:project) }
let(:parent_name) { :project }
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::ProjectValueStream, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:stages) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(100) }
it 'validates uniqueness of name' do
project = create(:project)
create(:cycle_analytics_project_value_stream, name: 'test', project: project)
value_stream = build(:cycle_analytics_project_value_stream, name: 'test', project: project)
expect(value_stream).to be_invalid
expect(value_stream.errors.messages).to eq(name: [I18n.t('errors.messages.taken')])
end
end
it 'is not custom' do
expect(described_class.new).not_to be_custom
end
describe '.build_default_value_stream' do
it 'builds the default value stream' do
project = build(:project)
value_stream = described_class.build_default_value_stream(project)
expect(value_stream.name).to eq('default')
end
end
end

View File

@ -113,7 +113,8 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:lfs_file_locks) }
it { is_expected.to have_many(:project_deploy_tokens) }
it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) }
it { is_expected.to have_many(:cycle_analytics_stages) }
it { is_expected.to have_many(:cycle_analytics_stages).inverse_of(:project) }
it { is_expected.to have_many(:value_streams).inverse_of(:project) }
it { is_expected.to have_many(:external_pull_requests) }
it { is_expected.to have_many(:sourced_pipelines) }
it { is_expected.to have_many(:source_pipelines) }

View File

@ -728,6 +728,7 @@ RSpec.describe User do
let_it_be(:blocked_user) { create(:user, :blocked) }
let_it_be(:ldap_blocked_user) { create(:omniauth_user, :ldap_blocked) }
let_it_be(:blocked_pending_approval_user) { create(:user, :blocked_pending_approval) }
let_it_be(:banned_user) { create(:user, :banned) }
describe '.blocked' do
subject { described_class.blocked }
@ -738,7 +739,7 @@ RSpec.describe User do
ldap_blocked_user
)
expect(subject).not_to include(active_user, blocked_pending_approval_user)
expect(subject).not_to include(active_user, blocked_pending_approval_user, banned_user)
end
end
@ -749,6 +750,14 @@ RSpec.describe User do
expect(subject).to contain_exactly(blocked_pending_approval_user)
end
end
describe '.banned' do
subject { described_class.banned }
it 'returns only banned users' do
expect(subject).to contain_exactly(banned_user)
end
end
end
describe ".with_two_factor" do
@ -1934,6 +1943,12 @@ RSpec.describe User do
expect(described_class.filter_items('blocked')).to include user
end
it 'filters by banned' do
expect(described_class).to receive(:banned).and_return([user])
expect(described_class.filter_items('banned')).to include user
end
it 'filters by blocked pending approval' do
expect(described_class).to receive(:blocked_pending_approval).and_return([user])

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::StageEntity do
let(:stage) { build(:cycle_analytics_project_stage, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) }
subject(:entity_json) { described_class.new(Analytics::CycleAnalytics::StagePresenter.new(stage)).as_json }
it 'exposes start and end event descriptions' do
expect(entity_json).to have_key(:start_event_html_description)
expect(entity_json).to have_key(:end_event_html_description)
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::Stages::ListService do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let(:value_stream) { Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(project) }
let(:stages) { subject.payload[:stages] }
subject { described_class.new(parent: project, current_user: user).execute }
before_all do
project.add_reporter(user)
end
it 'returns only the default stages' do
expect(stages.size).to eq(Gitlab::Analytics::CycleAnalytics::DefaultStages.all.size)
end
it 'provides the default stages as non-persisted objects' do
expect(stages.map(&:id)).to all(be_nil)
end
end

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::BanService do
let(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
describe '#execute' do
subject(:operation) { service.execute(user) }
context 'when successful' do
let(:user) { create(:user) }
it { is_expected.to eq(status: :success) }
it "bans the user" do
expect { operation }.to change { user.state }.to('banned')
end
it "blocks the user" do
expect { operation }.to change { user.blocked? }.from(false).to(true)
end
it 'logs ban in application logs' do
allow(Gitlab::AppLogger).to receive(:info)
operation
expect(Gitlab::AppLogger).to have_received(:info).with(message: "User banned", user: "#{user.username}", email: "#{user.email}", banned_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
context 'when failed' do
let(:user) { create(:user, :blocked) }
it 'returns error result' do
aggregate_failures 'error result' do
expect(operation[:status]).to eq(:error)
expect(operation[:message]).to match(/State cannot transition/)
end
end
it "does not change the user's state" do
expect { operation }.not_to change { user.state }
end
end
end
end

View File

@ -58,6 +58,19 @@ RSpec.shared_examples 'value stream analytics stage' do
it { expect(stage).not_to be_valid }
end
# rubocop: disable Rails/SaveBang
describe '.by_value_stream' do
it 'finds stages by value stream' do
stage1 = create(factory)
create(factory) # other stage with different value stream
result = described_class.by_value_stream(stage1.value_stream)
expect(result).to eq([stage1])
end
end
# rubocop: enable Rails/SaveBang
end
describe '#subject_class' do

View File

@ -66,10 +66,20 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Releases' do
it 'has a link to the project releases path' do
it 'does not have a link to the project releases path' do
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
expect(rendered).not_to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
end
context 'when feature flag :sidebar refactor is disabled' do
it 'has a link to the project releases path' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
end
end
end
@ -417,6 +427,86 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'Deployments' do
let(:page) { Nokogiri::HTML.parse(rendered) }
describe 'Feature Flags' do
it 'has a link to the feature flags page' do
render
expect(page.at_css('.shortcuts-deployments').parent.css('[aria-label="Feature Flags"]')).not_to be_empty
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
end
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'does not have a Feature Flags menu item' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_selector('.shortcuts-deployments')
end
end
end
describe 'Environments' do
it 'has a link to the environments page' do
render
expect(page.at_css('.shortcuts-deployments').parent.css('[aria-label="Environments"]')).not_to be_empty
expect(rendered).to have_link('Environments', href: project_environments_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
end
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'does not have a Environments menu item' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_selector('.shortcuts-deployments')
end
end
end
describe 'Releases' do
it 'has a link to the project releases path' do
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-deployments-releases')
end
context 'when feature flag :sidebar refactor is disabled' do
it 'does not have a link to the project releases path' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-deployments-releases')
end
end
end
end
describe 'Operations' do
it 'top level navigation link is visible for user with permissions' do
render
@ -610,37 +700,67 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Environments' do
it 'has a link to the environments page' do
let(:page) { Nokogiri::HTML.parse(rendered) }
it 'does not have a link to the environments page' do
render
expect(rendered).to have_link('Environments', href: project_environments_path(project))
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Environments"]')).to be_empty
end
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'does not have a link to the environments page' do
it 'has a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Environments"]')).not_to be_empty
expect(rendered).to have_link('Environments', href: project_environments_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
end
end
end
end
describe 'Feature Flags' do
it 'has a link to the feature flags page' do
let(:page) { Nokogiri::HTML.parse(rendered) }
it 'does not have a link to the feature flags page' do
render
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Feature Flags"]')).to be_empty
end
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'does not have a link to the feature flags page' do
it 'has a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Feature Flags"]')).not_to be_empty
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
end
end
end
end

View File

@ -103,5 +103,14 @@ RSpec.describe Packages::Nuget::ExtractionWorker, type: :worker do
it_behaves_like 'handling the metadata error'
end
end
context 'handles a processing an unaccounted for error' do
before do
expect(::Packages::Nuget::UpdatePackageFromMetadataService).to receive(:new)
.and_raise(Zip::Error)
end
it_behaves_like 'handling the metadata error', exception_class: Zip::Error
end
end
end

View File

@ -37,6 +37,20 @@ RSpec.describe Packages::Rubygems::ExtractionWorker, type: :worker do
expect(package.reload).to be_error
end
it 'handles processing an unaccounted for error', :aggregate_failures do
expect(::Packages::Rubygems::ProcessGemService).to receive(:new)
.and_raise(Zip::Error)
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
instance_of(Zip::Error),
project_id: package.project_id
)
subject
expect(package.reload).to be_error
end
context 'returns when there is no package file' do
let(:package_file_id) { 999999 }