Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-11-01 18:12:39 +00:00
parent b21334799a
commit 68ce709bef
67 changed files with 725 additions and 283 deletions

View File

@ -2655,7 +2655,6 @@ RSpec/FeatureCategory:
- 'spec/lib/bitbucket/representation/issue_spec.rb'
- 'spec/lib/bitbucket/representation/pull_request_comment_spec.rb'
- 'spec/lib/bitbucket/representation/pull_request_spec.rb'
- 'spec/lib/bitbucket/representation/repo_spec.rb'
- 'spec/lib/bitbucket/representation/user_spec.rb'
- 'spec/lib/bitbucket_server/client_spec.rb'
- 'spec/lib/bitbucket_server/collection_spec.rb'

View File

@ -495,7 +495,7 @@ group :test do
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab_quality-test_tooling', '~> 1.3.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab_quality-test_tooling', '~> 1.4.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory
end
gem 'octokit', '~> 6.0' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -19,6 +19,7 @@
{"name":"aes_key_wrap","version":"1.1.0","platform":"ruby","checksum":"b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5"},
{"name":"akismet","version":"3.0.0","platform":"ruby","checksum":"74991b8e3d3257eeea996b47069abb8da2006c84a144255123e8dffd1c86b230"},
{"name":"aliyun-sdk","version":"0.8.0","platform":"ruby","checksum":"65915d3f9b528082253d1f9ad0e4d13d6b552933fe49251c68c6915cd4d75b9d"},
{"name":"amatch","version":"0.4.1","platform":"ruby","checksum":"d3ff15226a2e627c72802e94579db829e5e10c96cf89d329494caec5889145f7"},
{"name":"android_key_attestation","version":"0.3.0","platform":"ruby","checksum":"467eb01a99d2bb48ef9cf24cc13712669d7056cba5a52d009554ff037560570b"},
{"name":"apollo_upload_server","version":"2.1.0","platform":"ruby","checksum":"e5f3c9dda0c2ca775d007072742b98d517dfd91a667111fedbcdc94dfabd904e"},
{"name":"app_store_connect","version":"0.29.0","platform":"ruby","checksum":"01d7a923825a4221892099acb5a72f86f6ee7d8aa95815d3c459ba6816ea430f"},
@ -219,7 +220,7 @@
{"name":"gitlab-styles","version":"10.1.0","platform":"ruby","checksum":"f42745f5397d042fe24cf2d0eb56c995b37f9f43d8fb79b834d197a1cafdc84a"},
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"gitlab_quality-test_tooling","version":"1.3.0","platform":"ruby","checksum":"0c932e0a98839c219ef21e2da336edb59ff48cc43cd06e22d780738715a4652e"},
{"name":"gitlab_quality-test_tooling","version":"1.4.0","platform":"ruby","checksum":"0fdfb574138ac9228ccce5fc40687aa81dcb8158900ca26982e15c14797959cf"},
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},
@ -366,6 +367,7 @@
{"name":"mixlib-log","version":"3.0.9","platform":"ruby","checksum":"fd6ca2c8075f8085065dffcee0805c5b3f88d643d5c954acdc3282f463a9ad58"},
{"name":"mixlib-shellout","version":"3.2.5","platform":"ruby","checksum":"121a54005e52b6596a945f7bfc95bbcbd7d8ee7685cb3736dd3cef5ff46029bd"},
{"name":"mixlib-shellout","version":"3.2.5","platform":"universal-mingw32","checksum":"c40ef5f34a68eec5e0cad13482497f6c3898a30cff1747517f2169d4fa4055e0"},
{"name":"mize","version":"0.4.1","platform":"ruby","checksum":"55bcba0cf001cbff5a647a18172c4a885061ceec586395fb08ecbb98d039f627"},
{"name":"msgpack","version":"1.5.4","platform":"java","checksum":"05b3bd16a65dddc64c878634b7ecb9cd613569ca3dd6e480d7295626a0a3f562"},
{"name":"msgpack","version":"1.5.4","platform":"ruby","checksum":"a53db320fba40f58c07c5b66ed9fd4d73cbe8eba4cb28fe9e3218444341a4e09"},
{"name":"multi_json","version":"1.14.1","platform":"ruby","checksum":"d971296c0eacea289d31e4a7ab7ac5eda97262c62bbc8c110de4f5e36425c577"},
@ -457,6 +459,7 @@
{"name":"prometheus-client-mmap","version":"0.28.1","platform":"ruby","checksum":"92fb3989a16927fb0cacfcb3ebc6c8ea5e4abf82e4aef22ab62c3c4b8f17e52a"},
{"name":"prometheus-client-mmap","version":"0.28.1","platform":"x86_64-darwin","checksum":"66e7cad96ad581174edf4f1f52da141e5a15389ce3283fba7b4e3e5968dd46b7"},
{"name":"prometheus-client-mmap","version":"0.28.1","platform":"x86_64-linux","checksum":"4d3e92a249b16e41ef3e55078537bca599659578c0f86e31d195429c6e5e1f3a"},
{"name":"protocol","version":"2.0.0","platform":"ruby","checksum":"dcd7c509e53b8cd6284e965a2e2e71d5291ca9e2d50acfa3d7ee0561c0df16b9"},
{"name":"pry","version":"0.14.2","platform":"java","checksum":"fd780670977ba04ff7ee32dabd4d02fe4bf02e977afe8809832d5dca1412862e"},
{"name":"pry","version":"0.14.2","platform":"ruby","checksum":"c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d"},
{"name":"pry-byebug","version":"3.10.1","platform":"ruby","checksum":"c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8"},
@ -555,6 +558,7 @@
{"name":"ruby-saml","version":"1.15.0","platform":"ruby","checksum":"3a9dda2b448310f4f90d5cf0967d4b668530fa7994d2a4d9cbfdfa62e35f76a3"},
{"name":"ruby-statistics","version":"3.0.0","platform":"ruby","checksum":"610301370346931cb701e3a8d3d3e28eb65681162cae6066c0c11abf20efdc81"},
{"name":"ruby2_keywords","version":"0.0.5","platform":"ruby","checksum":"ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef"},
{"name":"ruby_parser","version":"3.20.3","platform":"ruby","checksum":"8d2289a695dc81ffddcdd5a56e80c9a109806bc0d0b1239a1c852b0c71251c49"},
{"name":"rubyntlm","version":"0.6.3","platform":"ruby","checksum":"5b321456dba3130351f7451f8669f1afa83a0d26fd63cdec285b7b88e667102d"},
{"name":"rubypants","version":"0.2.0","platform":"ruby","checksum":"f07e38eac793655a0323fe91946081052341b9e69807026fcf102346589eedee"},
{"name":"rubyzip","version":"2.3.2","platform":"ruby","checksum":"3f57e3935dc2255c414484fbf8d673b4909d8a6a57007ed754dde39342d2373f"},
@ -575,6 +579,7 @@
{"name":"sentry-ruby","version":"5.8.0","platform":"ruby","checksum":"caeb121433be379fb94e991a45265a287b13a9a9083e7264f539752369d37110"},
{"name":"sentry-sidekiq","version":"5.8.0","platform":"ruby","checksum":"90d1123d16a9fc5fd99dbad190b766dd189eaf9e2baddad641f1334e1877c779"},
{"name":"set","version":"1.0.2","platform":"ruby","checksum":"02ffa4de1f2621495e05b72326040dd014d7abbcb02fea698bc600a389992c02"},
{"name":"sexp_processor","version":"4.17.0","platform":"ruby","checksum":"4daa4874ce1838cd801c65e66ed5d4f140024404a3de7482c36d4ef2604dff6f"},
{"name":"shellany","version":"0.0.1","platform":"ruby","checksum":"0e127a9132698766d7e752e82cdac8250b6adbd09e6c0a7fbbb6f61964fedee7"},
{"name":"shoulda-matchers","version":"5.1.0","platform":"ruby","checksum":"a01d20589989e9653ab4a28c67d9db2b82bcf0a2496cf01d5e1a95a4aaaf5b07"},
{"name":"sidekiq","version":"6.5.12","platform":"ruby","checksum":"b4f93b2204c42220d0b526a7b8e0c49b5f9da82c1ce1a05d2baf1e8f744c197f"},

View File

