diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-08 21:11:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-08 21:11:09 +0000 |
commit | 3d42e098d9658853984534c9dfc2bf76284a8ac0 (patch) | |
tree | 269e9d9e017124f5f6d841311661832ea7e4eff8 | |
parent | a3487798aeff89daff78749e6d6f392d3ca23687 (diff) | |
download | gitlab-ce-3d42e098d9658853984534c9dfc2bf76284a8ac0.tar.gz |
Add latest changes from gitlab-org/gitlab@master
47 files changed, 1054 insertions, 493 deletions
diff --git a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue index 93b31ea7d20..ca17443081c 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue @@ -1,18 +1,63 @@ <script> -import { GlDropdown, GlDropdownItem, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; +import { GlTooltip, GlDisclosureDropdown } from '@gitlab/ui'; +import { uniqueId } from 'lodash'; + +import { __ } from '~/locale'; export default { components: { - GlDropdown, - GlDropdownItem, - }, - directives: { + GlDisclosureDropdown, GlTooltip, }, inject: ['tiptapEditor'], data() { return { - isActive: {}, + toggleId: uniqueId('dropdown-toggle-btn-'), + items: [ + { + text: __('Comment'), + action: () => this.insert('comment'), + }, + { + text: __('Code block'), + action: () => this.insert('codeBlock'), + }, + { + text: __('Details block'), + action: () => this.insertList('details', 'detailsContent'), + }, + { + text: __('Bullet list'), + action: () => this.insertList('bulletList', 'listItem'), + wrapperClass: 'gl-sm-display-none!', + }, + { + text: __('Ordered list'), + action: () => this.insertList('orderedList', 'listItem'), + wrapperClass: 'gl-sm-display-none!', + }, + { + text: __('Task list'), + action: () => this.insertList('taskList', 'taskItem'), + wrapperClass: 'gl-sm-display-none!', + }, + { + text: __('Horizontal rule'), + action: () => this.execute('setHorizontalRule', 'horizontalRule'), + }, + { + text: __('Mermaid diagram'), + action: () => this.insert('diagram', { language: 'mermaid' }), + }, + { + text: __('PlantUML diagram'), + action: () => this.insert('diagram', { language: 'plantuml' }), + }, + { + text: __('Table of contents'), + action: () => this.execute('insertTableOfContents', 'tableOfContents'), + }, + ], }; }, methods: { @@ -46,47 +91,17 @@ export default { }; </script> <template> - <gl-dropdown - v-gl-tooltip - size="small" - category="tertiary" - icon="plus" - :text="__('More')" - :title="__('More')" - text-sr-only - class="content-editor-dropdown" - right - lazy - > - <gl-dropdown-item @click="insert('comment')"> - {{ __('Comment') }} - </gl-dropdown-item> - <gl-dropdown-item @click="insert('codeBlock')"> - {{ __('Code block') }} - </gl-dropdown-item> - <gl-dropdown-item @click="insertList('details', 'detailsContent')"> - {{ __('Details block') }} - </gl-dropdown-item> - <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('bulletList', 'listItem')"> - {{ __('Bullet list') }} - </gl-dropdown-item> - <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('orderedList', 'listItem')"> - {{ __('Ordered list') }} - </gl-dropdown-item> - <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('taskList', 'taskItem')"> - {{ __('Task list') }} - </gl-dropdown-item> - <gl-dropdown-item @click="execute('setHorizontalRule', 'horizontalRule')"> - {{ __('Horizontal rule') }} - </gl-dropdown-item> - <gl-dropdown-item @click="insert('diagram', { language: 'mermaid' })"> - {{ __('Mermaid diagram') }} - </gl-dropdown-item> - <gl-dropdown-item @click="insert('diagram', { language: 'plantuml' })"> - {{ __('PlantUML diagram') }} - </gl-dropdown-item> - <gl-dropdown-item @click="execute('insertTableOfContents', 'tableOfContents')"> - {{ __('Table of contents') }} - </gl-dropdown-item> - </gl-dropdown> + <div class="gl-display-inline-flex gl-vertical-align-middle"> + <gl-disclosure-dropdown + :items="items" + :toggle-id="toggleId" + size="small" + category="tertiary" + icon="plus" + :toggle-text="__('More options')" + text-sr-only + right + /> + <gl-tooltip :target="toggleId" placement="top">{{ __('More options') }}</gl-tooltip> + </div> </template> diff --git a/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue b/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue deleted file mode 100644 index 89dc68ec73e..00000000000 --- a/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue +++ /dev/null @@ -1,76 +0,0 @@ -<script> -import { GlAlert, GlSprintf, GlLink, GlButton } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import Tracking from '~/tracking'; -import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '../constants'; - -export default { - name: 'SecurityPatchUpgradeAlert', - i18n: { - alertTitle: s__('VersionCheck|Critical security upgrade available'), - alertBody: s__( - 'VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}', - ), - learnMore: s__('VersionCheck|Learn more about this critical security release.'), - primaryButtonText: s__('VersionCheck|Upgrade now'), - }, - components: { - GlAlert, - GlSprintf, - GlLink, - GlButton, - }, - mixins: [Tracking.mixin()], - props: { - currentVersion: { - type: String, - required: true, - }, - }, - mounted() { - this.track('render', { - label: 'security_patch_upgrade_alert', - property: this.currentVersion, - }); - }, - methods: { - trackLearnMoreClick() { - this.track('click_link', { - label: 'security_patch_upgrade_alert_learn_more', - property: this.currentVersion, - }); - }, - trackUpgradeNowClick() { - this.track('click_link', { - label: 'security_patch_upgrade_alert_upgrade_now', - property: this.currentVersion, - }); - }, - }, - UPGRADE_DOCS_URL, - ABOUT_RELEASES_PAGE, -}; -</script> - -<template> - <gl-alert :title="$options.i18n.alertTitle" variant="danger" :dismissible="false"> - <gl-sprintf :message="$options.i18n.alertBody"> - <template #currentVersion> - <span class="gl-font-weight-bold">{{ currentVersion }}</span> - </template> - <template #link> - <gl-link :href="$options.ABOUT_RELEASES_PAGE" @click="trackLearnMoreClick">{{ - $options.i18n.learnMore - }}</gl-link> - </template> - </gl-sprintf> - <template #actions> - <gl-button - :href="$options.UPGRADE_DOCS_URL" - variant="confirm" - @click="trackUpgradeNowClick" - >{{ $options.i18n.primaryButtonText }}</gl-button - > - </template> - </gl-alert> -</template> diff --git a/app/assets/javascripts/gitlab_version_check/index.js b/app/assets/javascripts/gitlab_version_check/index.js index edb7e9abe49..dff09d2cb51 100644 --- a/app/assets/javascripts/gitlab_version_check/index.js +++ b/app/assets/javascripts/gitlab_version_check/index.js @@ -1,7 +1,6 @@ import Vue from 'vue'; import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import GitlabVersionCheckBadge from './components/gitlab_version_check_badge.vue'; -import SecurityPatchUpgradeAlert from './components/security_patch_upgrade_alert.vue'; import SecurityPatchUpgradeAlertModal from './components/security_patch_upgrade_alert_modal.vue'; const mountGitlabVersionCheckBadge = (el) => { @@ -33,25 +32,6 @@ const mountGitlabVersionCheckBadge = (el) => { } }; -const mountSecurityPatchUpgradeAlert = (el) => { - const { currentVersion } = el.dataset; - - try { - return new Vue({ - el, - render(createElement) { - return createElement(SecurityPatchUpgradeAlert, { - props: { - currentVersion, - }, - }); - }, - }); - } catch { - return null; - } -}; - const mountSecurityPatchUpgradeAlertModal = (el) => { const { currentVersion, version } = el.dataset; @@ -78,16 +58,11 @@ const mountSecurityPatchUpgradeAlertModal = (el) => { export default () => { const renderedApps = []; - const securityPatchUpgradeAlert = document.getElementById('js-security-patch-upgrade-alert'); const securityPatchUpgradeAlertModal = document.getElementById( 'js-security-patch-upgrade-alert-modal', ); const versionCheckBadges = [...document.querySelectorAll('.js-gitlab-version-check-badge')]; - if (securityPatchUpgradeAlert) { - renderedApps.push(mountSecurityPatchUpgradeAlert(securityPatchUpgradeAlert)); - } - if (securityPatchUpgradeAlertModal) { renderedApps.push(mountSecurityPatchUpgradeAlertModal(securityPatchUpgradeAlertModal)); } diff --git a/app/assets/javascripts/token_access/components/inbound_token_access.vue b/app/assets/javascripts/token_access/components/inbound_token_access.vue new file mode 100644 index 00000000000..1a225550c95 --- /dev/null +++ b/app/assets/javascripts/token_access/components/inbound_token_access.vue @@ -0,0 +1,258 @@ +<script> +import { + GlAlert, + GlButton, + GlCard, + GlFormInput, + GlLink, + GlLoadingIcon, + GlSprintf, + GlToggle, +} from '@gitlab/ui'; +import { createAlert } from '~/flash'; +import { __, s__ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import inboundAddProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql'; +import inboundRemoveProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql'; +import inboundUpdateCIJobTokenScopeMutation from '../graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql'; +import inboundGetCIJobTokenScopeQuery from '../graphql/queries/inbound_get_ci_job_token_scope.query.graphql'; +import inboundGetProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql'; +import TokenProjectsTable from './token_projects_table.vue'; + +export default { + i18n: { + toggleLabelTitle: s__('CICD|Allow access to this project with a CI_JOB_TOKEN'), + toggleHelpText: s__( + `CICD|Manage which projects can use their CI_JOB_TOKEN to access this project. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}`, + ), + cardHeaderTitle: s__( + 'CICD|Allow CI job tokens from the following projects to access this project', + ), + settingDisabledMessage: s__( + 'CICD|Enable feature to allow job token access by the following projects.', + ), + addProject: __('Add project'), + cancel: __('Cancel'), + addProjectPlaceholder: __('Paste project path (i.e. gitlab-org/gitlab)'), + projectsFetchError: __('There was a problem fetching the projects'), + scopeFetchError: __('There was a problem fetching the job token scope value'), + }, + fields: [ + { + key: 'project', + label: __('Project with access'), + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-40p', + }, + { + key: 'namespace', + label: __('Namespace'), + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-40p', + }, + { + key: 'actions', + label: '', + tdClass: 'gl-text-right', + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-10p', + }, + ], + components: { + GlAlert, + GlButton, + GlCard, + GlFormInput, + GlLink, + GlLoadingIcon, + GlSprintf, + GlToggle, + TokenProjectsTable, + }, + inject: { + fullPath: { + default: '', + }, + }, + apollo: { + inboundJobTokenScopeEnabled: { + query: inboundGetCIJobTokenScopeQuery, + variables() { + return { + fullPath: this.fullPath, + }; + }, + update({ project }) { + return project.ciCdSettings.inboundJobTokenScopeEnabled; + }, + error() { + createAlert({ message: this.$options.i18n.scopeFetchError }); + }, + }, + projects: { + query: inboundGetProjectsWithCIJobTokenScopeQuery, + variables() { + return { + fullPath: this.fullPath, + }; + }, + update({ project }) { + return project?.ciJobTokenScope?.inboundAllowlist?.nodes ?? []; + }, + error() { + createAlert({ message: this.$options.i18n.projectsFetchError }); + }, + }, + }, + data() { + return { + inboundJobTokenScopeEnabled: null, + targetProjectPath: '', + projects: [], + }; + }, + computed: { + isProjectPathEmpty() { + return this.targetProjectPath === ''; + }, + ciJobTokenHelpPage() { + return helpPagePath('ci/jobs/ci_job_token'); + }, + }, + methods: { + async updateCIJobTokenScope() { + try { + const { + data: { + ciCdSettingsUpdate: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: inboundUpdateCIJobTokenScopeMutation, + variables: { + input: { + fullPath: this.fullPath, + inboundJobTokenScopeEnabled: this.inboundJobTokenScopeEnabled, + }, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + this.inboundJobTokenScopeEnabled = !this.inboundJobTokenScopeEnabled; + createAlert({ message: error.message }); + } + }, + async addProject() { + try { + const { + data: { + ciJobTokenScopeAddProject: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: inboundAddProjectCIJobTokenScopeMutation, + variables: { + projectPath: this.fullPath, + targetProjectPath: this.targetProjectPath, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + createAlert({ message: error.message }); + } finally { + this.clearTargetProjectPath(); + this.getProjects(); + } + }, + async removeProject(removeTargetPath) { + try { + const { + data: { + ciJobTokenScopeRemoveProject: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: inboundRemoveProjectCIJobTokenScopeMutation, + variables: { + projectPath: this.fullPath, + targetProjectPath: removeTargetPath, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + createAlert({ message: error.message }); + } finally { + this.getProjects(); + } + }, + clearTargetProjectPath() { + this.targetProjectPath = ''; + }, + getProjects() { + this.$apollo.queries.projects.refetch(); + }, + }, +}; +</script> +<template> + <div> + <gl-loading-icon v-if="$apollo.loading" size="lg" class="gl-mt-5" /> + <template v-else> + <gl-toggle + v-model="inboundJobTokenScopeEnabled" + :label="$options.i18n.toggleLabelTitle" + @change="updateCIJobTokenScope" + > + <template #help> + <gl-sprintf :message="$options.i18n.toggleHelpText"> + <template #link="{ content }"> + <gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </template> + </gl-toggle> + + <div> + <gl-card class="gl-mt-5 gl-mb-3"> + <template #header> + <h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5> + </template> + <template #default> + <gl-form-input + v-model="targetProjectPath" + :placeholder="$options.i18n.addProjectPlaceholder" + /> + </template> + <template #footer> + <gl-button variant="confirm" :disabled="isProjectPathEmpty" @click="addProject"> + {{ $options.i18n.addProject }} + </gl-button> + <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button> + </template> + </gl-card> + <gl-alert + v-if="!inboundJobTokenScopeEnabled" + class="gl-mb-3" + variant="warning" + :dismissible="false" + :show-icon="false" + > + {{ $options.i18n.settingDisabledMessage }} + </gl-alert> + <token-projects-table + :projects="projects" + :table-fields="$options.fields" + @removeProject="removeProject" + /> + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/token_access/components/token_access.vue b/app/assets/javascripts/token_access/components/outbound_token_access.vue index 527f01f0a6f..47ec0910a7b 100644 --- a/app/assets/javascripts/token_access/components/token_access.vue +++ b/app/assets/javascripts/token_access/components/outbound_token_access.vue @@ -17,7 +17,6 @@ import removeProjectCIJobTokenScopeMutation from '../graphql/mutations/remove_pr import updateCIJobTokenScopeMutation from '../graphql/mutations/update_ci_job_token_scope.mutation.graphql'; import getCIJobTokenScopeQuery from '../graphql/queries/get_ci_job_token_scope.query.graphql'; import getProjectsWithCIJobTokenScopeQuery from '../graphql/queries/get_projects_with_ci_job_token_scope.query.graphql'; -import OptInJwt from './opt_in_jwt.vue'; import TokenProjectsTable from './token_projects_table.vue'; export default { @@ -36,6 +35,27 @@ export default { projectsFetchError: __('There was a problem fetching the projects'), scopeFetchError: __('There was a problem fetching the job token scope value'), }, + fields: [ + { + key: 'project', + label: __('Project that can be accessed'), + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-40p', + }, + { + key: 'namespace', + label: __('Namespace'), + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-40p', + }, + { + key: 'actions', + label: '', + tdClass: 'gl-text-right', + thClass: 'gl-border-t-none!', + columnClass: 'gl-w-10p', + }, + ], components: { GlAlert, GlButton, @@ -45,7 +65,6 @@ export default { GlLoadingIcon, GlSprintf, GlToggle, - OptInJwt, TokenProjectsTable, }, inject: { @@ -230,9 +249,12 @@ export default { > {{ $options.i18n.settingDisabledMessage }} </gl-alert> - <token-projects-table :projects="projects" @removeProject="removeProject" /> + <token-projects-table + :projects="projects" + :table-fields="$options.fields" + @removeProject="removeProject" + /> </div> - <opt-in-jwt /> </template> </div> </template> diff --git a/app/assets/javascripts/token_access/components/token_access_app.vue b/app/assets/javascripts/token_access/components/token_access_app.vue new file mode 100644 index 00000000000..410a6267dc8 --- /dev/null +++ b/app/assets/javascripts/token_access/components/token_access_app.vue @@ -0,0 +1,27 @@ +<script> +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import OutboundTokenAccess from './outbound_token_access.vue'; +import InboundTokenAccess from './inbound_token_access.vue'; +import OptInJwt from './opt_in_jwt.vue'; + +export default { + components: { + OutboundTokenAccess, + InboundTokenAccess, + OptInJwt, + }, + mixins: [glFeatureFlagMixin()], + computed: { + inboundTokenAccessEnabled() { + return this.glFeatures.ciInboundJobTokenScope; + }, + }, +}; +</script> +<template> + <div> + <outbound-token-access /> + <inbound-token-access v-if="inboundTokenAccessEnabled" class="gl-pt-5" /> + <opt-in-jwt /> + </div> +</template> diff --git a/app/assets/javascripts/token_access/components/token_projects_table.vue b/app/assets/javascripts/token_access/components/token_projects_table.vue index ce33478cbee..c00dd882895 100644 --- a/app/assets/javascripts/token_access/components/token_projects_table.vue +++ b/app/assets/javascripts/token_access/components/token_projects_table.vue @@ -1,32 +1,11 @@ <script> import { GlButton, GlTable } from '@gitlab/ui'; -import { __, s__ } from '~/locale'; +import { s__ } from '~/locale'; export default { i18n: { emptyText: s__('CI/CD|No projects have been added to the scope'), }, - fields: [ - { - key: 'project', - label: __('Projects that can be accessed'), - thClass: 'gl-border-t-none!', - columnClass: 'gl-w-40p', - }, - { - key: 'namespace', - label: __('Namespace'), - thClass: 'gl-border-t-none!', - columnClass: 'gl-w-40p', - }, - { - key: 'actions', - label: '', - tdClass: 'gl-text-right', - thClass: 'gl-border-t-none!', - columnClass: 'gl-w-10p', - }, - ], components: { GlButton, GlTable, @@ -41,6 +20,10 @@ export default { type: Array, required: true, }, + tableFields: { + type: Array, + required: true, + }, }, methods: { removeProject(project) { @@ -52,7 +35,7 @@ export default { <template> <gl-table :items="projects" - :fields="$options.fields" + :fields="tableFields" :tbody-tr-attr="{ 'data-testid': 'projects-token-table-row' }" :empty-text="$options.i18n.emptyText" show-empty diff --git a/app/assets/javascripts/token_access/graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql b/app/assets/javascripts/token_access/graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql new file mode 100644 index 00000000000..f030a892af2 --- /dev/null +++ b/app/assets/javascripts/token_access/graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql @@ -0,0 +1,7 @@ +mutation inboundAddProjectCIJobTokenScope($projectPath: ID!, $targetProjectPath: ID!) { + ciJobTokenScopeAddProject( + input: { projectPath: $projectPath, targetProjectPath: $targetProjectPath, direction: INBOUND } + ) { + errors + } +} diff --git a/app/assets/javascripts/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql b/app/assets/javascripts/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql new file mode 100644 index 00000000000..cc6736bb80d --- /dev/null +++ b/app/assets/javascripts/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql @@ -0,0 +1,7 @@ +mutation inboundRemoveProjectCIJobTokenScope($projectPath: ID!, $targetProjectPath: ID!) { + ciJobTokenScopeRemoveProject( + input: { projectPath: $projectPath, targetProjectPath: $targetProjectPath, direction: INBOUND } + ) { + errors + } +} diff --git a/app/assets/javascripts/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql b/app/assets/javascripts/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql new file mode 100644 index 00000000000..aac9feab237 --- /dev/null +++ b/app/assets/javascripts/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql @@ -0,0 +1,8 @@ +mutation inboundUpdateCIJobTokenScope($input: CiCdSettingsUpdateInput!) { + ciCdSettingsUpdate(input: $input) { + ciCdSettings { + inboundJobTokenScopeEnabled + } + errors + } +} diff --git a/app/assets/javascripts/token_access/graphql/queries/inbound_get_ci_job_token_scope.query.graphql b/app/assets/javascripts/token_access/graphql/queries/inbound_get_ci_job_token_scope.query.graphql new file mode 100644 index 00000000000..68d506a6c41 --- /dev/null +++ b/app/assets/javascripts/token_access/graphql/queries/inbound_get_ci_job_token_scope.query.graphql @@ -0,0 +1,8 @@ +query inboundGetCIJobTokenScope($fullPath: ID!) { + project(fullPath: $fullPath) { + id + ciCdSettings { + inboundJobTokenScopeEnabled + } + } +} diff --git a/app/assets/javascripts/token_access/graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql b/app/assets/javascripts/token_access/graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql new file mode 100644 index 00000000000..c51bdcbf7d2 --- /dev/null +++ b/app/assets/javascripts/token_access/graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql @@ -0,0 +1,18 @@ +query inboundGetProjectsWithCIJobTokenScope($fullPath: ID!) { + project(fullPath: $fullPath) { + id + ciJobTokenScope { + inboundAllowlist { + nodes { + id + name + namespace { + id + fullPath + } + fullPath + } + } + } + } +} diff --git a/app/assets/javascripts/token_access/index.js b/app/assets/javascripts/token_access/index.js index 6a29883290a..0253abe393e 100644 --- a/app/assets/javascripts/token_access/index.js +++ b/app/assets/javascripts/token_access/index.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import TokenAccess from './components/token_access.vue'; +import TokenAccessApp from './components/token_access_app.vue'; Vue.use(VueApollo); @@ -25,7 +25,7 @@ export const initTokenAccess = (containerId = 'js-ci-token-access-app') => { fullPath, }, render(createElement) { - return createElement(TokenAccess); + return createElement(TokenAccessApp); }, }); }; diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 545fac1433b..ac355b861b3 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -7,6 +7,8 @@ class Explore::GroupsController < Explore::ApplicationController urgency :low def index - render_group_tree GroupsFinder.new(nil).execute + user = Feature.enabled?(:generic_explore_groups, current_user, type: :experiment) ? nil : current_user + + render_group_tree GroupsFinder.new(user).execute end end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 5ec1f606ef2..5ba0bd67f4d 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -14,6 +14,7 @@ module Projects before_action do push_frontend_feature_flag(:ci_remove_character_limitation_raw_masked_var, type: :development) + push_frontend_feature_flag(:ci_inbound_job_token_scope, @project) end helper_method :highlight_badge diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb index 4bd89a3d4e2..1fec0a916b8 100644 --- a/app/helpers/version_check_helper.rb +++ b/app/helpers/version_check_helper.rb @@ -3,8 +3,6 @@ module VersionCheckHelper include Gitlab::Utils::StrongMemoize - SECURITY_ALERT_SEVERITY = 'danger' - def show_version_check? return false unless Gitlab::CurrentSettings.version_check_enabled return false if User.single_user&.requires_usage_stats_consent? @@ -18,9 +16,9 @@ module VersionCheckHelper strong_memoize_attr :gitlab_version_check def show_security_patch_upgrade_alert? - return false unless Feature.enabled?(:critical_security_alert) && show_version_check? && gitlab_version_check + return false unless show_version_check? && gitlab_version_check - gitlab_version_check['severity'] === SECURITY_ALERT_SEVERITY + Gitlab::Utils.to_boolean(gitlab_version_check['critical_vulnerability']) end def link_to_version diff --git a/app/models/repository.rb b/app/models/repository.rb index f71d7bb8d20..31c65555cb2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -696,22 +696,14 @@ class Repository end def head_tree(skip_flat_paths: true) - if Feature.enabled?(:optimized_head_tree) - return if empty? || root_ref.nil? + return if empty? || root_ref.nil? - @head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths) - elsif head_commit - @head_tree ||= Tree.new(self, head_commit.sha, nil, skip_flat_paths: skip_flat_paths) - end + @head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths) end def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil) if sha == :head - if Feature.enabled?(:optimized_head_tree) - return if empty? || root_ref.nil? - else - return unless head_commit - end + return if empty? || root_ref.nil? if path.nil? return head_tree(skip_flat_paths: skip_flat_paths) diff --git a/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml b/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml index 9fe1400e877..2b1ab9f21e5 100644 --- a/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml +++ b/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml @@ -1,4 +1,3 @@ - return unless show_security_patch_upgrade_alert? -#js-security-patch-upgrade-alert{ data: { "current_version": Gitlab.version_info } } #js-security-patch-upgrade-alert-modal{ data: { "current_version": Gitlab.version_info, "version": gitlab_version_check.to_json } } diff --git a/config/feature_flags/development/critical_security_alert.yml b/config/feature_flags/development/critical_security_alert.yml deleted file mode 100644 index 6bb5100efc8..00000000000 --- a/config/feature_flags/development/critical_security_alert.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: critical_security_alert -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108732 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387719 -milestone: '15.8' -type: development -group: group::distribution -default_enabled: false diff --git a/config/feature_flags/development/optimized_head_tree.yml b/config/feature_flags/development/optimized_head_tree.yml deleted file mode 100644 index 144a1d967dd..00000000000 --- a/config/feature_flags/development/optimized_head_tree.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: optimized_head_tree -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110248 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/389448 -milestone: '15.9' -type: development -group: group::source code -default_enabled: false diff --git a/config/feature_flags/experiment/generic_explore_groups.yml b/config/feature_flags/experiment/generic_explore_groups.yml new file mode 100644 index 00000000000..d928dcd4189 --- /dev/null +++ b/config/feature_flags/experiment/generic_explore_groups.yml @@ -0,0 +1,8 @@ +--- +name: generic_explore_groups +introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103019" +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381564 +milestone: '15.6' +type: experiment +group: group::source code +default_enabled: true diff --git a/data/deprecations/15-9-praefect-metrics-config-variable.yml b/data/deprecations/15-9-praefect-metrics-config-variable.yml index d5a4a9c69fc..71d1ebee3a6 100644 --- a/data/deprecations/15-9-praefect-metrics-config-variable.yml +++ b/data/deprecations/15-9-praefect-metrics-config-variable.yml @@ -1,15 +1,22 @@ # REQUIRED FIELDS # - title: "Support for Praefect custom metrics endpoint configuration" - announcement_milestone: "15.9" # (required) The milestone when this feature was first announced as deprecated. - removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed - breaking_change: true # (required) Change to false if this is not a breaking change. - reporter: mjwood # (required) GitLab username of the person reporting the change - stage: Gitaly # (required) String value of the stage that the feature was created in. e.g., Growth - issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390266 # (required) Link to the deprecation issue in GitLab - body: | # (required) Do not modify this line, instead modify the lines below. - Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated because using it is non-performant. Instead, all metrics - that scrape the Praefect database will be exported to the `/db_metrics` endpoint. This may require updating your metrics collection targets. + announcement_milestone: "15.9" + removal_milestone: "16.0" + breaking_change: true + reporter: mjwood + stage: Gitaly + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390266 + body: | + Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated in GitLab + 15.9 and will be removed in GitLab 16.0. We are removing this configuration value because using it is non-performant. + This change means the following metrics will become unavailable on `/metrics`: + + - `gitaly_praefect_unavailable_repositories`. + - `gitaly_praefect_verification_queue_depth`. + - `gitaly_praefect_replication_queue_depth`. + + This may require updating your metrics collection targets to also scrape `/db_metrics`. # # OPTIONAL END OF SUPPORT FIELDS # diff --git a/db/docs/ar_internal_metadata.yml b/db/docs/ar_internal_metadata.yml index 4e5c0c2d833..34049edf7ba 100644 --- a/db/docs/ar_internal_metadata.yml +++ b/db/docs/ar_internal_metadata.yml @@ -1,10 +1,11 @@ --- table_name: ar_internal_metadata -classes: [] +classes: +- ActiveRecord::InternalMetadata feature_categories: - database -description: >- - An internal table used by ActiveRecord to store information about how the database was migrated. +description: An internal table used by ActiveRecord to store information about how + the database was migrated. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685 milestone: '0.8' gitlab_schema: gitlab_internal diff --git a/db/docs/dast_profiles_tags.yml b/db/docs/dast_profiles_tags.yml index 8210b93f3af..b3129d6a82f 100644 --- a/db/docs/dast_profiles_tags.yml +++ b/db/docs/dast_profiles_tags.yml @@ -1,9 +1,9 @@ --- table_name: dast_profiles_tags classes: - - Dast::ScannerProfileTag +- Dast::ProfileTag feature_categories: - - dynamic_application_security_testing +- dynamic_application_security_testing description: Join Table for Runner tags and DAST Profiles introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108371 milestone: '15.8' diff --git a/db/docs/dast_scanner_profiles_tags.yml b/db/docs/dast_scanner_profiles_tags.yml index 9766ce6c4fc..e423680f862 100644 --- a/db/docs/dast_scanner_profiles_tags.yml +++ b/db/docs/dast_scanner_profiles_tags.yml @@ -1,9 +1,8 @@ --- table_name: dast_scanner_profiles_tags -classes: - - Dast::ScannerProfileTag +classes: [] feature_categories: - - dynamic_application_security_testing +- dynamic_application_security_testing description: Join Table for Runner tags and DAST Scanner Profiles introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104909 milestone: '15.7' diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md index 4916dc12322..0fd4bff1bff 100644 --- a/doc/ci/runners/configure_runners.md +++ b/doc/ci/runners/configure_runners.md @@ -668,7 +668,7 @@ variables: test: script: - - pwd + - pwd -P ``` The `$CI_CONCURRENT_PROJECT_ID` should be used in conjunction with `$CI_PROJECT_PATH` @@ -680,7 +680,7 @@ variables: test: script: - - pwd + - pwd -P ``` #### Nested paths diff --git a/doc/ci/runners/runners_scope.md b/doc/ci/runners/runners_scope.md index baf5e5bfc85..d20ef846df7 100644 --- a/doc/ci/runners/runners_scope.md +++ b/doc/ci/runners/runners_scope.md @@ -109,9 +109,7 @@ shared runner resources. The fair usage queue algorithm assigns jobs based on the projects that have the fewest number of jobs already running on shared runners. -**Example 1** - -If these jobs are in the queue: +For example, if these jobs are in the queue: - Job 1 for Project 1 - Job 2 for Project 1 @@ -120,7 +118,7 @@ If these jobs are in the queue: - Job 5 for Project 2 - Job 6 for Project 3 -The fair usage algorithm assigns jobs in this order: +When several CI/CD jobs run concurrently, the fair usage algorithm assigns jobs in this order: 1. Job 1 is first, because it has the lowest job number from projects with no running jobs (that is, all projects). 1. Job 4 is next, because 4 is now the lowest job number from projects with no running jobs (Project 1 has a job running). @@ -129,20 +127,7 @@ The fair usage algorithm assigns jobs in this order: 1. Job 5 is next, because Project 1 now has 2 jobs running and Job 5 is the lowest remaining job number between Projects 2 and 3. 1. Finally is Job 3... because it's the only job left. ---- - -**Example 2** - -If these jobs are in the queue: - -- Job 1 for Project 1 -- Job 2 for Project 1 -- Job 3 for Project 1 -- Job 4 for Project 2 -- Job 5 for Project 2 -- Job 6 for Project 3 - -The fair usage algorithm assigns jobs in this order: +When only one job runs at a time, the fair usage algorithm assigns jobs in this order: 1. Job 1 is chosen first, because it has the lowest job number from projects with no running jobs (that is, all projects). 1. We finish Job 1. diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index e2ac52929e0..79cd864b36a 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -179,8 +179,15 @@ WARNING: This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). Review the details carefully before upgrading. -Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated because using it is non-performant. Instead, all metrics -that scrape the Praefect database will be exported to the `/db_metrics` endpoint. This may require updating your metrics collection targets. +Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated in GitLab +15.9 and will be removed in GitLab 16.0. We are removing this configuration value because using it is non-performant. +This change means the following metrics will become unavailable on `/metrics`: + +- `gitaly_praefect_unavailable_repositories`. +- `gitaly_praefect_verification_queue_depth`. +- `gitaly_praefect_replication_queue_depth`. + +This may require updating your metrics collection targets to also scrape `/db_metrics`. </div> </div> diff --git a/doc/update/index.md b/doc/update/index.md index ade300e4a64..ef93380117c 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -275,11 +275,11 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap ### 15.7.2 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.7.1 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.7.0 @@ -320,23 +320,31 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap Sites that have configured `max_concurrency` will not be affected by this change. [Read more about the Sidekiq concurrency setting](../administration/sidekiq/extra_sidekiq_processes.md#concurrency). -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. + +### 15.6.6 + +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. + +### 15.6.5 + +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.6.4 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6, and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.6.3 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.6.2 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.6.1 -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.6.0 @@ -356,7 +364,7 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap This issue was [fixed in GitLab 15.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105375) and backported to GitLab 15.6.2. The issue can also be worked around: [read about how to create these indexes](https://gitlab.com/gitlab-org/gitlab/-/issues/378343#note_1199863087). -- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. +- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. ### 15.5.0 diff --git a/doc/user/ssh.md b/doc/user/ssh.md index c21fd1ac82a..003bc5c91c1 100644 --- a/doc/user/ssh.md +++ b/doc/user/ssh.md @@ -13,6 +13,21 @@ GitLab uses the SSH protocol to securely communicate with Git. When you use SSH keys to authenticate to the GitLab remote server, you don't need to supply your username and password each time. +## What are SSH keys + +SSH uses two keys, a public key and a private key. + +- The public key can be distributed. +- The private key should be protected. + +When you need to copy or upload your SSH public key, make sure you do not accidentally copy or upload your private key instead. + +You cannot expose data by uploading your public key. For example, you can use your public key to +[sign commits](project/repository/ssh_signed_commits/index.md), +which makes your use of GitLab and your data even more secure. + +For details, see [Asymmetric cryptography, also known as public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography). + ## Prerequisites To use SSH to communicate with GitLab, you need: diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index dd4fbc7c8f6..51e361de73c 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -485,7 +485,7 @@ namespace :gitlab do outdated = true end - if existing_metadata['classes'].sort != table_metadata['classes'].sort + if existing_metadata['classes'] && existing_metadata['classes'].sort != table_metadata['classes'].sort existing_metadata['classes'] = table_metadata['classes'] outdated = true end @@ -514,10 +514,9 @@ namespace :gitlab do File.join(path, sub_directory, "#{source_name}.yml") end - # Temporary disable this, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85760#note_998452069 - # Rake::Task['db:migrate'].enhance do - # Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development? - # end + Rake::Task['db:migrate'].enhance do + Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development? + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 63a720125d7..ba3ea693b8f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7783,6 +7783,12 @@ msgstr "" msgid "CICD|Add an existing project to the scope" msgstr "" +msgid "CICD|Allow CI job tokens from the following projects to access this project" +msgstr "" + +msgid "CICD|Allow access to this project with a CI_JOB_TOKEN" +msgstr "" + msgid "CICD|An error occurred while update the setting. Please try again." msgstr "" @@ -7807,6 +7813,9 @@ msgstr "" msgid "CICD|Deployment strategy" msgstr "" +msgid "CICD|Enable feature to allow job token access by the following projects." +msgstr "" + msgid "CICD|Enable feature to limit job token access to the following projects." msgstr "" @@ -7822,6 +7831,9 @@ msgstr "" msgid "CICD|Limit JSON Web Token (JWT) access" msgstr "" +msgid "CICD|Manage which projects can use their CI_JOB_TOKEN to access this project. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}" +msgstr "" + msgid "CICD|Select the projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}" msgstr "" @@ -12766,9 +12778,6 @@ msgstr "" msgid "DastProfiles|Manage profiles" msgstr "" -msgid "DastProfiles|Manage site profiles" -msgstr "" - msgid "DastProfiles|Minimum = 0 (no timeout enabled), Maximum = 2880 minutes" msgstr "" @@ -27387,9 +27396,6 @@ msgstr "" msgid "Months" msgstr "" -msgid "More" -msgstr "" - msgid "More Details" msgstr "" @@ -27414,6 +27420,9 @@ msgstr "" msgid "More information." msgstr "" +msgid "More options" +msgstr "" + msgid "More than %{number_commits_distance} commits different with %{default_branch}" msgstr "" @@ -33014,6 +33023,9 @@ msgstr "" msgid "Project slug" msgstr "" +msgid "Project that can be accessed" +msgstr "" + msgid "Project uploads" msgstr "" @@ -33026,6 +33038,9 @@ msgstr "" msgid "Project was not found or you do not have permission to add this project to Security Dashboards." msgstr "" +msgid "Project with access" +msgstr "" + msgid "Project: %{name}" msgstr "" @@ -33890,9 +33905,6 @@ msgstr "" msgid "Projects shared with %{group_name}" msgstr "" -msgid "Projects that can be accessed" -msgstr "" - msgid "Projects to index" msgstr "" @@ -46657,9 +46669,6 @@ msgstr "" msgid "VersionCheck|%{details}" msgstr "" -msgid "VersionCheck|Critical security upgrade available" -msgstr "" - msgid "VersionCheck|Important notice - Critical security release" msgstr "" @@ -46687,9 +46696,6 @@ msgstr "" msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}." msgstr "" -msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}" -msgstr "" - msgid "VersionCheck|Your GitLab Version" msgstr "" diff --git a/package.json b/package.json index 1953edd068d..b2624d45395 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", "@gitlab/fonts": "^1.2.0", - "@gitlab/svgs": "3.18.0", - "@gitlab/ui": "55.0.1", + "@gitlab/svgs": "3.20.0", + "@gitlab/ui": "55.1.0", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "0.0.1-dev-20230120231236", "@rails/actioncable": "6.1.4-7", diff --git a/spec/controllers/explore/groups_controller_spec.rb b/spec/controllers/explore/groups_controller_spec.rb index d53cb233df9..a3bd8102462 100644 --- a/spec/controllers/explore/groups_controller_spec.rb +++ b/spec/controllers/explore/groups_controller_spec.rb @@ -40,4 +40,12 @@ RSpec.describe Explore::GroupsController do end it_behaves_like 'explore groups' + + context 'generic_explore_groups flag is disabled' do + before do + stub_feature_flags(generic_explore_groups: false) + end + + it_behaves_like 'explore groups' + end end diff --git a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js index a23f8370adf..d4fc47601cf 100644 --- a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js +++ b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js @@ -1,10 +1,9 @@ -import { GlDropdown } from '@gitlab/ui'; +import { GlDisclosureDropdown } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdown.vue'; import Diagram from '~/content_editor/extensions/diagram'; import HorizontalRule from '~/content_editor/extensions/horizontal_rule'; import eventHubFactory from '~/helpers/event_hub_factory'; -import { stubComponent } from 'helpers/stub_component'; import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils'; describe('content_editor/components/toolbar_more_dropdown', () => { @@ -25,14 +24,11 @@ describe('content_editor/components/toolbar_more_dropdown', () => { tiptapEditor, eventHub, }, - stubs: { - GlDropdown: stubComponent(GlDropdown), - }, propsData, }); }; - const findDropdown = () => wrapper.findComponent(GlDropdown); + const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); beforeEach(() => { buildEditor(); @@ -60,7 +56,7 @@ describe('content_editor/components/toolbar_more_dropdown', () => { beforeEach(async () => { commands = mockChainedCommands(tiptapEditor, [command, 'focus', 'run']); - btn = wrapper.findByRole('menuitem', { name }); + btn = wrapper.findByRole('button', { name }); }); it(`inserts a ${contentType}`, async () => { @@ -76,12 +72,11 @@ describe('content_editor/components/toolbar_more_dropdown', () => { }); describe('a11y tests', () => { - it('sets text, title, and text-sr-only properties to the table button dropdown', () => { + it('sets toggleText and text-sr-only properties to the table button dropdown', () => { expect(findDropdown().props()).toMatchObject({ - text: 'More', textSrOnly: true, + toggleText: 'More options', }); - expect(findDropdown().attributes('title')).toBe('More'); }); }); }); diff --git a/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js b/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js deleted file mode 100644 index 665dacd5c47..00000000000 --- a/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js +++ /dev/null @@ -1,84 +0,0 @@ -import { GlAlert, GlButton, GlLink, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; -import SecurityPatchUpgradeAlert from '~/gitlab_version_check/components/security_patch_upgrade_alert.vue'; -import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '~/gitlab_version_check/constants'; - -describe('SecurityPatchUpgradeAlert', () => { - let wrapper; - let trackingSpy; - - const defaultProps = { - currentVersion: '99.9', - }; - - const createComponent = () => { - trackingSpy = mockTracking(undefined, undefined, jest.spyOn); - - wrapper = shallowMount(SecurityPatchUpgradeAlert, { - propsData: { - ...defaultProps, - }, - stubs: { - GlAlert, - GlSprintf, - }, - }); - }; - - afterEach(() => { - unmockTracking(); - }); - - const findGlAlert = () => wrapper.findComponent(GlAlert); - const findGlButton = () => wrapper.findComponent(GlButton); - const findGlLink = () => wrapper.findComponent(GlLink); - - describe('template', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders non-dismissible GlAlert with version information', () => { - expect(findGlAlert().text()).toContain( - `You are currently on version ${defaultProps.currentVersion}.`, - ); - expect(findGlAlert().props('dismissible')).toBe(false); - }); - - it('tracks render security_patch_upgrade_alert correctly', () => { - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'render', { - label: 'security_patch_upgrade_alert', - property: defaultProps.currentVersion, - }); - }); - - it('renders GlLink with correct text and link', () => { - expect(findGlLink().text()).toBe('Learn more about this critical security release.'); - expect(findGlLink().attributes('href')).toBe(ABOUT_RELEASES_PAGE); - }); - - it('tracks click security_patch_upgrade_alert_learn_more when link is clicked', async () => { - await findGlLink().vm.$emit('click'); - - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', { - label: 'security_patch_upgrade_alert_learn_more', - property: defaultProps.currentVersion, - }); - }); - - it('renders GlButton with correct text and link', () => { - expect(findGlButton().text()).toBe('Upgrade now'); - expect(findGlButton().attributes('href')).toBe(UPGRADE_DOCS_URL); - }); - - it('tracks click security_patch_upgrade_alert_upgrade_now when button is clicked', async () => { - await findGlButton().vm.$emit('click'); - - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', { - label: 'security_patch_upgrade_alert_upgrade_now', - property: defaultProps.currentVersion, - }); - }); - }); -}); diff --git a/spec/frontend/gitlab_version_check/index_spec.js b/spec/frontend/gitlab_version_check/index_spec.js index 92bc103cede..893105969ed 100644 --- a/spec/frontend/gitlab_version_check/index_spec.js +++ b/spec/frontend/gitlab_version_check/index_spec.js @@ -7,9 +7,6 @@ import { VERSION_CHECK_BADGE_FIXTURE, VERSION_CHECK_BADGE_FINDER, VERSION_BADGE_TEXT, - SECURITY_PATCH_FIXTURE, - SECURITY_PATCH_FINDER, - SECURITY_PATCH_TEXT, SECURITY_MODAL_FIXTURE, SECURITY_MODAL_FINDER, SECURITY_MODAL_TEXT, @@ -29,15 +26,13 @@ describe('initGitlabVersionCheck', () => { }); describe.each` - description | fixture | finders | componentTexts - ${'with no version check elements'} | ${'<div></div>'} | ${[]} | ${[]} - ${'with version check badge el but no prop data'} | ${VERSION_CHECK_BADGE_NO_PROP_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]} - ${'with version check badge el but no severity data'} | ${VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]} - ${'with version check badge el and version data'} | ${VERSION_CHECK_BADGE_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[VERSION_BADGE_TEXT]} - ${'with security patch el'} | ${SECURITY_PATCH_FIXTURE} | ${[SECURITY_PATCH_FINDER]} | ${[SECURITY_PATCH_TEXT]} - ${'with security patch and version badge els'} | ${`${SECURITY_PATCH_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_PATCH_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_PATCH_TEXT, VERSION_BADGE_TEXT]} - ${'with security modal el'} | ${SECURITY_MODAL_FIXTURE} | ${[SECURITY_MODAL_FINDER]} | ${[SECURITY_MODAL_TEXT]} - ${'with security modal, security patch, and version badge els'} | ${`${SECURITY_PATCH_FIXTURE}${SECURITY_MODAL_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_PATCH_FINDER, SECURITY_MODAL_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_PATCH_TEXT, SECURITY_MODAL_TEXT, VERSION_BADGE_TEXT]} + description | fixture | finders | componentTexts + ${'with no version check elements'} | ${'<div></div>'} | ${[]} | ${[]} + ${'with version check badge el but no prop data'} | ${VERSION_CHECK_BADGE_NO_PROP_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]} + ${'with version check badge el but no severity data'} | ${VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]} + ${'with version check badge el and version data'} | ${VERSION_CHECK_BADGE_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[VERSION_BADGE_TEXT]} + ${'with security modal el'} | ${SECURITY_MODAL_FIXTURE} | ${[SECURITY_MODAL_FINDER]} | ${[SECURITY_MODAL_TEXT]} + ${'with security modal and version badge els'} | ${`${SECURITY_MODAL_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_MODAL_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_MODAL_TEXT, VERSION_BADGE_TEXT]} `('$description', ({ fixture, finders, componentTexts }) => { beforeEach(() => { createApp(fixture); diff --git a/spec/frontend/gitlab_version_check/mock_data.js b/spec/frontend/gitlab_version_check/mock_data.js index 707d45550eb..fb8c9ec81a7 100644 --- a/spec/frontend/gitlab_version_check/mock_data.js +++ b/spec/frontend/gitlab_version_check/mock_data.js @@ -9,12 +9,6 @@ export const VERSION_CHECK_BADGE_FINDER = '[data-testid="badge-click-wrapper"]'; export const VERSION_BADGE_TEXT = 'Up to date'; -export const SECURITY_PATCH_FIXTURE = `<div id="js-security-patch-upgrade-alert" data-current-version="15.1"></div>`; - -export const SECURITY_PATCH_FINDER = 'h2'; - -export const SECURITY_PATCH_TEXT = 'Critical security upgrade available'; - export const SECURITY_MODAL_FIXTURE = `<div id="js-security-patch-upgrade-alert-modal" data-current-version="15.1" data-version='{ "details": "test details", "latest-stable-versions": "[]" }'></div>`; export const SECURITY_MODAL_FINDER = '[data-testid="alert-modal-title"]'; diff --git a/spec/frontend/token_access/inbound_token_access_spec.js b/spec/frontend/token_access/inbound_token_access_spec.js new file mode 100644 index 00000000000..fcd1a33fa68 --- /dev/null +++ b/spec/frontend/token_access/inbound_token_access_spec.js @@ -0,0 +1,311 @@ +import { GlAlert, GlFormInput, GlToggle, GlLoadingIcon } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { createAlert } from '~/flash'; +import InboundTokenAccess from '~/token_access/components/inbound_token_access.vue'; +import inboundAddProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql'; +import inboundRemoveProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql'; +import inboundUpdateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql'; +import inboundGetCIJobTokenScopeQuery from '~/token_access/graphql/queries/inbound_get_ci_job_token_scope.query.graphql'; +import inboundGetProjectsWithCIJobTokenScopeQuery from '~/token_access/graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql'; +import { + inboundJobTokenScopeEnabledResponse, + inboundJobTokenScopeDisabledResponse, + inboundProjectsWithScopeResponse, + inboundAddProjectSuccessResponse, + inboundRemoveProjectSuccess, + inboundUpdateScopeSuccessResponse, +} from './mock_data'; + +const projectPath = 'root/my-repo'; +const message = 'An error occurred'; +const error = new Error(message); + +Vue.use(VueApollo); + +jest.mock('~/flash'); + +describe('TokenAccess component', () => { + let wrapper; + + const inboundJobTokenScopeEnabledResponseHandler = jest + .fn() + .mockResolvedValue(inboundJobTokenScopeEnabledResponse); + const inboundJobTokenScopeDisabledResponseHandler = jest + .fn() + .mockResolvedValue(inboundJobTokenScopeDisabledResponse); + const inboundProjectsWithScopeResponseHandler = jest + .fn() + .mockResolvedValue(inboundProjectsWithScopeResponse); + const inboundAddProjectSuccessResponseHandler = jest + .fn() + .mockResolvedValue(inboundAddProjectSuccessResponse); + const inboundRemoveProjectSuccessHandler = jest + .fn() + .mockResolvedValue(inboundRemoveProjectSuccess); + const inboundUpdateScopeSuccessResponseHandler = jest + .fn() + .mockResolvedValue(inboundUpdateScopeSuccessResponse); + const failureHandler = jest.fn().mockRejectedValue(error); + + const findToggle = () => wrapper.findComponent(GlToggle); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' }); + const findCancelBtn = () => wrapper.findByRole('button', { name: 'Cancel' }); + const findProjectInput = () => wrapper.findComponent(GlFormInput); + const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' }); + const findTokenDisabledAlert = () => wrapper.findComponent(GlAlert); + + const createMockApolloProvider = (requestHandlers) => { + return createMockApollo(requestHandlers); + }; + + const createComponent = (requestHandlers, mountFn = shallowMountExtended) => { + wrapper = mountFn(InboundTokenAccess, { + provide: { + fullPath: projectPath, + }, + apolloProvider: createMockApolloProvider(requestHandlers), + data() { + return { + targetProjectPath: 'root/test', + }; + }, + }); + }; + + describe('loading state', () => { + it('shows loading state while waiting on query to resolve', async () => { + createComponent([ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ]); + + expect(findLoadingIcon().exists()).toBe(true); + + await waitForPromises(); + + expect(findLoadingIcon().exists()).toBe(false); + }); + }); + + describe('fetching projects and scope', () => { + it('fetches projects and scope correctly', () => { + const expectedVariables = { + fullPath: 'root/my-repo', + }; + + createComponent([ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ]); + + expect(inboundJobTokenScopeEnabledResponseHandler).toHaveBeenCalledWith(expectedVariables); + expect(inboundProjectsWithScopeResponseHandler).toHaveBeenCalledWith(expectedVariables); + }); + + it('handles fetch projects error correctly', async () => { + createComponent([ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, failureHandler], + ]); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: 'There was a problem fetching the projects', + }); + }); + + it('handles fetch scope error correctly', async () => { + createComponent([ + [inboundGetCIJobTokenScopeQuery, failureHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ]); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: 'There was a problem fetching the job token scope value', + }); + }); + }); + + describe('toggle', () => { + it('the toggle is on and the alert is hidden', async () => { + createComponent([ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ]); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(true); + expect(findTokenDisabledAlert().exists()).toBe(false); + }); + + it('the toggle is off and the alert is visible', async () => { + createComponent([ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeDisabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ]); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(false); + expect(findTokenDisabledAlert().exists()).toBe(true); + }); + + describe('update ci job token scope', () => { + it('calls inboundUpdateCIJobTokenScopeMutation mutation', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundUpdateCIJobTokenScopeMutation, inboundUpdateScopeSuccessResponseHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(true); + + findToggle().vm.$emit('change', false); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(false); + expect(inboundUpdateScopeSuccessResponseHandler).toHaveBeenCalledWith({ + input: { + fullPath: 'root/my-repo', + inboundJobTokenScopeEnabled: false, + }, + }); + }); + + it('handles update scope error correctly', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeDisabledResponseHandler], + [inboundUpdateCIJobTokenScopeMutation, failureHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(false); + + findToggle().vm.$emit('change', true); + + await waitForPromises(); + + expect(findToggle().props('value')).toBe(false); + expect(createAlert).toHaveBeenCalledWith({ message }); + }); + }); + }); + + describe('add project', () => { + it('calls add project mutation', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + [inboundAddProjectCIJobTokenScopeMutation, inboundAddProjectSuccessResponseHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + findAddProjectBtn().trigger('click'); + + expect(inboundAddProjectSuccessResponseHandler).toHaveBeenCalledWith({ + projectPath, + targetProjectPath: 'root/test', + }); + }); + + it('add project handles error correctly', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + [inboundAddProjectCIJobTokenScopeMutation, failureHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + findAddProjectBtn().trigger('click'); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ message }); + }); + + it('clicking cancel clears target path', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + expect(findProjectInput().element.value).toBe('root/test'); + + await findCancelBtn().trigger('click'); + + expect(findProjectInput().element.value).toBe(''); + }); + }); + + describe('remove project', () => { + it('calls remove project mutation', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + [inboundRemoveProjectCIJobTokenScopeMutation, inboundRemoveProjectSuccessHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + findRemoveProjectBtn().trigger('click'); + + expect(inboundRemoveProjectSuccessHandler).toHaveBeenCalledWith({ + projectPath, + targetProjectPath: 'root/ci-project', + }); + }); + + it('remove project handles error correctly', async () => { + createComponent( + [ + [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], + [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], + [inboundRemoveProjectCIJobTokenScopeMutation, failureHandler], + ], + mountExtended, + ); + + await waitForPromises(); + + findRemoveProjectBtn().trigger('click'); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ message }); + }); + }); +}); diff --git a/spec/frontend/token_access/mock_data.js b/spec/frontend/token_access/mock_data.js index fff5a0ad4d0..ab04735b985 100644 --- a/spec/frontend/token_access/mock_data.js +++ b/spec/frontend/token_access/mock_data.js @@ -106,6 +106,21 @@ export const mockProjects = [ }, ]; +export const mockFields = [ + { + key: 'project', + label: 'Project with access', + }, + { + key: 'namespace', + label: 'Namespace', + }, + { + key: 'actions', + label: '', + }, +]; + export const optInJwtQueryResponse = (optInJwt) => ({ data: { project: { @@ -131,3 +146,84 @@ export const optInJwtMutationResponse = (optInJwt) => ({ }, }, }); + +export const inboundJobTokenScopeEnabledResponse = { + data: { + project: { + id: '1', + ciCdSettings: { + inboundJobTokenScopeEnabled: true, + __typename: 'ProjectCiCdSetting', + }, + __typename: 'Project', + }, + }, +}; + +export const inboundJobTokenScopeDisabledResponse = { + data: { + project: { + id: '1', + ciCdSettings: { + inboundJobTokenScopeEnabled: false, + __typename: 'ProjectCiCdSetting', + }, + __typename: 'Project', + }, + }, +}; + +export const inboundProjectsWithScopeResponse = { + data: { + project: { + __typename: 'Project', + id: '1', + ciJobTokenScope: { + __typename: 'CiJobTokenScopeType', + inboundAllowlist: { + __typename: 'ProjectConnection', + nodes: [ + { + __typename: 'Project', + fullPath: 'root/ci-project', + id: 'gid://gitlab/Project/23', + name: 'ci-project', + namespace: { id: 'gid://gitlab/Namespaces::UserNamespace/1', fullPath: 'root' }, + }, + ], + }, + }, + }, + }, +}; + +export const inboundAddProjectSuccessResponse = { + data: { + ciJobTokenScopeAddProject: { + errors: [], + __typename: 'CiJobTokenScopeAddProjectPayload', + }, + }, +}; + +export const inboundRemoveProjectSuccess = { + data: { + ciJobTokenScopeRemoveProject: { + errors: [], + __typename: 'CiJobTokenScopeRemoveProjectPayload', + }, + }, +}; + +export const inboundUpdateScopeSuccessResponse = { + data: { + ciCdSettingsUpdate: { + ciCdSettings: { + inboundJobTokenScopeEnabled: false, + __typename: 'ProjectCiCdSetting', + }, + errors: [], + __typename: 'CiCdSettingsUpdatePayload', + }, + }, +}; diff --git a/spec/frontend/token_access/token_access_spec.js b/spec/frontend/token_access/outbound_token_access_spec.js index 62f546463a1..893a021197f 100644 --- a/spec/frontend/token_access/token_access_spec.js +++ b/spec/frontend/token_access/outbound_token_access_spec.js @@ -5,8 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/flash'; -import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; -import TokenAccess from '~/token_access/components/token_access.vue'; +import OutboundTokenAccess from '~/token_access/components/outbound_token_access.vue'; import addProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/add_project_ci_job_token_scope.mutation.graphql'; import removeProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql'; import updateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/update_ci_job_token_scope.mutation.graphql'; @@ -41,7 +40,6 @@ describe('TokenAccess component', () => { const failureHandler = jest.fn().mockRejectedValue(error); const findToggle = () => wrapper.findComponent(GlToggle); - const findOptInJwt = () => wrapper.findComponent(OptInJwt); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' }); const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' }); @@ -52,7 +50,7 @@ describe('TokenAccess component', () => { }; const createComponent = (requestHandlers, mountFn = shallowMountExtended) => { - wrapper = mountFn(TokenAccess, { + wrapper = mountFn(OutboundTokenAccess, { provide: { fullPath: projectPath, }, @@ -65,10 +63,6 @@ describe('TokenAccess component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('loading state', () => { it('shows loading state while waiting on query to resolve', async () => { createComponent([ @@ -84,21 +78,6 @@ describe('TokenAccess component', () => { }); }); - describe('template', () => { - beforeEach(async () => { - createComponent([ - [getCIJobTokenScopeQuery, enabledJobTokenScopeHandler], - [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler], - ]); - - await waitForPromises(); - }); - - it('renders the opt in jwt component', () => { - expect(findOptInJwt().exists()).toBe(true); - }); - }); - describe('fetching projects and scope', () => { it('fetches projects and scope correctly', () => { const expectedVariables = { diff --git a/spec/frontend/token_access/token_access_app_spec.js b/spec/frontend/token_access/token_access_app_spec.js new file mode 100644 index 00000000000..7f269ee5fda --- /dev/null +++ b/spec/frontend/token_access/token_access_app_spec.js @@ -0,0 +1,47 @@ +import { shallowMount } from '@vue/test-utils'; +import OutboundTokenAccess from '~/token_access/components/outbound_token_access.vue'; +import InboundTokenAccess from '~/token_access/components/inbound_token_access.vue'; +import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; +import TokenAccessApp from '~/token_access/components/token_access_app.vue'; + +describe('TokenAccessApp component', () => { + let wrapper; + + const findOutboundTokenAccess = () => wrapper.findComponent(OutboundTokenAccess); + const findInboundTokenAccess = () => wrapper.findComponent(InboundTokenAccess); + const findOptInJwt = () => wrapper.findComponent(OptInJwt); + + const createComponent = (flagState = false) => { + wrapper = shallowMount(TokenAccessApp, { + provide: { + glFeatures: { ciInboundJobTokenScope: flagState }, + }, + }); + }; + + describe('default', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the opt in jwt component', () => { + expect(findOptInJwt().exists()).toBe(true); + }); + + it('renders the outbound token access component', () => { + expect(findOutboundTokenAccess().exists()).toBe(true); + }); + + it('does not render the inbound token access component', () => { + expect(findInboundTokenAccess().exists()).toBe(false); + }); + }); + + describe('with feature flag enabled', () => { + it('renders the inbound token access component', () => { + createComponent(true); + + expect(findInboundTokenAccess().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/token_access/token_projects_table_spec.js b/spec/frontend/token_access/token_projects_table_spec.js index 0fa1a2453f7..b51d8b3ccea 100644 --- a/spec/frontend/token_access/token_projects_table_spec.js +++ b/spec/frontend/token_access/token_projects_table_spec.js @@ -1,7 +1,7 @@ import { GlTable, GlButton } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import TokenProjectsTable from '~/token_access/components/token_projects_table.vue'; -import { mockProjects } from './mock_data'; +import { mockProjects, mockFields } from './mock_data'; describe('Token projects table', () => { let wrapper; @@ -12,6 +12,7 @@ describe('Token projects table', () => { fullPath: 'root/ci-project', }, propsData: { + tableFields: mockFields, projects: mockProjects, }, }); @@ -28,10 +29,6 @@ describe('Token projects table', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('displays a table', () => { expect(findTable().exists()).toBe(true); }); diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb index c76eb08820a..1c8eacf088a 100644 --- a/spec/helpers/version_check_helper_spec.rb +++ b/spec/helpers/version_check_helper_spec.rb @@ -49,26 +49,29 @@ RSpec.describe VersionCheckHelper do describe '#show_security_patch_upgrade_alert?' do describe 'return conditions' do - where(:feature_enabled, :show_version_check, :gitlab_version_check, :result) do + where(:show_version_check, :gitlab_version_check, :result) do [ - [false, false, nil, false], - [false, false, { "severity" => "success" }, false], - [false, false, { "severity" => "danger" }, false], - [false, true, nil, false], - [false, true, { "severity" => "success" }, false], - [false, true, { "severity" => "danger" }, false], - [true, false, nil, false], - [true, false, { "severity" => "success" }, false], - [true, false, { "severity" => "danger" }, false], - [true, true, nil, false], - [true, true, { "severity" => "success" }, false], - [true, true, { "severity" => "danger" }, true] + [false, nil, false], + [false, { "severity" => "success" }, false], + [false, { "severity" => "danger" }, false], + [false, { "severity" => "danger", "critical_vulnerability" => 'some text' }, false], + [false, { "severity" => "danger", "critical_vulnerability" => 'false' }, false], + [false, { "severity" => "danger", "critical_vulnerability" => false }, false], + [false, { "severity" => "danger", "critical_vulnerability" => 'true' }, false], + [false, { "severity" => "danger", "critical_vulnerability" => true }, false], + [true, nil, false], + [true, { "severity" => "success" }, nil], + [true, { "severity" => "danger" }, nil], + [true, { "severity" => "danger", "critical_vulnerability" => 'some text' }, nil], + [true, { "severity" => "danger", "critical_vulnerability" => 'false' }, false], + [true, { "severity" => "danger", "critical_vulnerability" => false }, false], + [true, { "severity" => "danger", "critical_vulnerability" => 'true' }, true], + [true, { "severity" => "danger", "critical_vulnerability" => true }, true] ] end with_them do before do - stub_feature_flags(critical_security_alert: feature_enabled) allow(helper).to receive(:show_version_check?).and_return(show_version_check) allow(helper).to receive(:gitlab_version_check).and_return(gitlab_version_check) end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index e9de636bb0d..f3a8a9cded3 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2775,16 +2775,6 @@ RSpec.describe Repository, feature_category: :source_code_management do it 'returns a Tree' do expect(repository.head_tree).to be_an_instance_of(Tree) end - - context 'when feature flag "optimized_head_tree" is disabled' do - before do - stub_feature_flags(optimized_head_tree: false) - end - - it 'returns a Tree' do - expect(repository.head_tree).to be_an_instance_of(Tree) - end - end end context 'with a non-existing repository' do @@ -2793,18 +2783,6 @@ RSpec.describe Repository, feature_category: :source_code_management do expect(repository.head_tree).to be_nil end - - context 'when feature flag "optimized_head_tree" is disabled' do - before do - stub_feature_flags(optimized_head_tree: false) - end - - it 'returns nil' do - expect(repository).to receive(:head_commit).and_return(nil) - - expect(repository.head_tree).to be_nil - end - end end end @@ -2817,33 +2795,16 @@ RSpec.describe Repository, feature_category: :source_code_management do let(:pagination_params) { nil } context 'using a non-existing repository' do - context 'when feature flag "optimized_head_tree" is enabled' do - before do - allow(repository).to receive(:root_ref).and_return(nil) - end - - it { is_expected.to be_nil } - - context 'when path is defined' do - let(:path) { 'README.md' } - - it { is_expected.to be_nil } - end + before do + allow(repository).to receive(:root_ref).and_return(nil) end - context 'when feature flag "optimized_head_tree" is disabled' do - before do - stub_feature_flags(optimized_head_tree: false) - allow(repository).to receive(:head_commit).and_return(nil) - end - - it { is_expected.to be_nil } + it { is_expected.to be_nil } - context 'when path is defined' do - let(:path) { 'README.md' } + context 'when path is defined' do + let(:path) { 'README.md' } - it { is_expected.to be_nil } - end + it { is_expected.to be_nil } end end diff --git a/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb index 4387a3f5b07..c94511a0c82 100644 --- a/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb +++ b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb @@ -9,10 +9,6 @@ RSpec.describe 'shared/gitlab_version/_security_patch_upgrade_alert' do render end - it 'renders the security patch upgrade alert' do - expect(rendered).to have_selector('#js-security-patch-upgrade-alert') - end - it 'renders the security patch upgrade alert modal' do expect(rendered).to have_selector('#js-security-patch-upgrade-alert-modal') end diff --git a/yarn.lock b/yarn.lock index 495ce755cb2..59e654c52e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1342,15 +1342,15 @@ stylelint-declaration-strict-value "1.8.0" stylelint-scss "4.2.0" -"@gitlab/svgs@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.18.0.tgz#d89364feb42404a35824a54d518b51af8c1e900f" - integrity sha512-ni9TmhusXpt/3k/DZzovMEUeB/6UTXiDpuujI8HDBqR4Mwlah6FBco5ZfolkW6YjFL0YvtcLWhnwZA0iM3hfMw== - -"@gitlab/ui@55.0.1": - version "55.0.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-55.0.1.tgz#3d5c22b864d64435eff01fb39e4edb6e1b8a0432" - integrity sha512-vPwmGhc73HTSBGoMi4jL+Wf11w/1o62pwXuYSYNOs2xis1xMLJiaPBGXNKDFliC5AaRDXAIxCiC5CMJZH3qXMg== +"@gitlab/svgs@3.20.0": + version "3.20.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.20.0.tgz#4ee4f2f24304d13ccce58f82c2ecd87e556f35b4" + integrity sha512-nYTF4j5kon4XbBr/sAzuubgxjIne9+RTZLmSrSaL9FL4eyuv9aa7YMCcOrlIbYX5jlSYlcD+ck2F2M1sqXXOBA== + +"@gitlab/ui@55.1.0": + version "55.1.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-55.1.0.tgz#a2ea6951365c495df7acdd1351b1660771607b67" + integrity sha512-0E+l76jNsK3BPqQmbuTKAvC4RfjQfpaLvmlShe8wxrnMrS0IKsse43RST0ttV+mhkOVfac0me8pDhN4ijSm7Tw== dependencies: "@popperjs/core" "^2.11.2" bootstrap-vue "2.20.1" |