Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5103041796
commit
ad2d90fb24
|
|
@ -1,14 +1,59 @@
|
|||
.preflight-job-base:
|
||||
stage: preflight
|
||||
extends:
|
||||
- .default-retry
|
||||
needs: []
|
||||
|
||||
.qa-preflight-job:
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
- .qa-cache
|
||||
variables:
|
||||
USE_BUNDLE_INSTALL: "false"
|
||||
SETUP_DB: "false"
|
||||
before_script:
|
||||
- !reference [.default-before_script, before_script]
|
||||
- cd qa && bundle install
|
||||
|
||||
rails-production-environment:
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
- .default-before_script
|
||||
- .production
|
||||
- .ruby-cache
|
||||
- .setup:rules:rails-production-environment
|
||||
- .use-pg12
|
||||
stage: preflight
|
||||
variables:
|
||||
BUNDLE_WITHOUT: "development:test"
|
||||
BUNDLE_WITH: "production"
|
||||
needs: []
|
||||
script:
|
||||
- bundle exec rails runner --environment=production 'puts Rails.env'
|
||||
|
||||
no-ee-check:
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
- .setup:rules:no-ee-check
|
||||
script:
|
||||
- scripts/no-dir-check ee
|
||||
|
||||
no-jh-check:
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
- .setup:rules:no-jh-check
|
||||
script:
|
||||
- scripts/no-dir-check jh
|
||||
|
||||
qa:selectors:
|
||||
extends:
|
||||
- .qa-preflight-job
|
||||
- .qa:rules:ee-and-foss
|
||||
script:
|
||||
- bundle exec bin/qa Test::Sanity::Selectors
|
||||
|
||||
qa:selectors-as-if-foss:
|
||||
extends:
|
||||
- qa:selectors
|
||||
- .qa:rules:as-if-foss
|
||||
- .as-if-foss
|
||||
|
|
|
|||
|
|
@ -25,13 +25,6 @@ qa:internal-as-if-foss:
|
|||
- .qa:rules:internal-as-if-foss
|
||||
- .as-if-foss
|
||||
|
||||
qa:selectors:
|
||||
extends:
|
||||
- .qa-job-base
|
||||
- .qa:rules:ee-and-foss
|
||||
script:
|
||||
- bundle exec bin/qa Test::Sanity::Selectors
|
||||
|
||||
qa:master-auto-quarantine-dequarantine:
|
||||
extends:
|
||||
- .qa-job-base
|
||||
|
|
@ -50,12 +43,6 @@ qa:nightly-auto-quarantine-dequarantine:
|
|||
- bundle exec confiner -r .confiner/nightly.yml
|
||||
allow_failure: true
|
||||
|
||||
qa:selectors-as-if-foss:
|
||||
extends:
|
||||
- qa:selectors
|
||||
- .qa:rules:as-if-foss
|
||||
- .as-if-foss
|
||||
|
||||
qa:update-qa-cache:
|
||||
extends:
|
||||
- .qa-job-base
|
||||
|
|
|
|||
|
|
@ -51,22 +51,6 @@ gitlab_git_test:
|
|||
script:
|
||||
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
|
||||
|
||||
no-ee-check:
|
||||
extends:
|
||||
- .predictive-job
|
||||
- .setup:rules:no-ee-check
|
||||
stage: test
|
||||
script:
|
||||
- scripts/no-dir-check ee
|
||||
|
||||
no-jh-check:
|
||||
extends:
|
||||
- .predictive-job
|
||||
- .setup:rules:no-jh-check
|
||||
stage: test
|
||||
script:
|
||||
- scripts/no-dir-check jh
|
||||
|
||||
verify-ruby-3.0:
|
||||
extends:
|
||||
- .absolutely-predictive-job
|
||||
|
|
|
|||
|
|
@ -459,12 +459,20 @@ export default {
|
|||
@sort="handleSort"
|
||||
>
|
||||
<template #nav-actions>
|
||||
<gl-button :href="rssPath" icon="rss">
|
||||
{{ $options.i18n.rssLabel }}
|
||||
</gl-button>
|
||||
<gl-button :href="calendarPath" icon="calendar">
|
||||
{{ $options.i18n.calendarLabel }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
:href="rssPath"
|
||||
icon="rss"
|
||||
:title="$options.i18n.rssLabel"
|
||||
class="has-tooltip btn-icon"
|
||||
/>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
:href="calendarPath"
|
||||
icon="calendar"
|
||||
:title="$options.i18n.calendarLabel"
|
||||
class="has-tooltip btn-icon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #timeframe="{ issuable = {} }">
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ export default {
|
|||
emailConfirmationSettingsOffHelpText: s__(
|
||||
'ApplicationSettings|New users can sign up without confirming their email address.',
|
||||
),
|
||||
emailConfirmationSettingsSoftLabel: s__('ApplicationSettings|Soft'),
|
||||
emailConfirmationSettingsSoftHelpText: s__(
|
||||
'ApplicationSettings|Send a confirmation email during sign up. New users can log in immediately, but must confirm their email within three days.',
|
||||
),
|
||||
emailConfirmationSettingsHardLabel: s__('ApplicationSettings|Hard'),
|
||||
emailConfirmationSettingsHardHelpText: s__(
|
||||
'ApplicationSettings|Send a confirmation email during sign up. New users must confirm their email address before they can log in.',
|
||||
|
|
@ -286,16 +290,23 @@ export default {
|
|||
v-model="form.emailConfirmationSetting"
|
||||
name="application_setting[email_confirmation_setting]"
|
||||
>
|
||||
<gl-form-radio value="hard">
|
||||
{{ $options.i18n.emailConfirmationSettingsHardLabel }}
|
||||
|
||||
<template #help> {{ $options.i18n.emailConfirmationSettingsHardHelpText }} </template>
|
||||
</gl-form-radio>
|
||||
<gl-form-radio value="off">
|
||||
{{ $options.i18n.emailConfirmationSettingsOffLabel }}
|
||||
|
||||
<template #help> {{ $options.i18n.emailConfirmationSettingsOffHelpText }} </template>
|
||||
</gl-form-radio>
|
||||
|
||||
<gl-form-radio value="soft">
|
||||
{{ $options.i18n.emailConfirmationSettingsSoftLabel }}
|
||||
|
||||
<template #help> {{ $options.i18n.emailConfirmationSettingsSoftHelpText }} </template>
|
||||
</gl-form-radio>
|
||||
|
||||
<gl-form-radio value="hard">
|
||||
{{ $options.i18n.emailConfirmationSettingsHardLabel }}
|
||||
|
||||
<template #help> {{ $options.i18n.emailConfirmationSettingsHardHelpText }} </template>
|
||||
</gl-form-radio>
|
||||
</gl-form-radio-group>
|
||||
</gl-form-group>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
// TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
|
||||
// https://docs.sentry.io/platforms/javascript/configuration/filtering/#decluttering-sentry
|
||||
export const IGNORE_ERRORS = [
|
||||
// Random plugins/extensions
|
||||
'top.GLOBALS',
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import * as Sentry5 from 'sentrybrowser5';
|
||||
import $ from 'jquery';
|
||||
import { __ } from '~/locale';
|
||||
import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants';
|
||||
import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './legacy_constants';
|
||||
|
||||
const SentryConfig = {
|
||||
IGNORE_ERRORS,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import * as Sentry from 'sentrybrowser7';
|
||||
import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants';
|
||||
|
||||
const SentryConfig = {
|
||||
init(options = {}) {
|
||||
|
|
@ -17,9 +16,6 @@ const SentryConfig = {
|
|||
release,
|
||||
allowUrls,
|
||||
environment,
|
||||
ignoreErrors: IGNORE_ERRORS,
|
||||
denyUrls: DENY_URLS,
|
||||
sampleRate: SAMPLE_RATE,
|
||||
});
|
||||
|
||||
Sentry.setTags(tags);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module ConfirmEmailWarning
|
|||
protected
|
||||
|
||||
def show_confirm_warning?
|
||||
html_request? && request.get? && Feature.enabled?(:soft_email_confirmation)
|
||||
html_request? && request.get? && Gitlab::CurrentSettings.email_confirmation_setting_soft?
|
||||
end
|
||||
|
||||
def set_confirm_warning
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ConfirmationsController < Devise::ConfirmationsController
|
|||
protected
|
||||
|
||||
def after_resending_confirmation_instructions_path_for(resource)
|
||||
return users_almost_there_path unless Feature.enabled?(:soft_email_confirmation)
|
||||
return users_almost_there_path unless Gitlab::CurrentSettings.email_confirmation_setting_soft?
|
||||
|
||||
stored_location_for(resource) || dashboard_projects_path
|
||||
end
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ module Registrations
|
|||
|
||||
def requires_confirmation?(user)
|
||||
return false if user.confirmed?
|
||||
return false if Feature.enabled?(:soft_email_confirmation)
|
||||
return false unless Gitlab::CurrentSettings.email_confirmation_setting_hard?
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
def after_inactive_sign_up_path_for(resource)
|
||||
Gitlab::AppLogger.info(user_created_message)
|
||||
return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval?
|
||||
return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation)
|
||||
return dashboard_projects_path if Gitlab::CurrentSettings.email_confirmation_setting_soft?
|
||||
|
||||
# when email confirmation is enabled, path to redirect is saved
|
||||
# when email_confirmation_setting is set to `hard`, path to redirect is saved
|
||||
# after user confirms and comes back, he will be redirected
|
||||
store_location_for(:redirect, after_sign_up_path)
|
||||
|
||||
|
|
|
|||
|
|
@ -835,6 +835,33 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
|
|||
false
|
||||
end
|
||||
|
||||
# Overriding the enum check for `email_confirmation_setting` as the feature flag is being removed and is taking a
|
||||
# release M, M.N+1 strategy as noted in:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107302#note_1286005956
|
||||
def email_confirmation_setting_off?
|
||||
if Feature.enabled?(:soft_email_confirmation)
|
||||
false
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def email_confirmation_setting_soft?
|
||||
if Feature.enabled?(:soft_email_confirmation)
|
||||
true
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def email_confirmation_setting_hard?
|
||||
if Feature.enabled?(:soft_email_confirmation)
|
||||
false
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parsed_grafana_url
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ class Project < ApplicationRecord
|
|||
has_one :project_setting, inverse_of: :project, autosave: true
|
||||
has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
|
||||
has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
|
||||
has_one :service_desk_custom_email_verification, class_name: 'ServiceDesk::CustomEmailVerification'
|
||||
|
||||
# Merge requests for target project should be removed with it
|
||||
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ServiceDesk
|
||||
def self.table_name_prefix
|
||||
'service_desk_'
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ServiceDesk
|
||||
class CustomEmailVerification < ApplicationRecord
|
||||
enum state: {
|
||||
running: 0,
|
||||
verified: 1,
|
||||
error: 2
|
||||
}, _default: 'running'
|
||||
|
||||
enum error: {
|
||||
incorrect_token: 0,
|
||||
incorrect_from: 1,
|
||||
mail_not_received_within_timeframe: 2,
|
||||
invalid_credentials: 3,
|
||||
smtp_host_issue: 4
|
||||
}
|
||||
|
||||
TIMEFRAME = 30.minutes
|
||||
|
||||
attr_encrypted :token,
|
||||
mode: :per_attribute_iv,
|
||||
algorithm: 'aes-256-gcm',
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
encode: false,
|
||||
encode_iv: false
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :triggerer, class_name: 'User', optional: true
|
||||
|
||||
validates :project, presence: true
|
||||
validates :state, presence: true
|
||||
|
||||
delegate :service_desk_setting, to: :project
|
||||
|
||||
class << self
|
||||
def generate_token
|
||||
SecureRandom.alphanumeric(12)
|
||||
end
|
||||
end
|
||||
|
||||
def accepted_until
|
||||
return unless running?
|
||||
return unless triggered_at.present?
|
||||
|
||||
TIMEFRAME.since(triggered_at)
|
||||
end
|
||||
|
||||
def in_timeframe?
|
||||
return false unless running?
|
||||
|
||||
!!accepted_until&.future?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
class ServiceDeskSetting < ApplicationRecord
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
CUSTOM_EMAIL_VERIFICATION_SUBADDRESS = '+verify'
|
||||
|
||||
attribute :custom_email_enabled, default: false
|
||||
attr_encrypted :custom_email_smtp_password,
|
||||
mode: :per_attribute_iv,
|
||||
|
|
@ -12,6 +14,7 @@ class ServiceDeskSetting < ApplicationRecord
|
|||
encode_iv: false
|
||||
|
||||
belongs_to :project
|
||||
|
||||
validates :project_id, presence: true
|
||||
validate :valid_issue_template
|
||||
validate :valid_project_key
|
||||
|
|
@ -32,21 +35,25 @@ class ServiceDeskSetting < ApplicationRecord
|
|||
validates :custom_email,
|
||||
presence: true,
|
||||
devise_email: true,
|
||||
if: :custom_email_enabled?
|
||||
if: :needs_custom_email_smtp_credentials?
|
||||
validates :custom_email_smtp_address,
|
||||
presence: true,
|
||||
hostname: { allow_numeric_hostname: true, require_valid_tld: true },
|
||||
if: :custom_email_enabled?
|
||||
if: :needs_custom_email_smtp_credentials?
|
||||
validates :custom_email_smtp_username,
|
||||
presence: true,
|
||||
if: :custom_email_enabled?
|
||||
if: :needs_custom_email_smtp_credentials?
|
||||
validates :custom_email_smtp_port,
|
||||
presence: true,
|
||||
numericality: { only_integer: true, greater_than: 0 },
|
||||
if: :custom_email_enabled?
|
||||
if: :needs_custom_email_smtp_credentials?
|
||||
|
||||
scope :with_project_key, ->(key) { where(project_key: key) }
|
||||
|
||||
def custom_email_verification
|
||||
project&.service_desk_custom_email_verification
|
||||
end
|
||||
|
||||
def custom_email_delivery_options
|
||||
{
|
||||
user_name: custom_email_smtp_username,
|
||||
|
|
@ -57,6 +64,12 @@ class ServiceDeskSetting < ApplicationRecord
|
|||
}
|
||||
end
|
||||
|
||||
def custom_email_address_for_verification
|
||||
return unless custom_email.present?
|
||||
|
||||
custom_email.sub("@", "#{CUSTOM_EMAIL_VERIFICATION_SUBADDRESS}@")
|
||||
end
|
||||
|
||||
def issue_template_content
|
||||
strong_memoize(:issue_template_content) do
|
||||
next unless issue_template_key.present?
|
||||
|
|
@ -102,6 +115,10 @@ class ServiceDeskSetting < ApplicationRecord
|
|||
setting.project.full_path_slug == project_slug
|
||||
end
|
||||
end
|
||||
|
||||
def needs_custom_email_smtp_credentials?
|
||||
custom_email_enabled? || custom_email_verification.present?
|
||||
end
|
||||
end
|
||||
|
||||
ServiceDeskSetting.prepend_mod
|
||||
|
|
|
|||
|
|
@ -2129,7 +2129,15 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def confirmation_required_on_sign_in?
|
||||
!confirmed? && !confirmation_period_valid?
|
||||
return false if confirmed?
|
||||
|
||||
if ::Gitlab::CurrentSettings.email_confirmation_setting_off?
|
||||
false
|
||||
elsif ::Gitlab::CurrentSettings.email_confirmation_setting_soft?
|
||||
!in_confirmation_period?
|
||||
elsif ::Gitlab::CurrentSettings.email_confirmation_setting_hard?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def impersonated?
|
||||
|
|
@ -2210,10 +2218,13 @@ class User < ApplicationRecord
|
|||
|
||||
# override from Devise::Confirmable
|
||||
def confirmation_period_valid?
|
||||
return false if Feature.disabled?(:soft_email_confirmation)
|
||||
return super if ::Gitlab::CurrentSettings.email_confirmation_setting_soft?
|
||||
|
||||
super
|
||||
# Following devise logic for method, we want to return `true`
|
||||
# See: https://github.com/heartcombo/devise/blob/main/lib/devise/models/confirmable.rb#L191-L218
|
||||
true
|
||||
end
|
||||
alias_method :in_confirmation_period?, :confirmation_period_valid?
|
||||
|
||||
# This is copied from Devise::Models::TwoFactorAuthenticatable#consume_otp!
|
||||
#
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ module BulkImports
|
|||
validate_symlink
|
||||
|
||||
extract_archive
|
||||
remove_symlinks
|
||||
tmpdir
|
||||
end
|
||||
|
||||
|
|
@ -60,15 +59,5 @@ module BulkImports
|
|||
def extract_archive
|
||||
untar_xf(archive: filepath, dir: tmpdir)
|
||||
end
|
||||
|
||||
def extracted_files
|
||||
Dir.glob(File.join(tmpdir, '**', '*'))
|
||||
end
|
||||
|
||||
def remove_symlinks
|
||||
extracted_files.each do |path|
|
||||
FileUtils.rm(path) if symlink?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -113,7 +113,13 @@ module Ci
|
|||
end
|
||||
|
||||
def accessibility(params)
|
||||
params[:accessibility] || 'public'
|
||||
accessibility = params[:accessibility]
|
||||
|
||||
return :public if Feature.disabled?(:non_public_artifacts, type: :development)
|
||||
|
||||
return accessibility if accessibility.present?
|
||||
|
||||
job.artifacts_public? ? :public : :private
|
||||
end
|
||||
|
||||
def parse_artifact(artifact)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
= render_if_exists 'admin/application_settings/ldap_access_setting', form: f
|
||||
|
||||
= render_if_exists 'admin/application_settings/saml_group_locks_setting', form: f
|
||||
|
||||
.form-group{ data: { testid: 'project-export' } }
|
||||
= f.label :project_export, s_('AdminSettings|Project export'), class: 'label-bold'
|
||||
= f.gitlab_ui_checkbox_component :project_export_enabled, s_('AdminSettings|Enabled')
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
- show_calendar_button = local_assigns.fetch(:show_calendar_button, true)
|
||||
|
||||
= render Pajamas::ButtonComponent.new(href: safe_params.merge(rss_url_options), icon: 'rss', button_options: { class: 'has-tooltip', 'aria-label': _('Subscribe to RSS feed'), data: { container: 'body', testid: 'rss-feed-link' } }) do
|
||||
= _('Subscribe to RSS feed')
|
||||
= render Pajamas::ButtonComponent.new(href: safe_params.merge(rss_url_options), button_options: { class: 'has-tooltip btn-icon', title: _('Subscribe to RSS feed'), 'aria-label': _('Subscribe to RSS feed'), data: { container: 'body', testid: 'rss-feed-link' } }) do
|
||||
= sprite_icon('rss')
|
||||
|
||||
- if show_calendar_button
|
||||
= render Pajamas::ButtonComponent.new(href: safe_params.merge(calendar_url_options), icon: 'calendar', button_options: { class: 'has-tooltip', 'aria-label': _('Subscribe to calendar'), data: { container: 'body' } }) do
|
||||
= _('Subscribe to calendar')
|
||||
= render Pajamas::ButtonComponent.new(href: safe_params.merge(calendar_url_options), button_options: { class: 'has-tooltip btn-icon', title: _('Subscribe to calendar'), 'aria-label': _('Subscribe to calendar'), data: { container: 'body' } }) do
|
||||
= sprite_icon('calendar')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
table_name: service_desk_custom_email_verifications
|
||||
classes:
|
||||
- ServiceDesk::CustomEmailVerification
|
||||
feature_categories:
|
||||
- service_desk
|
||||
description: Holds the verification state and additional information for custom email
|
||||
addresses for Service Desk
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112938
|
||||
milestone: '15.10'
|
||||
gitlab_schema: gitlab_main
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddServiceDeskCustomEmailVerifications < Gitlab::Database::Migration[2.1]
|
||||
enable_lock_retries!
|
||||
|
||||
def up
|
||||
create_table(:service_desk_custom_email_verifications, id: false, primary_key: :project_id) do |t|
|
||||
t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false
|
||||
t.references :triggerer, index: true, foreign_key: { to_table: :users, on_delete: :nullify }
|
||||
t.timestamps_with_timezone
|
||||
t.datetime_with_timezone :triggered_at
|
||||
t.integer :state, limit: 2, null: false, default: 0
|
||||
t.integer :error, limit: 2
|
||||
t.binary :encrypted_token
|
||||
t.binary :encrypted_token_iv
|
||||
end
|
||||
|
||||
execute "ALTER TABLE service_desk_custom_email_verifications ADD PRIMARY KEY (project_id);"
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :service_desk_custom_email_verifications
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSamlGroupLockToApplicationSettings < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
add_column :application_settings, :lock_memberships_to_saml, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueDeleteOrphanedPackagesDependencies < Gitlab::Database::Migration[2.1]
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = 'DeleteOrphanedPackagesDependencies'
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 6000
|
||||
SUB_BATCH_SIZE = 100
|
||||
|
||||
disable_ddl_transaction!
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:packages_dependencies,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :packages_dependencies, :id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RecreateUserTypeMigrationIndexes < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INCORRECT_BILLABLE_INDEX = 'index_users_for_active_billable_users_migration'
|
||||
BILLABLE_INDEX = 'migrate_index_users_for_active_billable_users'
|
||||
|
||||
def up
|
||||
# Temporary index to migrate human user_type. See https://gitlab.com/gitlab-org/gitlab/-/issues/386474
|
||||
add_concurrent_index :users, :id, name: BILLABLE_INDEX,
|
||||
where: "state = 'active' AND ((user_type IS NULL OR user_type = 0) OR (user_type = ANY (ARRAY[0, 6, 4, 13]))) " \
|
||||
"AND ((user_type IS NULL OR user_type = 0) OR (user_type = ANY (ARRAY[0, 4, 5])))"
|
||||
|
||||
remove_concurrent_index_by_name :users, INCORRECT_BILLABLE_INDEX
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :users, :id, name: INCORRECT_BILLABLE_INDEX,
|
||||
where: "state = 'active' AND ((user_type IS NULL OR user_type = 0) OR (user_type = ANY (ARRAY[6, 4, 13]))) " \
|
||||
"AND ((user_type IS NULL OR user_type = 0) OR (user_type = ANY (ARRAY[4, 5])))"
|
||||
remove_concurrent_index_by_name :users, BILLABLE_INDEX
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
d6fdfc530a49b230aa041d4629a0484462abacb824f6bbf23d9740068e3ca781
|
||||
|
|
@ -0,0 +1 @@
|
|||
191d7be803e9e3a2a5292bbcd562c34a67c07b73da2c429ac2f115b28d04f00c
|
||||
|
|
@ -0,0 +1 @@
|
|||
5f2176abfc462e65c9ef2b9b28c9feb60cac868aa491d4d4207a8904deb60f18
|
||||
|
|
@ -0,0 +1 @@
|
|||
d1accdc2bbe9aa5266df98a893176fba94148f9754d2c0b2de04e9d8d66d8eba
|
||||
|
|
@ -11748,6 +11748,7 @@ CREATE TABLE application_settings (
|
|||
projects_api_rate_limit_unauthenticated integer DEFAULT 400 NOT NULL,
|
||||
deny_all_requests_except_allowed boolean DEFAULT false NOT NULL,
|
||||
product_analytics_data_collector_host text,
|
||||
lock_memberships_to_saml boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
|
|
@ -22180,6 +22181,18 @@ CREATE TABLE serverless_domain_cluster (
|
|||
certificate text
|
||||
);
|
||||
|
||||
CREATE TABLE service_desk_custom_email_verifications (
|
||||
project_id bigint NOT NULL,
|
||||
triggerer_id bigint,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
triggered_at timestamp with time zone,
|
||||
state smallint DEFAULT 0 NOT NULL,
|
||||
error smallint,
|
||||
encrypted_token bytea,
|
||||
encrypted_token_iv bytea
|
||||
);
|
||||
|
||||
CREATE TABLE service_desk_settings (
|
||||
project_id bigint NOT NULL,
|
||||
issue_template_key character varying(255),
|
||||
|
|
@ -27610,6 +27623,9 @@ ALTER TABLE ONLY sprints
|
|||
ALTER TABLE ONLY serverless_domain_cluster
|
||||
ADD CONSTRAINT serverless_domain_cluster_pkey PRIMARY KEY (uuid);
|
||||
|
||||
ALTER TABLE ONLY service_desk_custom_email_verifications
|
||||
ADD CONSTRAINT service_desk_custom_email_verifications_pkey PRIMARY KEY (project_id);
|
||||
|
||||
ALTER TABLE ONLY service_desk_settings
|
||||
ADD CONSTRAINT service_desk_settings_pkey PRIMARY KEY (project_id);
|
||||
|
||||
|
|
@ -31836,6 +31852,8 @@ CREATE INDEX index_serverless_domain_cluster_on_creator_id ON serverless_domain_
|
|||
|
||||
CREATE INDEX index_serverless_domain_cluster_on_pages_domain_id ON serverless_domain_cluster USING btree (pages_domain_id);
|
||||
|
||||
CREATE INDEX index_service_desk_custom_email_verifications_on_triggerer_id ON service_desk_custom_email_verifications USING btree (triggerer_id);
|
||||
|
||||
CREATE INDEX index_service_desk_enabled_projects_on_id_creator_id_created_at ON projects USING btree (id, creator_id, created_at) WHERE (service_desk_enabled = true);
|
||||
|
||||
CREATE INDEX index_service_desk_settings_on_file_template_project_id ON service_desk_settings USING btree (file_template_project_id);
|
||||
|
|
@ -32154,8 +32172,6 @@ CREATE UNIQUE INDEX index_user_synced_attributes_metadata_on_user_id ON user_syn
|
|||
|
||||
CREATE INDEX index_users_for_active_billable_users ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[6, 4, 13]))) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[4, 5]))));
|
||||
|
||||
CREATE INDEX index_users_for_active_billable_users_migration ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = 0) OR (user_type = ANY (ARRAY[6, 4, 13]))) AND ((user_type IS NULL) OR (user_type = 0) OR (user_type = ANY (ARRAY[4, 5]))));
|
||||
|
||||
CREATE INDEX index_users_on_accepted_term_id ON users USING btree (accepted_term_id);
|
||||
|
||||
CREATE INDEX index_users_on_admin ON users USING btree (admin);
|
||||
|
|
@ -32484,6 +32500,8 @@ CREATE UNIQUE INDEX merge_request_user_mentions_on_mr_id_index ON merge_request_
|
|||
|
||||
CREATE INDEX merge_requests_state_id_temp_index ON merge_requests USING btree (id) WHERE (state_id = ANY (ARRAY[2, 3]));
|
||||
|
||||
CREATE INDEX migrate_index_users_for_active_billable_users ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = 0) OR (user_type = ANY (ARRAY[0, 6, 4, 13]))) AND ((user_type IS NULL) OR (user_type = 0) OR (user_type = ANY (ARRAY[0, 4, 5]))));
|
||||
|
||||
CREATE INDEX note_mentions_temp_index ON notes USING btree (id, noteable_type) WHERE (note ~~ '%@%'::text);
|
||||
|
||||
CREATE UNIQUE INDEX one_canonical_wiki_page_slug_per_metadata ON wiki_page_slugs USING btree (wiki_page_meta_id) WHERE (canonical = true);
|
||||
|
|
@ -35109,6 +35127,9 @@ ALTER TABLE ONLY diff_note_positions
|
|||
ALTER TABLE ONLY analytics_cycle_analytics_aggregations
|
||||
ADD CONSTRAINT fk_rails_13c8374c7a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY service_desk_custom_email_verifications
|
||||
ADD CONSTRAINT fk_rails_14dcaf4c92 FOREIGN KEY (triggerer_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY namespaces_storage_limit_exclusions
|
||||
ADD CONSTRAINT fk_rails_14e8f7b0e0 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -36462,6 +36483,9 @@ ALTER TABLE ONLY dast_scanner_profiles_tags
|
|||
ALTER TABLE ONLY vulnerability_feedback
|
||||
ADD CONSTRAINT fk_rails_debd54e456 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY service_desk_custom_email_verifications
|
||||
ADD CONSTRAINT fk_rails_debe4c4acc FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY packages_debian_project_distributions
|
||||
ADD CONSTRAINT fk_rails_df44271a30 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE RESTRICT;
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ It does not cover all data types.
|
|||
| LFS objects (using Git) | **{check-circle}** Yes |
|
||||
| Pages | **{dotted-circle}** No <sup>2</sup> |
|
||||
| Advanced search (using the web UI) | **{dotted-circle}** No |
|
||||
| Container registry | **{dotted-circle}** No |
|
||||
|
||||
1. Git reads are served from the local secondary while pushes get proxied to the primary.
|
||||
Selective sync or cases where repositories don't exist locally on the Geo secondary throw a "not found" error.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
Keep your GitLab instance up and running smoothly.
|
||||
|
||||
- [Upgrading GitLab](../../update/index.md).
|
||||
- [Rake tasks](../../raketasks/index.md): Tasks for common administration and operational processes such as
|
||||
[cleaning up unneeded items from GitLab instance](../../raketasks/cleanup.md), integrity checks,
|
||||
and more.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Pods
|
||||
group: Tenant Scale
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ or a [CI/CD job token](../ci/jobs/ci_job_token.md) for authentication.
|
|||
|
||||
With a CI/CD job token, the [triggered pipeline is a multi-project pipeline](../ci/pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api).
|
||||
The job that authenticates the request becomes associated with the upstream pipeline,
|
||||
which is visible on the [pipeline graph](../ci/pipelines/downstream_pipelines.md#view-multi-project-pipelines-in-pipeline-graphs).
|
||||
which is visible on the pipeline graph.
|
||||
|
||||
If you use a trigger token in a job, the job is not associated with the upstream pipeline.
|
||||
|
||||
|
|
|
|||
|
|
@ -315,18 +315,32 @@ trigger_pipeline:
|
|||
> Hover behavior for pipeline cards [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/197140/) in GitLab 13.2.
|
||||
|
||||
In the [pipeline graph view](index.md#view-full-pipeline-graph), downstream pipelines display
|
||||
as a list of cards on the right of the graph. Hover over the pipeline's card to view
|
||||
which job triggered the downstream pipeline.
|
||||
as a list of cards on the right of the graph. From this view, you can:
|
||||
|
||||
### Retry a downstream pipeline
|
||||
- Select a trigger job to see the triggered downstream pipeline's jobs.
|
||||
- Select **Expand jobs** **{chevron-lg-right}** on a pipeline card to expand the view
|
||||
with the downstream pipeline's jobs. You can view one downstream pipeline at a time.
|
||||
- Hover over a pipeline card to have the job that triggered the downstream pipeline highlighted.
|
||||
|
||||
### Retry failed and canceled jobs in a downstream pipeline
|
||||
|
||||
> - Retry from graph view [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354974) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `downstream_retry_action`. Disabled by default.
|
||||
> - Retry from graph view [generally available and feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357406) in GitLab 15.1.
|
||||
|
||||
To retry a completed downstream pipeline, select **Retry** (**{retry}**):
|
||||
To retry failed and canceled jobs, select **Retry** (**{retry}**):
|
||||
|
||||
- From the downstream pipeline's details page.
|
||||
- On the pipeline's card in the [pipeline graph view](index.md#view-full-pipeline-graph).
|
||||
- On the pipeline's card in the pipeline graph view.
|
||||
|
||||
### Recreate a downstream pipeline
|
||||
|
||||
> Retry trigger job from graph view [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367547) in GitLab 15.10 [with a flag](../../administration/feature_flags.md) named `ci_recreate_downstream_pipeline`. Disabled by default.
|
||||
|
||||
You can recreate a downstream pipeline by retrying its corresponding trigger job. The newly created downstream pipeline replaces the current downstream pipeline in the pipeline graph.
|
||||
|
||||
To recreate a downstream pipeline:
|
||||
|
||||
- Select **Run again** (**{retry}**) on the trigger job's card in the pipeline graph view.
|
||||
|
||||
### Cancel a downstream pipeline
|
||||
|
||||
|
|
@ -336,7 +350,7 @@ To retry a completed downstream pipeline, select **Retry** (**{retry}**):
|
|||
To cancel a downstream pipeline that is still running, select **Cancel** (**{cancel}**):
|
||||
|
||||
- From the downstream pipeline's details page.
|
||||
- On the pipeline's card in the [pipeline graph view](index.md#view-full-pipeline-graph).
|
||||
- On the pipeline's card in the pipeline graph view.
|
||||
|
||||
### Mirror the status of a downstream pipeline in the trigger job
|
||||
|
||||
|
|
@ -371,13 +385,9 @@ trigger_job:
|
|||
After you trigger a multi-project pipeline, the downstream pipeline displays
|
||||
to the right of the [pipeline graph](index.md#visualize-pipelines).
|
||||
|
||||

|
||||
|
||||
In [pipeline mini graphs](index.md#pipeline-mini-graphs), the downstream pipeline
|
||||
displays to the right of the mini graph.
|
||||
|
||||

|
||||
|
||||
## Fetch artifacts from an upstream pipeline
|
||||
|
||||
Use [`needs:project`](../yaml/index.md#needsproject) to fetch artifacts from an
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB |
|
|
@ -423,8 +423,7 @@ You can group the jobs by:
|
|||
- [Job dependencies](#view-job-dependencies-in-the-pipeline-graph), which arranges
|
||||
jobs based on their [`needs`](../yaml/index.md#needs) dependencies.
|
||||
|
||||
[Multi-project pipeline graphs](downstream_pipelines.md#view-multi-project-pipelines-in-pipeline-graphs) help
|
||||
you visualize the entire pipeline, including all cross-project inter-dependencies.
|
||||
Multi-project pipeline graphs help you visualize the entire pipeline, including all cross-project inter-dependencies.
|
||||
|
||||
If a stage contains more than 100 jobs, only the first 100 jobs are listed in the
|
||||
pipeline graph. The remaining jobs still run as usual. To see the jobs:
|
||||
|
|
|
|||
|
|
@ -961,10 +961,8 @@ job:
|
|||
|
||||
#### `artifacts:public`
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49775) in GitLab 13.8
|
||||
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223273) in GitLab 13.8 [with a flag](../../user/feature_flags.md) named `non_public_artifacts`, disabled by default.
|
||||
> - [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/322454) in GitLab 15.10. Artifacts created with `artifacts:public` before 15.10 are not guaranteed to remain private after this update.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ We were using Overcommit prior to Lefthook, so you may want to uninstall it firs
|
|||
|
||||
### Install Lefthook
|
||||
|
||||
1. Install the `lefthook` Ruby gem:
|
||||
1. You can install lefthook in [different ways](https://github.com/evilmartians/lefthook/blob/master/docs/install.md#install-lefthook).
|
||||
If you do not choose to install it globally (e.g. via Homebrew or package managers), and only want to use it for the GitLab project,
|
||||
you can install the Ruby gem via:
|
||||
|
||||
```shell
|
||||
bundle install
|
||||
|
|
@ -39,12 +41,18 @@ We were using Overcommit prior to Lefthook, so you may want to uninstall it firs
|
|||
1. Install Lefthook managed Git hooks:
|
||||
|
||||
```shell
|
||||
# If installed globally
|
||||
lefthook install
|
||||
# Or if installed via ruby gem
|
||||
bundle exec lefthook install
|
||||
```
|
||||
|
||||
1. Test Lefthook is working by running the Lefthook `pre-push` Git hook:
|
||||
|
||||
```shell
|
||||
# If installed globally
|
||||
lefthook run pre-push
|
||||
# Or if installed via ruby gem
|
||||
bundle exec lefthook run pre-push
|
||||
```
|
||||
|
||||
|
|
@ -57,6 +65,18 @@ Lefthook is configured with a combination of:
|
|||
- Project configuration in [`lefthook.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lefthook.yml).
|
||||
- Any [local configuration](https://github.com/evilmartians/lefthook/blob/master/README.md#local-config).
|
||||
|
||||
### Lefthook auto-fixing files
|
||||
|
||||
We have a custom lefthook target to run all the linters with auto-fix capabilities,
|
||||
but just on the files which changed in your branch.
|
||||
|
||||
```shell
|
||||
# If installed globally
|
||||
lefthook run auto-fix
|
||||
# Or if installed via ruby gem
|
||||
bundle exec lefthook run auto-fix
|
||||
```
|
||||
|
||||
### Disable Lefthook temporarily
|
||||
|
||||
To disable Lefthook temporarily, you can set the `LEFTHOOK` environment variable to `0`. For instance:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Pods
|
||||
group: Tenant Scale
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ This means that new dependencies should, at a minimum, meet the following criter
|
|||
- There are no issues open that we know may impact the availability or performance of GitLab.
|
||||
- The project is tested using some form of test automation. The test suite must be passing
|
||||
using the Ruby version currently used by GitLab.
|
||||
- CI builds for all supported platforms must succeed using the new dependency. For more information, see
|
||||
how to [build a package for testing](build_test_package.md#building-a-package-for-testing).
|
||||
- If the project uses a C extension, consider requesting an additional review from a C or MRI
|
||||
domain expert. C extensions can greatly impact GitLab stability and performance.
|
||||
|
||||
|
|
|
|||
|
|
@ -662,6 +662,7 @@ For basic guidance on choosing a cluster configuration you may refer to [Elastic
|
|||
|
||||
- Generally, you want to use at least a 2-node cluster configuration with one replica, which allows you to have resilience. If your storage usage is growing quickly, you may want to plan horizontal scaling (adding more nodes) beforehand.
|
||||
- It's not recommended to use HDD storage with the search cluster, because it takes a hit on performance. It's better to use SSD storage (NVMe or SATA SSD drives for example).
|
||||
- You should not use [coordinating-only nodes](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#coordinating-only-node) with large instances. Coordinating-only nodes are smaller than [data nodes](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#data-node), which can impact performance and advanced search migrations.
|
||||
- You can use the [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) to benchmark search performance with different search cluster sizes and configurations.
|
||||
- `Heap size` should be set to no more than 50% of your physical RAM. Additionally, it shouldn't be set to more than the threshold for zero-based compressed oops. The exact threshold varies, but 26 GB is safe on most systems, but can also be as large as 30 GB on some systems. See [Heap size settings](https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#heap-size-settings) and [Setting JVM options](https://www.elastic.co/guide/en/elasticsearch/reference/current/jvm-options.html) for more details.
|
||||
- Number of CPUs (CPU cores) per node usually corresponds to the `Number of Elasticsearch shards` setting described below.
|
||||
|
|
|
|||
|
|
@ -196,11 +196,11 @@ accordingly, while also consulting the
|
|||
|
||||
NOTE:
|
||||
When not explicitly specified, upgrade GitLab to the latest available patch
|
||||
release rather than the first patch release, for example `13.8.8` instead of `13.8.0`.
|
||||
This includes versions you must stop at on the upgrade path as there may
|
||||
release of the `major`.`minor` release rather than the first patch release, for example `13.8.8` instead of `13.8.0`.
|
||||
This includes `major`.`minor` versions you must stop at on the upgrade path as there may
|
||||
be fixes for issues relating to the upgrade process.
|
||||
Specifically around a [major version](#upgrading-to-a-new-major-version),
|
||||
crucial database schema and migration patches are included in the latest patch releases.
|
||||
crucial database schema and migration patches may be included in the latest patch releases.
|
||||
|
||||
## Upgrading between editions
|
||||
|
||||
|
|
@ -237,7 +237,7 @@ possible.
|
|||
|
||||
## Version-specific upgrading instructions
|
||||
|
||||
Each month, major, minor, or patch releases of GitLab are published along with a
|
||||
Each month, major or minor as well as possibly patch releases of GitLab are published along with a
|
||||
[release post](https://about.gitlab.com/releases/categories/releases/).
|
||||
You should read the release posts for all versions you're passing over.
|
||||
At the end of major and minor release posts, there are three sections to look for specifically:
|
||||
|
|
@ -267,7 +267,6 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
### 15.9.0
|
||||
|
||||
- There is a [database migration bug in GitLab 15.9.x](#user-profile-data-loss-bug-in-159x) that can cause data to be lost from the user profile fields. This bug affects all currently available 15.9.x releases. Until a bug fix is released, you should upgrade to 15.6.x, 15.7.x, or 15.8.x first.
|
||||
- This version removes `SanitizeConfidentialTodos` background migration [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87908/diffs) in 15.6, which removed any user inaccessible to-do items. Make sure that this migration is finished before upgrading to 15.9.
|
||||
- As part of the [CI Partitioning effort](../architecture/blueprints/ci_data_decay/pipeline_partitioning.md), a [new Foreign Key](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107547) was added to `ci_builds_needs`. On GitLab instances with large CI tables, adding this constraint can take longer than usual. Make sure that this migration is finished before upgrading to 15.9.
|
||||
- Praefect's metadata verifier's [invalid metadata deletion behavior](../administration/gitaly/praefect.md#enable-deletions) is now enabled by default.
|
||||
|
||||
|
|
|
|||
|
|
@ -51,17 +51,26 @@ signing up using OmniAuth or LDAP, set `block_auto_created_users` to `true` in t
|
|||
[OmniAuth configuration](../../../integration/omniauth.md#configure-common-settings) or
|
||||
[LDAP configuration](../../../administration/auth/ldap/index.md#basic-configuration-settings).
|
||||
|
||||
## Require email confirmation
|
||||
## Confirm user email
|
||||
|
||||
> - Soft email confirmation [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2 [with a flag](../../../operations/feature_flags.md) named `soft_email_confirmation`.
|
||||
> - Soft email confirmation [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107302/diffs) from a feature flag to an application setting in GitLab 15.9.
|
||||
|
||||
You can send confirmation emails during sign up and require that users confirm
|
||||
their email address before they are allowed to sign in.
|
||||
|
||||
To enforce confirmation of the email address used for new sign ups:
|
||||
For example, to enforce confirmation of the email address used for new sign ups:
|
||||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General**, and expand **Sign-up restrictions**.
|
||||
1. Under **Email confirmation settings**, select **Hard**.
|
||||
|
||||
The following settings are available:
|
||||
|
||||
- **Hard** - Send a confirmation email during sign up. New users must confirm their email address before they can log in.
|
||||
- **Soft** - Send a confirmation email during sign up. New users can log in immediately, but must confirm their email in three days. Unconfirmed accounts are deleted.
|
||||
- **Off** - New users can sign up without confirming their email address.
|
||||
|
||||
## User cap
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.7.
|
||||
|
|
@ -95,22 +104,6 @@ New user sign ups are subject to the user cap restriction.
|
|||
New users sign ups are not subject to the user cap restriction. Users in pending approval state are
|
||||
automatically approved in a background job.
|
||||
|
||||
## Soft email confirmation
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2.
|
||||
> - It's [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-soft-email-confirmation).
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
The soft email confirmation improves the sign-up experience for new users by allowing
|
||||
them to sign in without an immediate confirmation when an email confirmation is required.
|
||||
GitLab shows the user a reminder to confirm their email address, and the user can't
|
||||
create or update pipelines until their email address is confirmed.
|
||||
|
||||
## Minimum password length limit
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20661) in GitLab 12.6
|
||||
|
|
@ -171,25 +164,6 @@ semicolon, comma, or a new line.
|
|||
|
||||

|
||||
|
||||
### Enable or disable soft email confirmation
|
||||
|
||||
Soft email confirmation is under development but ready for production use.
|
||||
It is deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:soft_email_confirmation)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:soft_email_confirmation)
|
||||
```
|
||||
|
||||
## Set up LDAP user filter
|
||||
|
||||
You can limit GitLab access to a subset of the LDAP users on your LDAP server.
|
||||
|
|
|
|||
|
|
@ -106,6 +106,30 @@ Users granted:
|
|||
|
||||
SAML group membership is evaluated each time a user signs in.
|
||||
|
||||
### Global SAML group memberships lock **(PREMIUM SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/386390) in GitLab 15.10.
|
||||
|
||||
GitLab administrators can use the global SAML group memberships lock to prevent group members from inviting new members to subgroups that have their membership synchronized with SAML Group Links.
|
||||
|
||||
Global group memberships lock only applies to subgroups of a top-level group where SAML Group Links synchronization is configured. No user can modify the
|
||||
membership of a top-level group configured for SAML Group Links synchronization.
|
||||
|
||||
When global group memberships lock is enabled:
|
||||
|
||||
- Only an administrator can manage memberships of any group including access levels.
|
||||
- Users cannot:
|
||||
- Share a project with other groups.
|
||||
- Invite members to a project created in a group.
|
||||
|
||||
To enable global group memberships lock:
|
||||
|
||||
1. [Configure SAML](../../../integration/saml.md) for your self-managed GitLab instance.
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand the **Visibility and access controls** section.
|
||||
1. Ensure the **Lock memberships to SAML synchronization** checkbox is selected.
|
||||
|
||||
### Automatic member removal
|
||||
|
||||
After a group sync, users who are not members of a mapped SAML group are removed from the group.
|
||||
|
|
|
|||
|
|
@ -266,19 +266,28 @@ The GitLab npm repository supports the following commands for the npm CLI (`npm`
|
|||
|
||||
### `404 Not Found` errors are happening on `npm install` or `yarn`
|
||||
|
||||
Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project gives you 404 Not Found errors. A fix for this problem is proposed in [issue 352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962).
|
||||
Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project gives you 404 Not Found errors. You need to authenticate with a token that has access to the package and all its dependencies.
|
||||
|
||||
As a workaround, you can:
|
||||
If the package and its dependencies are in separate projects but in the same group, you can use a
|
||||
[group deploy token](../../project/deploy_tokens/index.md#create-a-deploy-token):
|
||||
|
||||
1. Create a [personal access token](../../profile/personal_access_tokens.md).
|
||||
1. Authenticate at both the instance level and project level for each package:
|
||||
```ini
|
||||
//gitlab.example.com/api/v4/packages/npm/:_authToken=<group-token>
|
||||
@group-scope:registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
```
|
||||
|
||||
```ini
|
||||
@foo:registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
//gitlab.example.com/api/v4/packages/npm/:_authToken=${MY_TOKEN}
|
||||
//gitlab.example.com/api/v4/projects/<your_project_id_a>/packages/npm/:_authToken=${MY_TOKEN}
|
||||
//gitlab.example.com/api/v4/projects/<your_project_id_b>/packages/npm/:_authToken=${MY_TOKEN}
|
||||
```
|
||||
If the package and its dependencies are spread across multiple groups, you can use a [personal access token](../../profile/personal_access_tokens.md)
|
||||
from a user that has access to all the groups or individual projects:
|
||||
|
||||
```ini
|
||||
//gitlab.example.com/api/v4/packages/npm/:_authToken=<personal-access-token>
|
||||
@group-1:registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
@group-2:registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
```
|
||||
|
||||
WARNING:
|
||||
Personal access tokens must be treated carefully. Read our [token security considerations](../../../security/token_overview.md#security-considerations)
|
||||
for guidance on managing personal access tokens (for example, setting a short expiry and using minimal scopes).
|
||||
|
||||
### `npm publish` targets default npm registry (`registry.npmjs.org`)
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ Advanced search uses [Elasticsearch syntax](https://www.elastic.co/guide/en/elas
|
|||
|
||||
### Refining user search
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/388409) in GitLab 15.10.
|
||||
|
||||
In user search, a [fuzzy query](https://www.elastic.co/guide/en/elasticsearch/reference/7.2/query-dsl-fuzzy-query.html) is used by default. You can refine your search with [Elasticsearch syntax](#syntax).
|
||||
|
||||
### Code search
|
||||
|
|
|
|||
25
lefthook.yml
25
lefthook.yml
|
|
@ -96,6 +96,7 @@ pre-push:
|
|||
"merge_conflicts":
|
||||
skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#skip
|
||||
runner: bash
|
||||
|
||||
pre-commit:
|
||||
parallel: true
|
||||
commands:
|
||||
|
|
@ -103,3 +104,27 @@ pre-commit:
|
|||
tags: secrets
|
||||
files: git diff --name-only --diff-filter=d --staged
|
||||
run: 'if command -v gitleaks > /dev/null 2>&1; then gitleaks protect --no-banner --staged --redact --verbose; else echo "WARNING: gitleaks is not installed. Please install it. See https://github.com/zricethezav/gitleaks#installing."; fi'
|
||||
|
||||
auto-fix:
|
||||
parallel: true
|
||||
commands:
|
||||
frontend:
|
||||
tags: frontend style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
glob: '*.{js,vue}'
|
||||
run: 'yarn run lint:eslint:fix {files} && yarn run prettier --write --list-different {files}'
|
||||
jsonlint:
|
||||
tags: style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
glob: '*.{json}'
|
||||
run: scripts/lint-json --format --verbose {files}
|
||||
prettier-graphql:
|
||||
tags: frontend style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
glob: '*.{graphql}'
|
||||
run: yarn run prettier --write --list-different {files}
|
||||
rubocop:
|
||||
tags: backend style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
glob: '*.{rb,rake}'
|
||||
run: REVEAL_RUBOCOP_TODO=0 bundle exec rubocop --parallel --autocorrect --force-exclusion {files}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
# Deletes orphaned packages_dependencies records that have no packages_dependency_links
|
||||
class DeleteOrphanedPackagesDependencies < BatchedMigrationJob
|
||||
operation_name :delete_all
|
||||
feature_category :package_registry
|
||||
|
||||
scope_to ->(relation) {
|
||||
relation.where(
|
||||
<<~SQL.squish
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM packages_dependency_links
|
||||
WHERE packages_dependency_links.dependency_id = packages_dependencies.id
|
||||
)
|
||||
SQL
|
||||
)
|
||||
}
|
||||
|
||||
def perform
|
||||
each_sub_batch(&:delete_all)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -90,6 +90,7 @@ module Gitlab
|
|||
def untar_with_options(archive:, dir:, options:)
|
||||
execute_cmd(%W(tar -#{options} #{archive} -C #{dir}))
|
||||
execute_cmd(%W(chmod -R #{UNTAR_MASK} #{dir}))
|
||||
remove_symlinks(dir)
|
||||
end
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
|
|
@ -120,6 +121,19 @@ module Gitlab
|
|||
FileUtils.copy_entry(source, destination)
|
||||
true
|
||||
end
|
||||
|
||||
def remove_symlinks(dir)
|
||||
ignore_file_names = %w[. ..]
|
||||
|
||||
# Using File::FNM_DOTMATCH to also delete symlinks starting with "."
|
||||
Dir.glob("#{dir}/**/*", File::FNM_DOTMATCH)
|
||||
.reject { |f| ignore_file_names.include?(File.basename(f)) }
|
||||
.each do |filepath|
|
||||
FileUtils.rm(filepath) if File.lstat(filepath).symlink?
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ module Gitlab
|
|||
ImporterError = Class.new(StandardError)
|
||||
|
||||
MAX_RETRIES = 8
|
||||
IGNORED_FILENAMES = %w(. ..).freeze
|
||||
|
||||
def self.import(*args, **kwargs)
|
||||
new(*args, **kwargs).import
|
||||
|
|
@ -24,7 +23,7 @@ module Gitlab
|
|||
mkdir_p(@shared.export_path)
|
||||
mkdir_p(@shared.archive_path)
|
||||
|
||||
remove_symlinks
|
||||
remove_symlinks(@shared.export_path)
|
||||
copy_archive
|
||||
|
||||
wait_for_archived_file do
|
||||
|
|
@ -36,7 +35,7 @@ module Gitlab
|
|||
false
|
||||
ensure
|
||||
remove_import_file
|
||||
remove_symlinks
|
||||
remove_symlinks(@shared.export_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -86,22 +85,10 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def remove_symlinks
|
||||
extracted_files.each do |path|
|
||||
FileUtils.rm(path) if File.lstat(path).symlink?
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def remove_import_file
|
||||
FileUtils.rm_rf(@archive_file)
|
||||
end
|
||||
|
||||
def extracted_files
|
||||
Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| IGNORED_FILENAMES.include?(File.basename(f)) }
|
||||
end
|
||||
|
||||
def validate_decompressed_archive_size
|
||||
raise ImporterError, _('Decompressed archive size validation failed.') unless size_validator.valid?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5099,12 +5099,18 @@ msgstr ""
|
|||
msgid "ApplicationSettings|See %{linkStart}password policy guidelines%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Send a confirmation email during sign up. New users can log in immediately, but must confirm their email within three days."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Send a confirmation email during sign up. New users must confirm their email address before they can log in."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Sign-up enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Soft"
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Text shown after a user signs up. Markdown enabled."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8716,12 +8722,6 @@ msgstr ""
|
|||
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Failed to confirm your order! Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Failed to confirm your order: %{message}. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Failed to load countries. Please try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19488,6 +19488,9 @@ msgstr ""
|
|||
msgid "Given epic is already related to this epic."
|
||||
msgstr ""
|
||||
|
||||
msgid "Global SAML group membership lock"
|
||||
msgstr ""
|
||||
|
||||
msgid "Global Search is disabled for this scope"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21557,6 +21560,9 @@ msgstr ""
|
|||
msgid "If checked, new group memberships and permissions can only be added via LDAP synchronization"
|
||||
msgstr ""
|
||||
|
||||
msgid "If checked, new group memberships and permissions can only be added via SAML Group Links synchronization"
|
||||
msgstr ""
|
||||
|
||||
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored. %{link_start}Learn more.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -25902,6 +25908,9 @@ msgstr ""
|
|||
msgid "Lock memberships to LDAP synchronization"
|
||||
msgstr ""
|
||||
|
||||
msgid "Lock memberships to SAML Group Links synchronization"
|
||||
msgstr ""
|
||||
|
||||
msgid "Lock merge request"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -37891,6 +37900,9 @@ msgstr ""
|
|||
msgid "SAML for %{group_name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SAML group membership settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "SAML single sign-on"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -38930,6 +38942,9 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|No rules defined - policy will not run."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|No tags available"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|Non-existing tags have been detected in the policy yaml. As a result, rule mode has been disabled. To enable rule mode, remove those non-existing tags from the policy yaml."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -39023,7 +39038,7 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|Scan to be performed on every pipeline on the %{branches}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|Scan will automatically choose a runner to run on because there are no tags exist on runners"
|
||||
msgid "SecurityOrchestration|Scan will automatically choose a runner to run on because there are no tags exist on runners. You can %{linkStart}create a new tag in settings%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|Security Approvals"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe ConfirmEmailWarning do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
controller(ApplicationController) do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ConfirmationsController do
|
||||
RSpec.describe ConfirmationsController, feature_category: :system_access do
|
||||
include DeviseHelpers
|
||||
|
||||
before do
|
||||
|
|
@ -148,51 +148,69 @@ RSpec.describe ConfirmationsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA is disabled' do
|
||||
context "when `email_confirmation_setting` is set to `soft`" do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: false)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
it 'successfully sends password reset when reCAPTCHA is not solved' do
|
||||
perform_request
|
||||
context 'when reCAPTCHA is disabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: false)
|
||||
end
|
||||
|
||||
expect(response).to redirect_to(dashboard_projects_path)
|
||||
it 'successfully sends password reset when reCAPTCHA is not solved' do
|
||||
perform_request
|
||||
|
||||
expect(response).to redirect_to(dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA is enabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
end
|
||||
|
||||
context 'when the reCAPTCHA is not solved' do
|
||||
before do
|
||||
Recaptcha.configuration.skip_verify_env.delete('test')
|
||||
end
|
||||
|
||||
it 'displays an error' do
|
||||
perform_request
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
expect(flash[:alert]).to include _('There was an error with the reCAPTCHA.')
|
||||
end
|
||||
|
||||
it 'sets gon variables' do
|
||||
Gon.clear
|
||||
|
||||
perform_request
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
expect(Gon.all_variables).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it 'successfully sends password reset when reCAPTCHA is solved' do
|
||||
Recaptcha.configuration.skip_verify_env << 'test'
|
||||
|
||||
perform_request
|
||||
|
||||
expect(response).to redirect_to(dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA is enabled' do
|
||||
context "when `email_confirmation_setting` is not set to `soft`" do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
end
|
||||
|
||||
context 'when the reCAPTCHA is not solved' do
|
||||
before do
|
||||
Recaptcha.configuration.skip_verify_env.delete('test')
|
||||
end
|
||||
|
||||
it 'displays an error' do
|
||||
perform_request
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
|
||||
end
|
||||
|
||||
it 'sets gon variables' do
|
||||
Gon.clear
|
||||
|
||||
perform_request
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
expect(Gon.all_variables).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it 'successfully sends password reset when reCAPTCHA is solved' do
|
||||
Recaptcha.configuration.skip_verify_env << 'test'
|
||||
|
||||
it 'redirects to the users_almost_there path' do
|
||||
perform_request
|
||||
|
||||
expect(response).to redirect_to(dashboard_projects_path)
|
||||
expect(response).to redirect_to(users_almost_there_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe OmniauthCallbacksController, type: :controller do
|
||||
RSpec.describe OmniauthCallbacksController, type: :controller, feature_category: :system_access do
|
||||
include LoginHelpers
|
||||
|
||||
describe 'omniauth' do
|
||||
|
|
@ -202,20 +202,30 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when user with 2FA is unconfirmed' do
|
||||
context 'when a user has 2FA enabled' do
|
||||
render_views
|
||||
|
||||
let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider) }
|
||||
|
||||
before do
|
||||
user.update_column(:confirmed_at, nil)
|
||||
context 'when a user is unconfirmed' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
|
||||
user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
post provider
|
||||
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
expect(flash[:alert]).to match(/You have to confirm your email address before continuing./)
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
post provider
|
||||
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
expect(flash[:alert]).to match(/You have to confirm your email address before continuing./)
|
||||
context 'when a user is confirmed' do
|
||||
it 'returns 200 response' do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,32 @@ RSpec.describe Registrations::WelcomeController, feature_category: :system_acces
|
|||
expect(subject).not_to redirect_to(profile_two_factor_auth_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when welcome step is completed' do
|
||||
before do
|
||||
user.update!(setup_for_company: true)
|
||||
end
|
||||
|
||||
context 'when user is confirmed' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it { is_expected.to redirect_to dashboard_projects_path }
|
||||
end
|
||||
|
||||
context 'when user is not confirmed' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
|
||||
sign_in(user)
|
||||
|
||||
user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
it { is_expected.to redirect_to user_session_path }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ RSpec.describe RegistrationsController, feature_category: :user_profile do
|
|||
end
|
||||
|
||||
context 'email confirmation' do
|
||||
context 'when `email_confirmation_setting` is set to `hard`' do
|
||||
context 'when email confirmation setting is set to `hard`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
end
|
||||
|
|
@ -122,7 +122,7 @@ RSpec.describe RegistrationsController, feature_category: :user_profile do
|
|||
end
|
||||
|
||||
context 'email confirmation' do
|
||||
context 'when `email_confirmation_setting` is set to `hard`' do
|
||||
context 'when email confirmation setting is set to `hard`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
stub_feature_flags(identity_verification: false)
|
||||
|
|
@ -157,7 +157,7 @@ RSpec.describe RegistrationsController, feature_category: :user_profile do
|
|||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
context 'when `email_confirmation_setting` is set to `off`' do
|
||||
context 'when email confirmation setting is set to `off`' do
|
||||
it 'signs the user in' do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'off')
|
||||
|
||||
|
|
@ -166,103 +166,97 @@ RSpec.describe RegistrationsController, feature_category: :user_profile do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when `email_confirmation_setting` is set to `hard`' do
|
||||
context 'when email confirmation setting is set to `hard`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
|
||||
end
|
||||
|
||||
context 'when soft email confirmation is not enabled' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
|
||||
end
|
||||
it 'does not authenticate the user and sends a confirmation email' do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
|
||||
it 'does not authenticate the user and sends a confirmation email' do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
it 'tracks an almost there redirect' do
|
||||
post_create
|
||||
|
||||
it 'tracks an almost there redirect' do
|
||||
post_create
|
||||
expect_snowplow_event(
|
||||
category: described_class.name,
|
||||
action: 'render',
|
||||
user: User.find_by(email: base_user_params[:email])
|
||||
)
|
||||
end
|
||||
|
||||
expect_snowplow_event(
|
||||
category: described_class.name,
|
||||
action: 'render',
|
||||
user: User.find_by(email: base_user_params[:email])
|
||||
)
|
||||
end
|
||||
context 'when registration is triggered from an accepted invite' do
|
||||
context 'when it is part from the initial invite email', :snowplow do
|
||||
let_it_be(:member) { create(:project_member, :invited, invite_email: user_params.dig(:user, :email)) }
|
||||
|
||||
context 'when registration is triggered from an accepted invite' do
|
||||
context 'when it is part from the initial invite email', :snowplow do
|
||||
let_it_be(:member) { create(:project_member, :invited, invite_email: user_params.dig(:user, :email)) }
|
||||
let(:originating_member_id) { member.id }
|
||||
let(:session_params) do
|
||||
{
|
||||
invite_email: user_params.dig(:user, :email),
|
||||
originating_member_id: originating_member_id
|
||||
}
|
||||
end
|
||||
|
||||
let(:originating_member_id) { member.id }
|
||||
let(:session_params) do
|
||||
{
|
||||
invite_email: user_params.dig(:user, :email),
|
||||
originating_member_id: originating_member_id
|
||||
}
|
||||
end
|
||||
context 'when member exists from the session key value' do
|
||||
it 'tracks the invite acceptance' do
|
||||
subject
|
||||
|
||||
context 'when member exists from the session key value' do
|
||||
it 'tracks the invite acceptance' do
|
||||
subject
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'accepted',
|
||||
label: 'invite_email',
|
||||
property: member.id.to_s,
|
||||
user: member.reload.user
|
||||
)
|
||||
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'accepted',
|
||||
label: 'invite_email',
|
||||
property: member.id.to_s,
|
||||
user: member.reload.user
|
||||
)
|
||||
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'create_user',
|
||||
label: 'invited',
|
||||
user: member.reload.user
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when member does not exist from the session key value' do
|
||||
let(:originating_member_id) { nil }
|
||||
|
||||
it 'does not track invite acceptance' do
|
||||
subject
|
||||
|
||||
expect_no_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'accepted',
|
||||
label: 'invite_email'
|
||||
)
|
||||
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'create_user',
|
||||
label: 'signup',
|
||||
user: member.reload.user
|
||||
)
|
||||
end
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'create_user',
|
||||
label: 'invited',
|
||||
user: member.reload.user
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invite email matches email used on registration' do
|
||||
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
|
||||
context 'when member does not exist from the session key value' do
|
||||
let(:originating_member_id) { nil }
|
||||
|
||||
it 'signs the user in without sending a confirmation email', :aggregate_failures do
|
||||
expect { subject }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_confirmed
|
||||
it 'does not track invite acceptance' do
|
||||
subject
|
||||
|
||||
expect_no_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'accepted',
|
||||
label: 'invite_email'
|
||||
)
|
||||
|
||||
expect_snowplow_event(
|
||||
category: 'RegistrationsController',
|
||||
action: 'create_user',
|
||||
label: 'signup',
|
||||
user: member.reload.user
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invite email does not match the email used on registration' do
|
||||
let(:session_params) { { invite_email: 'bogus@email.com' } }
|
||||
context 'when invite email matches email used on registration' do
|
||||
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
|
||||
|
||||
it 'does not authenticate the user and sends a confirmation email', :aggregate_failures do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
it 'signs the user in without sending a confirmation email', :aggregate_failures do
|
||||
expect { subject }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_confirmed
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invite email does not match the email used on registration' do
|
||||
let(:session_params) { { invite_email: 'bogus@email.com' } }
|
||||
|
||||
it 'does not authenticate the user and sends a confirmation email', :aggregate_failures do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -286,45 +280,45 @@ RSpec.describe RegistrationsController, feature_category: :user_profile do
|
|||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when soft email confirmation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
|
||||
context 'when email confirmation setting is set to `soft`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
|
||||
end
|
||||
|
||||
it 'authenticates the user and sends a confirmation email' do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_present
|
||||
expect(response).to redirect_to(users_sign_up_welcome_path)
|
||||
end
|
||||
|
||||
it 'does not track an almost there redirect' do
|
||||
post_create
|
||||
|
||||
expect_no_snowplow_event(
|
||||
category: described_class.name,
|
||||
action: 'render',
|
||||
user: User.find_by(email: base_user_params[:email])
|
||||
)
|
||||
end
|
||||
|
||||
context 'when invite email matches email used on registration' do
|
||||
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
|
||||
|
||||
it 'signs the user in without sending a confirmation email', :aggregate_failures do
|
||||
expect { subject }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_confirmed
|
||||
end
|
||||
end
|
||||
|
||||
it 'authenticates the user and sends a confirmation email' do
|
||||
context 'when invite email does not match the email used on registration' do
|
||||
let(:session_params) { { invite_email: 'bogus@email.com' } }
|
||||
|
||||
it 'authenticates the user and sends a confirmation email without confirming', :aggregate_failures do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_present
|
||||
expect(response).to redirect_to(users_sign_up_welcome_path)
|
||||
end
|
||||
|
||||
it 'does not track an almost there redirect' do
|
||||
post_create
|
||||
|
||||
expect_no_snowplow_event(
|
||||
category: described_class.name,
|
||||
action: 'render',
|
||||
user: User.find_by(email: base_user_params[:email])
|
||||
)
|
||||
end
|
||||
|
||||
context 'when invite email matches email used on registration' do
|
||||
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
|
||||
|
||||
it 'signs the user in without sending a confirmation email', :aggregate_failures do
|
||||
expect { subject }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).to be_confirmed
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invite email does not match the email used on registration' do
|
||||
let(:session_params) { { invite_email: 'bogus@email.com' } }
|
||||
|
||||
it 'authenticates the user and sends a confirmation email without confirming', :aggregate_failures do
|
||||
expect { subject }.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
expect(controller.current_user).not_to be_confirmed
|
||||
end
|
||||
expect(controller.current_user).not_to be_confirmed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :non_public_artifacts do
|
||||
trait :with_private_artifacts_config do
|
||||
options do
|
||||
{
|
||||
artifacts: { public: false }
|
||||
|
|
@ -702,6 +702,14 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :with_public_artifacts_config do
|
||||
options do
|
||||
{
|
||||
artifacts: { public: true }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
trait :non_playable do
|
||||
status { 'created' }
|
||||
self.when { 'manual' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :service_desk_custom_email_verification, class: '::ServiceDesk::CustomEmailVerification' do
|
||||
project
|
||||
state { "running" }
|
||||
end
|
||||
end
|
||||
|
|
@ -244,9 +244,8 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures, feature_cate
|
|||
context 'the user sign-up using a different email address' do
|
||||
let(:invite_email) { build_stubbed(:user).email }
|
||||
|
||||
context 'when soft email confirmation is not enabled' do
|
||||
context 'when email confirmation is not set to `soft`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
|
@ -261,9 +260,9 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures, feature_cate
|
|||
end
|
||||
end
|
||||
|
||||
context 'when soft email confirmation is enabled' do
|
||||
context 'when email confirmation setting is set to `soft`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
|
|||
end
|
||||
|
||||
context 'within the grace period' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
it 'allows to login' do
|
||||
expect(authentication_metrics).to increment(:user_authenticated_counter)
|
||||
|
||||
|
|
@ -137,11 +141,9 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
|
|||
end
|
||||
|
||||
context 'when resending the confirmation email' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it 'redirects to the "almost there" page' do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
|
||||
user = create(:user)
|
||||
|
||||
visit new_user_confirmation_path
|
||||
fill_in 'user_email', with: user.email
|
||||
click_button 'Resend'
|
||||
|
|
@ -971,8 +973,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
|
|||
let(:alert_message) { "To continue, you need to select the link in the confirmation email we sent to verify your email address. If you didn't get our email, select Resend confirmation email" }
|
||||
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
stub_feature_flags(identity_verification: false)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
|
||||
end
|
||||
|
|
|
|||
|
|
@ -200,9 +200,8 @@ RSpec.describe 'Signup', feature_category: :user_profile do
|
|||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
end
|
||||
|
||||
context 'when soft email confirmation is not enabled' do
|
||||
context 'when email confirmation setting is not `soft`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
|
|
@ -221,9 +220,9 @@ RSpec.describe 'Signup', feature_category: :user_profile do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when soft email confirmation is enabled' do
|
||||
context 'when email confirmation setting is `soft`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
it 'creates the user account and sends a confirmation email' do
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import * as Sentry from 'sentrybrowser7';
|
||||
import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from '~/sentry/constants';
|
||||
|
||||
import SentryConfig from '~/sentry/sentry_config';
|
||||
|
||||
|
|
@ -62,11 +61,8 @@ describe('SentryConfig', () => {
|
|||
expect(Sentry.init).toHaveBeenCalledWith({
|
||||
dsn: options.dsn,
|
||||
release: options.release,
|
||||
sampleRate: SAMPLE_RATE,
|
||||
allowUrls: options.allowUrls,
|
||||
environment: options.environment,
|
||||
ignoreErrors: IGNORE_ERRORS,
|
||||
denyUrls: DENY_URLS,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -82,11 +78,8 @@ describe('SentryConfig', () => {
|
|||
expect(Sentry.init).toHaveBeenCalledWith({
|
||||
dsn: options.dsn,
|
||||
release: options.release,
|
||||
sampleRate: SAMPLE_RATE,
|
||||
allowUrls: options.allowUrls,
|
||||
environment: 'development',
|
||||
ignoreErrors: IGNORE_ERRORS,
|
||||
denyUrls: DENY_URLS,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedPackagesDependencies, schema: 20230303105806,
|
||||
feature_category: :package_registry do
|
||||
let!(:migration_attrs) do
|
||||
{
|
||||
start_id: 1,
|
||||
end_id: 1000,
|
||||
batch_table: :packages_dependencies,
|
||||
batch_column: :id,
|
||||
sub_batch_size: 500,
|
||||
pause_ms: 0,
|
||||
connection: ApplicationRecord.connection
|
||||
}
|
||||
end
|
||||
|
||||
let!(:migration) { described_class.new(**migration_attrs) }
|
||||
|
||||
let(:packages_dependencies) { table(:packages_dependencies) }
|
||||
|
||||
let!(:namespace) { table(:namespaces).create!(name: 'project', path: 'project', type: 'Project') }
|
||||
let!(:project) do
|
||||
table(:projects).create!(name: 'project', path: 'project', project_namespace_id: namespace.id,
|
||||
namespace_id: namespace.id)
|
||||
end
|
||||
|
||||
let!(:package) do
|
||||
table(:packages_packages).create!(name: 'test', version: '1.2.3', package_type: 2, project_id: project.id)
|
||||
end
|
||||
|
||||
let!(:orphan_dependency_1) { packages_dependencies.create!(name: 'dependency 1', version_pattern: '~0.0.1') }
|
||||
let!(:orphan_dependency_2) { packages_dependencies.create!(name: 'dependency 2', version_pattern: '~0.0.2') }
|
||||
let!(:orphan_dependency_3) { packages_dependencies.create!(name: 'dependency 3', version_pattern: '~0.0.3') }
|
||||
let!(:linked_dependency) do
|
||||
packages_dependencies.create!(name: 'dependency 4', version_pattern: '~0.0.4').tap do |dependency|
|
||||
table(:packages_dependency_links).create!(package_id: package.id, dependency_id: dependency.id,
|
||||
dependency_type: 'dependencies')
|
||||
end
|
||||
end
|
||||
|
||||
subject(:perform_migration) { migration.perform }
|
||||
|
||||
it 'executes 3 queries' do
|
||||
queries = ActiveRecord::QueryRecorder.new do
|
||||
perform_migration
|
||||
end
|
||||
|
||||
expect(queries.count).to eq(3)
|
||||
end
|
||||
|
||||
it 'deletes only orphaned dependencies' do
|
||||
expect { perform_migration }.to change { packages_dependencies.count }.by(-3)
|
||||
expect(packages_dependencies.all).to eq([linked_dependency])
|
||||
end
|
||||
end
|
||||
|
|
@ -722,6 +722,7 @@ project:
|
|||
- upstream_project_subscriptions
|
||||
- downstream_project_subscriptions
|
||||
- service_desk_setting
|
||||
- service_desk_custom_email_verification
|
||||
- security_setting
|
||||
- import_failures
|
||||
- container_expiration_policy
|
||||
|
|
@ -963,6 +964,8 @@ bulk_import_export:
|
|||
- group
|
||||
service_desk_setting:
|
||||
- file_template_project
|
||||
service_desk_custom_email_verification:
|
||||
- triggerer
|
||||
approvals:
|
||||
- user
|
||||
- merge_request
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
||||
RSpec.describe Gitlab::ImportExport::CommandLineUtil, feature_category: :importers do
|
||||
include ExportFileHelper
|
||||
|
||||
let(:path) { "#{Dir.tmpdir}/symlink_test" }
|
||||
let(:archive) { 'spec/fixtures/symlink_export.tar.gz' }
|
||||
let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
|
||||
let(:tmpdir) { Dir.mktmpdir }
|
||||
let(:archive_dir) { Dir.mktmpdir }
|
||||
|
||||
subject do
|
||||
Class.new do
|
||||
|
|
@ -25,20 +26,38 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
|||
|
||||
before do
|
||||
FileUtils.mkdir_p(path)
|
||||
subject.untar_zxf(archive: archive, dir: path)
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(path)
|
||||
FileUtils.rm_rf(archive_dir)
|
||||
FileUtils.remove_entry(tmpdir)
|
||||
end
|
||||
|
||||
it 'has the right mask for project.json' do
|
||||
expect(file_permissions("#{path}/project.json")).to eq(0755) # originally 777
|
||||
end
|
||||
shared_examples 'deletes symlinks' do |compression, decompression|
|
||||
it 'deletes the symlinks', :aggregate_failures do
|
||||
Dir.mkdir("#{tmpdir}/.git")
|
||||
Dir.mkdir("#{tmpdir}/folder")
|
||||
FileUtils.touch("#{tmpdir}/file.txt")
|
||||
FileUtils.touch("#{tmpdir}/folder/file.txt")
|
||||
FileUtils.touch("#{tmpdir}/.gitignore")
|
||||
FileUtils.touch("#{tmpdir}/.git/config")
|
||||
File.symlink('file.txt', "#{tmpdir}/.symlink")
|
||||
File.symlink('file.txt', "#{tmpdir}/.git/.symlink")
|
||||
File.symlink('file.txt', "#{tmpdir}/folder/.symlink")
|
||||
archive = File.join(archive_dir, 'archive')
|
||||
subject.public_send(compression, archive: archive, dir: tmpdir)
|
||||
|
||||
it 'has the right mask for uploads' do
|
||||
expect(file_permissions("#{path}/uploads")).to eq(0755) # originally 555
|
||||
subject.public_send(decompression, archive: archive, dir: archive_dir)
|
||||
|
||||
expect(File.exist?("#{archive_dir}/file.txt")).to eq(true)
|
||||
expect(File.exist?("#{archive_dir}/folder/file.txt")).to eq(true)
|
||||
expect(File.exist?("#{archive_dir}/.gitignore")).to eq(true)
|
||||
expect(File.exist?("#{archive_dir}/.git/config")).to eq(true)
|
||||
expect(File.exist?("#{archive_dir}/.symlink")).to eq(false)
|
||||
expect(File.exist?("#{archive_dir}/.git/.symlink")).to eq(false)
|
||||
expect(File.exist?("#{archive_dir}/folder/.symlink")).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#download_or_copy_upload' do
|
||||
|
|
@ -228,12 +247,6 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
|||
end
|
||||
|
||||
describe '#tar_cf' do
|
||||
let(:archive_dir) { Dir.mktmpdir }
|
||||
|
||||
after do
|
||||
FileUtils.remove_entry(archive_dir)
|
||||
end
|
||||
|
||||
it 'archives a folder without compression' do
|
||||
archive_file = File.join(archive_dir, 'archive.tar')
|
||||
|
||||
|
|
@ -256,13 +269,25 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#untar_xf' do
|
||||
let(:archive_dir) { Dir.mktmpdir }
|
||||
describe '#untar_zxf' do
|
||||
it_behaves_like 'deletes symlinks', :tar_czf, :untar_zxf
|
||||
|
||||
after do
|
||||
FileUtils.remove_entry(archive_dir)
|
||||
it 'has the right mask for project.json' do
|
||||
subject.untar_zxf(archive: archive, dir: path)
|
||||
|
||||
expect(file_permissions("#{path}/project.json")).to eq(0755) # originally 777
|
||||
end
|
||||
|
||||
it 'has the right mask for uploads' do
|
||||
subject.untar_zxf(archive: archive, dir: path)
|
||||
|
||||
expect(file_permissions("#{path}/uploads")).to eq(0755) # originally 555
|
||||
end
|
||||
end
|
||||
|
||||
describe '#untar_xf' do
|
||||
it_behaves_like 'deletes symlinks', :tar_cf, :untar_xf
|
||||
|
||||
it 'extracts archive without decompression' do
|
||||
filename = 'archive.tar.gz'
|
||||
archive_file = File.join(archive_dir, 'archive.tar')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueDeleteOrphanedPackagesDependencies, feature_category: :package_registry do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :packages_dependencies,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1525,4 +1525,50 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
|
|||
expect(setting.personal_access_tokens_disabled?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email_confirmation_setting prefixes' do
|
||||
before do
|
||||
described_class.create_from_defaults
|
||||
end
|
||||
|
||||
context 'when feature flag `soft_email_confirmation` is not enabled' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
end
|
||||
|
||||
where(:email_confirmation_setting, :off, :soft, :hard) do
|
||||
'off' | true | false | false
|
||||
'soft' | false | true | false
|
||||
'hard' | false | false | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'returns the correct value when prefixed' do
|
||||
stub_application_setting_enum('email_confirmation_setting', email_confirmation_setting)
|
||||
|
||||
expect(described_class.last.email_confirmation_setting_off?).to be off
|
||||
expect(described_class.last.email_confirmation_setting_soft?).to be soft
|
||||
expect(described_class.last.email_confirmation_setting_hard?).to be hard
|
||||
end
|
||||
end
|
||||
|
||||
it 'calls super' do
|
||||
expect(described_class.last.email_confirmation_setting_off?).to be true
|
||||
expect(described_class.last.email_confirmation_setting_soft?).to be false
|
||||
expect(described_class.last.email_confirmation_setting_hard?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag `soft_email_confirmation` is enabled' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
end
|
||||
|
||||
it 'returns correct value when enum is prefixed' do
|
||||
expect(described_class.last.email_confirmation_setting_off?).to be false
|
||||
expect(described_class.last.email_confirmation_setting_soft?).to be true
|
||||
expect(described_class.last.email_confirmation_setting_hard?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1040,7 +1040,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
end
|
||||
|
||||
context 'non public artifacts' do
|
||||
let(:build) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
|
||||
let(:build) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do
|
|||
it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') }
|
||||
it { is_expected.to have_one(:mock_ci_integration) }
|
||||
it { is_expected.to have_one(:mock_monitoring_integration) }
|
||||
it { is_expected.to have_one(:service_desk_custom_email_verification).class_name('ServiceDesk::CustomEmailVerification') }
|
||||
it { is_expected.to have_many(:commit_statuses) }
|
||||
it { is_expected.to have_many(:ci_pipelines) }
|
||||
it { is_expected.to have_many(:ci_refs) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ServiceDesk::CustomEmailVerification, feature_category: :service_desk do
|
||||
let(:user) { build_stubbed(:user) }
|
||||
let(:project) { build_stubbed(:project) }
|
||||
let(:verification) { build_stubbed(:service_desk_custom_email_verification, project: project) }
|
||||
let(:token) { 'XXXXXXXXXXXX' }
|
||||
|
||||
describe '.generate_token' do
|
||||
it 'matches expected output' do
|
||||
expect(described_class.generate_token).to match(/\A\p{Alnum}{12}\z/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:state) }
|
||||
end
|
||||
|
||||
describe '#accepted_until' do
|
||||
context 'when no custom email is set up' do
|
||||
it 'returns nil' do
|
||||
expect(subject.accepted_until).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom email is set up' do
|
||||
subject { verification.accepted_until }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
|
||||
context 'when verification process started' do
|
||||
let(:triggered_at) { 2.minutes.ago }
|
||||
|
||||
before do
|
||||
verification.assign_attributes(
|
||||
state: "running",
|
||||
triggered_at: triggered_at,
|
||||
triggerer: user,
|
||||
token: token
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(described_class::TIMEFRAME.since(triggered_at)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#in_timeframe?' do
|
||||
context 'when no custom email is set up' do
|
||||
it 'returns false' do
|
||||
expect(subject).not_to be_in_timeframe
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom email is set up' do
|
||||
it { is_expected.not_to be_in_timeframe }
|
||||
|
||||
context 'when verification process started' do
|
||||
let(:triggered_at) { 1.second.ago }
|
||||
|
||||
before do
|
||||
subject.assign_attributes(
|
||||
state: "running",
|
||||
triggered_at: triggered_at,
|
||||
triggerer: user,
|
||||
token: token
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to be_in_timeframe }
|
||||
|
||||
context 'and timeframe was missed' do
|
||||
let(:triggered_at) { (described_class::TIMEFRAME + 1).ago }
|
||||
|
||||
before do
|
||||
subject.triggered_at = triggered_at
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_in_timeframe }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'encrypted #token' do
|
||||
subject { build_stubbed(:service_desk_custom_email_verification, token: token) }
|
||||
|
||||
it 'saves and retrieves the encrypted token and iv correctly' do
|
||||
expect(subject.encrypted_token).not_to be_nil
|
||||
expect(subject.encrypted_token_iv).not_to be_nil
|
||||
|
||||
expect(subject.token).to eq(token)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to belong_to(:triggerer) }
|
||||
|
||||
it 'can access service desk setting from project' do
|
||||
setting = build_stubbed(:service_desk_setting, project: project)
|
||||
|
||||
expect(verification.service_desk_setting).to eq(setting)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
||||
let(:verification) { build(:service_desk_custom_email_verification) }
|
||||
let(:project) { build(:project) }
|
||||
|
||||
describe 'validations' do
|
||||
subject(:service_desk_setting) { create(:service_desk_setting) }
|
||||
|
||||
|
|
@ -23,6 +26,8 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
|||
|
||||
context 'when custom_email_enabled is true' do
|
||||
before do
|
||||
# Test without ServiceDesk::CustomEmailVerification for simplicity
|
||||
# See dedicated simplified tests below
|
||||
subject.custom_email_enabled = true
|
||||
end
|
||||
|
||||
|
|
@ -55,7 +60,18 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
|||
it { is_expected.not_to allow_value('/example').for(:custom_email_smtp_address) }
|
||||
end
|
||||
|
||||
describe '.valid_issue_template' do
|
||||
context 'when custom email verification is present/was triggered' do
|
||||
before do
|
||||
subject.project.service_desk_custom_email_verification = verification
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of(:custom_email) }
|
||||
it { is_expected.to validate_presence_of(:custom_email_smtp_username) }
|
||||
it { is_expected.to validate_presence_of(:custom_email_smtp_port) }
|
||||
it { is_expected.to validate_presence_of(:custom_email_smtp_address) }
|
||||
end
|
||||
|
||||
describe '#valid_issue_template' do
|
||||
let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/issue_templates/service_desk.md' => 'template' }) }
|
||||
|
||||
it 'is not valid if template does not exist' do
|
||||
|
|
@ -73,7 +89,20 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.valid_project_key' do
|
||||
describe '#custom_email_address_for_verification' do
|
||||
it 'returns nil' do
|
||||
expect(subject.custom_email_address_for_verification).to be_nil
|
||||
end
|
||||
|
||||
context 'when custom_email exists' do
|
||||
it 'returns correct verification address' do
|
||||
subject.custom_email = 'support@example.com'
|
||||
expect(subject.custom_email_address_for_verification).to eq('support+verify@example.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid_project_key' do
|
||||
# Creates two projects with same full path slug
|
||||
# group1/test/one and group1/test-one will both have 'group-test-one' slug
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
|
@ -109,15 +138,15 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'encrypted password' do
|
||||
describe 'encrypted #custom_email_smtp_password' do
|
||||
let_it_be(:settings) do
|
||||
create(
|
||||
:service_desk_setting,
|
||||
custom_email_enabled: true,
|
||||
custom_email: 'supersupport@example.com',
|
||||
custom_email: 'support@example.com',
|
||||
custom_email_smtp_address: 'smtp.example.com',
|
||||
custom_email_smtp_port: 587,
|
||||
custom_email_smtp_username: 'supersupport@example.com',
|
||||
custom_email_smtp_username: 'support@example.com',
|
||||
custom_email_smtp_password: 'supersecret'
|
||||
)
|
||||
end
|
||||
|
|
@ -131,6 +160,24 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do
|
|||
end
|
||||
|
||||
describe 'associations' do
|
||||
let(:custom_email_settings) do
|
||||
build_stubbed(
|
||||
:service_desk_setting,
|
||||
custom_email: 'support@example.com',
|
||||
custom_email_smtp_address: 'smtp.example.com',
|
||||
custom_email_smtp_port: 587,
|
||||
custom_email_smtp_username: 'support@example.com',
|
||||
custom_email_smtp_password: 'supersecret'
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to belong_to(:project) }
|
||||
|
||||
it 'can access custom email verification from project' do
|
||||
project.service_desk_custom_email_verification = verification
|
||||
custom_email_settings.project = project
|
||||
|
||||
expect(custom_email_settings.custom_email_verification).to eq(verification)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7102,43 +7102,105 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
context 'when user is confirmed' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it 'is falsey' do
|
||||
expect(user.confirmed?).to be_truthy
|
||||
expect(subject).to be_falsey
|
||||
it 'is false' do
|
||||
expect(user.confirmed?).to be(true)
|
||||
expect(subject).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not confirmed' do
|
||||
let_it_be(:user) { build_stubbed(:user, :unconfirmed, confirmation_sent_at: Time.current) }
|
||||
|
||||
it 'is truthy when soft_email_confirmation feature is disabled' do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
expect(subject).to be_truthy
|
||||
context 'when email confirmation setting is set to `off`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'off')
|
||||
end
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
end
|
||||
|
||||
context 'when soft_email_confirmation feature is enabled' do
|
||||
context 'when email confirmation setting is set to `soft`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
it 'is falsey when confirmation period is valid' do
|
||||
expect(subject).to be_falsey
|
||||
context 'when confirmation period is valid' do
|
||||
it { is_expected.to be(false) }
|
||||
end
|
||||
|
||||
it 'is truthy when confirmation period is expired' do
|
||||
travel_to(User.allow_unconfirmed_access_for.from_now + 1.day) do
|
||||
expect(subject).to be_truthy
|
||||
context 'when confirmation period is expired' do
|
||||
before do
|
||||
travel_to(User.allow_unconfirmed_access_for.from_now + 1.day)
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'when user has no confirmation email sent' do
|
||||
let(:user) { build(:user, :unconfirmed, confirmation_sent_at: nil) }
|
||||
|
||||
it 'is truthy' do
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email confirmation setting is set to `hard`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#confirmation_period_valid?' do
|
||||
subject { user.send(:confirmation_period_valid?) }
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
context 'when email confirmation setting is set to `off`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'when email confirmation setting is set to `soft`' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
end
|
||||
|
||||
context 'when within confirmation window' do
|
||||
before do
|
||||
user.update!(confirmation_sent_at: Date.today)
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'when outside confirmation window' do
|
||||
before do
|
||||
user.update!(confirmation_sent_at: Date.today - described_class.confirm_within - 7.days)
|
||||
end
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email confirmation setting is set to `hard`' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
stub_application_setting_enum('email_confirmation_setting', 'hard')
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
describe '#in_confirmation_period?' do
|
||||
it 'is expected to be an alias' do
|
||||
expect(user.method(:in_confirmation_period?).original_name).to eq(:confirmation_period_valid?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
|
|||
|
||||
context 'inactive user' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
current_user.update!(confirmed_at: nil, confirmation_sent_at: 5.days.ago)
|
||||
end
|
||||
|
||||
|
|
@ -412,6 +413,7 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
|
|||
|
||||
describe 'inactive user' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
current_user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
|
|
@ -516,6 +518,7 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
|
|||
|
||||
describe 'inactive user' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
current_user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
|
|||
end
|
||||
|
||||
context 'when project is public with artifacts that are non public' do
|
||||
let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
|
||||
let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
|
||||
|
||||
it 'rejects access to artifacts' do
|
||||
project.update_column(:visibility_level,
|
||||
|
|
@ -439,7 +439,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
|
|||
|
||||
context 'when public project guest and artifacts are non public' do
|
||||
let(:api_user) { guest }
|
||||
let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
|
||||
let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
|
||||
|
||||
before do
|
||||
project.update_column(:visibility_level,
|
||||
|
|
@ -644,7 +644,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
|
|||
end
|
||||
|
||||
context 'when project is public with non public artifacts' do
|
||||
let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline, user: api_user) }
|
||||
let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline, user: api_user) }
|
||||
let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
|
||||
let(:public_builds) { true }
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ RSpec.describe 'OAuth tokens', feature_category: :system_access do
|
|||
|
||||
context 'when user account is not confirmed' do
|
||||
before do
|
||||
stub_application_setting_enum('email_confirmation_setting', 'soft')
|
||||
|
||||
user.update!(confirmed_at: nil)
|
||||
|
||||
request_oauth_token(user, client_basic_auth_header(client))
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ RSpec.describe BuildDetailsEntity do
|
|||
end
|
||||
|
||||
context 'when the build has non public archive type artifacts' do
|
||||
let(:build) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
|
||||
let(:build) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
|
||||
|
||||
it 'does not expose non public artifacts' do
|
||||
expect(subject.keys).not_to include(:artifact)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,66 @@ RSpec.describe Ci::JobArtifacts::CreateService, feature_category: :build_artifac
|
|||
describe '#execute' do
|
||||
subject { service.execute(artifacts_file, params, metadata_file: metadata_file) }
|
||||
|
||||
def expect_accessibility_be(accessibility)
|
||||
if accessibility == :public
|
||||
expect(job.job_artifacts).to all be_public_accessibility
|
||||
else
|
||||
expect(job.job_artifacts).to all be_private_accessibility
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'job does not have public artifacts in the CI config' do |expected_artifacts_count, accessibility|
|
||||
it "sets accessibility by default to #{accessibility}" do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(expected_artifacts_count)
|
||||
|
||||
expect_accessibility_be(accessibility)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'job artifact set as private in the CI config' do |expected_artifacts_count, accessibility|
|
||||
let!(:job) { create(:ci_build, :with_private_artifacts_config, project: project) }
|
||||
|
||||
it "sets accessibility to #{accessibility}" do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(expected_artifacts_count)
|
||||
|
||||
expect_accessibility_be(accessibility)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'job artifact set as public in the CI config' do |expected_artifacts_count, accessibility|
|
||||
let!(:job) { create(:ci_build, :with_public_artifacts_config, project: project) }
|
||||
|
||||
it "sets accessibility to #{accessibility}" do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(expected_artifacts_count)
|
||||
|
||||
expect_accessibility_be(accessibility)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'when accessibility level passed as private' do |expected_artifacts_count, accessibility|
|
||||
before do
|
||||
params.merge!('accessibility' => 'private')
|
||||
end
|
||||
|
||||
it 'sets accessibility to private level' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(expected_artifacts_count)
|
||||
|
||||
expect_accessibility_be(accessibility)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'when accessibility passed as public' do |expected_artifacts_count|
|
||||
before do
|
||||
params.merge!('accessibility' => 'public')
|
||||
end
|
||||
|
||||
it 'sets accessibility level to public' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(expected_artifacts_count)
|
||||
|
||||
expect(job.job_artifacts).to all be_public_accessibility
|
||||
end
|
||||
end
|
||||
|
||||
context 'when artifacts file is uploaded' do
|
||||
it 'logs the created artifact' do
|
||||
expect(Gitlab::Ci::Artifacts::Logger)
|
||||
|
|
@ -61,37 +121,19 @@ RSpec.describe Ci::JobArtifacts::CreateService, feature_category: :build_artifac
|
|||
expect(new_artifact.locked).to eq(job.pipeline.locked)
|
||||
end
|
||||
|
||||
it 'sets accessibility level by default to public' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_public_accessibility
|
||||
end
|
||||
|
||||
context 'when accessibility level passed as private' do
|
||||
context 'when non_public_artifacts feature flag is disabled' do
|
||||
before do
|
||||
params.merge!('accessibility' => 'private')
|
||||
stub_feature_flags(non_public_artifacts: false)
|
||||
end
|
||||
|
||||
it 'sets accessibility level to private' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_private_accessibility
|
||||
end
|
||||
end
|
||||
|
||||
context 'when accessibility passed as public' do
|
||||
before do
|
||||
params.merge!('accessibility' => 'public')
|
||||
context 'when accessibility level not passed to the service' do
|
||||
it_behaves_like 'job does not have public artifacts in the CI config', 1, :public
|
||||
it_behaves_like 'job artifact set as private in the CI config', 1, :public
|
||||
it_behaves_like 'job artifact set as public in the CI config', 1, :public
|
||||
end
|
||||
|
||||
it 'sets accessibility to public level' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_public_accessibility
|
||||
end
|
||||
it_behaves_like 'when accessibility level passed as private', 1, :public
|
||||
it_behaves_like 'when accessibility passed as public', 1
|
||||
end
|
||||
|
||||
context 'when accessibility passed as invalid value' do
|
||||
|
|
@ -104,6 +146,16 @@ RSpec.describe Ci::JobArtifacts::CreateService, feature_category: :build_artifac
|
|||
end
|
||||
end
|
||||
|
||||
context 'when accessibility level not passed to the service' do
|
||||
it_behaves_like 'job does not have public artifacts in the CI config', 1, :public
|
||||
it_behaves_like 'job artifact set as private in the CI config', 1, :private
|
||||
it_behaves_like 'job artifact set as public in the CI config', 1, :public
|
||||
end
|
||||
|
||||
it_behaves_like 'when accessibility level passed as private', 1, :private
|
||||
|
||||
it_behaves_like 'when accessibility passed as public', 1
|
||||
|
||||
context 'when metadata file is also uploaded' do
|
||||
let(:metadata_file) do
|
||||
file_to_upload('spec/fixtures/ci_build_artifacts_metadata.gz', sha256: artifacts_sha256)
|
||||
|
|
@ -125,13 +177,16 @@ RSpec.describe Ci::JobArtifacts::CreateService, feature_category: :build_artifac
|
|||
expect(new_artifact.locked).to eq(job.pipeline.locked)
|
||||
end
|
||||
|
||||
it 'sets accessibility by default to public' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(2)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_public_accessibility
|
||||
context 'when accessibility level not passed to the service' do
|
||||
it_behaves_like 'job does not have public artifacts in the CI config', 2, :public
|
||||
it_behaves_like 'job artifact set as private in the CI config', 2, :private
|
||||
it_behaves_like 'job artifact set as public in the CI config', 2, :public
|
||||
end
|
||||
|
||||
it_behaves_like 'when accessibility level passed as private', 2, :privatge
|
||||
|
||||
it_behaves_like 'when accessibility passed as public', 2
|
||||
|
||||
it 'logs the created artifact and metadata' do
|
||||
expect(Gitlab::Ci::Artifacts::Logger)
|
||||
.to receive(:log_created)
|
||||
|
|
@ -140,32 +195,6 @@ RSpec.describe Ci::JobArtifacts::CreateService, feature_category: :build_artifac
|
|||
subject
|
||||
end
|
||||
|
||||
context 'when accessibility level passed as private' do
|
||||
before do
|
||||
params.merge!('accessibility' => 'private')
|
||||
end
|
||||
|
||||
it 'sets accessibility to private level' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(2)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_private_accessibility
|
||||
end
|
||||
end
|
||||
|
||||
context 'when accessibility passed as public' do
|
||||
before do
|
||||
params.merge!('accessibility' => 'public')
|
||||
end
|
||||
|
||||
it 'sets accessibility level to public' do
|
||||
expect { subject }.to change { Ci::JobArtifact.count }.by(2)
|
||||
|
||||
new_artifact = job.job_artifacts.last
|
||||
expect(new_artifact).to be_public_accessibility
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets expiration date according to application settings' do
|
||||
expected_expire_at = 1.day.from_now
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ RSpec.shared_examples 'Secure OAuth Authorizations' do
|
|||
end
|
||||
|
||||
context 'when user is unconfirmed' do
|
||||
let(:user) { create(:user, confirmed_at: nil) }
|
||||
let(:user) { create(:user, :unconfirmed) }
|
||||
|
||||
it 'displays an error' do
|
||||
expect(page).to have_text I18n.t('doorkeeper.errors.messages.unconfirmed_email')
|
||||
|
|
|
|||
Loading…
Reference in New Issue