@ -242,6 +242,9 @@ GEM
aliyun-sdk (0.8.0)
nokogiri (~> 1.6)
rest-client (~> 2.0)
amatch (0.4.1)
mize
tins (~> 1.0)
android_key_attestation (0.3.0)
apollo_upload_server (2.1.0)
actionpack (>= 4.2)
@ -692,8 +695,9 @@ GEM
omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
gitlab_quality-test_tooling (1.3.0)
gitlab_quality-test_tooling (1.4.0)
activesupport (>= 6.1, < 7.2)
amatch (~> 0.4.1)
gitlab (~> 4.19)
http (~> 5.0)
nokogiri (~> 1.10)
@ -1032,6 +1036,8 @@ GEM
mixlib-log (3.0.9)
mixlib-shellout (3.2.5)
chef-utils
mize (0.4.1)
protocol (~> 2.0)
msgpack (1.5.4)
multi_json (1.14.1)
multi_xml (0.6.0)
@ -1222,6 +1228,8 @@ GEM
unparser
prometheus-client-mmap (0.28.1)
rb_sys (~> 0.9)
protocol (2.0.0)
ruby_parser (~> 3.0)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
@ -1436,6 +1444,8 @@ GEM
rexml
ruby-statistics (3.0.0)
ruby2_keywords (0.0.5)
ruby_parser (3.20.3)
sexp_processor (~> 4.16)
rubyntlm (0.6.3)
rubypants (0.2.0)
rubyzip (2.3.2)
@ -1480,6 +1490,7 @@ GEM
sentry-ruby (~> 5.8.0)
sidekiq (>= 3.0)
set (1.0.2)
sexp_processor (4.17.0)
shellany (0.0.1)
shoulda-matchers (5.1.0)
activesupport (>= 5.2.0)
@ -1837,7 +1848,7 @@ DEPENDENCIES
gitlab-utils!
gitlab_chronic_duration (~> 0.12)
gitlab_omniauth-ldap (~> 2.2.0)
gitlab_quality-test_tooling (~> 1.3.0)
gitlab_quality-test_tooling (~> 1.4.0)
gon (~> 6.4.0)
google-apis-androidpublisher_v3 (~> 0.34.0)
google-apis-cloudbilling_v1 (~> 0.21.0)

View File

