Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9590b41091
commit
aa01c59edf
|
|
@ -1 +1 @@
|
|||
c467a18d6d952c904fa033df2ea5b92aba98e8dc
|
||||
b54405cd2d14a8a25aa2a5a464008ec9421b7aa6
|
||||
|
|
|
|||
|
|
@ -224,13 +224,13 @@
|
|||
{"name":"gitlab-dangerfiles","version":"4.8.1","platform":"ruby","checksum":"bbad321c9638152a643d27a20b35ba1e2d8eddcc6bdfc4493d7b96e816ecf300"},
|
||||
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
|
||||
{"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"aarch64-linux-gnu","checksum":"bb954cb5dc995f4720edb84caba2112aa54e3d48c18fc1cb552f4ea994a9d518"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"aarch64-linux-musl","checksum":"53cc1b44658860dae476c27ea2e78a36d5e8d39c51003d98267821685665162b"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"arm64-darwin","checksum":"ea05243121cc05ae41fce162be2fd0b86be06b6edc122ac0688cbb0e45341c97"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"ruby","checksum":"82cf8ffc22092cf1e17eb28a1dae8b63a4a141acc1b2864a0c11efc544b53c0f"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-darwin","checksum":"ec6775054481b3e07a97d4be945fe41d043f89dc1fa1d95cdfc6a70b439ea0e4"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-linux-gnu","checksum":"ebcccc617db4f669cd2de900e6d31fae5de67acedeb178d242063e338cb57050"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-linux-musl","checksum":"77cf356f2a2fc496ec17206d68a6a1fe4f4d680bc1aac2206c32ee5393611a15"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"aarch64-linux-gnu","checksum":"67a2e7d2cd1208d22abb9c162e88aa725f0fa31ea7fa8a6f56481370a5d1ef2d"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"aarch64-linux-musl","checksum":"c4b8ce9061238f0cebdaeeceb64bf2e6f1d72f4bfe72b11ac191f45dce12e302"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"arm64-darwin","checksum":"fe71765e04305f5a34647b3338e5101f92547f627a76ddedab35631f98907420"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"ruby","checksum":"960e481c037bbe319ec73792a3fc0fa024a9c11ab16786f24591417a255514a1"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-darwin","checksum":"1434de2be23464ac379e30a125213ce00e6c907f47362b50a2d50488bc1e73d2"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-linux-gnu","checksum":"59c5a0c577cb9301253bce6c9c0e2b9585110a34c46197cedc16e1142fb2feaf"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-linux-musl","checksum":"298757eb48905451285d8c2ef4ecbb1691ed2b30f522998bc65474be340def8a"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.9.1","platform":"ruby","checksum":"fd480c1669c741ceab8d5f86b7e5e32b71f4f25af8b523725382dae425aaa958"},
|
||||
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
|
||||
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},
|
||||
|
|
|
|||
|
|
@ -750,7 +750,7 @@ GEM
|
|||
mime-types
|
||||
net-http-persistent (~> 4.0)
|
||||
nokogiri (~> 1, >= 1.10.8)
|
||||
gitlab-glfm-markdown (0.0.27)
|
||||
gitlab-glfm-markdown (0.0.28)
|
||||
rb_sys (~> 0.9.109)
|
||||
gitlab-kas-grpc (17.9.1)
|
||||
grpc (~> 1.0)
|
||||
|
|
|
|||
|
|
@ -224,13 +224,13 @@
|
|||
{"name":"gitlab-dangerfiles","version":"4.8.1","platform":"ruby","checksum":"bbad321c9638152a643d27a20b35ba1e2d8eddcc6bdfc4493d7b96e816ecf300"},
|
||||
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
|
||||
{"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"aarch64-linux-gnu","checksum":"bb954cb5dc995f4720edb84caba2112aa54e3d48c18fc1cb552f4ea994a9d518"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"aarch64-linux-musl","checksum":"53cc1b44658860dae476c27ea2e78a36d5e8d39c51003d98267821685665162b"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"arm64-darwin","checksum":"ea05243121cc05ae41fce162be2fd0b86be06b6edc122ac0688cbb0e45341c97"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"ruby","checksum":"82cf8ffc22092cf1e17eb28a1dae8b63a4a141acc1b2864a0c11efc544b53c0f"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-darwin","checksum":"ec6775054481b3e07a97d4be945fe41d043f89dc1fa1d95cdfc6a70b439ea0e4"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-linux-gnu","checksum":"ebcccc617db4f669cd2de900e6d31fae5de67acedeb178d242063e338cb57050"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.27","platform":"x86_64-linux-musl","checksum":"77cf356f2a2fc496ec17206d68a6a1fe4f4d680bc1aac2206c32ee5393611a15"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"aarch64-linux-gnu","checksum":"67a2e7d2cd1208d22abb9c162e88aa725f0fa31ea7fa8a6f56481370a5d1ef2d"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"aarch64-linux-musl","checksum":"c4b8ce9061238f0cebdaeeceb64bf2e6f1d72f4bfe72b11ac191f45dce12e302"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"arm64-darwin","checksum":"fe71765e04305f5a34647b3338e5101f92547f627a76ddedab35631f98907420"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"ruby","checksum":"960e481c037bbe319ec73792a3fc0fa024a9c11ab16786f24591417a255514a1"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-darwin","checksum":"1434de2be23464ac379e30a125213ce00e6c907f47362b50a2d50488bc1e73d2"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-linux-gnu","checksum":"59c5a0c577cb9301253bce6c9c0e2b9585110a34c46197cedc16e1142fb2feaf"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.28","platform":"x86_64-linux-musl","checksum":"298757eb48905451285d8c2ef4ecbb1691ed2b30f522998bc65474be340def8a"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.9.1","platform":"ruby","checksum":"fd480c1669c741ceab8d5f86b7e5e32b71f4f25af8b523725382dae425aaa958"},
|
||||
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
|
||||
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},
|
||||
|
|
|
|||
|
|
@ -762,7 +762,7 @@ GEM
|
|||
mime-types
|
||||
net-http-persistent (~> 4.0)
|
||||
nokogiri (~> 1, >= 1.10.8)
|
||||
gitlab-glfm-markdown (0.0.27)
|
||||
gitlab-glfm-markdown (0.0.28)
|
||||
rb_sys (~> 0.9.109)
|
||||
gitlab-kas-grpc (17.9.1)
|
||||
grpc (~> 1.0)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export default {
|
|||
i18n: {
|
||||
deleteLabel: __('Delete'),
|
||||
editLabel: __('Edit'),
|
||||
editAriaLabel: (name) => sprintf(__('Edit %{name}'), { name }),
|
||||
toggleLabel: __('Feature flag status'),
|
||||
},
|
||||
components: {
|
||||
|
|
@ -207,7 +208,7 @@ export default {
|
|||
data-testid="feature-flag-edit-button"
|
||||
class="gl-flex-grow"
|
||||
icon="pencil"
|
||||
:aria-label="$options.i18n.editLabel"
|
||||
:aria-label="$options.i18n.editAriaLabel(item.name)"
|
||||
:href="item.edit_path"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -234,7 +235,7 @@ export default {
|
|||
data-testid="feature-flag-edit-button"
|
||||
class="gl-flex-grow"
|
||||
icon="pencil"
|
||||
:aria-label="$options.i18n.editLabel"
|
||||
:aria-label="$options.i18n.editAriaLabel(item.name)"
|
||||
:href="item.edit_path"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ module Ci
|
|||
scope :for_project, ->(accessed_project) { where(accessed_project: accessed_project) }
|
||||
scope :preload_origin_project, -> { includes(origin_project: :route) }
|
||||
|
||||
attribute :job_token_policies, ::Gitlab::Database::Type::SymbolizedJsonb.new
|
||||
validates :job_token_policies, json_schema: {
|
||||
filename: 'ci_job_token_authorizations_policies', detail_errors: true
|
||||
}
|
||||
|
||||
# Record in SafeRequestStore a cross-project access attempt
|
||||
def self.capture(origin_project:, accessed_project:)
|
||||
label = origin_project == accessed_project ? 'same-project' : 'cross-project'
|
||||
|
|
@ -43,9 +48,11 @@ module Ci
|
|||
# completes. We will do that in a middleware. This is because the policy
|
||||
# rule about job token scope may be satisfied but a subsequent rule in
|
||||
# the Declarative Policies may block the authorization.
|
||||
Gitlab::SafeRequestStore.fetch(REQUEST_CACHE_KEY) do
|
||||
{ accessed_project_id: accessed_project.id, origin_project_id: origin_project.id }
|
||||
end
|
||||
add_to_request_store_hash(accessed_project_id: accessed_project.id, origin_project_id: origin_project.id)
|
||||
end
|
||||
|
||||
def self.capture_job_token_policies(policies)
|
||||
add_to_request_store_hash(policies: policies)
|
||||
end
|
||||
|
||||
# Schedule logging of captured authorizations in a background worker.
|
||||
|
|
@ -57,18 +64,38 @@ module Ci
|
|||
return unless authorizations
|
||||
|
||||
accessed_project_id = authorizations[:accessed_project_id]
|
||||
origin_project_id = authorizations[:origin_project_id]
|
||||
return unless accessed_project_id && origin_project_id
|
||||
|
||||
Ci::JobToken::LogAuthorizationWorker # rubocop:disable CodeReuse/Worker -- This method is called from a middleware and it's better tested
|
||||
.perform_in(CAPTURE_DELAY, accessed_project_id, authorizations[:origin_project_id])
|
||||
.perform_in(CAPTURE_DELAY, accessed_project_id, origin_project_id)
|
||||
end
|
||||
|
||||
def self.log_captures!(accessed_project_id:, origin_project_id:)
|
||||
upsert({
|
||||
def self.log_captures!(accessed_project_id:, origin_project_id:, policies: [])
|
||||
current_time = Time.current
|
||||
attributes = {
|
||||
accessed_project_id: accessed_project_id,
|
||||
origin_project_id: origin_project_id,
|
||||
last_authorized_at: Time.current
|
||||
},
|
||||
unique_by: [:accessed_project_id, :origin_project_id],
|
||||
on_duplicate: :update)
|
||||
last_authorized_at: current_time
|
||||
}
|
||||
|
||||
transaction do
|
||||
if policies.present?
|
||||
auth_log = lock.find_by(
|
||||
accessed_project_id: accessed_project_id,
|
||||
origin_project_id: origin_project_id
|
||||
)
|
||||
policies = policies.index_with(current_time)
|
||||
attributes[:job_token_policies] = auth_log ? auth_log.job_token_policies.merge(policies) : policies
|
||||
end
|
||||
|
||||
upsert(attributes, unique_by: [:accessed_project_id, :origin_project_id], on_duplicate: :update)
|
||||
end
|
||||
end
|
||||
|
||||
def self.add_to_request_store_hash(hash)
|
||||
new_hash = captured_authorizations.present? ? captured_authorizations.merge(hash) : hash
|
||||
Gitlab::SafeRequestStore[REQUEST_CACHE_KEY] = new_hash
|
||||
end
|
||||
|
||||
def self.captured_authorizations
|
||||
|
|
|
|||
|
|
@ -36,9 +36,14 @@ module Ci
|
|||
|
||||
def policies_allowed?(accessed_project, policies)
|
||||
return true if self_referential?(accessed_project)
|
||||
return true unless accessed_project.ci_inbound_job_token_scope_enabled?
|
||||
return false unless inbound_accessible?(accessed_project)
|
||||
|
||||
# We capture policies even if the inbound scopes are disabled or the feature flag is disabled
|
||||
Ci::JobToken::Authorization.capture_job_token_policies(policies) if policies.present?
|
||||
|
||||
return true unless accessed_project.ci_inbound_job_token_scope_enabled?
|
||||
return true unless Feature.enabled?(:add_policies_to_ci_job_token, accessed_project)
|
||||
|
||||
policies_allowed_for_accessed_project?(accessed_project, policies)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Policies for CI job token authorization logs, captured with their last authorized time",
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "./ci_job_token_policies.json#/items"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,10 +12,11 @@ module Ci
|
|||
idempotent!
|
||||
deduplicate :until_executed, including_scheduled: true, if_deduplicated: :reschedule_once
|
||||
|
||||
def perform(accessed_project_id, origin_project_id)
|
||||
def perform(accessed_project_id, origin_project_id, policies = [])
|
||||
Ci::JobToken::Authorization.log_captures!(
|
||||
accessed_project_id: accessed_project_id,
|
||||
origin_project_id: origin_project_id
|
||||
origin_project_id: origin_project_id,
|
||||
policies: policies.map(&:to_sym)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
migration_job_name: BackfillOnboardingStatusSetupForCompany
|
||||
description: Moves data from user_preferences.setup_for_company to the new setup_for_company field in user_details.onboarding_status
|
||||
feature_category: onboarding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180602
|
||||
milestone: '17.10'
|
||||
queued_migration_version: 20250228155146
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPoliciesColumnToCiJobTokenAuthorizations < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.10'
|
||||
|
||||
def change
|
||||
add_column :ci_job_token_authorizations, :job_token_policies, :jsonb, default: {}, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueBackfillOnboardingStatusSetupForCompany < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.10'
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = "BackfillOnboardingStatusSetupForCompany"
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1000
|
||||
SUB_BATCH_SIZE = 100
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:user_details,
|
||||
:user_id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :user_details, :user_id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
885e811ba46d6fde0e865d44e89241ce7f777ec65887e59ce7507bf830de333c
|
||||
|
|
@ -0,0 +1 @@
|
|||
9ce991961668a1e306c2348cf338a353966c35f9c2f11b105380f0a51df7f4d4
|
||||
|
|
@ -10554,7 +10554,8 @@ CREATE TABLE ci_job_token_authorizations (
|
|||
id bigint NOT NULL,
|
||||
accessed_project_id bigint NOT NULL,
|
||||
origin_project_id bigint NOT NULL,
|
||||
last_authorized_at timestamp with time zone NOT NULL
|
||||
last_authorized_at timestamp with time zone NOT NULL,
|
||||
job_token_policies jsonb DEFAULT '{}'::jsonb NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_job_token_authorizations_id_seq
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ this provider also allows Crowd authentication for Git-over-https requests.
|
|||
|
||||
## Configure a new Crowd application
|
||||
|
||||
1. Choose 'Applications' in the top menu, then 'Add application'.
|
||||
1. Go through the 'Add application' steps, entering the appropriate details.
|
||||
The screenshot below shows an example configuration.
|
||||
|
||||

|
||||
1. On the top menu, select **Applications > Add application**.
|
||||
1. Go through the **Add application** steps, entering the appropriate details.
|
||||
1. When complete, select **Add application**.
|
||||
|
||||
## Configure GitLab
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
|
|
@ -499,24 +499,37 @@ Now that we have mitmproxy and Docker running, we can attempt to sign in and
|
|||
push a container image. You may need to run as root to do this. For example:
|
||||
|
||||
```shell
|
||||
docker login s3-testing.myregistry.com:5050
|
||||
docker push s3-testing.myregistry.com:5050/root/docker-test/docker-image
|
||||
docker login example.s3.amazonaws.com:5050
|
||||
docker push example.s3.amazonaws.com:5050/root/docker-test/docker-image
|
||||
```
|
||||
|
||||
In the example above, we see the following trace on the mitmproxy window:
|
||||
|
||||

|
||||
```plaintext
|
||||
PUT https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/uploads/(UUID)/(QUERYSTRING)
|
||||
← 201 text/plain [no content] 661ms
|
||||
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
|
||||
← 307 application/octet-stream [no content] 93ms
|
||||
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
|
||||
← 307 application/octet-stream [no content] 101ms
|
||||
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
|
||||
← 307 application/octet-stream [no content] 87ms
|
||||
HEAD https://amazonaws.example.com/docker/registry/vs/blobs/sha256/dd/(UUID)/data(QUERYSTRING)
|
||||
← 403 application/xml [no content] 80ms
|
||||
HEAD https://amazonaws.example.com/docker/registry/vs/blobs/sha256/dd/(UUID)/data(QUERYSTRING)
|
||||
← 403 application/xml [no content] 62ms
|
||||
```
|
||||
|
||||
The above image shows:
|
||||
This output shows:
|
||||
|
||||
- The initial PUT requests went through fine with a 201 status code.
|
||||
- The 201 redirected the client to the S3 bucket.
|
||||
- The HEAD request to the AWS bucket reported a 403 Unauthorized.
|
||||
- The initial PUT requests went through fine with a `201` status code.
|
||||
- The `201` redirected the client to the Amazon S3 bucket.
|
||||
- The HEAD request to the AWS bucket reported a `403 Unauthorized`.
|
||||
|
||||
What does this mean? This strongly suggests that the S3 user does not have the right
|
||||
[permissions to perform a HEAD request](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
|
||||
The solution: check the [IAM permissions again](https://distribution.github.io/distribution/storage-drivers/s3/).
|
||||
Once the right permissions were set, the error goes away.
|
||||
After the right permissions were set, the error went away.
|
||||
|
||||
## Missing `gitlab-registry.key` prevents container repository deletion
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 139 KiB |
|
|
@ -2,16 +2,14 @@
|
|||
stage: none
|
||||
group: unassigned
|
||||
info: 'See the Technical Writers assigned to Development Guidelines: https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-development-guidelines'
|
||||
description: Developer documentation about GitLab feature flags.
|
||||
title: Feature flags in the development of GitLab
|
||||
---
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
This document explains how to contribute to the development and operations of the GitLab product.
|
||||
If you want to use feature flags to show and hide functionality in your own applications,
|
||||
view [this feature flags information](../../operations/feature_flags.md) instead.
|
||||
|
||||
{{< /alert >}}
|
||||
This page explains how developers contribute to the development and operations of the GitLab product
|
||||
through feature flags. To create custom feature flags to show and hide features in your own applications,
|
||||
see [Create a feature flag](../../operations/feature_flags.md#create-a-feature-flag).
|
||||
A [complete list of feature flags](../../user/feature_flags.md) in GitLab is also available.
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
stage: Deploy
|
||||
group: Environments
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: Create and maintain a custom feature flag for your GitLab application.
|
||||
title: Feature flags
|
||||
---
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ You can toggle a feature on and off to subsets of users, helping you achieve Con
|
|||
Feature flags help reduce risk, allowing you to do controlled testing, and separate feature
|
||||
delivery from customer launch.
|
||||
|
||||
A [complete list of feature flags](../user/feature_flags.md) in GitLab is also available.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an example of feature flags in action, see [Eliminating risk with feature flags](https://www.youtube.com/watch?v=U9WqoK9froI).
|
||||
<!-- Video published on 2024-02-01 -->
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ You can enforce 2FA for all users in a group or subgroup.
|
|||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Maintainer or Owner role for the group.
|
||||
- You must have the Owner role for the group.
|
||||
|
||||
To enforce 2FA for a group:
|
||||
|
||||
|
|
|
|||
|
|
@ -246,9 +246,17 @@ The following operational features are not available:
|
|||
|
||||
### Feature flags
|
||||
|
||||
[Feature flags](../../administration/feature_flags.md) are not available for GitLab Dedicated.
|
||||
GitLab uses [feature flags](../../user/feature_flags.md) to support the development and rollout of new or experimental features.
|
||||
In GitLab Dedicated:
|
||||
|
||||
Feature flags support the development and rollout of new or experimental features on GitLab.com. Features behind feature flags are not considered ready for production use, are experimental and therefore unsafe for GitLab Dedicated. Stability and SLAs may be affected by changing default settings.
|
||||
- Features using feature flags that are **enabled by default** are available.
|
||||
- Features using feature flags that are **disabled by default** are not available and cannot be enabled by administrators.
|
||||
|
||||
Features behind flags that are disabled by default are not ready for production use and therefore unsafe for GitLab Dedicated.
|
||||
|
||||
When a feature becomes generally available and the flag is enabled or removed, the feature becomes available in
|
||||
GitLab Dedicated in the same GitLab version. GitLab Dedicated follows its own
|
||||
[release schedule](../gitlab_dedicated/maintenance.md) for version deployments.
|
||||
|
||||
## Migrate to GitLab Dedicated
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@
|
|||
stage: none
|
||||
group: unassigned
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: Complete list of flags.
|
||||
description: Complete list of all feature flags in GitLab.
|
||||
title: All feature flags in GitLab
|
||||
layout: feature_flags
|
||||
---
|
||||
|
||||
The following feature flags exist in GitLab. These flags determine the availability of each feature.
|
||||
GitLab provides feature flags to turn specific features on or off.
|
||||
This page contains a list of all feature flags provided by GitLab. In GitLab Self-Managed,
|
||||
GitLab administrators can [change the state of these feature flags](../administration/feature_flags.md).
|
||||
|
||||
For self-managed instances, [GitLab administrators can change the state of the flag](../administration/feature_flags.md).
|
||||
For help developing custom feature flags, see
|
||||
[Create a feature flag](../operations/feature_flags.md#create-a-feature-flag).
|
||||
|
||||
<!-- markdownlint-disable MD044 -->
|
||||
<!-- MD044/proper-names test disabled after this line to make page compatible with markdownlint-cli 0.29.0. -->
|
||||
|
|
|
|||
|
|
@ -12,9 +12,12 @@ title: Members of a project
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
Members are the users and groups who have access to your project.
|
||||
Members are the users and groups who have access to your project. Members can be added directly to your
|
||||
project or inherit access through groups.
|
||||
|
||||
Each member gets a role, which determines what they can do in the project.
|
||||
Each member has a role that determines what they can do in the project. Project members with the
|
||||
appropriate role can add users to projects, remove users from projects, and manage access requests to
|
||||
control access to project resources.
|
||||
|
||||
## Membership types
|
||||
|
||||
|
|
|
|||
|
|
@ -221,19 +221,37 @@ After you [specify a user cap for the group](../../group/manage.md#specify-a-use
|
|||
|
||||
## Sharing groups
|
||||
|
||||
When you want a group to have access to your group,
|
||||
you can invite another [group](../../group/_index.md) to the group.
|
||||
The invited group's direct members get access to the group.
|
||||
When you want another group's members to have access to your group,
|
||||
you can invite the [group](../../group/_index.md) to your group.
|
||||
The group's direct members get access to the group, which becomes a **shared group**.
|
||||
|
||||
Only direct members of the invited group get access to the shared group, not inherited or subgroup members. To grant subgroup members access, invite the subgroup directly.
|
||||
|
||||
The following table provides an overview of the group members that get access to a shared group:
|
||||
|
||||
| Group member source | Access to shared group |
|
||||
|--------------------------------------------------------------|------------------------|
|
||||
| Direct member of the group that is invited | {{< icon name="check-circle" >}} Yes |
|
||||
| Inherited member of the group that is invited | {{< icon name="dotted-circle" >}} No |
|
||||
| Member of a subgroup, but not of the group that is invited | {{< icon name="dotted-circle" >}} No |
|
||||
|
||||
### Member access and roles
|
||||
|
||||
Each member's access is based on the:
|
||||
|
||||
- Role they're assigned in the invited group.
|
||||
- Maximum role you choose when you invite the group.
|
||||
|
||||
If a group member has a role for the invited group with fewer permissions than the maximum role for your group,
|
||||
the member keeps the permissions of their invited group role.
|
||||
The least access is granted between the access in the invited group and the access in the inviting group.
|
||||
|
||||
After you invite a group to your group:
|
||||
|
||||
- The **Groups** tab of the group's **Members** page lists the invited group. This list includes both public and private groups.
|
||||
- The **Members** tab of the group's **Members** page lists the members of the invited group.
|
||||
- All direct members of the invited group have access to the inviting group.
|
||||
The least access is granted between the access in the invited group and the access in the inviting group.
|
||||
- Inherited members of the invited group do not gain access to the inviting group.
|
||||
- On the group's usage quota page, direct members of the invited group who have the **Group Invite** badge
|
||||
next to their profile count towards the billable members of the inviting group.
|
||||
- On the group's overview page, groups that this group has been shared with are listed on the **Shared groups** tab.
|
||||
- On the group's **Members** page, the invited group is listed on the **Groups** tab. This list includes both public and private groups.
|
||||
- On the group's **Members** page, the members of the invited group are listed on the **Members** tab.
|
||||
- On the group's usage quota page, direct members of the invited group who have the **Group Invite** badge next to their profile count towards the billable members of the inviting group.
|
||||
|
||||
[In GitLab 16.11 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144638),
|
||||
the invited group's name and membership source are masked on the **Members** and the **Groups** tabs,
|
||||
|
|
|
|||
|
|
@ -134,8 +134,6 @@ Whether it's a user or a project website, the DNS record
|
|||
should point to your Pages domain (`namespace.gitlab.io`),
|
||||
without any path.
|
||||
|
||||

|
||||
|
||||
##### For both root and subdomains
|
||||
|
||||
There are a few cases where you need to point both the subdomain and root
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 4.9 KiB |
|
|
@ -1036,7 +1036,6 @@ module API
|
|||
|
||||
def job_token_policies_authorized?(project)
|
||||
return true unless current_user&.from_ci_job_token?
|
||||
return true unless Feature.enabled?(:add_policies_to_ci_job_token, project)
|
||||
return true if skip_job_token_policies?
|
||||
return true if publicly_accessible_feature?(project)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class BackfillOnboardingStatusSetupForCompany < BatchedMigrationJob
|
||||
operation_name :backfill_onboarding_status_setup_for_company
|
||||
feature_category :onboarding
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
UserDetail
|
||||
.where(user_id: sub_batch.select(:user_id))
|
||||
.where("(user_details.onboarding_status->'setup_for_company') IS NULL")
|
||||
.joins("INNER JOIN user_preferences ON user_details.user_id = user_preferences.user_id")
|
||||
.where.not(user_preferences: { setup_for_company: nil })
|
||||
.update_all(
|
||||
"onboarding_status = jsonb_set(
|
||||
COALESCE(onboarding_status, '{}'::jsonb),
|
||||
'{setup_for_company}',
|
||||
(SELECT to_jsonb(user_preferences.setup_for_company)
|
||||
FROM user_preferences
|
||||
WHERE user_details.user_id = user_preferences.user_id)
|
||||
)"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -126,9 +126,10 @@ describe('Feature flag table', () => {
|
|||
expect(wrapper.findByTestId('flags-table-action-buttons').exists()).toBe(true);
|
||||
expect(wrapper.findByTestId('feature-flag-delete-button').exists()).toBe(true);
|
||||
expect(wrapper.findByTestId('feature-flag-edit-button').exists()).toBe(true);
|
||||
expect(wrapper.findByTestId('feature-flag-edit-button').attributes('href')).toEqual(
|
||||
'edit/path',
|
||||
);
|
||||
expect(wrapper.findByTestId('feature-flag-edit-button').attributes()).toMatchObject({
|
||||
'aria-label': 'Edit flag name',
|
||||
href: 'edit/path',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -327,14 +327,6 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
it { is_expected.to eq project }
|
||||
end
|
||||
|
||||
context 'when the `add_policies_to_ci_job_token` feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(add_policies_to_ci_job_token: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq project }
|
||||
end
|
||||
|
||||
context 'when the project feature is publicly accessible' do
|
||||
let_it_be(:project) { create(:project, :public, :builds_enabled) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::BackfillOnboardingStatusSetupForCompany, feature_category: :onboarding do
|
||||
let(:users) { table(:users) }
|
||||
let(:user_details) { table(:user_details) }
|
||||
let(:user_preferences) { table(:user_preferences) }
|
||||
let(:first_user) { users.create!(projects_limit: 0, email: 'user1@example.com') }
|
||||
let!(:user_preference) do
|
||||
user_preferences.create!(
|
||||
user_id: first_user.id,
|
||||
setup_for_company: true
|
||||
)
|
||||
end
|
||||
|
||||
let!(:user_detail) do
|
||||
user_details.create!(
|
||||
user_id: first_user.id,
|
||||
onboarding_status: { 'setup_for_company' => true }
|
||||
)
|
||||
end
|
||||
|
||||
let(:second_user) { users.create!(projects_limit: 0, email: 'user2@example.com') }
|
||||
let!(:user_preference_to_change) do
|
||||
user_preferences.create!(
|
||||
user_id: second_user.id,
|
||||
setup_for_company: false
|
||||
)
|
||||
end
|
||||
|
||||
let!(:user_detail_to_change) do
|
||||
user_details.create!(
|
||||
user_id: second_user.id,
|
||||
onboarding_status: {}
|
||||
)
|
||||
end
|
||||
|
||||
let(:last_user) { users.create!(projects_limit: 0, email: 'user3@example.com') }
|
||||
let!(:user_preference_not_to_be_set) do
|
||||
user_preferences.create!(
|
||||
user_id: last_user.id,
|
||||
setup_for_company: nil
|
||||
)
|
||||
end
|
||||
|
||||
let!(:user_detail_not_to_be_set) do
|
||||
user_details.create!(
|
||||
user_id: last_user.id,
|
||||
onboarding_status: {}
|
||||
)
|
||||
end
|
||||
|
||||
subject(:migration) do
|
||||
described_class.new(
|
||||
start_id: user_detail.user_id,
|
||||
end_id: user_detail_not_to_be_set.user_id,
|
||||
batch_table: :user_details,
|
||||
batch_column: :user_id,
|
||||
sub_batch_size: 100,
|
||||
pause_ms: 0,
|
||||
connection: ApplicationRecord.connection
|
||||
)
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
it 'updates the correct data' do
|
||||
migration.perform
|
||||
|
||||
expect(user_detail.reload.onboarding_status).to eq({ 'setup_for_company' => true })
|
||||
expect(user_detail_to_change.reload.onboarding_status).to eq({ 'setup_for_company' => false })
|
||||
expect(user_detail_not_to_be_set.reload.onboarding_status).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillOnboardingStatusSetupForCompany, migration: :gitlab_main, feature_category: :onboarding do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :user_details,
|
||||
column_name: :user_id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,12 +6,68 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
let_it_be(:origin_project) { create(:project) }
|
||||
let_it_be(:accessed_project) { create(:project) }
|
||||
let_it_be(:another_project) { create(:project) }
|
||||
let_it_be(:policies) { [:read_jobs, :admin_environments] }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:origin_project).class_name('Project') }
|
||||
it { is_expected.to belong_to(:accessed_project).class_name('Project') }
|
||||
end
|
||||
|
||||
describe 'validating job_token_policies' do
|
||||
subject(:auth_log) { described_class.new(job_token_policies: policies) }
|
||||
|
||||
let(:valid_policies) do
|
||||
schema = 'app/validators/json_schemas/ci_job_token_policies.json'
|
||||
Gitlab::Json.parse(File.read(schema)).dig('items', 'enum')
|
||||
end
|
||||
|
||||
context 'when not assigning any policies' do
|
||||
let(:policies) { {} }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
|
||||
context 'when assigning all valid policies with valid values' do
|
||||
let(:policies) { valid_policies.index_with(Time.current) }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
|
||||
context 'when assigning an invalid policy with a valid value' do
|
||||
let(:policies) { { invalid_policy: Time.current } }
|
||||
|
||||
it { is_expected.to be_invalid }
|
||||
|
||||
it 'contains a descriptive error' do
|
||||
auth_log.valid?
|
||||
expect(auth_log.errors[:job_token_policies]).to include("value at root is not one of: #{valid_policies}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when assigning a valid policy with an invalid value' do
|
||||
let(:policies) { { read_jobs: true } }
|
||||
|
||||
it { is_expected.to be_invalid }
|
||||
|
||||
it 'contains a descriptive error' do
|
||||
auth_log.valid?
|
||||
expect(auth_log.errors[:job_token_policies]).to include('value at `/read_jobs` is not a string')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when assigning a valid policy with a string that is not a datetime' do
|
||||
let(:policies) { { read_jobs: 'not a date time string' } }
|
||||
|
||||
it { is_expected.to be_invalid }
|
||||
|
||||
it 'contains a descriptive error' do
|
||||
auth_log.valid?
|
||||
expect(auth_log.errors[:job_token_policies])
|
||||
.to include('value at `/read_jobs` does not match format: date-time')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.capture', :request_store do
|
||||
subject(:capture) do
|
||||
described_class.capture(origin_project: origin_project, accessed_project: accessed_project)
|
||||
|
|
@ -50,6 +106,41 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
end
|
||||
end
|
||||
|
||||
describe '.capture_job_token_policies', :request_store do
|
||||
subject(:capture_job_token_policies) { described_class.capture_job_token_policies(policies) }
|
||||
|
||||
let(:policies) { [:read_environments, :read_jobs] }
|
||||
|
||||
it 'captures the policies in the RequestStore' do
|
||||
capture_job_token_policies
|
||||
expect(described_class.captured_authorizations).to eq(policies: policies)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.add_to_request_store_hash', :request_store do
|
||||
subject(:captured_authorizations) { described_class.captured_authorizations }
|
||||
|
||||
let(:old_hash) { { old_value: 1 } }
|
||||
let(:new_hash) { { new_value: 2 } }
|
||||
|
||||
context 'when no values have been captured in the RequestStore yet' do
|
||||
before do
|
||||
described_class.add_to_request_store_hash(old_hash)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(old_hash) }
|
||||
end
|
||||
|
||||
context 'when previous values have been captured in the RequestStore' do
|
||||
before do
|
||||
described_class.add_to_request_store_hash(old_hash)
|
||||
described_class.add_to_request_store_hash(new_hash)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(old_hash.merge(new_hash)) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.log_captures_async', :request_store do
|
||||
subject(:log_captures_async) do
|
||||
described_class.log_captures_async
|
||||
|
|
@ -65,9 +156,9 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
|
||||
context 'when authorizations have been captured during the request' do
|
||||
before do
|
||||
described_class.capture(
|
||||
origin_project: origin_project,
|
||||
accessed_project: accessed_project)
|
||||
described_class.add_to_request_store_hash(
|
||||
origin_project_id: origin_project&.id,
|
||||
accessed_project_id: accessed_project&.id)
|
||||
end
|
||||
|
||||
context 'when authorization is cross project' do
|
||||
|
|
@ -79,8 +170,14 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
end
|
||||
end
|
||||
|
||||
context 'when authorization is self-referential' do
|
||||
let(:accessed_project) { origin_project }
|
||||
context 'when the origin project is missing' do
|
||||
let(:origin_project) { nil }
|
||||
|
||||
it_behaves_like 'does not log the authorization'
|
||||
end
|
||||
|
||||
context 'when the accessed project is missing' do
|
||||
let(:accessed_project) { nil }
|
||||
|
||||
it_behaves_like 'does not log the authorization'
|
||||
end
|
||||
|
|
@ -93,16 +190,23 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
|
||||
describe '.log_captures!' do
|
||||
subject(:log_captures) do
|
||||
described_class.log_captures!(origin_project_id: origin_project.id, accessed_project_id: accessed_project.id)
|
||||
described_class.log_captures!(
|
||||
origin_project_id: origin_project.id,
|
||||
accessed_project_id: accessed_project.id,
|
||||
policies: policies)
|
||||
end
|
||||
|
||||
context 'when authorization does not exist in the database' do
|
||||
it 'creates a new authorization' do
|
||||
it 'creates a new authorization', :freeze_time do
|
||||
expect { log_captures }.to change { described_class.count }.by(1)
|
||||
|
||||
expect(described_class.last).to have_attributes(
|
||||
origin_project: origin_project,
|
||||
accessed_project: accessed_project)
|
||||
accessed_project: accessed_project,
|
||||
job_token_policies: {
|
||||
read_jobs: Time.current,
|
||||
admin_environments: Time.current
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -111,12 +215,15 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
|
|||
create(:ci_job_token_authorization,
|
||||
origin_project: origin_project,
|
||||
accessed_project: accessed_project,
|
||||
last_authorized_at: 1.day.ago)
|
||||
last_authorized_at: 1.day.ago,
|
||||
job_token_policies: { read_jobs: 1.day.ago })
|
||||
end
|
||||
|
||||
it 'updates the timestamp instead of creating a new record' do
|
||||
it 'updates the policies and the last_authorized_at timestamp instead of creating a new record', :freeze_time do
|
||||
expect { log_captures }
|
||||
.to change { existing_authorization.reload.last_authorized_at }
|
||||
.to change { existing_authorization.reload.last_authorized_at }.to(Time.current)
|
||||
.and change { existing_authorization.job_token_policies.keys }.to(policies)
|
||||
.and change { existing_authorization.job_token_policies[:read_jobs] }.to(Time.current)
|
||||
.and not_change { described_class.count }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -230,17 +230,35 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
|
|||
end
|
||||
|
||||
describe '#policies_allowed?' do
|
||||
subject { scope.policies_allowed?(accessed_project, policies) }
|
||||
subject(:policies_allowed?) { scope.policies_allowed?(accessed_project, policies) }
|
||||
|
||||
let(:scope) { described_class.new(target_project) }
|
||||
let_it_be(:target_project) { create(:project) }
|
||||
let_it_be(:allowed_policy) { ::Ci::JobToken::Policies::POLICIES.first }
|
||||
let(:accessed_project) { create_inbound_accessible_project_for_policies(target_project, [allowed_policy]) }
|
||||
|
||||
shared_examples 'capturing job token policies' do
|
||||
it 'captures job token policies' do
|
||||
expect(::Ci::JobToken::Authorization).to receive(:capture_job_token_policies).with(policies)
|
||||
|
||||
policies_allowed?
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'not capturing job token policies' do
|
||||
it 'does not capture job token policies' do
|
||||
expect(::Ci::JobToken::Authorization).not_to receive(:capture_job_token_policies)
|
||||
|
||||
policies_allowed?
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no policies are given' do
|
||||
let_it_be(:policies) { [] }
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
|
||||
it_behaves_like 'not capturing job token policies'
|
||||
end
|
||||
|
||||
context 'when the policies are defined in the scope' do
|
||||
|
|
@ -248,6 +266,8 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
|
|||
|
||||
it { is_expected.to be(true) }
|
||||
|
||||
it_behaves_like 'capturing job token policies'
|
||||
|
||||
context 'when an admin policy is defined in the scope' do
|
||||
let_it_be(:allowed_policy) { 'admin_jobs' }
|
||||
let_it_be(:policies) { [:admin_jobs, :read_jobs] }
|
||||
|
|
@ -261,18 +281,24 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
|
|||
let(:accessed_project) { create(:project) }
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
|
||||
it_behaves_like 'not capturing job token policies'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the policy are not defined in the scope' do
|
||||
context 'when the policies are not defined in the scope' do
|
||||
let_it_be(:policies) { [:not_allowed_policy] }
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
|
||||
it_behaves_like 'capturing job token policies'
|
||||
|
||||
context 'when the accessed project is the target project' do
|
||||
let(:accessed_project) { target_project }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
|
||||
it_behaves_like 'not capturing job token policies'
|
||||
end
|
||||
|
||||
context 'when the accessed project does not have ci_inbound_job_token_scope_enabled set to true' do
|
||||
|
|
@ -281,12 +307,26 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
|
|||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
|
||||
it_behaves_like 'capturing job token policies'
|
||||
end
|
||||
|
||||
context 'when the `add_policies_to_ci_job_token` flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(add_policies_to_ci_job_token: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
|
||||
it_behaves_like 'capturing job token policies'
|
||||
end
|
||||
|
||||
context 'when the accessed project has not enabled fine grained permissions' do
|
||||
let(:accessed_project) { create_inbound_accessible_project(target_project) }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
|
||||
it_behaves_like 'capturing job token policies'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ RSpec.describe Ci::JobToken::LogAuthorizationWorker, feature_category: :secrets_
|
|||
let_it_be(:origin_project) { build_stubbed(:project) }
|
||||
let_it_be(:accessed_project) { build_stubbed(:project) }
|
||||
|
||||
subject(:perform) { described_class.new.perform(accessed_project.id, origin_project.id) }
|
||||
subject(:perform) { described_class.new.perform(*job_args) }
|
||||
|
||||
it_behaves_like 'an idempotent worker' do
|
||||
let(:job_args) { [accessed_project.id, origin_project.id] }
|
||||
let(:job_args) { [accessed_project.id, origin_project.id, ['read_jobs']] }
|
||||
|
||||
it 'calls Ci::JobToken::Authorization#log_captures!' do
|
||||
expect(Ci::JobToken::Authorization)
|
||||
.to receive(:log_captures!)
|
||||
.with(accessed_project_id: accessed_project.id, origin_project_id: origin_project.id)
|
||||
.with(accessed_project_id: accessed_project.id, origin_project_id: origin_project.id, policies: [:read_jobs])
|
||||
.and_call_original
|
||||
|
||||
perform
|
||||
|
|
|
|||
Loading…
Reference in New Issue