Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-16 21:09:42 +00:00
parent 339b34679c
commit 61066c990c
55 changed files with 480 additions and 283 deletions

View File

@ -58,6 +58,10 @@ config/bounded_contexts.yml @fabiopitino @grzesiek @stanhu @cwoolley-gitlab @tku
/spec/frontend_integration/
/ee/spec/frontend_integration/
[Data Seeder] @gl-dx
/ee/db/seeds/data_seeder/
/scripts/data_seeder/
[Clickhouse] @gitlab-org/maintainers/clickhouse
/db/click_house/
/ee/db/click_house/

View File

@ -18,7 +18,7 @@ variables:
# Helm chart ref used by test-on-cng pipeline
GITLAB_HELM_CHART_REF: "074bb942c9c65613c2576ce418f59b8577fff37c"
# Specific ref for cng-mirror project to trigger builds for
GITLAB_CNG_MIRROR_REF: "0df7d62154535093ae256da75efb702ef11b69a4"
GITLAB_CNG_MIRROR_REF: "01f587c24c52e4bbf8a67135ae4f6adafa19fa2b"
# Makes sure some of the common scripts from pipeline-common use bundler to execute commands
RUN_WITH_BUNDLE: "true"
# Makes sure reporting script defined in .gitlab-qa-report from pipeline-common is executed from correct folder

View File

@ -15,7 +15,6 @@ Layout/LineBreakAfterFinalMixin:
- 'app/services/authorized_project_update/project_recalculate_service.rb'
- 'app/services/batched_git_ref_updates/cleanup_scheduler_service.rb'
- 'app/services/jira_connect_subscriptions/create_service.rb'
- 'app/services/merge_requests/refresh_service.rb'
- 'app/services/projects/transfer_service.rb'
- 'app/workers/admin_email_worker.rb'
- 'app/workers/ci/delete_unit_tests_worker.rb'

View File

@ -777,7 +777,7 @@
{"name":"view_component","version":"3.21.0","platform":"ruby","checksum":"7f5a77bca29e7385495fad2b7c1acdcd2c581b3cd2e573a831a9808f6710df5c"},
{"name":"virtus","version":"2.0.0","platform":"ruby","checksum":"8841dae4eb7fcc097320ba5ea516bf1839e5d056c61ee27138aa4bddd6e3d1c2"},
{"name":"vite_rails","version":"3.0.19","platform":"ruby","checksum":"195c44677bc05c1f94e7a69f1264e49d4bad2729ab06538ee858c2962f5bb500"},
{"name":"vite_ruby","version":"3.9.1","platform":"ruby","checksum":"e4584a4ba1578602f13a3ac73402007aed044bd660daaac220523a97c49a4cc4"},
{"name":"vite_ruby","version":"3.9.2","platform":"ruby","checksum":"e10a7c851b590cccab57904bc96c2eb078ed6e22560a778db1dcefa3818a4972"},
{"name":"vmstat","version":"2.3.1","platform":"ruby","checksum":"5587cb430a54dbfc4a5c29dd01bd6a4031b2ff4c1d387504d74ff246f3b39104"},
{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
{"name":"warning","version":"1.5.0","platform":"ruby","checksum":"0f12c49fea0c06757778eefdcc7771e4fd99308901e3d55c504d87afdd718c53"},

View File

@ -1955,7 +1955,7 @@ GEM
vite_rails (3.0.19)
railties (>= 5.1, < 9)
vite_ruby (~> 3.0, >= 3.2.2)
vite_ruby (3.9.1)
vite_ruby (3.9.2)
dry-cli (>= 0.7, < 2)
logger (~> 1.6)
mutex_m

View File

@ -790,7 +790,7 @@
{"name":"view_component","version":"3.21.0","platform":"ruby","checksum":"7f5a77bca29e7385495fad2b7c1acdcd2c581b3cd2e573a831a9808f6710df5c"},
{"name":"virtus","version":"2.0.0","platform":"ruby","checksum":"8841dae4eb7fcc097320ba5ea516bf1839e5d056c61ee27138aa4bddd6e3d1c2"},
{"name":"vite_rails","version":"3.0.19","platform":"ruby","checksum":"195c44677bc05c1f94e7a69f1264e49d4bad2729ab06538ee858c2962f5bb500"},
{"name":"vite_ruby","version":"3.9.1","platform":"ruby","checksum":"e4584a4ba1578602f13a3ac73402007aed044bd660daaac220523a97c49a4cc4"},
{"name":"vite_ruby","version":"3.9.2","platform":"ruby","checksum":"e10a7c851b590cccab57904bc96c2eb078ed6e22560a778db1dcefa3818a4972"},
{"name":"vmstat","version":"2.3.1","platform":"ruby","checksum":"5587cb430a54dbfc4a5c29dd01bd6a4031b2ff4c1d387504d74ff246f3b39104"},
{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
{"name":"warning","version":"1.5.0","platform":"ruby","checksum":"0f12c49fea0c06757778eefdcc7771e4fd99308901e3d55c504d87afdd718c53"},

View File

@ -1989,7 +1989,7 @@ GEM
vite_rails (3.0.19)
railties (>= 5.1, < 9)
vite_ruby (~> 3.0, >= 3.2.2)
vite_ruby (3.9.1)
vite_ruby (3.9.2)
dry-cli (>= 0.7, < 2)
logger (~> 1.6)
mutex_m

View File

@ -28,8 +28,8 @@ export default {
},
icon() {
return this.canPlay
? { name: 'check-circle-filled', class: 'gl-fill-green-500' }
: { name: 'timer', class: 'gl-fill-current' };
? { name: 'check-circle-filled', variant: 'success' }
: { name: 'timer', variant: 'current' };
},
text() {
return this.canPlay ? this.$options.i18n.ready : this.$options.i18n.waiting;

View File

@ -154,6 +154,6 @@ export default {
:is-icon-button="true"
@click.stop.prevent="toggleTodo"
>
<gl-icon :class="{ '!gl-fill-blue-500': pendingTodo }" :name="buttonIcon" />
<gl-icon :name="buttonIcon" :variant="pendingTodo ? 'info' : null" />
</todo-button>
</template>

View File

@ -424,9 +424,10 @@ export default {
<gl-icon
v-if="isClosed(item)"
name="issue-close"
class="gl-ml-2 gl-shrink-0 gl-fill-blue-500"
class="gl-ml-2 gl-shrink-0"
:size="16"
data-testid="incident-closed"
variant="info"
/>
</div>
</template>

View File

@ -14,7 +14,6 @@ import {
GlLink,
GlSprintf,
} from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__, __, n__, sprintf } from '~/locale';
import { queryToObject, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
@ -79,7 +78,6 @@ export default {
directives: {
GlModal: GlModalDirective,
},
mixins: [glFeatureFlagsMixin()],
inject: ['group'],
data() {
return {
@ -141,9 +139,6 @@ export default {
return PLACEHOLDER_USER_STATUS.REASSIGNED;
},
isCsvReassignmentEnabled() {
return this.glFeatures.importerUserMappingReassignmentCsv;
},
sortOptions() {
return [
{
@ -403,17 +398,15 @@ export default {
<template #tabs-end>
<div class="gl-ml-auto gl-flex gl-gap-2">
<template v-if="isCsvReassignmentEnabled">
<gl-button
v-gl-modal="$options.uploadCsvModalId"
variant="link"
icon="media"
data-testid="reassign-csv-button"
>
{{ s__('UserMapping|Reassign with CSV file') }}
</gl-button>
<csv-upload-modal :modal-id="$options.uploadCsvModalId" />
</template>
<gl-button
v-gl-modal="$options.uploadCsvModalId"
variant="link"
icon="media"
data-testid="reassign-csv-button"
>
{{ s__('UserMapping|Reassign with CSV file') }}
</gl-button>
<csv-upload-modal :modal-id="$options.uploadCsvModalId" />
<gl-disclosure-dropdown
icon="ellipsis_v"
placement="bottom-end"

View File

@ -90,9 +90,6 @@ export const ALL_BRANCHES_WILDCARD = '*';
export const REQUIRED_ICON = 'check-circle-filled';
export const NOT_REQUIRED_ICON = 'status-failed';
export const REQUIRED_ICON_CLASS = 'gl-fill-green-500';
export const NOT_REQUIRED_ICON_CLASS = 'gl-text-danger';
export const DELETE_RULE_MODAL_ID = 'delete-branch-rule-modal';
export const projectUsersOptions = { push_code: true, active: true };

View File

@ -1,12 +1,7 @@
<script>
import { GlToggle, GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
REQUIRED_ICON,
NOT_REQUIRED_ICON,
REQUIRED_ICON_CLASS,
NOT_REQUIRED_ICON_CLASS,
} from './constants';
import { REQUIRED_ICON, NOT_REQUIRED_ICON } from './constants';
export default {
components: {
@ -60,8 +55,8 @@ export default {
iconName() {
return this.isProtected ? REQUIRED_ICON : NOT_REQUIRED_ICON;
},
iconClass() {
return this.isProtected ? REQUIRED_ICON_CLASS : NOT_REQUIRED_ICON_CLASS;
iconVariant() {
return this.isProtected ? 'success' : 'danger';
},
iconDataTestId() {
// eslint-disable-next-line @gitlab/require-i18n-strings
@ -102,7 +97,7 @@ export default {
</div>
<div v-else class="gl-mb-5">
<div class="gl-flex gl-items-center">
<gl-icon :data-testid="iconDataTestId" :size="14" :name="iconName" :class="iconClass" />
<gl-icon :data-testid="iconDataTestId" :size="14" :name="iconName" :variant="iconVariant" />
<strong class="gl-ml-2">{{ iconTitle }}</strong>
</div>
<gl-sprintf v-if="hasDescription" :message="description" data-testid="protection-description">

View File

@ -11,8 +11,6 @@ module Groups
feature_category :importers
def show
return render_404 unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
csv_response = Import::SourceUsers::GenerateCsvService.new(group, current_user: current_user).execute
if csv_response.success?
@ -27,8 +25,6 @@ module Groups
end
def create
return render_404 unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
unless file_type_is_valid?(file_params[:file])
render_unprocessable_entity(s_('UserMapping|You must upload a CSV file with a .csv file extension.'))
return

View File

@ -18,7 +18,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
before_action only: [:index] do
push_frontend_feature_flag(:importer_user_mapping, current_user)
push_frontend_feature_flag(:importer_user_mapping_reassignment_csv, current_user)
push_frontend_feature_flag(:importer_user_mapping_allow_bypass_of_confirmation, @group)
push_frontend_feature_flag(:service_accounts_crud, @group)
end

View File

@ -56,6 +56,12 @@ module Ci
@subject.projects.visible_to_user_and_access_level(@user, Gitlab::Access::MAINTAINER).exists?
end
with_options score: 6
condition(:maintainer_in_owner_project) do
# Check if user is a maintainer+ in the project owning the runner
@user.authorized_projects(Gitlab::Access::MAINTAINER).id_in(@subject.owner).exists?
end
with_options score: 8
condition(:maintainer_in_any_associated_groups) do
user_group_ids = @user.owned_or_maintainers_groups.select(:id)
@ -79,7 +85,11 @@ module Ci
rule { admin | owned_runner }.policy do
enable :read_builds
enable :read_runner
enable :assign_runner
enable :update_runner
enable :delete_runner
end
rule { is_instance_runner & any_maintainer_owned_groups_inheriting_shared_runners }.policy do
@ -94,6 +104,10 @@ module Ci
enable :read_runner
end
rule { is_project_runner & maintainer_in_owner_project }.policy do
enable :update_runner
end
rule { is_group_runner & maintainer_in_any_associated_groups }.policy do
enable :read_runner
end
@ -102,12 +116,6 @@ module Ci
enable :read_runner
end
rule { admin | owned_runner }.policy do
enable :assign_runner
enable :update_runner
enable :delete_runner
end
rule { ~admin & belongs_to_multiple_projects }.prevent :delete_runner
rule { ~admin & locked }.prevent :assign_runner

View File

@ -19,8 +19,7 @@ module Groups # rubocop:disable Gitlab/BoundedContexts -- existing top-level mod
private
def send_group_deletion_notification
return unless ::Feature.enabled?(:group_deletion_notification_email, group) &&
group.adjourned_deletion?
return unless group.adjourned_deletion?
::NotificationService.new.group_scheduled_for_deletion(group)
end

View File

@ -3,6 +3,7 @@
module MergeRequests
class RefreshService < MergeRequests::BaseService
include Gitlab::Utils::StrongMemoize
attr_reader :push
def execute(oldrev, newrev, ref)

View File

@ -4,6 +4,10 @@ module Snippets
class DestroyService
include Gitlab::Allowable
FAILED_TO_DELETE_ERROR = :failed_to_delete_error
SNIPPET_NOT_FOUND_ERROR = :snippet_not_found_error
SNIPPET_ACCESS_ERROR = :snippet_access_error
attr_reader :current_user, :snippet
DestroyError = Class.new(StandardError)
@ -15,13 +19,13 @@ module Snippets
def execute
if snippet.nil?
return service_response_error('No snippet found.', 404)
return service_response_error('No snippet found.', SNIPPET_NOT_FOUND_ERROR)
end
unless user_can_delete_snippet?
return service_response_error(
"You don't have access to delete this snippet.",
403
SNIPPET_ACCESS_ERROR
)
end
@ -29,9 +33,9 @@ module Snippets
ServiceResponse.success(message: 'Snippet was deleted.')
rescue DestroyError
service_response_error('Failed to remove snippet repository.', 400)
service_response_error('Failed to remove snippet repository.', FAILED_TO_DELETE_ERROR)
rescue StandardError
service_response_error('Failed to remove snippet.', 400)
service_response_error('Failed to remove snippet.', FAILED_TO_DELETE_ERROR)
end
private

View File

@ -8,18 +8,16 @@ module Import
idempotent!
feature_category :importers
def perform(source_user_id)
source_user = Import::SourceUser.find_by_id(source_user_id)
return if source_user.nil? || source_user.placeholder_user.nil?
return unless source_user.placeholder_user.placeholder?
def perform(source_user_or_placeholder_user_id, params = {})
placeholder_user = find_placeholder_user(source_user_or_placeholder_user_id, params.symbolize_keys)
return unless placeholder_user
if placeholder_user_referenced?(source_user)
log_placeholder_user_not_deleted(source_user)
placeholder_user_id = placeholder_user.id
if placeholder_user_referenced?(placeholder_user_id)
log_placeholder_user_not_deleted(placeholder_user_id)
return
end
placeholder_user = source_user.placeholder_user
placeholder_user.delete_async(
deleted_by: placeholder_user,
params: { "skip_authorization" => true }
@ -28,18 +26,33 @@ module Import
private
def log_placeholder_user_not_deleted(source_user)
def find_placeholder_user(id, params)
if params[:type].blank?
source_user = Import::SourceUser.find_by_id(id)
return if source_user.nil? || source_user.placeholder_user.nil?
return unless source_user.placeholder_user.placeholder?
source_user.placeholder_user
else
user = User.find_by_id(id)
return if user.nil? || !user.placeholder?
user
end
end
def log_placeholder_user_not_deleted(placeholder_user_id)
::Import::Framework::Logger.warn(
message: 'Unable to delete placeholder user because it is still referenced in other tables',
source_user_id: source_user.id
placeholder_user_id: placeholder_user_id
)
end
def placeholder_user_referenced?(source_user)
def placeholder_user_referenced?(placeholder_user_id)
PlaceholderReferences::AliasResolver.models_with_data.any? do |model, data|
columns = data[:columns].values - data[:columns_ignored_on_deletion].to_a
(columns & ::Gitlab::ImportExport::Base::RelationFactory::USER_REFERENCES).any? do |user_reference_column|
model.where(user_reference_column => source_user.placeholder_user_id).any? # rubocop:disable CodeReuse/ActiveRecord -- Adding a scope for all possible models would not be feasible here
model.where(user_reference_column => placeholder_user_id).any? # rubocop:disable CodeReuse/ActiveRecord -- Adding a scope for all possible models would not be feasible here
end
end
end

View File

@ -29,7 +29,8 @@ module Import
return self.class.perform_in(BACKOFF_PERIOD, import_source_user.id, params)
end
Import::DeletePlaceholderUserWorker.perform_async(import_source_user.id)
Import::DeletePlaceholderUserWorker.perform_async(import_source_user.placeholder_user_id,
type: 'placeholder_user')
end
def perform_failure(exception, import_source_user_id)

View File

@ -1,9 +0,0 @@
---
name: importer_user_mapping_reassignment_csv
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455906
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162267
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/478022
milestone: '17.4'
group: group::import and integrate
type: beta
default_enabled: true

View File

@ -1,9 +0,0 @@
---
name: group_deletion_notification_email
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/522883
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/185270
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/526037
milestone: '17.11'
group: group::authorization
type: gitlab_com_derisk
default_enabled: false

View File

@ -2,7 +2,7 @@
removal_milestone: "18.0"
announcement_milestone: "17.5"
breaking_change: true
window: 1
window: 2
reporter: g.hickman
stage: security risk management
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/510897

View File

@ -7,4 +7,4 @@ feature_category: runner
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182527
milestone: '17.10'
queued_migration_version: 20250224182011
finalized_by: # version of the migration that finalized this BBM
finalized_by: 20250414181659

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class FinalizeFixBadShardingKeyIdOnProjectCiRunners < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
milestone '18.0'
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'FixBadShardingKeyIdOnProjectCiRunners',
table_name: :ci_runners,
column_name: :id,
job_arguments: [],
finalize: true
)
end
def down; end
end

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
class AsyncRemoveIdxExpireAtJobIdOnCiJobArtifacts < Gitlab::Database::Migration[2.2]
include Gitlab::Database::PartitioningMigrationHelpers
milestone '18.0'
TABLE_NAME = :p_ci_job_artifacts
INDEX_DEFINITION = 'CREATE _ btree (expire_at, job_id)'
COLUMNS = [:expire_at, :job_id]
# TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/532779
def up
return unless index_name
prepare_async_index_removal TABLE_NAME, COLUMNS, name: index_name
end
def down
return unless index_name
unprepare_async_index TABLE_NAME, COLUMNS, name: index_name
end
private
# This index has a different name on Production DB and possibly on other instances.
# So we must find the index by definition instead.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/520213#note_2450943371.
def index_name
indexes_by_definition = indexes_by_definition_for_table(TABLE_NAME)
indexes_by_definition[INDEX_DEFINITION]
end
end

View File

@ -0,0 +1 @@
ec6c32dca920868ba6db982545710803bcb8fbbafb465430cb3cf5e102245b67

View File

@ -0,0 +1 @@
ceaea792d4212c3768695a28ee0558ec2d5ea68b1a11198a4cf854e97e1ce587

View File

@ -526,6 +526,14 @@ Report any issues or feedback using [issue 525855](https://gitlab.com/gitlab-org
<!--- end_remove -->
The `s3_v2` driver (in Beta) uses AWS SDK v2 and only supports Signature Version 4 for authentication.
This driver improves performance and reliability while ensuring compatibility with AWS authentication requirements,
as support for older signature methods is deprecated. For more information, see [epic 16272](https://gitlab.com/groups/gitlab-org/-/epics/16272).
For a complete list of configuration parameters for each driver, see [`s3_v1`](https://gitlab.com/gitlab-org/container-registry/-/blob/f4ece8cdba4413b968c8a3fd20497a8186f23d26/docs/storage-drivers/s3_v1.md) and [`s3_v2`](https://gitlab.com/gitlab-org/container-registry/-/blob/f4ece8cdba4413b968c8a3fd20497a8186f23d26/docs/storage-drivers/s3_v2.md).
To configure the S3 storage driver, add one of the following configurations to your `/etc/gitlab/gitlab.rb` file:
```ruby
# Deprecated: Will be removed in GitLab 19.0
registry['storage'] = {
@ -537,7 +545,11 @@ registry['storage'] = {
'regionendpoint' => '<your-s3-regionendpoint>'
}
}
```
Or
```ruby
# Beta: s3_v2 driver
registry['storage'] = {
's3_v2' => {
@ -550,9 +562,33 @@ registry['storage'] = {
}
```
The `s3_v2` driver (in Beta) uses AWS SDK v2 and only supports Signature Version 4 for authentication. This driver improves performance and reliability while ensuring compatibility with AWS authentication requirements, as they are phasing out support for older signature methods. For more information, see [epic 16272](https://gitlab.com/groups/gitlab-org/-/epics/16272).
For improved security, you can use an IAM role instead of static credentials by not including the `accesskey` and `secretkey` parameters.
For improved security, you can use an IAM role instead of static credentials by omitting the `accesskey` and `secretkey` parameters.
To prevent storage cost increases, configure a lifecycle policy in your S3 bucket to purge incomplete multipart uploads.
The container registry does not automatically clean these up.
A three-day expiration policy for incomplete multipart uploads works well for most usage patterns.
{{< alert type="note" >}}
`loglevel` settings differ between the [`s3_v1`](https://gitlab.com/gitlab-org/container-registry/-/blob/f4ece8cdba4413b968c8a3fd20497a8186f23d26/docs/storage-drivers/s3_v1.md#configuration-parameters) and [`s3_v2`](https://gitlab.com/gitlab-org/container-registry/-/blob/f4ece8cdba4413b968c8a3fd20497a8186f23d26/docs/storage-drivers/s3_v2.md#configuration-parameters) drivers.
If you set the `loglevel` for the wrong driver, it is ignored and a warning message is printed.
{{< /alert >}}
When using MinIO with the `s3_v2` driver, add the `checksum_disabled` parameter to disable AWS checksums:
```ruby
registry['storage'] = {
's3_v2' => {
'accesskey' => '<s3-access-key>',
'secretkey' => '<s3-secret-key-for-access-key>',
'bucket' => '<your-s3-bucket>',
'region' => '<your-s3-region>',
'regionendpoint' => '<your-s3-regionendpoint>',
'checksum_disabled' => true
}
}
```
For S3 VPC endpoints:
@ -569,11 +605,13 @@ registry['storage'] = {
}
```
- `regionendpoint` is only required when configuring an S3 compatible service such as MinIO, or when using an AWS S3 VPC Endpoint.
- `<your-s3-bucket>` should be the name of a bucket that exists, and can't include subdirectories.
- `pathstyle` should be set to `true` to use host/bucket_name/object style paths instead of bucket_name.host/object. Set to `false` for AWS S3.
S3 configuration parameters:
You can set a rate limit on connections to S3 to avoid 503 errors from the S3 API:
- `<your-s3-bucket>`: The name of an existing bucket. Cannot include subdirectories.
- `regionendpoint`: Required only when using an S3-compatible service like MinIO or an AWS S3 VPC Endpoint.
- `pathstyle`: Controls URL formatting. Set to `true` for `host/bucket_name/object` (most S3-compatible services) or `false` for `bucket_name.host/object` (AWS S3).
To avoid 503 errors from the S3 API, add the `maxrequestspersecond` parameter to set a rate limit on connections:
```ruby
registry['storage'] = {
@ -606,6 +644,10 @@ Report any issues or feedback using [issue 525855](https://gitlab.com/gitlab-org
{{< /alert >}}
For a complete list of configuration parameters for each driver, see [`azure_v1`](https://gitlab.com/gitlab-org/container-registry/-/blob/7b1786d261481a3c69912ad3423225f47f7c8242/docs/storage-drivers/azure_v1.md) and [`azure_v2`](https://gitlab.com/gitlab-org/container-registry/-/blob/7b1786d261481a3c69912ad3423225f47f7c8242/docs/storage-drivers/azure_v2.md).
To configure the Azure storage driver, add one of the following configurations to your `/etc/gitlab/gitlab.rb` file:
```ruby
# Deprecated: Will be removed in GitLab 19.0
registry['storage'] = {
@ -615,7 +657,11 @@ registry['storage'] = {
'container' => '<container_name>'
}
}
```
Or
```ruby
# Beta: azure_v2 driver
registry['storage'] = {
'azure_v2' => {

View File

@ -336,7 +336,7 @@ Returns [`BlobSearch`](#blobsearch).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryblobsearchchunkcount"></a>`chunkCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.2. **Status**: Experiment. Maximum chunks per file. |
| <a id="queryblobsearchexcludeforks"></a>`excludeForks` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Excludes forked projects in the search. Always false for project search. Not available for global search. |
| <a id="queryblobsearchexcludeforks"></a>`excludeForks` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Excludes forked projects in the search. Always false for project search. |
| <a id="queryblobsearchgroupid"></a>`groupId` {{< icon name="warning-solid" >}} | [`GroupID`](#groupid) | **Introduced** in GitLab 17.2. **Status**: Experiment. Group to search in. |
| <a id="queryblobsearchincludearchived"></a>`includeArchived` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.7. **Status**: Experiment. Includes archived projects in the search. Always true for project search. Default is false. |
| <a id="queryblobsearchincludeforked"></a>`includeForked` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.7. **Status**: Experiment. Includes forked projects in the search. Always true for project search. Not available for global search. |

View File

@ -13,17 +13,11 @@ title: Group placeholder reassignments API
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/513794) in GitLab 17.10 [with a flag](../administration/feature_flags.md) named `importer_user_mapping_reassignment_csv`. [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/478022).
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/513794) in GitLab 17.10 [with a flag](../administration/feature_flags.md) named `importer_user_mapping_reassignment_csv`. Enabled by default.
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/478022) in GitLab 18.0. Feature flag `importer_user_mapping_reassignment_csv` removed.
{{< /history >}}
{{< alert type="flag" >}}
The availability of this feature is controlled by a feature flag.
For more information, see the history.
{{< /alert >}}
Prerequisites:
- You must have the Owner role for the group.

View File

@ -12,7 +12,8 @@ title: Service account users API
{{< /details >}}
Use this API to interact with service accounts. For more information, see [Service accounts](../user/profile/service_accounts.md).
Use this API to interact with service accounts. Service accounts are a specific type of user, and many attributes of a service account can also be managed through the
[Users API](users.md) by administrators on GitLab Self Self-Managed instances.
## List all service account users

View File

@ -28,7 +28,6 @@ This window takes place on April 21 - 23, 2025 from 09:00 UTC to 22:00 UTC.
| [CI/CD job token - **Authorized groups and projects** allowlist enforcement](deprecations.md#cicd-job-token---authorized-groups-and-projects-allowlist-enforcement) | High | Software supply chain security | Project | Refer to the [Understanding this change](https://gitlab.com/gitlab-org/gitlab/-/issues/383084#understanding-this-change) section for details. |
| [Replace `add_on_purchase` GraphQL field with `add_on_purchases`](deprecations.md#replace-add_on_purchase-graphql-field-with-add_on_purchases) | Low | Fulfillment | Instance, group | |
| [Replace namespace `add_on_purchase` GraphQL field with `add_on_purchases`](deprecations.md#replace-namespace-add_on_purchase-graphql-field-with-add_on_purchases) | Low | Fulfillment | Instance, group | |
| [Limit number of scan execution policy actions allowed per policy](deprecations.md#limit-number-of-scan-execution-policy-actions-allowed-per-policy) | Low | Security risk management | Instance, group, project | |
| [Deprecation of `name` field in `ProjectMonthlyUsageType` GraphQL API](deprecations.md#deprecation-of-name-field-in-projectmonthlyusagetype-graphql-api) | Low | Fulfillment | Project | |
| [Deprecation of `STORAGE` enum in `NamespaceProjectSortEnum` GraphQL API](deprecations.md#deprecation-of-storage-enum-in-namespaceprojectsortenum-graphql-api) | Low | Fulfillment | Group | |
| [DAST `dast_devtools_api_timeout` will have a lower default value](deprecations.md#dast-dast_devtools_api_timeout-will-have-a-lower-default-value) | Low | Application security testing | Project | |
@ -42,6 +41,7 @@ This window takes place on April 28 - 30, 2025 from 09:00 UTC to 22:00 UTC.
| Deprecation | Impact | Stage | Scope | Check potential impact |
|-------------|--------|-------|-------|------------------------|
| [Limit number of scan execution policy actions allowed per policy](deprecations.md#limit-number-of-scan-execution-policy-actions-allowed-per-policy) | Low | Security risk management | Instance, group, project | |
| [Behavior change for Upcoming and Started milestone filters](deprecations.md#behavior-change-for-upcoming-and-started-milestone-filters) | Low | Plan | Group, project | |
## Window 3

View File

@ -456,7 +456,10 @@ Audit event types belong to the following product categories.
| Type name | Event triggered when | Saved to database | Introduced in | Scope |
|:----------|:---------------------|:------------------|:--------------|:------|
| [`admin_role_assigned_to_user`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186570) | A custom admin role is assigned to a user | {{< icon name="check-circle" >}} Yes | GitLab [18.0](https://gitlab.com/gitlab-org/gitlab/-/issues/507958) | User |
| [`admin_role_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188367) | A custom admin role is created | {{< icon name="check-circle" >}} Yes | GitLab [18.0](https://gitlab.com/gitlab-org/gitlab/-/issues/536131) | Instance |
| [`admin_role_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188367) | A custom admin role is deleted | {{< icon name="check-circle" >}} Yes | GitLab [18.0](https://gitlab.com/gitlab-org/gitlab/-/issues/536131) | Instance |
| [`admin_role_unassigned_from_user`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186570) | A custom admin role is unassigned from a user | {{< icon name="check-circle" >}} Yes | GitLab [18.0](https://gitlab.com/gitlab-org/gitlab/-/issues/507958) | User |
| [`admin_role_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188367) | A custom admin role is updated | {{< icon name="check-circle" >}} Yes | GitLab [18.0](https://gitlab.com/gitlab-org/gitlab/-/issues/536131) | Instance |
| [`member_role_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137087) | A custom role is created | {{< icon name="check-circle" >}} Yes | GitLab [16.7](https://gitlab.com/gitlab-org/gitlab/-/issues/388934) | Group, Instance |
| [`member_role_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141630) | A custom role is deleted | {{< icon name="check-circle" >}} Yes | GitLab [16.9](https://gitlab.com/gitlab-org/gitlab/-/issues/437672) | Group, Instance |
| [`member_role_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141630) | A custom role is updated | {{< icon name="check-circle" >}} Yes | GitLab [16.9](https://gitlab.com/gitlab-org/gitlab/-/issues/437672) | Group, Instance |

View File

@ -416,17 +416,11 @@ Before a user accepts the reassignment, you can [cancel the request](#cancel-rea
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/455901) in GitLab 17.10 [with a flag](../../../administration/feature_flags.md) named `importer_user_mapping_reassignment_csv`. [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/478022).
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/455901) in GitLab 17.10 [with a flag](../../../administration/feature_flags.md) named `importer_user_mapping_reassignment_csv`. Enabled by default.
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/478022) in GitLab 18.0. Feature flag `importer_user_mapping_reassignment_csv` removed.
{{< /history >}}
{{< alert type="flag" >}}
The availability of this feature is controlled by a feature flag.
For more information, see the history.
{{< /alert >}}
Prerequisites:
- You must have the Owner role for the group.

View File

@ -10,7 +10,6 @@ module API
before do
authenticate!
not_found! unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
forbidden! unless can?(current_user, :owner_access, user_group)
end

View File

@ -174,9 +174,10 @@ module API
destroy_conditionally!(snippet) do |snippet|
service = ::Snippets::DestroyService.new(current_user, snippet)
response = service.execute
http_status = Helpers::Snippets::HttpResponseMap.status_for(response.reason)
if response.error?
render_api_error!({ error: response.message }, response.reason)
render_api_error!({ error: response.message }, http_status)
end
end
end

View File

@ -225,9 +225,10 @@ module API
destroy_conditionally!(snippet) do |snippet|
service = ::Snippets::DestroyService.new(current_user, snippet)
response = service.execute
http_status = Helpers::Snippets::HttpResponseMap.status_for(response.reason)
if response.error?
render_api_error!({ error: response.message }, response.reason)
render_api_error!({ error: response.message }, http_status)
end
end
end

View File

@ -8,6 +8,7 @@ module Gitlab
SEARCH_CHAR_LIMIT = 4096
SEARCH_TERM_LIMIT = 64
MIN_TERM_LENGTH = 2
BOOLEAN_PARAMS = %i[confidential exclude_forks include_archived include_forked].freeze
# Generic validation
validates :query_string, length: { maximum: SEARCH_CHAR_LIMIT }
@ -38,6 +39,10 @@ module Gitlab
end
end
def slice(*keys)
keys.index_with { |key| self[key] }.with_indifferent_access
end
def abusive?
detect_abuse? && abuse_detection.errors.any?
end
@ -93,7 +98,8 @@ module Gitlab
end
def process_params(params)
processed_params = convert_all_boolean_params(params)
processed_params = params.is_a?(Hash) ? params.with_indifferent_access : params.dup
processed_params = convert_all_boolean_params(processed_params)
convert_not_params(processed_params)
end
@ -109,25 +115,11 @@ module Gitlab
end
def convert_all_boolean_params(params)
converted_params = params.is_a?(Hash) ? params.with_indifferent_access : params.dup
if converted_params.key?(:confidential)
converted_params[:confidential] = Gitlab::Utils.to_boolean(converted_params[:confidential])
BOOLEAN_PARAMS.each do |key|
params[key] = Gitlab::Utils.to_boolean(params[key]) if params.key?(key)
end
if converted_params.key?(:include_archived)
converted_params[:include_archived] = Gitlab::Utils.to_boolean(converted_params[:include_archived])
end
if converted_params.key?(:include_forked)
converted_params[:include_forked] = Gitlab::Utils.to_boolean(converted_params[:include_forked])
end
if converted_params.key?(:exclude_forks)
converted_params[:exclude_forks] = Gitlab::Utils.to_boolean(converted_params[:exclude_forks])
end
converted_params
params
end
end
end

View File

@ -302,39 +302,21 @@ describe('PlaceholdersTabApp', () => {
});
describe('reassign CSV button', () => {
describe('when the feature flag is enabled', () => {
beforeEach(() => {
createComponent({
provide: {
glFeatures: { importerUserMappingReassignmentCsv: true },
},
mountFn: mountExtended,
});
});
it('renders the button and the modal', () => {
expect(findReassignCsvButton().exists()).toBe(true);
expect(findCsvModal().exists()).toBe(true);
});
it('shows modal when button is clicked', async () => {
findReassignCsvButton().trigger('click');
await nextTick();
expect(findCsvModal().findComponent(GlModal).isVisible()).toBe(true);
});
beforeEach(() => {
createComponent({ mountFn: mountExtended });
});
describe('when the feature flag is disabled', () => {
beforeEach(() => {
createComponent({ provide: { glFeatures: { importerUserMappingReassignmentCsv: false } } });
});
it('renders the button and the modal', () => {
expect(findReassignCsvButton().exists()).toBe(true);
expect(findCsvModal().exists()).toBe(true);
});
it('does not render the button and the modal', () => {
expect(findReassignCsvButton().exists()).toBe(false);
expect(findCsvModal().exists()).toBe(false);
});
it('shows modal when button is clicked', async () => {
findReassignCsvButton().trigger('click');
await nextTick();
expect(findCsvModal().findComponent(GlModal).isVisible()).toBe(true);
});
});

View File

@ -39,6 +39,38 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
end
describe '#slice' do
let(:controller_params) { ActionController::Parameters.new(group_id: 123, search: search, exclude_forks: true) }
let(:params) { described_class.new(controller_params) }
it 'returns a new params object with only the specified keys' do
sliced = params.slice(:exclude_forks, :group_id, :project_id)
expect(sliced).to be_a(Hash)
expect(sliced[:exclude_forks]).to be(true)
expect(sliced[:group_id]).to eq(123)
expect(sliced[:project_id]).to be_nil
end
it 'works with string keys' do
sliced = params.slice('exclude_forks', 'group_id', 'project_id')
expect(sliced).to be_a(Hash)
expect(sliced['exclude_forks']).to be(true)
expect(sliced['group_id']).to eq(123)
expect(sliced['project_id']).to be_nil
end
it 'handles mixed string and symbol keys' do
sliced = params.slice(:exclude_forks, 'group_id')
expect(sliced).to be_a(Hash)
expect(sliced[:exclude_forks]).to be(true)
expect(sliced[:group_id]).to eq(123)
expect(sliced[:project_id]).to be_nil
end
end
describe '#query_string' do
let(:term) { 'term' }

View File

@ -5,11 +5,14 @@ require 'spec_helper'
RSpec.describe Ci::RunnerManagerPolicy, feature_category: :fleet_visibility do
let_it_be(:owner) { create(:user) }
subject(:policy) { described_class.new(user, runner_manager) }
describe 'ability :read_runner_manager' do
let(:runner_manager) { runner.runner_managers.first }
subject(:policy) { described_class.new(user, runner_manager) }
include_context 'with runner policy environment'
it_behaves_like 'runner read policy', :read_runner_manager
it_behaves_like 'runner policy not allowed for levels lower than maintainer', :read_runner_manager
it_behaves_like 'runner policy', :read_runner_manager
end
end

View File

@ -5,15 +5,93 @@ require 'spec_helper'
RSpec.describe Ci::RunnerPolicy, feature_category: :runner do
let_it_be(:owner) { create(:user) }
describe 'ability :read_runner' do
subject(:policy) { described_class.new(user, runner) }
subject(:policy) { described_class.new(user, runner) }
it_behaves_like 'runner read policy', :read_runner
include_context 'with runner policy environment'
describe 'ability :read_runner' do
it_behaves_like 'runner policy not allowed for levels lower than maintainer', :read_runner
it_behaves_like 'runner policy', :read_runner
end
describe 'ability :update_runner' do
it_behaves_like 'runner policy not allowed for levels lower than maintainer', :update_runner
context 'with maintainer access' do
let(:user) { maintainer }
it_behaves_like 'a policy disallowing access to instance runner/runner manager', :update_runner
context 'with group runner' do
let(:runner) { group_runner }
it { expect_disallowed :update_runner }
end
context 'with project runner' do
let(:runner) { project_runner }
it { expect_allowed :update_runner }
context 'when user is maintainer in an unrelated group' do
let_it_be(:maintainers_group_maintainer) { create(:user) }
let_it_be_with_reload(:maintainers_group) do
create(:group, name: 'maintainers', path: 'maintainers', maintainers: maintainers_group_maintainer)
end
let(:user) { maintainers_group_maintainer }
it { expect_disallowed :update_runner }
context 'when maintainers group is invited as maintainer to project' do
before do
create(:project_group_link, :maintainer, group: maintainers_group, project: project_invited_to)
end
context 'and target project is owner project' do
let(:project_invited_to) { owner_project }
it { expect_allowed :update_runner }
end
context 'and target project is other project' do
let(:project_invited_to) { other_project }
it { expect_disallowed :update_runner }
end
end
end
end
end
context 'with owner access' do
let(:user) { owner }
it_behaves_like 'a policy disallowing access to instance runner/runner manager', :update_runner
context 'with group runner' do
let(:runner) { group_runner }
it { expect_allowed :update_runner }
context 'with sharing of group runners disabled' do
before do
owner_project.update!(group_runners_enabled: false)
end
it { expect_allowed :update_runner }
end
end
context 'with project runner' do
let(:runner) { project_runner }
it { expect_allowed :update_runner }
end
end
end
describe 'ability :read_ephemeral_token' do
subject(:policy) { described_class.new(user, runner) }
let_it_be(:runner) { create(:ci_runner, creator: owner) }
let(:creator) { owner }

View File

@ -30,18 +30,6 @@ RSpec.describe API::GroupPlaceholderReassignments, feature_category: :importers
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when importer_user_mapping_reassignment_csv flag is disabled' do
before do
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'GET /groups/:id/placeholder_reassignments' do

View File

@ -398,7 +398,12 @@ RSpec.describe API::ProjectSnippets, :aggregate_failures, feature_category: :sou
context "when destruction fails" do
let(:error_message) { "some service error message" }
let(:error_response) { ServiceResponse.error(message: error_message, reason: :bad_request) }
let(:error_response) do
ServiceResponse.error(
message: error_message,
reason: ::Snippets::DestroyService::FAILED_TO_DELETE_ERROR
)
end
before do
allow_next_instance_of(::Snippets::DestroyService) do |service|

View File

@ -548,7 +548,13 @@ RSpec.describe API::Snippets, :aggregate_failures, factory_default: :keep, featu
context "when destruction fails" do
let(:error_message) { "some service error message" }
let(:error_response) { ServiceResponse.error(message: error_message, reason: :bad_request) }
let(:error_response) do
ServiceResponse.error(
message: error_message,
reason: ::Snippets::DestroyService::FAILED_TO_DELETE_ERROR
)
end
before do
allow_next_instance_of(::Snippets::DestroyService) do |service|

View File

@ -58,18 +58,6 @@ RSpec.describe Groups::BulkPlaceholderAssignmentsController, feature_category: :
expect(flash[:alert]).to eq('my error message')
end
end
context 'when :importer_user_mapping_reassignment_csv is disabled' do
before do
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
end
it 'responds with 404' do
request
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
@ -143,18 +131,6 @@ RSpec.describe Groups::BulkPlaceholderAssignmentsController, feature_category: :
})
end
end
context 'when :importer_user_mapping_reassignment_csv is disabled' do
before do
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
end
it 'responds with 404' do
request
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when not signed in' do

View File

@ -33,37 +33,8 @@ RSpec.describe Groups::MarkForDeletionService, feature_category: :groups_and_pro
result
end
context 'when notification feature flag is enabled and adjourned deletion is enabled' do
context 'when adjourned deletion is enabled' do
before do
stub_feature_flags(group_deletion_notification_email: true)
allow(group).to receive(:adjourned_deletion?).and_return(true)
end
it 'sends a notification email' do
expect_next_instance_of(NotificationService) do |service|
expect(service).to receive(:group_scheduled_for_deletion).with(group)
end
result
end
end
context 'when notification feature flag is disabled' do
before do
stub_feature_flags(group_deletion_notification_email: false)
allow(group).to receive(:adjourned_deletion?).and_return(true)
end
it 'does not send a notification email' do
expect(NotificationService).not_to receive(:new)
result
end
end
context 'when notification feature flag is enabled for specific group' do
before do
stub_feature_flags(group_deletion_notification_email: group)
allow(group).to receive(:adjourned_deletion?).and_return(true)
end
@ -78,7 +49,6 @@ RSpec.describe Groups::MarkForDeletionService, feature_category: :groups_and_pro
context 'when adjourned deletion is disabled' do
before do
stub_feature_flags(group_deletion_notification_email: true)
allow(group).to receive(:adjourned_deletion?).and_return(false)
end

View File

@ -15,7 +15,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
it 'returns a ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(404)
expect(subject.reason).to eq(described_class::SNIPPET_NOT_FOUND_ERROR)
end
end
@ -36,7 +36,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
it 'returns ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(403)
expect(subject.reason).to eq(described_class::SNIPPET_ACCESS_ERROR)
end
end
@ -47,7 +47,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
it 'returns ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(400)
expect(subject.reason).to eq(described_class::FAILED_TO_DELETE_ERROR)
end
end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'a policy allowing reading instance runner/runner manager depending on runner sharing' do
RSpec.shared_examples 'a policy allowing accessing instance runner/runner manager depending on runner sharing' do
|ability|
context 'with instance runner' do
using RSpec::Parameterized::TableSyntax
where(:shared_runners_enabled_on_group, :shared_runners_enabled_on_project, :expect_can_read) do
where(:shared_runners_enabled_on_group, :shared_runners_enabled_on_project, :expect_to_be_allowed) do
false | false | false
false | true | true
true | false | true
@ -17,11 +17,11 @@ RSpec.shared_examples 'a policy allowing reading instance runner/runner manager
before do
group.update!(shared_runners_enabled: shared_runners_enabled_on_group)
project.update!(shared_runners_enabled: shared_runners_enabled_on_project)
owner_project.update!(shared_runners_enabled: shared_runners_enabled_on_project)
end
specify do
if expect_can_read
if expect_to_be_allowed
expect_allowed ability
else
expect_disallowed ability
@ -31,18 +31,42 @@ RSpec.shared_examples 'a policy allowing reading instance runner/runner manager
end
end
RSpec.shared_examples 'a policy allowing reading group runner/runner manager depending on runner sharing' do
RSpec.shared_examples 'a policy disallowing access to instance runner/runner manager' do |ability|
context 'with instance runner' do
using RSpec::Parameterized::TableSyntax
where(:shared_runners_enabled_on_group, :shared_runners_enabled_on_project) do
false | false
false | true
true | false
true | true
end
with_them do
let(:runner) { instance_runner }
before do
group.update!(shared_runners_enabled: shared_runners_enabled_on_group)
owner_project.update!(shared_runners_enabled: shared_runners_enabled_on_project)
end
specify { expect_disallowed ability }
end
end
end
RSpec.shared_examples 'a policy allowing accessing group runner/runner manager depending on runner sharing' do
|ability, user_role|
let(:group_runners_enabled_on_project) { true }
before do
project.update!(group_runners_enabled: group_runners_enabled_on_project)
owner_project.update!(group_runners_enabled: group_runners_enabled_on_project)
end
context 'with group runner' do
let(:runner) { group_runner }
# NOTE: The user is allowed to read the runner/runner manager because:
# NOTE: The user is allowed to access the runner/runner manager because:
# - the user is a maintainer+ in the runner's group
# - the user is a maintainer+ in `group/subgroup/project`, and the runner is shared to that project
it { expect_allowed ability }
@ -61,7 +85,7 @@ RSpec.shared_examples 'a policy allowing reading group runner/runner manager dep
let(:user) { subgroup_member }
context 'with runner visible to group project' do
# NOTE: The user is allowed to read the runner/runner manager because the user is a maintainer+
# NOTE: The user is allowed to access the runner/runner manager because the user is a maintainer+
# in `group/subgroup/project`, and the runner is shared to that project
it { expect_allowed ability }
@ -114,7 +138,7 @@ RSpec.shared_examples 'a policy allowing reading group runner/runner manager dep
context 'when runner is in subgroup' do
let(:runner) { subgroup_runner }
# NOTE: The user is allowed to read the runner/runner manager because the user is a maintainer+ in
# NOTE: The user is allowed to access the runner/runner manager because the user is a maintainer+ in
# `group/subgroup/project`, and the runner is shared to that project
it { expect_allowed ability }
@ -126,7 +150,7 @@ RSpec.shared_examples 'a policy allowing reading group runner/runner manager dep
end
end
RSpec.shared_examples 'does not allow reading runners/runner managers on any scope' do |ability|
RSpec.shared_examples 'does not allow accessing runners/runner managers on any scope' do |ability|
context 'with instance runner' do
let(:runner) { instance_runner }
@ -135,7 +159,7 @@ RSpec.shared_examples 'does not allow reading runners/runner managers on any sco
context 'with shared runners disabled for groups and projects' do
before do
group.update!(shared_runners_enabled: false)
project.update!(shared_runners_enabled: false)
owner_project.update!(shared_runners_enabled: false)
end
it { expect_disallowed ability }
@ -160,7 +184,7 @@ RSpec.shared_examples 'does not allow reading runners/runner managers on any sco
context 'with sharing of group runners disabled' do
before do
project.update!(group_runners_enabled: false)
owner_project.update!(group_runners_enabled: false)
end
it { expect_disallowed ability }
@ -174,7 +198,7 @@ RSpec.shared_examples 'does not allow reading runners/runner managers on any sco
end
end
RSpec.shared_examples 'runner read policy' do |ability|
RSpec.shared_context 'with runner policy environment' do
let_it_be(:guest) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
@ -186,47 +210,79 @@ RSpec.shared_examples 'runner read policy' do |ability|
end
let_it_be_with_reload(:subgroup) { create(:group, name: 'subgroup', path: 'subgroup', parent: group) }
let_it_be_with_reload(:project) { create(:project, group: subgroup) }
let_it_be_with_reload(:owner_project) { create(:project, group: subgroup) }
let_it_be_with_reload(:other_project) { create(:project) }
let_it_be_with_reload(:group_without_project) { create(:group, name: 'top-level2', path: 'top-level2') }
let_it_be(:instance_runner) { create(:ci_runner, :instance, :with_runner_manager) }
let_it_be(:group_runner) { create(:ci_runner, :group, :with_runner_manager, groups: [group]) }
let_it_be(:subgroup_runner) { create(:ci_runner, :group, :with_runner_manager, groups: [subgroup]) }
let_it_be(:project_runner) { create(:ci_runner, :project, :with_runner_manager, projects: [project]) }
let_it_be(:project_runner) do
create(:ci_runner, :project, :with_runner_manager, projects: [owner_project, other_project])
end
let_it_be(:runner_on_group_without_project) do
create(:ci_runner, :group, :with_runner_manager, groups: [group_without_project])
end
end
RSpec.shared_examples 'runner policy not allowed for levels lower than maintainer' do |ability|
context 'without access' do
let_it_be(:user) { create(:user) }
it_behaves_like 'does not allow reading runners/runner managers on any scope', ability
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with guest access' do
let(:user) { guest }
it_behaves_like 'does not allow reading runners/runner managers on any scope', ability
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with reporter access' do
let(:user) { reporter }
it_behaves_like 'does not allow reading runners/runner managers on any scope', ability
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with developer access' do
let(:user) { developer }
it_behaves_like 'does not allow reading runners/runner managers on any scope', ability
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
end
RSpec.shared_examples 'runner policy' do |ability|
context 'without access' do
let_it_be(:user) { create(:user) }
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with guest access' do
let(:user) { guest }
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with reporter access' do
let(:user) { reporter }
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with developer access' do
let(:user) { developer }
it_behaves_like 'does not allow accessing runners/runner managers on any scope', ability
end
context 'with maintainer access' do
let(:user) { maintainer }
it_behaves_like 'a policy allowing reading instance runner/runner manager depending on runner sharing', ability
it_behaves_like 'a policy allowing accessing instance runner/runner manager depending on runner sharing', ability
it_behaves_like 'a policy allowing reading group runner/runner manager depending on runner sharing',
it_behaves_like 'a policy allowing accessing group runner/runner manager depending on runner sharing',
ability, :maintainer
context 'with project runner' do
@ -234,25 +290,32 @@ RSpec.shared_examples 'runner read policy' do |ability|
it { expect_allowed ability }
context 'when user is not maintainer in parent group' do
context 'when user is maintainer in an unrelated group' do
let_it_be(:maintainers_group_maintainer) { create(:user) }
let_it_be_with_reload(:maintainers_group) { create(:group, name: 'maintainers', path: 'maintainers') }
let_it_be_with_reload(:maintainers_group) do
create(:group, name: 'maintainers', path: 'maintainers', maintainers: maintainers_group_maintainer)
end
let(:user) { maintainers_group_maintainer }
before_all do
create(:project_group_link, :maintainer, group: maintainers_group, project: project)
maintainers_group.add_reporter(maintainers_group_maintainer)
end
it { expect_disallowed ability }
context 'when user is maintainer in a group invited to project as maintainer' do
before_all do
maintainers_group.add_maintainer(maintainers_group_maintainer)
context 'when maintainers group is invited as maintainer to project' do
before do
create(:project_group_link, :maintainer, group: maintainers_group, project: project_invited_to)
end
it { expect_allowed ability }
context 'and target project is owner project' do
let(:project_invited_to) { owner_project }
it { expect_allowed ability }
end
context 'and target project is other project' do
let(:project_invited_to) { other_project }
it { expect_allowed ability }
end
end
end
end
@ -261,7 +324,7 @@ RSpec.shared_examples 'runner read policy' do |ability|
context 'with owner access' do
let(:user) { owner }
it_behaves_like 'a policy allowing reading instance runner/runner manager depending on runner sharing', ability
it_behaves_like 'a policy allowing accessing instance runner/runner manager depending on runner sharing', ability
context 'with group runner' do
let(:runner) { group_runner }
@ -270,7 +333,7 @@ RSpec.shared_examples 'runner read policy' do |ability|
context 'with sharing of group runners disabled' do
before do
project.update!(group_runners_enabled: false)
owner_project.update!(group_runners_enabled: false)
end
it { expect_allowed ability }

View File

@ -147,6 +147,7 @@ RSpec.describe Tooling::Danger::ProjectHelper, feature_category: :tooling do
'lib/gitlab/sql/foo' | [:database, :backend]
'rubocop/cop/migration/foo' | [:database]
'ee/db/seeds/data_seeder/foo_seed.db' | [:backend]
'db/fixtures/foo.rb' | [:backend]
'ee/db/fixtures/foo.rb' | [:backend]

View File

@ -6,7 +6,7 @@ RSpec.describe Import::DeletePlaceholderUserWorker, feature_category: :importers
let_it_be(:placeholder_user) { create(:user, :placeholder) }
let_it_be(:source_user) { create(:import_source_user, placeholder_user: placeholder_user) }
let(:job_args) { source_user.id }
let(:job_args) { [placeholder_user.id, { type: 'placeholder_user' }] }
subject(:perform) { described_class.new.perform(*job_args) }
@ -26,7 +26,7 @@ RSpec.describe Import::DeletePlaceholderUserWorker, feature_category: :importers
it 'does not delete the placeholder_user and logs the issue' do
expect(::Import::Framework::Logger).to receive(:warn).with(
message: 'Unable to delete placeholder user because it is still referenced in other tables',
source_user_id: source_user.id
placeholder_user_id: placeholder_user.id
)
expect(DeleteUserWorker).not_to receive(:perform_async)
@ -68,7 +68,7 @@ RSpec.describe Import::DeletePlaceholderUserWorker, feature_category: :importers
end
context 'when there is no placeholder user' do
let_it_be(:source_user) { create(:import_source_user, :completed, placeholder_user: nil) }
let(:job_args) { [-1, { type: 'placeholder_user' }] }
it 'does not delete the placeholder_user and does not log an issue' do
expect(::Import::Framework::Logger).not_to receive(:warn)
@ -80,7 +80,7 @@ RSpec.describe Import::DeletePlaceholderUserWorker, feature_category: :importers
context 'when attempting to delete a user who is not a placeholder' do
let_it_be(:user) { create(:user, :import_user) }
let_it_be(:source_user) { create(:import_source_user, placeholder_user: user) }
let(:job_args) { [user.id, { type: 'placeholder_user' }] }
it 'does not delete the user' do
expect(DeleteUserWorker).not_to receive(:perform_async)
@ -88,4 +88,17 @@ RSpec.describe Import::DeletePlaceholderUserWorker, feature_category: :importers
perform
end
end
context 'when called with legacy parameters (source_user_id only)' do
let(:job_args) { [source_user.id] }
it_behaves_like 'deletes the placeholder user'
context 'when another table references the user from an author_id column' do
let!(:note) { create(:note, author: placeholder_user) }
let(:job_args) { [source_user.id] }
it_behaves_like 'does not delete the placeholder_user and logs the issue'
end
end
end

View File

@ -51,9 +51,9 @@ RSpec.describe Import::ReassignPlaceholderUserRecordsWorker, feature_category: :
it_behaves_like 'an invalid source user'
end
it 'queues a DeletePlaceholderUserWorker with the source user ID' do
it 'queues a DeletePlaceholderUserWorker with the placeholder user ID' do
expect(Import::DeletePlaceholderUserWorker)
.to receive(:perform_async).with(import_source_user.id)
.to receive(:perform_async).with(import_source_user.placeholder_user_id, { type: 'placeholder_user' })
perform_multiple(job_args)
end

View File

@ -93,6 +93,7 @@ module Tooling
\.gitlab/ci/frontend\.gitlab-ci\.yml
)\z}x => %i[frontend tooling],
%r{\Aee/db/seeds/data_seeder/} => [:backend],
%r{\A((ee|jh)/)?db/(geo/)?(?!click_house|fixtures)[^/]+} => [:database],
%r{\A((ee|jh)/)?db/[^/]+\z} => [:database], # db/ root files
%r{\Adb/docs/.+\.yml\z} => [:database],