@ -84,7 +84,7 @@ export default {
data-testid="issue-sticky-header"
>
<div
class="issue-sticky-header-text gl-display-flex gl-align-items-center gl-gap-2 gl-mx-auto gl-px-5"
class="issue-sticky-header-text gl-display-flex gl-align-items-center gl-gap-2 gl-mx-auto"
>
<gl-badge :variant="statusVariant">
<gl-icon :name="statusIcon" />

View File

@ -122,8 +122,8 @@ export default {
:class="{ 'gl-visibility-hidden': !isStickyHeaderVisible }"
>
<div
class="issue-sticky-header-text gl-display-flex gl-flex-direction-column gl-align-items-center gl-mx-auto gl-px-5 gl-w-full"
:class="{ 'gl-max-w-container-xl': !isFluidLayout }"
class="issue-sticky-header-text gl-display-flex gl-flex-direction-column gl-align-items-center gl-mx-auto gl-w-full"
:class="{ 'container-limited': !isFluidLayout }"
>
<div class="gl-w-full gl-display-flex gl-align-items-baseline">
<status-badge

View File

@ -2,9 +2,9 @@ import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export const CREATE_EXPERIMENT_HELP_PATH = helpPagePath(
'user/project/ml/experiment_tracking/index.md',
'user/project/ml/experiment_tracking/index',
{
anchor: 'tracking-new-experiments-and-trials',
anchor: 'track-new-experiments-and-candidates',
},
);

View File

@ -1,5 +1,4 @@
import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export const METRIC_KEY_PREFIX = 'metric.';
export const LIST_KEY_CREATED_AT = 'created_at';
@ -13,9 +12,3 @@ export const BASE_SORT_FIELDS = Object.freeze([
label: s__('MlExperimentTracking|Created at'),
},
]);
export const CREATE_CANDIDATE_HELP_PATH = helpPagePath(
'user/project/ml/experiment_tracking/index.md',
{
anchor: 'tracking-new-experiments-and-trials',
},
);

View File

@ -10,12 +10,8 @@ import KeysetPagination from '~/vue_shared/components/incubation/pagination.vue'
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
import {
LIST_KEY_CREATED_AT,
BASE_SORT_FIELDS,
METRIC_KEY_PREFIX,
CREATE_CANDIDATE_HELP_PATH,
} from './constants';
import { CREATE_EXPERIMENT_HELP_PATH as CREATE_CANDIDATE_HELP_PATH } from '../index/constants';
import { LIST_KEY_CREATED_AT, BASE_SORT_FIELDS, METRIC_KEY_PREFIX } from './constants';
import * as translations from './translations';
export default {

View File

@ -187,7 +187,7 @@ export default {
:roles="pushAccessLevels.roles"
:users="pushAccessLevels.users"
:groups="pushAccessLevels.groups"
data-qa-selector="allowed_to_push_content"
data-testid="allowed-to-push-content"
/>
<!-- Allowed to merge -->
@ -198,7 +198,7 @@ export default {
:roles="mergeAccessLevels.roles"
:users="mergeAccessLevels.users"
:groups="mergeAccessLevels.groups"
data-qa-selector="allowed_to_merge_content"
data-testid="allowed-to-merge-content"
/>
<!-- Force push -->

View File

@ -105,7 +105,6 @@ export default {
v-for="(item, index) in accessLevels"
:key="index"
data-testid="access-level"
data-qa-selector="access_level_content"
:data-qa-role="item.accessLevelDescription"
>
<span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span>

View File

@ -79,9 +79,7 @@ export default {
class="issue-sticky-header gl-fixed gl-z-index-3 gl-bg-white gl-border-1 gl-border-b-solid gl-border-b-gray-100 gl-py-3"
data-testid="header"
>
<div
class="issue-sticky-header-text gl-display-flex gl-align-items-baseline gl-mx-auto gl-px-5"
>
<div class="issue-sticky-header-text gl-display-flex gl-align-items-baseline gl-mx-auto">
<gl-badge
class="gl-white-space-nowrap gl-mr-3 gl-align-self-center"
:variant="badgeVariant"

View File

@ -901,7 +901,6 @@ table.code {
@media (max-width: map-get($grid-breakpoints, lg)-1) {
.diffs .files {
@include fixed-width-container;
flex-direction: column;
}

View File

@ -30,15 +30,6 @@
max-width: $max-width;
}
/**
* Mixin for fixed width container
*/
@mixin fixed-width-container {
max-width: $limited-layout-width - ($gl-padding * 2);
margin-left: auto;
margin-right: auto;
}
/*
* Base mixin for lists in GitLab
*/

View File

@ -2,7 +2,11 @@
width: 100%;
.container-fluid {
padding: 0 $gl-padding;
padding: 0 $container-margin;
@include media-breakpoint-up(xl) {
padding: 0 $container-margin-xl;
}
&.container-blank {
background: none;

View File

@ -468,8 +468,10 @@ $content-wrapper-padding: 100px;
$header-zindex: 1000;
$zindex-dropdown-menu: 300;
$ide-statusbar-height: 25px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$limited-layout-width: 1006px;
$fixed-layout-width: 1296px;
$container-margin: $gl-padding;
$container-margin-xl: $gl-padding-24;
$container-text-max-width: 540px;
$border-radius-default: 4px;
$border-radius-small: 2px;

View File

@ -1,34 +1,5 @@
@import 'mixins_and_variables_and_functions';
.limit-container-width {
.flash-container,
.detail-page-header,
.page-content-header,
.commit-box,
.info-well,
.commit-ci-menu,
.files-changed-inner,
.limited-header-width,
.limited-width-notes {
@include fixed-width-container;
}
.issuable-details {
.detail-page-description,
.mr-source-target,
.mr-state-widget,
.merge-manually {
@include fixed-width-container;
}
}
.merge-request-details {
.emoji-list-container {
@include fixed-width-container;
}
}
}
.issuable-details {
section {
.issuable-discussion {

View File

@ -988,7 +988,7 @@ $tabs-holder-z-index: 250;
.merge-request-tabs-container {
&.is-merge-request {
@include gl-mx-auto;
max-width: $fixed-layout-width - ($gl-padding * 2);
max-width: $fixed-layout-width - ($container-margin-xl * 2);
}
}
}
@ -1131,7 +1131,7 @@ $tabs-holder-z-index: 250;
.review-bar-content {
max-width: $limited-layout-width;
padding: 0 $gl-padding;
padding: 0 $container-margin;
width: 100%;
margin: 0 auto;
}

View File

@ -251,6 +251,14 @@ ul.related-merge-requests > li gl-emoji {
}
}
.issue-sticky-header-text {
padding: 0 $container-margin;
@include media-breakpoint-up(xl) {
padding: 0 $container-margin-xl;
}
}
.issuable-header-slide-enter-active,
.issuable-header-slide-leave-active {
@include gl-transition-medium;

View File

@ -54,6 +54,11 @@ module Ci
# if the setting is disabled any project is considered to be in scope.
return true unless current_project.ci_outbound_job_token_scope_enabled?
if !accessed_project.private? &&
Feature.enabled?(:restrict_ci_job_token_for_public_and_internal_projects, accessed_project)
return true
end
outbound_allowlist.includes?(accessed_project)
end

View File

@ -599,7 +599,7 @@ class Integration < ApplicationRecord
return if ::Gitlab::SilentMode.enabled?
return unless supported_events.include?(data[:object_kind])
Integrations::ExecuteWorker.perform_async(id, data)
Integrations::ExecuteWorker.perform_async(id, data.deep_stringify_keys)
end
# override if needed

View File

@ -1234,17 +1234,14 @@ class MergeRequest < ApplicationRecord
}
end
def mergeable?(
skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false, check_mergeability_retry_lease: false,
skip_draft_check: false, skip_rebase_check: false, skip_blocked_check: false)
return false unless mergeable_state?(
skip_ci_check: skip_ci_check,
skip_discussions_check: skip_discussions_check,
skip_draft_check: skip_draft_check,
skip_approved_check: skip_approved_check,
skip_blocked_check: skip_blocked_check
)
# mergeable_state_check_params allows a hash of merge checks to skip or not
# skip_ci_check
# skip_discussions_check
# skip_draft_check
# skip_approved_check
# skip_blocked_check
def mergeable?(check_mergeability_retry_lease: false, skip_rebase_check: false, **mergeable_state_check_params)
return false unless mergeable_state?(**mergeable_state_check_params)
check_mergeability(sync_retry_lease: check_mergeability_retry_lease)
mergeable_git_state?(skip_rebase_check: skip_rebase_check)
@ -1274,18 +1271,16 @@ class MergeRequest < ApplicationRecord
mergeable_state_checks + mergeable_git_state_checks
end
def mergeable_state?(
skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false,
skip_draft_check: false, skip_blocked_check: false)
# mergeable_state_check_params allows a hash of merge checks to skip or not
# skip_ci_check
# skip_discussions_check
# skip_draft_check
# skip_approved_check
# skip_blocked_check
def mergeable_state?(**mergeable_state_check_params)
additional_checks = execute_merge_checks(
self.class.mergeable_state_checks,
params: {
skip_ci_check: skip_ci_check,
skip_discussions_check: skip_discussions_check,
skip_approved_check: skip_approved_check,
skip_draft_check: skip_draft_check,
skip_blocked_check: skip_blocked_check
}
params: mergeable_state_check_params
)
additional_checks.success?
end

View File

@ -11,6 +11,8 @@ class PagesDomain < ApplicationRecord
MAX_CERTIFICATE_KEY_LENGTH = 8192
X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19
enum certificate_source: { user_provided: 0, gitlab_provided: 1 }, _prefix: :certificate
enum scope: { instance: 0, group: 1, project: 2 }, _prefix: :scope, _default: :project
enum usage: { pages: 0, serverless: 1 }, _prefix: :usage, _default: :pages
@ -122,15 +124,23 @@ class PagesDomain < ApplicationRecord
x509.check_private_key(pkey)
end
def has_intermediates?
def has_valid_intermediates?
return false unless x509
# self-signed certificates doesn't have the certificate chain
# self-signed certificates don't have the certificate chain
return true if x509.verify(x509.public_key)
store = OpenSSL::X509::Store.new
store.set_default_paths
store.verify_callback = ->(is_valid, store_ctx) {
# allow self signed certs, see https://gitlab.com/gitlab-org/gitlab/-/issues/356447
return true if store_ctx.error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
self.errors.add(:certificate, store_ctx.error_string) unless is_valid
is_valid
}
store.verify(x509, untrusted_ca_certs_bundle)
rescue OpenSSL::X509::StoreError
false
@ -260,9 +270,7 @@ class PagesDomain < ApplicationRecord
end
def validate_intermediates
unless has_intermediates?
self.errors.add(:certificate, 'misses intermediates')
end
self.errors.add(:certificate, 'misses intermediates') unless has_valid_intermediates?
end
def validate_pages_domain

View File

@ -1241,6 +1241,10 @@ class Repository
def get_patch_id(old_revision, new_revision)
raw_repository.get_patch_id(old_revision, new_revision)
rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository => e
# This is expected when there are no differences between the old_revision and the new_revision.
# It's not ideal, but is simpler to handle this here than making breaking changes to gitaly.
return if e.message.match?(/no difference between old and new revision./)
Gitlab::ErrorTracking.track_exception(
e,
project_id: project.id,

View File

@ -133,6 +133,29 @@ class ProjectPolicy < BasePolicy
!@user&.from_ci_job_token? || @user.ci_job_token_scope.accessible?(project)
end
desc "If the user is via CI job token and project container registry visibility allows access"
condition(:job_token_container_registry) { job_token_access_allowed_to?(:container_registry) }
desc "If the user is via CI job token and project package registry visibility allows access"
condition(:job_token_package_registry) { job_token_access_allowed_to?(:package_registry) }
desc "If the user is via CI job token and project ci/cd visibility allows access"
condition(:job_token_builds) { job_token_access_allowed_to?(:builds) }
desc "If the user is via CI job token and project releases visibility allows access"
condition(:job_token_releases) { job_token_access_allowed_to?(:releases) }
desc "If the user is via CI job token and project environment visibility allows access"
condition(:job_token_environments) { job_token_access_allowed_to?(:environments) }
desc "If the project is either public or internal"
condition(:public_or_internal) do
project.public? || project.internal?
end
with_scope :subject
condition(:restrict_job_token_enabled) { Feature.enabled?(:restrict_ci_job_token_for_public_and_internal_projects, @subject) }
with_scope :subject
condition(:forking_allowed) do
@subject.feature_available?(:forking, @user)
@ -680,8 +703,42 @@ class ProjectPolicy < BasePolicy
enable :read_project_for_iids
end
# If the project is private
rule { ~public_project & ~internal_access & ~project_allowed_for_job_token }.prevent_all
# If this project is public or internal we want to prevent all aside from a few public policies
rule { public_or_internal & ~project_allowed_for_job_token & restrict_job_token_enabled }.policy do
prevent :guest_access
prevent :public_access
prevent :public_user_access
prevent :reporter_access
prevent :developer_access
prevent :maintainer_access
prevent :owner_access
end
rule { public_or_internal & job_token_container_registry & restrict_job_token_enabled }.policy do
enable :build_read_container_image
enable :read_container_image
end
rule { public_or_internal & job_token_package_registry & restrict_job_token_enabled }.policy do
enable :read_package
enable :read_project
end
rule { public_or_internal & job_token_builds & restrict_job_token_enabled }.policy do
enable :read_commit_status # this is additionally needed to download artifacts
end
rule { public_or_internal & job_token_releases & restrict_job_token_enabled }.policy do
enable :read_release
end
rule { public_or_internal & job_token_environments & restrict_job_token_enabled }.policy do
enable :read_environment
end
rule { can?(:public_access) }.policy do
enable :read_package
enable :read_project
@ -1008,6 +1065,20 @@ class ProjectPolicy < BasePolicy
end
end
def job_token_access_allowed_to?(feature)
return false unless @user&.from_ci_job_token?
return false unless project.project_feature
case project.project_feature.access_level(feature)
when ProjectFeature::DISABLED
false
when ProjectFeature::PRIVATE
@user.ci_job_token_scope.accessible?(project)
else
true
end
end
def resource_access_token_feature_available?
true
end

View File

@ -61,15 +61,19 @@ module AutoMerge
merge_request.can_be_merged_by?(current_user) &&
merge_request.open? &&
!merge_request.broken? &&
(skip_draft_check(merge_request) || !merge_request.draft?) &&
(skip_discussions_check(merge_request) || merge_request.mergeable_discussions_state?) &&
(skip_blocked_check(merge_request) || !merge_request.merge_blocked_by_other_mrs?) &&
overrideable_available_for_checks(merge_request) &&
yield
end
end
private
def overrideable_available_for_checks(merge_request)
!merge_request.draft? &&
merge_request.mergeable_discussions_state? &&
!merge_request.merge_blocked_by_other_mrs?
end
# Overridden in child classes
def notify(merge_request)
end
@ -109,20 +113,5 @@ module AutoMerge
def track_exception(error, merge_request)
Gitlab::ErrorTracking.track_exception(error, merge_request_id: merge_request&.id)
end
# Will skip the draft check or not when checking if strategy is available
def skip_draft_check(merge_request)
false
end
# Will skip the blocked check or not when checking if strategy is available
def skip_blocked_check(merge_request)
false
end
# Will skip the discussions check or not when checking if strategy is available
def skip_discussions_check(merge_request)
false
end
end
end

View File

@ -110,10 +110,10 @@ class WebHookService
break log_recursion_blocked if recursion_blocked?
params = {
recursion_detection_request_uuid: Gitlab::WebHooks::RecursionDetection::UUID.instance.request_uuid
"recursion_detection_request_uuid" => Gitlab::WebHooks::RecursionDetection::UUID.instance.request_uuid
}.compact
WebHookWorker.perform_async(hook.id, data, hook_name, params)
WebHookWorker.perform_async(hook.id, data.deep_stringify_keys, hook_name, params)
end
end
@ -170,7 +170,7 @@ class WebHookService
def queue_log_execution_with_retry(log_data, category)
retried = false
begin
::WebHooks::LogExecutionWorker.perform_async(hook.id, log_data, category, uniqueness_token)
::WebHooks::LogExecutionWorker.perform_async(hook.id, log_data.deep_stringify_keys, category, uniqueness_token)
rescue Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError
raise if retried

View File

@ -0,0 +1,8 @@
---
name: restrict_ci_job_token_for_public_and_internal_projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135263
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/417172
milestone: '16.6'
type: development
group: group::pipeline security
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377723
milestone: '15.8'
type: development
group: group::code review
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class AddServiceAccessTokensExpirationApplicationSetting < Gitlab::Database::Migration[2.2]
milestone '16.6'
enable_lock_retries!
def change
add_column :application_settings, :service_access_tokens_expiration_enforced, :boolean, default: true, null: false
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class AddServiceAccessTokensExpirationNamespaceSetting < Gitlab::Database::Migration[2.2]
milestone '16.6'
enable_lock_retries!
def change
add_column :namespace_settings, :service_access_tokens_expiration_enforced, :boolean, default: true, null: false
end
end

View File

@ -0,0 +1 @@
2418c94e1e40f2765252f5c69dae7def898ed3c329fa5fc05d3b51ed812bb7c7

View File

@ -0,0 +1 @@
b8ecc7e8ead4cddc7dad712c46fdff0d559da1697bd5d16c1130f2c71272b890

View File

@ -11867,6 +11867,7 @@ CREATE TABLE application_settings (
encrypted_vertex_ai_access_token_iv bytea,
project_jobs_api_rate_limit integer DEFAULT 600 NOT NULL,
math_rendering_limits_enabled boolean DEFAULT true NOT NULL,
service_access_tokens_expiration_enforced boolean DEFAULT true 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)),
@ -19205,6 +19206,7 @@ CREATE TABLE namespace_settings (
experiment_features_enabled boolean DEFAULT false NOT NULL,
third_party_ai_features_enabled boolean DEFAULT true NOT NULL,
default_branch_protection_defaults jsonb DEFAULT '{}'::jsonb NOT NULL,
service_access_tokens_expiration_enforced boolean DEFAULT true NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))

View File

@ -179,14 +179,16 @@ nodes. In this example, we exclude all import-related jobs from a Sidekiq node.
sudo gitlab-ctl reconfigure
```
### Migrating from queue selectors to routing rules
## Migrating from queue selectors to routing rules
We recommend GitLab deployments add more Sidekiq processes listening to all queues, as in the
[Reference Architectures](../reference_architectures/index.md). For very large-scale deployments, we recommend
[routing rules](#routing-rules) instead of [queue selectors](#queue-selectors-deprecated). We use routing rules on GitLab.com as
it helps to lower the load on Redis.
To migrate from queue selectors to routing rules:
### Single node setup
To migrate from queue selectors to routing rules in a [single node setup](../reference_architectures/index.md#standalone-non-ha):
1. Open `/etc/gitlab/gitlab.rb`.
1. Set `sidekiq['queue_selector']` to `false`.
@ -213,9 +215,11 @@ NOTE:
It is important to run the Rake task immediately after reconfiguring GitLab.
After reconfiguring GitLab, existing jobs are not processed until the Rake task starts to migrate the jobs.
#### Migration example
The following example better illustrates the migration process above:
1. Check the following content of `/etc/gitlab/gitlab.rb`:
1. In `/etc/gitlab/gitlab.rb`, check the `urgency` queries in the `sidekiq['queue_groups']`. For example:
```ruby
sidekiq['routing_rules'] = []
@ -228,7 +232,7 @@ The following example better illustrates the migration process above:
]
```
1. Update `/etc/gitlab/gitlab.rb` to use routing rules:
1. Use these same `urgency` queries to update `/etc/gitlab/gitlab.rb` to use routing rules:
```ruby
sidekiq['min_concurrency'] = 20
@ -270,6 +274,31 @@ in a queue group entry is 1, while `min_concurrency` is set to `0`, and `max_con
concurrency is set to `2` instead. A concurrency of `2` might be too low in most cases, except for very highly-CPU
bound tasks.
### Multiple node setup
For a multiple node setup:
- Reconfigure all GitLab Rails and Sidekiq nodes with the same `sidekiq['routing_rules']` setting.
- Alternate between GitLab Rails and Sidekiq nodes as you update and reconfigure the nodes. This ensures the newly configured Sidekiq is ready to consume jobs from the new set of
queues during the migration. Otherwise, the new jobs hang until the end of the migration.
Consider the following example of three GitLab Rails nodes and two Sidekiq nodes. To migrate from queue selectors to routing rules:
1. In Sidekiq 1, follow all steps but one in [single node setup](#single-node-setup).
**Do not** run the Rake task to [migrate existing jobs](sidekiq_job_migration.md).
1. Configure the external load balancer to remove Rails 1 from accepting traffic. This step ensures Rails 1 is not serving any request while the Rails process is restarting. For more information, see [issue 428794](https://gitlab.com/gitlab-org/gitlab/-/issues/428794#note_1619505870).
1. In Rails 1, update `/etc/gitlab/gitlab.rb` to use the same `sidekiq['routing_rules']` setting as Sidekiq 1.
Only `sidekiq['routing_rules']` is required in Rails nodes.
1. Configure the external load balancer to register Rails 1 back.
1. Repeat steps 1 to 4 for Sidekiq 2 and Rails 2.
1. Repeat steps 2 to 4 for Rails 3.
1. If there are more Sidekiq nodes than Rails nodes, follow step 1 on the remaining Sidekiq nodes.
1. Run the Rake task to [migrate existing jobs](sidekiq_job_migration.md):
```shell
sudo gitlab-rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule gitlab:sidekiq:migrate_jobs:queued
```
<!--- end_remove -->
## Worker matching query

View File

@ -506,6 +506,22 @@ This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
### `Query.memberRole`
Finds a single custom role.
WARNING:
**Introduced** in 16.6.
This feature is an Experiment. It can be changed or removed at any time.
Returns [`MemberRole`](#memberrole).
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="querymemberroleid"></a>`id` | [`MemberRoleID`](#memberroleid) | Global ID of the member role to look up. |
### `Query.memberRolePermissions`
List of all customizable permissions.
@ -18106,7 +18122,6 @@ GPG signature for a signed commit.
| <a id="groupid"></a>`id` | [`ID!`](#id) | ID of the namespace. |
| <a id="groupistemporarystorageincreaseenabled"></a>`isTemporaryStorageIncreaseEnabled` | [`Boolean!`](#boolean) | Status of the temporary storage increase. |
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
| <a id="groupmemberroles"></a>`memberRoles` **{warning-solid}** | [`MemberRoleConnection`](#memberroleconnection) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Member roles available for the group. |
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |
| <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | Package settings for the namespace. |
@ -18724,6 +18739,26 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="grouplabelsonlygrouplabels"></a>`onlyGroupLabels` | [`Boolean`](#boolean) | Include only group level labels. |
| <a id="grouplabelssearchterm"></a>`searchTerm` | [`String`](#string) | Search term to find labels with. |
##### `Group.memberRoles`
Member roles available for the group.
WARNING:
**Introduced** in 16.5.
This feature is an Experiment. It can be changed or removed at any time.
Returns [`MemberRoleConnection`](#memberroleconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupmemberrolesid"></a>`id` | [`MemberRoleID`](#memberroleid) | Global ID of the member role to look up. |
##### `Group.mergeRequestViolations`
Compliance violations reported on merge requests merged within the group.
@ -22768,7 +22803,6 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectlanguages"></a>`languages` | [`[RepositoryLanguage!]`](#repositorylanguage) | Programming languages used in the project. |
| <a id="projectlastactivityat"></a>`lastActivityAt` | [`Time`](#time) | Timestamp of the project last activity. |
| <a id="projectlfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if the project has Large File Storage (LFS) enabled. |
| <a id="projectmemberroles"></a>`memberRoles` **{warning-solid}** | [`MemberRoleConnection`](#memberroleconnection) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Member roles available for the group. |
| <a id="projectmergecommittemplate"></a>`mergeCommitTemplate` | [`String`](#string) | Template used to create merge commit message in merge requests. |
| <a id="projectmergerequestsdisablecommittersapproval"></a>`mergeRequestsDisableCommittersApproval` | [`Boolean!`](#boolean) | Indicates that committers of the given merge request cannot approve. |
| <a id="projectmergerequestsenabled"></a>`mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge Requests are enabled for the current user. |
@ -23645,6 +23679,26 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectlabelsincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include labels from ancestor groups. |
| <a id="projectlabelssearchterm"></a>`searchTerm` | [`String`](#string) | Search term to find labels with. |
##### `Project.memberRoles`
Member roles available for the group.
WARNING:
**Introduced** in 16.5.
This feature is an Experiment. It can be changed or removed at any time.
Returns [`MemberRoleConnection`](#memberroleconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectmemberrolesid"></a>`id` | [`MemberRoleID`](#memberroleid) | Global ID of the member role to look up. |
##### `Project.mergeRequest`
A single merge request of the project.

View File

@ -43,7 +43,7 @@ Example response:
```json
[
{
"extern_uid": "4",
"extern_uid": "yrnZW46BrtBFqM7xDzE7dddd",
"user_id": 48
}
]
@ -67,14 +67,14 @@ Supported attributes:
Example request:
```shell
curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/saml/sydney_jones" --header "PRIVATE-TOKEN: <PRIVATE TOKEN>"
curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/saml/yrnZW46BrtBFqM7xDzE7dddd" --header "PRIVATE-TOKEN: <PRIVATE TOKEN>"
```
Example response:
```json
{
"extern_uid": "4",
"extern_uid": "yrnZW46BrtBFqM7xDzE7dddd",
"user_id": 48
}
```
@ -101,9 +101,9 @@ Supported attributes:
Example request:
```shell
curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/saml/sydney_jones" \
curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/saml/yrnZW46BrtBFqM7xDzE7dddd" \
--header "PRIVATE-TOKEN: <PRIVATE TOKEN>" \
--form "extern_uid=sydney_jones_new"
--form "extern_uid=be20d8dcc028677c931e04f387"
```
## Delete a single SAML identity
@ -124,7 +124,7 @@ Supported attributes:
Example request:
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/33/saml/sydney_jones"
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/33/saml/be20d8dcc028677c931e04f387"
```

View File

@ -53,7 +53,7 @@ Example response:
```json
[
{
"extern_uid": "4",
"extern_uid": "be20d8dcc028677c931e04f387",
"user_id": 48,
"active": true
}
@ -85,14 +85,14 @@ Supported attributes:
Example request:
```shell
curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/scim/sydney_jones" --header "PRIVATE-TOKEN: <PRIVATE TOKEN>"
curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/scim/be20d8dcc028677c931e04f387" --header "PRIVATE-TOKEN: <PRIVATE TOKEN>"
```
Example response:
```json
{
"extern_uid": "4",
"extern_uid": "be20d8dcc028677c931e04f387",
"user_id": 48,
"active": true
}
@ -122,9 +122,9 @@ Parameters:
Example request:
```shell
curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/scim/sydney_jones" \
curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/scim/be20d8dcc028677c931e04f387" \
--header "PRIVATE-TOKEN: <PRIVATE TOKEN>" \
--form "extern_uid=sydney_jones_new"
--form "extern_uid=yrnZW46BrtBFqM7xDzE7dddd"
```
## Delete a single SCIM identity
@ -145,7 +145,7 @@ Supported attributes:
Example request:
```shell
curl --request DELETE --header "Content-Type: application/json" --header "Authorization: Bearer <your_access_token>" "https://gitlab.example.com/api/v4/groups/33/scim/sydney_jones"
curl --request DELETE --header "Content-Type: application/json" --header "Authorization: Bearer <your_access_token>" "https://gitlab.example.com/api/v4/groups/33/scim/yrnZW46BrtBFqM7xDzE7dddd"
```

View File

@ -70,9 +70,7 @@ tries to steal tokens from other jobs.
You can control what projects a CI/CD job token can access to increase the
job token's security. A job token might give extra permissions that aren't necessary
to access specific private resources. The job token scope only controls access
to private projects. If an accessed project is public or internal, token scoping does
not apply.
to access specific private resources.
When enabled, and the job token is being used to access a different project:
@ -81,7 +79,7 @@ When enabled, and the job token is being used to access a different project:
- The accessed project must have the project attempting to access it [added to the allowlist](#add-a-project-to-the-job-token-scope-allowlist).
If a job token is leaked, it could potentially be used to access private data
to the job token's user. By limiting the job token access scope, private data cannot
to the job token's user. By limiting the job token access scope, project data cannot
be accessed unless projects are explicitly authorized.
There is a proposal to add more strategic control of the access permissions,
@ -101,8 +99,7 @@ their `CI_JOB_TOKEN`.
For example, project `A` can add project `B` to the allowlist. CI/CD jobs
in project `B` (the "allowed project") can now use their CI/CD job token to
authenticate API calls to access project `A`. If project `A` is public or internal,
the project can be accessed by project `B` without adding it to the allowlist.
authenticate API calls to access project `A`.
By default, the allowlist of any project only includes itself.
@ -110,6 +107,32 @@ It is a security risk to disable this feature, so project maintainers or owners
keep this setting enabled at all times. Add projects to the allowlist only when cross-project
access is needed.
### Limit job token scope for public or internal projects
Projects can use a job token to authenticate with public or internal projects for
the following actions without being added to the allowlist:
- Fetch artifacts
- Access the container registry
- Access the package registry
- Access releases, deployments, and environments
To limit access to these actions to only the projects on the allowlist, set the visibility
of each feature to be only accessible to project members:
Prerequisite:
- You must have the Maintainer role for the project.
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. Set the visibility to **Only project members** for the features you want to restrict access to.
- The ability to fetch artifacts is controlled by the CI/CD visibility setting.
1. Select **Save changes**.
Triggering pipelines and fetching Terraform plans is not affected by feature visibility.
### Disable the job token scope allowlist
> **Allow access to this project with a CI_JOB_TOKEN** setting [renamed to **Limit access _to_ this project**](https://gitlab.com/gitlab-org/gitlab/-/issues/411406) in GitLab 16.3.
@ -181,9 +204,7 @@ limited only by the user's access permissions.
For example, when the setting is enabled, jobs in a pipeline in project `A` have
a `CI_JOB_TOKEN` scope limited to project `A`. If the job needs to use the token
to make an API request to a private project `B`, then `B` must be added to the allowlist for `A`.
If project `B` is public or internal, you do not need to add
`B` to the allowlist to grant access.
to make an API request to project `B`, then `B` must be added to the allowlist for `A`.
### Configure the job token scope

View File

@ -343,7 +343,7 @@ Now you're ready to push changes from the community fork to the main GitLab repo
1. If you're happy with this merge request and want to start the review process, type
`@gitlab-bot ready` in a comment and then select **Comment**.
![GitLab bot ready comment](img/bot_ready.png)
![GitLab bot ready comment](img/bot_ready_v16_6.png)
Someone from GitLab will look at your request and let you know what the next steps are.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -372,7 +372,11 @@ end
### Unleash Proxy example
As of [Unleash Proxy](https://docs.getunleash.io/reference/unleash-proxy) version
0.2, the proxy is compatible with feature flags. To run a Docker container to
0.2, the proxy is compatible with feature flags.
You should use Unleash Proxy for production on GitLab.com. See the [performance note](#maximum-supported-clients-in-application-nodes) for details.
To run a Docker container to
connect to your project's feature flags, run the following command:
```shell
@ -418,10 +422,8 @@ Read [How it works](#how-it-works) section before diving into the details.
### Maximum supported clients in application nodes
GitLab accepts client requests as much as possible until it hits the [rate limiting](../security/rate_limits.md).
At the moment, the feature flag API falls into **Unauthenticated traffic (from a given IP address)**
in the [GitLab.com specific limits](../user/gitlab_com/index.md),
so it's **500 requests per minute**.
GitLab accepts as many client requests as possible until it hits the [rate limit](../security/rate_limits.md).
The feature flag API is considered **Unauthenticated traffic (from a given IP address)**. For GitLab.com, see the [GitLab.com specific limits](../user/gitlab_com/index.md).
The polling rate is configurable in SDKs. Provided that all clients are requesting from the same IP:
@ -429,7 +431,8 @@ The polling rate is configurable in SDKs. Provided that all clients are requesti
- Request once per 15 sec ... 125 clients can be supported.
For applications looking for more scalable solution, you should use [Unleash Proxy](#unleash-proxy-example).
This proxy server sits between the server and clients. It requests to the server as a behalf of the client groups,
On GitLab.com, you should use Unleash Proxy to reduce the chance of being rate limited across endpoints.
This proxy server sits between the server and clients. It makes requests to the server on behalf of the client groups,
so the number of outbound requests can be greatly reduced.
There is also an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/295472) to give more

View File

@ -30,7 +30,7 @@ a system alert notifies you of its successful addition.
The issue's description is automatically edited to include the Zoom link, and a button
appears right under the issue's title.
![Link Zoom Call in Issue](img/zoom-quickaction-button.png)
![Link Zoom Call in Issue](img/zoom_quickaction_button_v16_6.png)
You are only allowed to attach a single Zoom meeting to an issue. If you attempt
to add a second Zoom meeting using the `/zoom` quick action, it doesn't work. You

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -59,6 +59,10 @@ module Bitbucket
end
end
def default_branch
raw.dig('mainbranch', 'name')
end
def to_s
full_name
end

View File

@ -19,6 +19,7 @@ module Gitlab
validate_repository_size!
set_default_branch
update_clone_time
end
@ -76,6 +77,16 @@ module Gitlab
def validate_repository_size!
# Defined in EE
end
def set_default_branch
default_branch = client.repo(project.import_source).default_branch
project.change_head(default_branch) if default_branch
end
def client
Bitbucket::Client.new(project.import_data.credentials)
end
end
end
end

View File

@ -19,7 +19,6 @@ module Gitlab
validations do
validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
validates :key, alphanumeric: true
validates :input_default, alphanumeric: true, allow_nil: true
validates :input_description, alphanumeric: true, allow_nil: true
validates :input_regex, type: String, allow_nil: true
validates :input_type, allow_nil: true, allowed_values: Interpolation::Inputs.input_types

View File

@ -5596,6 +5596,9 @@ msgstr ""
msgid "Analytics|Event counts update hourly"
msgstr ""
msgid "Analytics|Events"
msgstr ""
msgid "Analytics|Failed to fetch data"
msgstr ""
@ -5644,6 +5647,9 @@ msgstr ""
msgid "Analytics|Previous month"
msgstr ""
msgid "Analytics|Projects"
msgstr ""
msgid "Analytics|Referer"
msgstr ""

View File

@ -6,23 +6,23 @@ module QA
module Settings
class BranchRulesDetails < Page::Base
view 'app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue' do
element :allowed_to_push_content
element :allowed_to_merge_content
element 'allowed-to-push-content'
element 'allowed-to-merge-content'
end
view 'app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue' do
element :access_level_content
element 'access-level'
end
def has_allowed_to_push?(role)
within_element(:allowed_to_push_content) do
has_element?(:access_level_content, role: role)
within_element('allowed-to-push-content') do
has_element?('access-level', role: role)
end
end
def has_allowed_to_merge?(role)
within_element(:allowed_to_merge_content) do
has_element?(:access_level_content, role: role)
within_element('allowed-to-merge-content') do
has_element?('access-level', role: role)
end
end
end

View File

@ -150,6 +150,87 @@ NVOFBkpdn627G190
end
end
trait :with_untrusted_root_ca_in_chain do
# This contains
# [Intermediate #2 (SHA-2)] 'CloudFlare Origin SSL Certificate Authority'
# [Intermediate #1 (SHA-2)] 'CloudFlare Origin Certificate'
certificate do
'-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
+AZxAeKCINT+b72x
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
NVOFBkpdn627G190
-----END CERTIFICATE-----'
end
key do
File.read(Rails.root.join('spec/fixtures/', 'origin_cert_key.pem'))
end
end
trait :with_trusted_expired_chain do
# This contains
# Let's Encrypt Authority X3

0
spec/fixtures/origin_cert_key.pem vendored Normal file
View File

View File

@ -2,7 +2,7 @@
require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::Repo do
RSpec.describe Bitbucket::Representation::Repo, feature_category: :importers do
describe '#has_wiki?' do
it { expect(described_class.new({ 'has_wiki' => false }).has_wiki?).to be_falsey }
it { expect(described_class.new({ 'has_wiki' => true }).has_wiki?).to be_truthy }
@ -42,6 +42,11 @@ RSpec.describe Bitbucket::Representation::Repo do
it { expect(described_class.new({ 'full_name' => 'ben/test' }).slug).to eq('test') }
end
describe '#default_branch' do
it { expect(described_class.new({ 'mainbranch' => { 'name' => 'master' } }).default_branch).to eq('master') }
it { expect(described_class.new({}).default_branch).to eq(nil) }
end
describe '#clone_url' do
it 'builds url' do
data = { 'links' => { 'clone' => [{ 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } }

View File

@ -7,6 +7,14 @@ RSpec.describe Gitlab::BitbucketImport::Importers::RepositoryImporter, feature_c
subject(:importer) { described_class.new(project) }
before do
allow_next_instance_of(Bitbucket::Client) do |client|
allow(client).to receive(:repo).and_return(Bitbucket::Representation::Repo.new(
{ 'mainbranch' => { 'name' => 'develop' } }
))
end
end
describe '#execute' do
context 'when repository is empty' do
it 'imports the repository' do
@ -17,6 +25,15 @@ RSpec.describe Gitlab::BitbucketImport::Importers::RepositoryImporter, feature_c
importer.execute
end
it 'sets the default branch' do
allow(project.repository).to receive(:import_repository)
allow(project.repository).to receive(:fetch_as_mirror)
expect(project).to receive(:change_head).with('develop')
importer.execute
end
end
context 'when repository is not empty' do

View File

@ -40,12 +40,24 @@ RSpec.describe Gitlab::Ci::Config::Header::Input, feature_category: :pipeline_co
end
end
context 'when has a default value' do
context 'when has a string default value' do
let(:input_hash) { { default: 'bar' } }
it_behaves_like 'a valid input'
end
context 'when has a numeric default value' do
let(:input_hash) { { default: 6.66 } }
it_behaves_like 'a valid input'
end
context 'when has a boolean default value' do
let(:input_hash) { { default: true } }
it_behaves_like 'a valid input'
end
context 'when has a description value' do
let(:input_hash) { { description: 'bar' } }

View File

@ -88,24 +88,6 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
end
end
RSpec.shared_examples 'enforces outbound scope only' do
include_context 'with accessible and inaccessible projects'
where(:accessed_project, :result) do
ref(:current_project) | true
ref(:inbound_allowlist_project) | false
ref(:unscoped_project1) | false
ref(:unscoped_project2) | false
ref(:outbound_allowlist_project) | true
ref(:inbound_accessible_project) | false
ref(:fully_accessible_project) | true
end
with_them do
it { is_expected.to eq(result) }
end
end
describe 'accessible?' do
subject { scope.accessible?(accessed_project) }
@ -121,6 +103,7 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
ref(:outbound_allowlist_project) | false
ref(:inbound_accessible_project) | false
ref(:fully_accessible_project) | true
ref(:unscoped_public_project) | false
end
with_them do
@ -147,6 +130,7 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
ref(:outbound_allowlist_project) | false
ref(:inbound_accessible_project) | true
ref(:fully_accessible_project) | true
ref(:unscoped_public_project) | false
end
with_them do
@ -160,7 +144,34 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
current_project.update!(ci_outbound_job_token_scope_enabled: true)
end
include_examples 'enforces outbound scope only'
include_context 'with accessible and inaccessible projects'
where(:accessed_project, :result) do
ref(:current_project) | true
ref(:inbound_allowlist_project) | false
ref(:unscoped_project1) | false
ref(:unscoped_project2) | false
ref(:outbound_allowlist_project) | true
ref(:inbound_accessible_project) | false
ref(:fully_accessible_project) | true
ref(:unscoped_public_project) | true
end
with_them do
it { is_expected.to eq(result) }
end
context "with FF restrict_ci_job_token_for_public_and_internal_projects disabled" do
before do
stub_feature_flags(restrict_ci_job_token_for_public_and_internal_projects: false)
end
let(:accessed_project) { unscoped_public_project }
it "restricts public and internal outbound projects not in allowlist" do
is_expected.to eq(false)
end
end
end
end
end

View File

@ -1327,6 +1327,7 @@ RSpec.describe Integration, feature_category: :integrations do
describe '#async_execute' do
let(:integration) { described_class.new(id: 123) }
let(:data) { { object_kind: 'build' } }
let(:serialized_data) { data.deep_stringify_keys }
let(:supported_events) { %w[push build] }
subject(:async_execute) { integration.async_execute(data) }
@ -1336,7 +1337,7 @@ RSpec.describe Integration, feature_category: :integrations do
end
it 'queues a Integrations::ExecuteWorker' do
expect(Integrations::ExecuteWorker).to receive(:perform_async).with(integration.id, data)
expect(Integrations::ExecuteWorker).to receive(:perform_async).with(integration.id, serialized_data)
async_execute
end

View File

@ -288,8 +288,8 @@ RSpec.describe PagesDomain, feature_category: :pages do
end
end
describe '#has_intermediates?' do
subject { domain.has_intermediates? }
describe '#has_valid_intermediates?' do
subject { domain.has_valid_intermediates? }
context 'for self signed' do
let(:domain) { build(:pages_domain) }
@ -312,6 +312,14 @@ RSpec.describe PagesDomain, feature_category: :pages do
it { is_expected.to be_truthy }
end
context 'for chain with unknown root CA' do
# In cases where users use an origin certificate the CA does not necessarily need to be in
# the trust store, eg. in the case of Cloudflare Origin Certs.
let(:domain) { build(:pages_domain, :with_untrusted_root_ca_in_chain) }
it { is_expected.to be_truthy }
end
end
describe '#expired?' do

View File

@ -3822,6 +3822,13 @@ RSpec.describe Repository, feature_category: :source_code_management do
it 'returns nil' do
expect(repository.get_patch_id('HEAD', 'HEAD')).to be_nil
end
it 'does not report the exception' do
expect(Gitlab::ErrorTracking)
.not_to receive(:track_exception)
repository.get_patch_id('HEAD', 'HEAD')
end
end
context 'when a Gitlab::Git::CommandError is raised' do
@ -3831,7 +3838,7 @@ RSpec.describe Repository, feature_category: :source_code_management do
end
it 'returns nil' do
expect(repository.get_patch_id('HEAD', 'HEAD')).to be_nil
expect(repository.get_patch_id('HEAD~', 'HEAD')).to be_nil
end
it 'reports the exception' do
@ -3840,11 +3847,11 @@ RSpec.describe Repository, feature_category: :source_code_management do
.with(
instance_of(Gitlab::Git::CommandError),
project_id: repository.project.id,
old_revision: 'HEAD',
old_revision: 'HEAD~',
new_revision: 'HEAD'
)
repository.get_patch_id('HEAD', 'HEAD')
repository.get_patch_id('HEAD~', 'HEAD')
end
end

View File

@ -2564,72 +2564,153 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
describe 'when user is authenticated via CI_JOB_TOKEN', :request_store do
using RSpec::Parameterized::TableSyntax
where(:project_visibility, :user_role, :external_user, :scope_project_type, :token_scope_enabled, :result) do
:private | :reporter | false | :same | true | true
:private | :reporter | false | :same | false | true
:private | :reporter | false | :different | true | false
:private | :reporter | false | :different | false | true
:private | :guest | false | :same | true | true
:private | :guest | false | :same | false | true
:private | :guest | false | :different | true | false
:private | :guest | false | :different | false | true
RSpec.shared_examples 'CI_JOB_TOKEN enforces the expected permissions' do
with_them do
let(:current_user) { public_send(user_role) }
let(:project) { public_send("#{project_visibility}_project") }
let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
:internal | :reporter | false | :same | true | true
:internal | :reporter | true | :same | true | true
:internal | :reporter | false | :same | false | true
:internal | :reporter | false | :different | true | true
:internal | :reporter | true | :different | true | false
:internal | :reporter | false | :different | false | true
:internal | :guest | false | :same | true | true
:internal | :guest | true | :same | true | true
:internal | :guest | false | :same | false | true
:internal | :guest | false | :different | true | true
:internal | :guest | true | :different | true | false
:internal | :guest | false | :different | false | true
let(:scope_project) do
if scope_project_type == :same
project
else
create(:project, :private)
end
end
:public | :reporter | false | :same | true | true
:public | :reporter | false | :same | false | true
:public | :reporter | false | :different | true | true
:public | :reporter | false | :different | false | true
:public | :guest | false | :same | true | true
:public | :guest | false | :same | false | true
:public | :guest | false | :different | true | true
:public | :guest | false | :different | false | true
before do
current_user.set_ci_job_token_scope!(job)
current_user.external = external_user
project.update!(
ci_outbound_job_token_scope_enabled: token_scope_enabled,
ci_inbound_job_token_scope_enabled: token_scope_enabled
)
scope_project.update!(
ci_outbound_job_token_scope_enabled: token_scope_enabled,
ci_inbound_job_token_scope_enabled: token_scope_enabled
)
end
it "enforces the expected permissions" do
if result
is_expected.to be_allowed("#{user_role}_access".to_sym)
else
is_expected.to be_disallowed("#{user_role}_access".to_sym)
end
end
end
end
with_them do
let(:current_user) { public_send(user_role) }
let(:project) { public_send("#{project_visibility}_project") }
let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
# Remove project_visibility on FF restrict_ci_job_token_for_public_and_internal_projects cleanup
where(:project_visibility, :user_role, :external_user, :scope_project_type, :token_scope_enabled, :result) do
:public | :reporter | false | :same | true | true
:public | :reporter | true | :same | true | true
:public | :reporter | false | :same | false | true
:public | :reporter | false | :different | true | false
:public | :reporter | true | :different | true | false
:public | :reporter | false | :different | false | true
:public | :guest | false | :same | true | true
:public | :guest | true | :same | true | true
:public | :guest | false | :same | false | true
:public | :guest | false | :different | true | false
:public | :guest | true | :different | true | false
:public | :guest | false | :different | false | true
end
let(:scope_project) do
if scope_project_type == :same
project
else
create(:project, :private)
end
include_examples "CI_JOB_TOKEN enforces the expected permissions"
context "when the project is public or internal and not on the allowlist" do
where(:feature, :permissions) do
:container_registry | [:build_read_container_image, :read_container_image]
:package_registry | [:read_package, :read_project]
:builds | [:read_commit_status]
:releases | [:read_release]
:environments | [:read_environment]
end
with_them do
let(:current_user) { developer }
let(:project) { public_project }
let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
let_it_be(:scope_project) { create(:project, :private) }
before do
current_user.set_ci_job_token_scope!(job)
scope_project.update!(ci_inbound_job_token_scope_enabled: true)
end
it 'allows the permissions based on the feature access level' do
project.project_feature.update!("#{feature}_access_level": ProjectFeature::ENABLED)
permissions.each { |p| expect_allowed(p) }
end
it 'disallows the permissions if feature access level is restricted' do
project.project_feature.update!("#{feature}_access_level": ProjectFeature::PRIVATE)
permissions.each { |p| expect_disallowed(p) }
end
it 'disallows the permissions if feature access level is disabled' do
project.project_feature.update!("#{feature}_access_level": ProjectFeature::DISABLED)
permissions.each { |p| expect_disallowed(p) }
end
context "with restrict_ci_job_token_for_public_and_internal_projects disabled" do
before do
stub_feature_flags(restrict_ci_job_token_for_public_and_internal_projects: false)
end
it 'allows all permissions for private' do
project.project_feature.update!("#{feature}_access_level": ProjectFeature::PRIVATE)
permissions.each { |p| expect_allowed(p) }
end
end
end
end
context "with FF restrict_ci_job_token_for_public_and_internal_projects disabled" do
before do
current_user.set_ci_job_token_scope!(job)
current_user.external = external_user
project.update!(
ci_outbound_job_token_scope_enabled: token_scope_enabled,
ci_inbound_job_token_scope_enabled: token_scope_enabled
)
scope_project.update!(
ci_outbound_job_token_scope_enabled: token_scope_enabled,
ci_inbound_job_token_scope_enabled: token_scope_enabled
)
stub_feature_flags(restrict_ci_job_token_for_public_and_internal_projects: false)
end
it "enforces the expected permissions" do
if result
is_expected.to be_allowed("#{user_role}_access".to_sym)
else
is_expected.to be_disallowed("#{user_role}_access".to_sym)
end
where(:project_visibility, :user_role, :external_user, :scope_project_type, :token_scope_enabled, :result) do
:private | :reporter | false | :same | true | true
:private | :reporter | false | :same | false | true
:private | :reporter | false | :different | true | false
:private | :reporter | false | :different | false | true
:private | :guest | false | :same | true | true
:private | :guest | false | :same | false | true
:private | :guest | false | :different | true | false
:private | :guest | false | :different | false | true
:internal | :reporter | false | :same | true | true
:internal | :reporter | true | :same | true | true
:internal | :reporter | false | :same | false | true
:internal | :reporter | false | :different | true | true
:internal | :reporter | true | :different | true | false
:internal | :reporter | false | :different | false | true
:internal | :guest | false | :same | true | true
:internal | :guest | true | :same | true | true
:internal | :guest | false | :same | false | true
:internal | :guest | false | :different | true | true
:internal | :guest | true | :different | true | false
:internal | :guest | false | :different | false | true
:public | :reporter | false | :same | true | true
:public | :reporter | false | :same | false | true
:public | :reporter | false | :different | true | true
:public | :reporter | false | :different | false | true
:public | :guest | false | :same | true | true
:public | :guest | false | :same | false | true
:public | :guest | false | :different | true | true
:public | :guest | false | :different | false | true
end
include_examples "CI_JOB_TOKEN enforces the expected permissions"
end
end

View File

@ -196,7 +196,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
context 'with a job token for a different user' do
let_it_be(:other_user) { create(:user) }
let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) }
let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) }
let(:headers) { build_token_auth_header(other_job.token) }
@ -245,7 +245,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
context 'with a job token for a different user' do
let_it_be(:other_user) { create(:user) }
let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) }
let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) }
let(:headers) { build_token_auth_header(other_job.token) }

View File

@ -664,8 +664,7 @@ RSpec.describe 'Git LFS API and storage', feature_category: :source_code_managem
context 'tries to push to other project' do
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
# I'm not sure what this tests that is different from the previous test
it_behaves_like 'LFS http 403 response'
it_behaves_like 'LFS http 404 response'
end
end
@ -1185,8 +1184,7 @@ RSpec.describe 'Git LFS API and storage', feature_category: :source_code_managem
context 'tries to push to other project' do
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
# I'm not sure what this tests that is different from the previous test
it_behaves_like 'LFS http 403 response'
it_behaves_like 'LFS http 404 response'
end
end

View File

@ -309,26 +309,18 @@ RSpec.describe AutoMerge::BaseService, feature_category: :code_review_workflow d
let(:merge_request) { create(:merge_request) }
where(:can_be_merged, :open, :broken, :discussions, :blocked, :draft, :skip_draft, :skip_blocked,
:skip_discussions, :result) do
true | true | false | true | false | false | false | false | false | true
true | true | false | true | false | false | true | true | false | true
true | true | false | true | false | true | true | false | false | true
true | true | false | true | true | false | false | true | false | true
true | true | false | false | false | false | false | false | true | true
true | true | false | true | false | true | false | false | false | false
false | true | false | true | false | false | false | false | false | false
true | false | false | true | false | false | false | false | false | false
true | true | true | true | false | false | false | false | false | false
true | true | false | false | false | false | false | false | false | false
true | true | false | true | true | false | false | false | false | false
where(:can_be_merged, :open, :broken, :discussions, :blocked, :draft, :result) do
true | true | false | true | false | false | true
false | true | false | true | false | false | false
true | false | false | true | false | false | false
true | true | true | true | false | false | false
true | true | false | false | false | false | false
true | true | false | true | true | false | false
true | true | false | true | false | true | false
end
with_them do
before do
allow(service).to receive(:skip_draft_check).and_return(skip_draft)
allow(service).to receive(:skip_blocked_check).and_return(skip_blocked)
allow(service).to receive(:skip_discussions_check).and_return(skip_discussions)
allow(merge_request).to receive(:can_be_merged_by?).and_return(can_be_merged)
allow(merge_request).to receive(:open?).and_return(open)
allow(merge_request).to receive(:broken?).and_return(broken)

View File

@ -13,6 +13,8 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
{ before: 'oldrev', after: 'newrev', ref: 'ref' }
end
let(:serialized_data) { data.deep_stringify_keys }
let(:service_instance) { described_class.new(project_hook, data, :push_hooks) }
describe '#initialize' do
@ -426,7 +428,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data),
hash_including(default_log_data.deep_stringify_keys),
:ok,
nil
)
@ -456,7 +458,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
default_log_data.merge(
response_body: 'Bad request',
response_status: 400
)
).deep_stringify_keys
),
:failed,
nil
@ -480,7 +482,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
response_body: '',
response_status: 'internal error',
internal_error_message: 'Some HTTP Post error'
)
).deep_stringify_keys
),
:error,
nil
@ -499,7 +501,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data.merge(response_body: '')),
hash_including(default_log_data.merge(response_body: '').deep_stringify_keys),
:ok,
nil
)
@ -520,7 +522,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data.merge(response_body: stripped_body)),
hash_including(default_log_data.merge(response_body: stripped_body).deep_stringify_keys),
:ok,
nil
)
@ -553,7 +555,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data.merge(response_headers: expected_response_headers)),
hash_including(default_log_data.merge(response_headers: expected_response_headers).deep_stringify_keys),
:ok,
nil
)
@ -578,7 +580,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data.merge(response_headers: expected_response_headers)),
hash_including(default_log_data.merge(response_headers: expected_response_headers).deep_stringify_keys),
:ok,
nil
)
@ -596,7 +598,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data),
hash_including(default_log_data.deep_stringify_keys),
:ok,
nil
)
@ -607,7 +609,9 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
hash_including(default_log_data.merge(request_data: WebHookLog::OVERSIZE_REQUEST_DATA)),
hash_including(default_log_data.merge(
request_data: WebHookLog::OVERSIZE_REQUEST_DATA
).deep_stringify_keys),
:ok,
nil
)
@ -636,7 +640,9 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state,
describe '#async_execute' do
def expect_to_perform_worker(hook)
expect(WebHookWorker).to receive(:perform_async).with(hook.id, data, 'push_hooks', an_instance_of(Hash))
expect(WebHookWorker).to receive(:perform_async).with(
hook.id, serialized_data, 'push_hooks', an_instance_of(Hash)
)
end
def expect_to_rate_limit(hook, threshold:, throttled: false)

View File

@ -16,12 +16,14 @@ end
RSpec.shared_context 'with inaccessible projects' do
let_it_be(:inbound_allowlist_project) { create_project_in_allowlist(source_project, direction: :inbound) }
include_context 'with unscoped projects'
end
RSpec.shared_context 'with unscoped projects' do
let_it_be(:unscoped_project1) { create(:project) }
let_it_be(:unscoped_project2) { create(:project) }
let_it_be(:unscoped_public_project) { create(:project, :public) }
let_it_be(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project1) }
end

View File

@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'forwardable'
require_relative 'suggestor'
module Tooling
@ -14,11 +13,8 @@ module Tooling
#
# @see Suggestor
class Suggestion
extend Forwardable
include ::Tooling::Danger::Suggestor
def_delegators :@context, :helper, :project_helper, :markdown
attr_reader :filename
def initialize(filename, context:)
@ -34,6 +30,22 @@ module Tooling
comment_text: self.class::SUGGESTION
)
end
private
def helper(...)
# Previously, we were using `forwardable` but it emitted a mysterious warning:
# forwarding to private method Danger::Rubocop#helper
@context.helper(...)
end
def project_helper(...)
@context.project_helper(...)
end
def markdown(...)
@context.markdown(...)
end
end
end
end