Commit a828892d authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 823ac6bc d8dccea0
<!-- This template is used as a starting point for understing and articulating a customer problem.
<!-- This template is used as a starting point for understanding and articulating a customer problem.
Learn more about it in the handbook: https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation
-->
......
......@@ -98,9 +98,6 @@ export default {
return this.$options.i18n[this.currentPage].btnText;
},
buttonKind() {
if (this.isNewForm) {
return 'success';
}
if (this.isDeleteForm) {
return 'danger';
}
......
......@@ -764,6 +764,9 @@
},
"filter": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "#/definitions/filter_refs"
},
......
......@@ -131,11 +131,28 @@ class Projects::BranchesController < Projects::ApplicationController
private
def sort_value_for_mode
return params[:sort] if params[:sort].present?
custom_sort || default_sort
end
def custom_sort
sort = params[:sort].presence
unless sort.in?(supported_sort_options)
flash.now[:alert] = _("Unsupported sort value.")
sort = nil
end
sort
end
def default_sort
'stale' == @mode ? sort_value_oldest_updated : sort_value_recently_updated
end
def supported_sort_options
[nil, sort_value_name, sort_value_oldest_updated, sort_value_recently_updated]
end
# It can be expensive to calculate the diverging counts for each
# branch. Normally the frontend should be specifying a set of branch
# names, but prior to
......
- page_title _('Background Migrations')
.tabs.gl-tabs
%div
%ul.nav.gl-tabs-nav{ role: 'tablist' }
- active_tab_classes = ['gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo']
= gl_tabs_nav do
= gl_tab_link_to admin_background_migrations_path, item_active: @current_tab == 'queued' do
= _('Queued')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['queued'])
= gl_tab_link_to admin_background_migrations_path(tab: 'failed'), item_active: @current_tab == 'failed' do
= _('Failed')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['failed'])
= gl_tab_link_to admin_background_migrations_path(tab: 'finished'), item_active: @current_tab == 'finished' do
= _('Finished')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['finished'])
%li.nav-item{ role: 'presentation' }
%a.nav-link.gl-tab-nav-item{ href: admin_background_migrations_path, class: (active_tab_classes if @current_tab == 'queued'), role: 'tab' }
= _('Queued')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['queued'])
%li.nav-item{ role: 'presentation' }
%a.nav-link.gl-tab-nav-item{ href: admin_background_migrations_path(tab: 'failed'), class: (active_tab_classes if @current_tab == 'failed'), role: 'tab' }
= _('Failed')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['failed'])
%li.nav-item{ role: 'presentation' }
%a.nav-link.gl-tab-nav-item{ href: admin_background_migrations_path(tab: 'finished'), class: (active_tab_classes if @current_tab == 'finished'), role: 'tab' }
= _('Finished')
= gl_tab_counter_badge limited_counter_with_delimiter(@relations_by_tab['finished'])
.tab-content.gl-tab-content
.tab-pane.active{ role: 'tabpanel' }
%table.table.b-table.gl-table.b-table-stacked-md{ role: 'table' }
%thead{ role: 'rowgroup' }
%tr{ role: 'row' }
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Migration')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Progress')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Status')
%th.table-th-transparent.border-bottom{ role: 'cell' }
%tbody{ role: 'rowgroup' }
= render partial: 'migration', collection: @migrations
.tab-content.gl-tab-content
.tab-pane.active{ role: 'tabpanel' }
%table.table.b-table.gl-table.b-table-stacked-md{ role: 'table' }
%thead{ role: 'rowgroup' }
%tr{ role: 'row' }
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Migration')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Progress')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Status')
%th.table-th-transparent.border-bottom{ role: 'cell' }
%tbody{ role: 'rowgroup' }
= render partial: 'migration', collection: @migrations
= paginate_collection @migrations
= paginate_collection @migrations
---
stage: Verify
group: Pipeline Authoring
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Configure OpenID Connect with GCP Workload Identity Federation
WARNING:
The `CI_JOB_JWT_V2` variable is under development [(alpha)](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha) and is not yet suitable for production use.
This tutorial demonstrates authenticating to Google Cloud from a GitLab CI/CD job
using a JSON Web Token (JWT) token and Workload Identity Federation. This configuration
generates on-demand, short-lived credentials without needing to store any secrets.
To get started, configure OpenID Connect (OIDC) for identity federation between GitLab
and Google Cloud. For more information on using OIDC with GitLab, read
[Connect to cloud services](../index.md).
This tutorial assumes you have a Google Cloud account and a Google Cloud project.
Your account must have at least the **Workload Identity Pool Admin** permission
on the Google Cloud project.
To complete this tutorial:
1. [Create the Google Cloud Workload Identity Pool](#create-the-google-cloud-workload-identity-pool).
1. [Create a Workload Identity Provider](#create-a-workload-identity-provider).
1. [Grant permissions for service account impersonation](#grant-permissions-for-service-account-impersonation).
1. [Retrieve a temporary credential](#retrieve-a-temporary-credential).
## Create the Google Cloud Workload Identity Pool
[Create a new Google Cloud Workload Identity Pool](https://cloud.google.com/iam/docs/configuring-workload-identity-federation#oidc) with the following options:
- **Name**: Human-friendly name for the Workload Identity Pool, such as `GitLab`.
- **Pool ID**: Unique ID in the Google Cloud project for the Workload Identity Pool,
such as `gitlab`. This value is used to refer to the pool. and appears in URLs.
- **Description**: Optional. A description of the pool.
- **Enabled Pool**: Ensure this option is `true`.
We recommend creating a single _pool_ per GitLab installation per Google Cloud project. If you have multiple GitLab repositories and CI/CD jobs on the same GitLab instance, they can authenticate using different _providers_ against the same _pool_.
## Create a Workload Identity Provider
[Create a new Google Cloud Workload Identity Provider](https://cloud.google.com/iam/docs/configuring-workload-identity-federation#create_the_workload_identity_pool_and_provider)
inside the Workload Identity Pool created in the previous step, using the following options:
- **Provider type**: OpenID Connect (OIDC).
- **Provider name**: Human-friendly name for the Workload Identity Provider,
such as `gitlab/gitlab`.
- **Provider ID**: Unique ID in the pool for the Workload Identity Provider,
such as `gitlab-gitlab`. This value is used to refer to the provider, and appears in URLs.
- **Issuer (URL)**: The address of your GitLab instance, such as `https://gitlab.com` or
`https://gitlab.example.com`.
- The address must use the `https://` protocol.
- The address must end in a trailing slash.
- **Audiences**: Manually set the allowed audiences list to the address of your
GitLab instance, such as `https://gitlab.com` or `https://gitlab.example.com`.
- The address must use the `https://` protocol.
- The address must not end in a trailing slash.
- **Provider attributes mapping**: Create the following mappings, where `attribute.X` is the
name of the attribute you would like to be present on Google's claims, and `assertion.X`
is the value to extract from the [GitLab claim](../index.md#how-it-works):
| Attribute (on Google) | Assertion (from GitLab) |
| --- | --- |
| `google.subject` | `assertion.sub` |
| `attribute.X` | `assertion.X` |
You can also [build complex attributes](https://cloud.google.com/iam/help/workload-identity/attribute-mapping)
using Common Expression Language (CEL).
You must map every attribute that you want to use for permission granting. For example, if you want to map permissions in the next step based on the user's email address, you must map `attribute.user_email` to `assertion.user_email`.
## Grant permissions for Service Account impersonation
Creating the Workload Identity Pool and Workload Identity Provider defines the _authentication_
into Google Cloud. At this point, you can authenticate from GitLab CI/CD job into Google Cloud.
However, you have no permissions on Google Cloud (_authorization_).
To grant your GitLab CI/CD job permissions on Google Cloud, you must:
1. [Create a Google Cloud Service Account](https://www.google.com/search?q=google+cloud+create+service+account).
You can use whatever name and ID you prefer.
1. [Grant IAM permissions](https://cloud.google.com/iam/docs/granting-changing-revoking-access) to your
service account on Google Cloud resources. These permissions vary significantly based on
your use case. In general, grant this service account the permissions on your Google Cloud
project and resources you want your GitLab CI/CD job to be able to use. For example, if you needed to upload a file to a Google Cloud Storage bucket in your GitLab CI/CD job, you would grant this Service Account the `roles/storage.objectCreator` role on your Cloud Storage bucket.
1. [Grant the external identity permissions](https://cloud.google.com/iam/docs/using-workload-identity-federation#impersonate)
to impersonate that Service Account. This step enables a GitLab CI/CD job to _authorize_
to Google Cloud, via Service Account impersonation. This step grants an IAM permission
_on the Service Account itself_, giving the external identity permissions to act as that
service account. External identities are expressed using the `principalSet://` protocol.
Much like the previous step, this step depends heavily on your desired configuration.
For example, to allow a GitLab CI/CD job to impersonate a Service Account named
`my-service-account` if the GitLab CI/CD job was initiated by a GitLab user with the
username `chris`, you would grant the `roles/iam.workloadIdentityUser` IAM role to the
external identity on `my-service-account`. The external identity takes the format:
```plaintext
principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.user_login/chris
```
where `PROJECT_NUMBER` is your Google Cloud project number, and `POOL_ID` is the
ID (not name) of the Workload Identity Pool created in the first section.
This configuration also assumes you added `user_login` as an attribute mapped from
the assertion in the previous section.
## Retrieve a temporary credential
After you configure the OIDC and role, the GitLab CI/CD job can retrieve a temporary credential from the
[Google Cloud Security Token Service (STS)](https://cloud.google.com/iam/docs/reference/sts/rest).
```shell
PAYLOAD="$(cat <<EOF
{
"audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
"grantType": "urn:ietf:params:oauth:grant-type:token-exchange",
"requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",
"scope": "https://www.googleapis.com/auth/cloud-platform",
"subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",
"subjectToken": "${CI_JOB_JWT_V2}"
}
EOF
)"
```
```shell
FEDERATED_TOKEN="$(curl --fail "https://sts.googleapis.com/v1/token" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "${PAYLOAD}" \
| jq -r '.access_token'
)"
```
Where:
- `PROJECT_NUMBER` is your Google Cloud project number (not name).
- `POOL_ID` is the ID of the Workload Identity Pool created in the first section.
- `PROVIDER_ID` is the ID of the Workload Identity Provider created in the second section.
- `CI_JOB_JWT_V2` is injected into the CI/CD job by GitLab. For more information about
this variable, read [Connect to cloud services](../index.md).
You can then use the resulting federated token to impersonate the service account created
in the previous section:
```shell
ACCESS_TOKEN="$(curl --fail "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT_EMAIL:generateAccessToken" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer FEDERATED_TOKEN" \
--data '{"scope": ["https://www.googleapis.com/auth/cloud-platform"]}' \
| jq -r '.accessToken'
)"
```
Where:
- `SERVICE_ACCOUNT_EMAIL` is the full email address of the service account to impersonate,
created in the previous section.
- `FEDERATED_TOKEN` is the federated token retrieved from the previous step.
The result is a Google Cloud OAuth 2.0 access token, which you can use to authenticate to
most Google Cloud APIs and services when used as a bearer token. You can also pass this
value to the `gcloud` CLI by setting the environment variable `CLOUDSDK_AUTH_ACCESS_TOKEN`.
## Working example
Review this
[reference project](https://gitlab.com/guided-explorations/gcp/configure-openid-connect-in-gcp)
for provisioning OIDC in GCP using Terraform and a sample script to retrieve temporary credentials.
## Troubleshooting
- When debugging `curl` responses, install the latest version of curl. Use `--fail-with-body`
instead of `-f`. This command prints the entire body, which can contain helpful error messages.
- Review Google Cloud's documentation for
[Troubleshooting Workload Identity Federation](https://cloud.google.com/iam/docs/troubleshooting-workload-identity-federation).
......@@ -99,9 +99,9 @@ sequenceDiagram
Note right of Cloud: Decode & verify JWT with public key (https://gitlab/-/jwks)
Note right of Cloud: Validate audience defined in OIDC
Note right of Cloud: Validate conditional (sub, aud) role
Note right of Cloud: Generate credential or fetch secret
Note right of Cloud: Generate credential or fetch secret
Cloud->>GitLab: Return temporary credential
Note left of GitLab: Perform operation
Note left of GitLab: Perform operation
```
......@@ -131,3 +131,4 @@ To configure the trust between GitLab and OIDC, you must create a conditional ro
To connect with your cloud provider, see the following tutorials:
- [Configure OpenID Connect in AWS](aws/index.md)
- [Configure OpenID Connect in Google Cloud](google_cloud/index.md)
......@@ -110,7 +110,7 @@ describe('BoardForm', () => {
it('passes correct primary action text and variant', () => {
expect(findModalActionPrimary().text).toBe('Create board');
expect(findModalActionPrimary().attributes[0].variant).toBe('success');
expect(findModalActionPrimary().attributes[0].variant).toBe('confirm');
});
it('does not render delete confirmation message', () => {
......
......@@ -24,7 +24,7 @@ module API
helpers do
params :filter_params do
optional :search, type: String, desc: 'Return list of branches matching the search criteria'
optional :sort, type: String, desc: 'Return list of branches sorted by the given field'
optional :sort, type: String, desc: 'Return list of branches sorted by the given field', values: %w[name_asc updated_asc updated_desc]
end
end
......
......@@ -38461,6 +38461,9 @@ msgstr ""
msgid "Unsubscribes from this %{quick_action_target}."
msgstr ""
msgid "Unsupported sort value."
msgstr ""
msgid "Unsupported todo type passed. Supported todo types are: %{todo_types}"
msgstr ""
......
......@@ -657,6 +657,36 @@ RSpec.describe Projects::BranchesController do
end
end
context 'sorting', :aggregate_failures do
let(:sort) { 'name_asc' }
before do
get :index, format: :html, params: {
namespace_id: project.namespace, project_id: project, state: 'all', sort: sort
}
end
it { expect(assigns[:sort]).to eq('name_asc') }
context 'when sort is not provided' do
let(:sort) { nil }
it 'uses a default sort without an error message' do
expect(assigns[:sort]).to eq('updated_desc')
expect(controller).not_to set_flash.now[:alert]
end
end
context 'when sort is not supported' do
let(:sort) { 'unknown' }
it 'uses a default sort and shows an error message' do
expect(assigns[:sort]).to eq('updated_desc')
expect(controller).to set_flash.now[:alert].to(/Unsupported sort/)
end
end
end
context 'when gitaly is not available' do
before do
allow_next_instance_of(Gitlab::GitalyClient::RefService) do |ref_service|
......
......@@ -68,7 +68,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
tab.click
expect(page).to have_current_path(admin_background_migrations_path(tab: 'failed'))
expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo')
expect(tab[:class]).to include('gl-tab-nav-item-active')
expect(page).to have_selector('tbody tr', count: 1)
......@@ -93,7 +93,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
tab.click
expect(page).to have_current_path(admin_background_migrations_path(tab: 'finished'))
expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo')
expect(tab[:class]).to include('gl-tab-nav-item-active')
expect(page).to have_selector('tbody tr', count: 1)
......
......@@ -130,7 +130,7 @@ describe('BoardForm', () => {
it('passes correct primary action text and variant', () => {
expect(findModalActionPrimary().text).toBe('Create board');
expect(findModalActionPrimary().attributes[0].variant).toBe('success');
expect(findModalActionPrimary().attributes[0].variant).toBe('confirm');
});
it('does not render delete confirmation message', () => {
......
......@@ -188,6 +188,24 @@ RSpec.describe API::Branches do
end
end
context 'when sort parameter is passed' do
it 'sorts branches' do
get api(route, user), params: { sort: 'name_asc', per_page: 10 }
sorted_branch_names = json_response.map { |branch| branch['name'] }
project_branch_names = project.repository.branch_names.sort.take(10)
expect(sorted_branch_names).to eq(project_branch_names)
end
context 'when sort value is not supported' do
it_behaves_like '400 response' do
let(:request) { get api(route, user), params: { sort: 'unknown' }}
end
end
end
context 'when unauthenticated', 'and project is public' do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment