diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 06:14:04 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 06:14:04 +0300 |
commit | 7be0c0133d5a2b11a346d8d2ec17a58028ad79ef (patch) | |
tree | 2df64a9d2ab129c129e7c465faeaf6f1e7ba5d4f | |
parent | cc2623bcc3040a8b9c9a302e3f660c0ca65a7699 (diff) |
Add latest changes from gitlab-org/gitlab@master
27 files changed, 467 insertions, 206 deletions
diff --git a/app/assets/javascripts/groups/components/transfer_group_form.vue b/app/assets/javascripts/groups/components/transfer_group_form.vue new file mode 100644 index 00000000000..ba8de2b0203 --- /dev/null +++ b/app/assets/javascripts/groups/components/transfer_group_form.vue @@ -0,0 +1,82 @@ +<script> +import { GlFormGroup } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue'; +import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue'; + +export const i18n = { + confirmationMessage: __( + 'You are going to transfer %{group_name} to another namespace. Are you ABSOLUTELY sure?', + ), + emptyNamespaceTitle: __('No parent group'), + dropdownTitle: s__('GroupSettings|Select parent group'), +}; + +export default { + name: 'TransferGroupForm', + components: { + ConfirmDanger, + GlFormGroup, + NamespaceSelect, + }, + props: { + parentGroups: { + type: Object, + required: true, + }, + isPaidGroup: { + type: Boolean, + required: true, + }, + confirmationPhrase: { + type: String, + required: true, + }, + confirmButtonText: { + type: String, + required: true, + }, + }, + data() { + return { + selectedId: null, + }; + }, + computed: { + selectedNamespaceId() { + return this.selectedId; + }, + disableSubmitButton() { + return this.isPaidGroup || !this.selectedId; + }, + }, + methods: { + handleSelected({ id }) { + this.selectedId = id; + }, + }, + i18n, +}; +</script> +<template> + <div> + <gl-form-group v-if="!isPaidGroup"> + <namespace-select + :default-text="$options.i18n.dropdownTitle" + :data="parentGroups" + :empty-namespace-title="$options.i18n.emptyNamespaceTitle" + :include-headers="false" + include-empty-namespace + @select="handleSelected" + /> + <input type="hidden" name="new_parent_group_id" :value="selectedId" /> + </gl-form-group> + <confirm-danger + button-class="qa-transfer-button" + :disabled="disableSubmitButton" + :phrase="confirmationPhrase" + :button-text="confirmButtonText" + @confirm="$emit('confirm')" + /> + </div> +</template> diff --git a/app/assets/javascripts/groups/init_transfer_group_form.js b/app/assets/javascripts/groups/init_transfer_group_form.js new file mode 100644 index 00000000000..c60255e6ec3 --- /dev/null +++ b/app/assets/javascripts/groups/init_transfer_group_form.js @@ -0,0 +1,50 @@ +import Vue from 'vue'; +import { sprintf } from '~/locale'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import TransferGroupForm, { i18n } from './components/transfer_group_form.vue'; + +const prepareGroups = (rawGroups) => { + const group = JSON.parse(rawGroups).map(({ id, text: humanName }) => ({ + id, + humanName, + })); + + return { group }; +}; + +export default () => { + const el = document.querySelector('.js-transfer-group-form'); + if (!el) { + return false; + } + + const { + targetFormId = null, + buttonText: confirmButtonText = '', + groupName = '', + parentGroups = [], + isPaidGroup, + } = el.dataset; + + return new Vue({ + el, + provide: { + confirmDangerMessage: sprintf(i18n.confirmationMessage, { groupName }), + }, + render(createElement) { + return createElement(TransferGroupForm, { + props: { + parentGroups: prepareGroups(parentGroups), + isPaidGroup: parseBoolean(isPaidGroup), + confirmButtonText, + confirmationPhrase: groupName, + }, + on: { + confirm: () => { + document.getElementById(targetFormId)?.submit(); + }, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/groups/transfer_dropdown.js b/app/assets/javascripts/groups/transfer_dropdown.js deleted file mode 100644 index d6343f698c0..00000000000 --- a/app/assets/javascripts/groups/transfer_dropdown.js +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery'; -import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; -import { __ } from '~/locale'; - -export default class TransferDropdown { - constructor() { - this.groupDropdown = $('.js-groups-dropdown'); - this.parentInput = $('#new_parent_group_id'); - this.data = this.groupDropdown.data('data'); - this.init(); - } - - init() { - this.buildDropdown(); - } - - buildDropdown() { - const extraOptions = [{ id: '-1', text: __('No parent group') }, { type: 'divider' }]; - - initDeprecatedJQueryDropdown(this.groupDropdown, { - selectable: true, - filterable: true, - toggleLabel: (item) => item.text, - search: { fields: ['text'] }, - data: extraOptions.concat(this.data), - text: (item) => item.text, - clicked: (options) => { - const { e } = options; - e.preventDefault(); - this.assignSelected(options.selectedObj); - }, - }); - } - - assignSelected(selected) { - this.parentInput.val(selected.id); - this.parentInput.change(); - } -} diff --git a/app/assets/javascripts/groups/transfer_edit.js b/app/assets/javascripts/groups/transfer_edit.js deleted file mode 100644 index bb15e11fd4c..00000000000 --- a/app/assets/javascripts/groups/transfer_edit.js +++ /dev/null @@ -1,11 +0,0 @@ -import $ from 'jquery'; - -export default function setupTransferEdit(formSelector, targetSelector) { - const $transferForm = $(formSelector); - const $selectNamespace = $transferForm.find(targetSelector); - - $selectNamespace.on('change', () => { - $transferForm.find(':submit').prop('disabled', !$selectNamespace.val()); - }); - $selectNamespace.trigger('change'); -} diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index f6155b2ab2f..96487e14e30 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -1,8 +1,7 @@ import { GROUP_BADGE } from '~/badges/constants'; import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; import initFilePickers from '~/file_pickers'; -import TransferDropdown from '~/groups/transfer_dropdown'; -import setupTransferEdit from '~/groups/transfer_edit'; +import initTransferGroupForm from '~/groups/init_transfer_group_form'; import groupsSelect from '~/groups_select'; import { initCascadingSettingsLockPopovers } from '~/namespaces/cascading_settings'; import mountBadgeSettings from '~/pages/shared/mount_badge_settings'; @@ -15,11 +14,11 @@ document.addEventListener('DOMContentLoaded', () => { initFilePickers(); initConfirmDanger(); initSettingsPanels(); + initTransferGroupForm(); dirtySubmitFactory( document.querySelectorAll('.js-general-settings-form, .js-general-permissions-form'), ); mountBadgeSettings(GROUP_BADGE); - setupTransferEdit('.js-group-transfer-form', '#new_parent_group_id'); // Initialize Subgroups selector groupsSelect(); @@ -28,6 +27,4 @@ document.addEventListener('DOMContentLoaded', () => { initSearchSettings(); initCascadingSettingsLockPopovers(); - - return new TransferDropdown(); }); diff --git a/app/assets/javascripts/projects/settings/components/transfer_project_form.vue b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue index b98e1101884..ee896ccc01e 100644 --- a/app/assets/javascripts/projects/settings/components/transfer_project_form.vue +++ b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue @@ -44,7 +44,6 @@ export default { <div> <gl-form-group> <namespace-select - class="qa-namespaces-list" data-testid="transfer-project-namespace" :full-width="true" :data="namespaces" diff --git a/app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue b/app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue index 7d2af7983d1..806212f0f2e 100644 --- a/app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue +++ b/app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue @@ -1,9 +1,17 @@ <script> -import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlSearchBoxByType } from '@gitlab/ui'; +import { + GlDropdown, + GlDropdownDivider, + GlDropdownItem, + GlDropdownSectionHeader, + GlSearchBoxByType, +} from '@gitlab/ui'; import { __ } from '~/locale'; +export const EMPTY_NAMESPACE_ID = -1; export const i18n = { DEFAULT_TEXT: __('Select a new namespace'), + DEFAULT_EMPTY_NAMESPACE_TEXT: __('No namespace'), GROUPS: __('Groups'), USERS: __('Users'), }; @@ -15,6 +23,7 @@ export default { name: 'NamespaceSelect', components: { GlDropdown, + GlDropdownDivider, GlDropdownItem, GlDropdownSectionHeader, GlSearchBoxByType, @@ -29,6 +38,26 @@ export default { required: false, default: false, }, + defaultText: { + type: String, + required: false, + default: i18n.DEFAULT_TEXT, + }, + includeHeaders: { + type: Boolean, + required: false, + default: true, + }, + emptyNamespaceTitle: { + type: String, + required: false, + default: i18n.DEFAULT_EMPTY_NAMESPACE_TEXT, + }, + includeEmptyNamespace: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -52,7 +81,11 @@ export default { return filterByName(this.data.user, this.searchTerm); }, selectedNamespaceText() { - return this.selectedNamespace?.humanName || this.$options.i18n.DEFAULT_TEXT; + return this.selectedNamespace?.humanName || this.defaultText; + }, + filteredEmptyNamespaceTitle() { + const { includeEmptyNamespace, emptyNamespaceTitle, searchTerm } = this; + return includeEmptyNamespace && emptyNamespaceTitle.toLowerCase().includes(searchTerm); }, }, methods: { @@ -60,31 +93,47 @@ export default { this.selectedNamespace = item; this.$emit('select', item); }, + handleSelectEmptyNamespace() { + this.handleSelect({ id: EMPTY_NAMESPACE_ID, humanName: this.emptyNamespaceTitle }); + }, }, i18n, }; </script> <template> - <gl-dropdown :text="selectedNamespaceText" :block="fullWidth"> + <gl-dropdown :text="selectedNamespaceText" :block="fullWidth" data-qa-selector="namespaces_list"> <template #header> <gl-search-box-by-type v-model.trim="searchTerm" /> </template> - <div v-if="hasGroupNamespaces" class="qa-namespaces-list-groups"> - <gl-dropdown-section-header>{{ $options.i18n.GROUPS }}</gl-dropdown-section-header> + <div v-if="filteredEmptyNamespaceTitle"> + <gl-dropdown-item + data-qa-selector="namespaces_list_item" + @click="handleSelectEmptyNamespace()" + > + {{ emptyNamespaceTitle }} + </gl-dropdown-item> + <gl-dropdown-divider /> + </div> + <div v-if="hasGroupNamespaces" data-qa-selector="namespaces_list_groups"> + <gl-dropdown-section-header v-if="includeHeaders">{{ + $options.i18n.GROUPS + }}</gl-dropdown-section-header> <gl-dropdown-item v-for="item in filteredGroupNamespaces" :key="item.id" - class="qa-namespaces-list-item" + data-qa-selector="namespaces_list_item" @click="handleSelect(item)" >{{ item.humanName }}</gl-dropdown-item > </div> - <div v-if="hasUserNamespaces" class="qa-namespaces-list-users"> - <gl-dropdown-section-header>{{ $options.i18n.USERS }}</gl-dropdown-section-header> + <div v-if="hasUserNamespaces" data-qa-selector="namespaces_list_users"> + <gl-dropdown-section-header v-if="includeHeaders">{{ + $options.i18n.USERS + }}</gl-dropdown-section-header> <gl-dropdown-item v-for="item in filteredUserNamespaces" :key="item.id" - class="qa-namespaces-list-item" + data-qa-selector="namespaces_list_item" @click="handleSelect(item)" >{{ item.humanName }}</gl-dropdown-item > diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index ca6c9b9a073..7ac3ef2221f 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -112,19 +112,6 @@ table.pipeline-project-metrics tr td { font-weight: $gl-font-weight-normal; } -.js-groups-dropdown { - width: 100%; -} - -.dropdown-group-transfer { - bottom: 100%; - top: initial; - - .dropdown-content { - overflow-y: unset; - } -} - .groups-list-tree-container { .has-no-search-results { text-align: center; diff --git a/app/views/groups/settings/_transfer.html.haml b/app/views/groups/settings/_transfer.html.haml index 59d52e99dec..214752889d4 100644 --- a/app/views/groups/settings/_transfer.html.haml +++ b/app/views/groups/settings/_transfer.html.haml @@ -1,8 +1,10 @@ +- form_id = "transfer-group-form" +- initial_data = { button_text: s_('GroupSettings|Transfer group'), group_name: @group.name, target_form_id: form_id, parent_groups: parent_group_options(group), is_paid_group: group.paid?.to_s } + .sub-section %h4.warning-title= s_('GroupSettings|Transfer group') %p= _('Transfer group to another parent group.') - = form_for group, url: transfer_group_path(group), method: :put, html: { class: 'js-group-transfer-form' } do |f| - + = form_for group, url: transfer_group_path(group), method: :put, html: { id: form_id, class: 'js-group-transfer-form' } do |f| %ul - learn_more_link_start = '<a href="https://docs.gitlab.com/ee/user/project/index.html#redirects-when-changing-repository-paths" target="_blank" rel="noopener noreferrer">'.html_safe - warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended side effects. %{learn_more_link_start}Learn more.%{learn_more_link_end}") % { learn_more_link_start: learn_more_link_start, learn_more_link_end: '</a>'.html_safe } @@ -10,14 +12,9 @@ %li= s_('GroupSettings|You can only transfer the group to a group you manage.') %li= s_('GroupSettings|You will need to update your local repositories to point to the new location.') %li= s_("GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.") - - .form-group - = dropdown_tag(s_('GroupSettings|Select parent group'), options: { toggle_class: 'js-groups-dropdown', title: s_('GroupSettings|Parent Group'), filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: s_('GroupSettings|Search groups'), disabled: group.paid?, data: { data: parent_group_options(group), qa_selector: 'select_group_dropdown' } }) - = hidden_field_tag 'new_parent_group_id' - if group.paid? .gl-alert.gl-alert-info.gl-mb-5 = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title') .gl-alert-body = html_escape(_("This group can't be transfered because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe } - - = f.submit s_('GroupSettings|Transfer group'), class: 'btn gl-button btn-warning', data: { qa_selector: "transfer_group_button" } + .js-transfer-group-form{ data: initial_data } diff --git a/app/views/layouts/nav/_classification_level_banner.html.haml b/app/views/layouts/nav/_classification_level_banner.html.haml index d76fb50aa0b..b36111df99c 100644 --- a/app/views/layouts/nav/_classification_level_banner.html.haml +++ b/app/views/layouts/nav/_classification_level_banner.html.haml @@ -1,5 +1,3 @@ - if ::Gitlab::ExternalAuthorization.enabled? && @project = content_for :header_content do - %span.badge.color-label.gl-bg-red-500.has-tooltip{ title: s_('ExternalAuthorizationService|Classification label') } - = sprite_icon('lock-open', size: 8, css_class: 'inline') - = @project.external_authorization_classification_label + = gl_badge_tag(@project.external_authorization_classification_label, { variant: :danger, icon: 'lock-open' }, { class: 'has-tooltip', title: s_('ExternalAuthorizationService|Classification label') }) diff --git a/config/feature_flags/development/security_report_ingestion_framework.yml b/config/feature_flags/development/security_report_ingestion_framework.yml index 490fd03c677..6c146b7073d 100644 --- a/config/feature_flags/development/security_report_ingestion_framework.yml +++ b/config/feature_flags/development/security_report_ingestion_framework.yml @@ -1,8 +1,8 @@ --- name: security_report_ingestion_framework introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66735 -rollout_issue_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343332 milestone: '14.4' type: development group: group::threat insights -default_enabled: false +default_enabled: true diff --git a/doc/administration/troubleshooting/group_saml_scim.md b/doc/administration/troubleshooting/group_saml_scim.md index ea58bf25341..a30ade058f6 100644 --- a/doc/administration/troubleshooting/group_saml_scim.md +++ b/doc/administration/troubleshooting/group_saml_scim.md @@ -40,7 +40,8 @@ User claims and attributes: SCIM mapping: -![Azure AD SCIM](img/AzureAD-scim_attribute_mapping.png) +![Azure AD SCIM Provisioning](img/AzureAD-scim_provisioning.png) +![Azure AD SCIM Attribute Mapping](img/AzureAD-scim_attribute_mapping.png) Group Sync: diff --git a/doc/administration/troubleshooting/img/AzureAD-scim_provisioning.png b/doc/administration/troubleshooting/img/AzureAD-scim_provisioning.png Binary files differnew file mode 100644 index 00000000000..b8edcfa31c2 --- /dev/null +++ b/doc/administration/troubleshooting/img/AzureAD-scim_provisioning.png diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md index ec3f2f61bde..0e8fd6608b8 100644 --- a/doc/api/merge_request_approvals.md +++ b/doc/api/merge_request_approvals.md @@ -278,6 +278,12 @@ GET /projects/:id/approval_rules/:approval_rule_id > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3. > - Moved to GitLab Premium in 13.9. +WARNING: +The Vulnerability-Check feature, including the Vulnerability-Check attributes listed here, is in its +end-of-life process. It is [deprecated](../update/deprecations.md#vulnerability-check) +for use in GitLab 14.8, and is planned for removal in GitLab 15.0. Users should migrate to the new +[Security Approval Policies](../user/application_security/policies/#scan-result-policy-editor). + You can create project approval rules using the following endpoint: ```plaintext @@ -295,11 +301,11 @@ POST /projects/:id/approval_rules | `user_ids` | Array | no | The ids of users as approvers | | `group_ids` | Array | no | The ids of groups as approvers | | `protected_branch_ids` | Array | no | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). | -| `report_type` | string | no | The report type required when the rule type is `report_approver`. The supported report types are: `vulnerability`, `license_scanning`, `code_coverage`. | -| `scanners` | Array | no | The security scanners the `Vulnerability-Check` approval rule considers. The supported scanners are: `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing`. Defaults to all supported scanners. | -| `severity_levels` | Array | no | The severity levels the `Vulnerability-Check` approval rule considers. The supported severity levels are: `info`, `unknown`, `low`, `medium`, `high`, `critical`. Defaults to `unknown`, `high`, and `critical`. | -| `vulnerabilities_allowed` | integer | no | The number of vulnerabilities allowed for the `Vulnerability-Check` approval rule. Defaults to `0`. | -| `vulnerability_states` | Array | no | The vulnerability states the `Vulnerability-Check` approval rule considers. The supported vulnerability states are: `newly_detected` (default), `detected`, `confirmed`, `resolved`, `dismissed`. | +| `report_type` | string | no | The report type required when the rule type is `report_approver`. The supported report types are: `vulnerability`, `license_scanning`, `code_coverage`. The `vulnerability` report type is part of the Vulnerability-Check feature, which deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `scanners` | Array | no | The security scanners the Vulnerability-Check approval rule considers. The supported scanners are: `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing`. Defaults to all supported scanners. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `severity_levels` | Array | no | The severity levels the Vulnerability-Check approval rule considers. The supported severity levels are: `info`, `unknown`, `low`, `medium`, `high`, `critical`. Defaults to `unknown`, `high`, and `critical`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `vulnerabilities_allowed` | integer | no | The number of vulnerabilities allowed for the Vulnerability-Check approval rule. Defaults to `0`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `vulnerability_states` | Array | no | The vulnerability states the Vulnerability-Check approval rule considers. The supported vulnerability states are: `newly_detected` (default), `detected`, `confirmed`, `resolved`, `dismissed`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | ```json { @@ -404,6 +410,12 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \ > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3. > - Moved to GitLab Premium in 13.9. +WARNING: +The Vulnerability-Check feature, including the Vulnerability-Check attributes listed here, is in its +end-of-life process. It is [deprecated](../update/deprecations.md#vulnerability-check) +for use in GitLab 14.8, and is planned for removal in GitLab 15.0. Users should migrate to the new +[Security Approval Policies](../user/application_security/policies/#scan-result-policy-editor). + You can update project approval rules using the following endpoint: ```plaintext @@ -423,10 +435,10 @@ PUT /projects/:id/approval_rules/:approval_rule_id | `user_ids` | Array | no | The ids of users as approvers | | `group_ids` | Array | no | The ids of groups as approvers | | `protected_branch_ids` | Array | no | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). | -| `scanners` | Array | no | The security scanners the `Vulnerability-Check` approval rule considers. The supported scanners are: `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing`. Defaults to all supported scanners. | -| `severity_levels` | Array | no | The severity levels the `Vulnerability-Check` approval rule considers. The supported severity levels are: `info`, `unknown`, `low`, `medium`, `high`, `critical`. Defaults to `unknown`, `high`, and `critical`. | -| `vulnerabilities_allowed` | integer | no | The number of vulnerabilities allowed for the `Vulnerability-Check` approval rule. Defaults to `0`. | -| `vulnerability_states` | Array | no | The vulnerability states the `Vulnerability-Check` approval rule considers. The supported vulnerability states are: `newly_detected` (default), `detected`, `confirmed`, `resolved`, `dismissed`. | +| `scanners` | Array | no | The security scanners the Vulnerability-Check approval rule considers. The supported scanners are: `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing`. Defaults to all supported scanners. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `severity_levels` | Array | no | The severity levels the Vulnerability-Check approval rule considers. The supported severity levels are: `info`, `unknown`, `low`, `medium`, `high`, `critical`. Defaults to `unknown`, `high`, and `critical`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `vulnerabilities_allowed` | integer | no | The number of vulnerabilities allowed for the Vulnerability-Check approval rule. Defaults to `0`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | +| `vulnerability_states` | Array | no | The vulnerability states the Vulnerability-Check approval rule considers. The supported vulnerability states are: `newly_detected` (default), `detected`, `confirmed`, `resolved`, `dismissed`. Deprecated in GitLab 14.8, and planned for removal in GitLab 15.0. | ```json { diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index 9511b365cf6..13953276adf 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -200,6 +200,11 @@ security issues: ### Vulnerability-Check rule +WARNING: +This feature is in its end-of-life process. It is [deprecated](../../update/deprecations.md#vulnerability-check) +for use in GitLab 14.8, and is planned for removal in GitLab 15.0. Users should migrate to the new +[Security Approval Policies](policies/#scan-result-policy-editor). + To prevent a merge request introducing a security vulnerability in a project, enable the Vulnerability-Check rule. While this rule is enabled, additional merge request approval by [eligible approvers](../project/merge_requests/approvals/rules.md#eligible-approvers) diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index b0d443896a5..d1e9ba29378 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -49,22 +49,21 @@ Once [Group Single Sign-On](index.md) has been configured, we can: ### Azure configuration steps -The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM. +The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM. You can refer to [Azure SCIM setup documentation](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#getting-started). -1. Enable automatic provisioning and administrative credentials by following the - [Azure's SCIM setup documentation](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim). +1. In your app, go to the Provisioning tab, and set the **Provisioning Mode** to **Automatic**. + Then fill in the **Admin Credentials**, and save. The **Tenant URL** and **secret token** are the items + retrieved in the [previous step](#gitlab-configuration). -During this configuration, note the following: +1. After saving, two more tabs appear: -- The `Tenant URL` and `secret token` are the items retrieved in the - [previous step](#gitlab-configuration). -- We recommend setting a notification email and selecting the **Send an email notification when a failure occurs** checkbox. -- For mappings, we only leave `Synchronize Azure Active Directory Users to AppName` enabled. - `Synchronize Azure Active Directory Groups to AppName` is usually disabled. However, this - does not mean Azure AD users cannot be provisioned in groups. Leaving it enabled does not break - the SCIM user provisioning, but causes errors in Azure AD that may be confusing and misleading. + - **Settings**: We recommend setting a notification email and selecting the **Send an email notification when a failure occurs** checkbox. + You also control what is actually synced by selecting the **Scope**. For example, **Sync only assigned users and groups** only syncs the users and groups assigned to the application. Otherwise, it syncs the whole Active Directory. -You can then test the connection by clicking on **Test Connection**. If the connection is successful, be sure to save your configuration before moving on. See below for [troubleshooting](#troubleshooting). + - **Mappings**: We recommend keeping **Provision Azure Active Directory Users** enabled, and disable **Provision Azure Active Directory Groups**. + Leaving **Provision Azure Active Directory Groups** enabled does not break the SCIM user provisioning, but it causes errors in Azure AD that may be confusing and misleading. + +1. You can then test the connection by selecting **Test Connection**. If the connection is successful, save your configuration before moving on. See below for [troubleshooting](#troubleshooting). #### Configure attribute mapping @@ -93,11 +92,6 @@ For guidance, you can view [an example configuration in the troubleshooting refe 1. Save all changes. 1. In the **Provisioning** step, set the `Provisioning Status` to `On`. - NOTE: - You can control what is actually synced by selecting the `Scope`. For example, - `Sync only assigned users and groups` only syncs the users assigned to - the application (`Users and groups`), otherwise, it syncs the whole Active Directory. - Once enabled, the synchronization details and any errors appears on the bottom of the **Provisioning** screen, together with a link to the audit events. diff --git a/lib/gitlab/changelog/config.rb b/lib/gitlab/changelog/config.rb index fd5d701b858..9cb3d71f5c3 100644 --- a/lib/gitlab/changelog/config.rb +++ b/lib/gitlab/changelog/config.rb @@ -37,7 +37,7 @@ module Gitlab attr_accessor :date_format, :categories, :template, :tag_regex, :always_credit_user_ids def self.from_git(project, user = nil) - if (yaml = project.repository.changelog_config) + if (yaml = project.repository.changelog_config.presence) from_hash(project, YAML.safe_load(yaml), user) else new(project) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fb3e67e8dc3..d338a879858 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17304,9 +17304,6 @@ msgstr "" msgid "GroupSettings|Overrides user notification preferences for all members of the group, subgroups, and projects." msgstr "" -msgid "GroupSettings|Parent Group" -msgstr "" - msgid "GroupSettings|Pipeline settings was updated for the group" msgstr "" @@ -17334,9 +17331,6 @@ msgstr "" msgid "GroupSettings|Projects will be permanently deleted after a %{waiting_period}-day delay. This delay can be %{link_start}customized by an admin%{link_end} in instance settings. Inherited by subgroups." msgstr "" -msgid "GroupSettings|Search groups" -msgstr "" - msgid "GroupSettings|Select a project with the %{code_start}.gitlab/insights.yml%{code_end} file" msgstr "" @@ -24225,6 +24219,9 @@ msgstr "" msgid "No milestones to show" msgstr "" +msgid "No namespace" +msgstr "" + msgid "No other labels with such name or description" msgstr "" @@ -40888,6 +40885,9 @@ msgstr "" msgid "You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?" msgstr "" +msgid "You are going to transfer %{group_name} to another namespace. Are you ABSOLUTELY sure?" +msgstr "" + msgid "You are going to transfer %{project_full_name} to another namespace. Are you ABSOLUTELY sure?" msgstr "" diff --git a/qa/qa/page/component/namespace_select.rb b/qa/qa/page/component/namespace_select.rb new file mode 100644 index 00000000000..924e1af876c --- /dev/null +++ b/qa/qa/page/component/namespace_select.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module NamespaceSelect + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue" do + element :namespaces_list + element :namespaces_list_groups + element :namespaces_list_item + end + end + + def select_namespace(item) + click_element :namespaces_list + + within_element(:namespaces_list) do + find_element(:namespaces_list_item, text: item).click + end + end + end + end + end +end diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb index 2e7ab131225..1877065f478 100644 --- a/qa/qa/page/group/settings/general.rb +++ b/qa/qa/page/group/settings/general.rb @@ -7,6 +7,8 @@ module QA class General < QA::Page::Base include ::QA::Page::Settings::Common include Page::Component::VisibilitySetting + include Page::Component::ConfirmModal + include Page::Component::NamespaceSelect view 'app/views/groups/edit.html.haml' do element :permission_lfs_2fa_content @@ -38,16 +40,6 @@ module QA element :project_creation_level_dropdown end - view 'app/views/groups/settings/_transfer.html.haml' do - element :select_group_dropdown - element :transfer_group_button - end - - view 'app/helpers/dropdowns_helper.rb' do - element :dropdown_input_field - element :dropdown_list_content - end - def set_group_name(name) find_element(:group_name_field).send_keys([:command, 'a'], :backspace) find_element(:group_name_field).set name @@ -111,17 +103,14 @@ module QA click_element(:save_permissions_changes_button) end - def transfer_group(target_group) + def transfer_group(target_group, source_group) expand_content :advanced_settings_content - click_element :select_group_dropdown - fill_element(:dropdown_input_field, target_group) - - within_element(:dropdown_list_content) do - click_on target_group - end + select_namespace(target_group) + click_element(:transfer_button) - click_element :transfer_group_button + fill_confirmation_text(source_group) + confirm_transfer end end end diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb index da1f16f4cfc..525210a08f6 100644 --- a/qa/qa/page/project/settings/advanced.rb +++ b/qa/qa/page/project/settings/advanced.rb @@ -6,18 +6,13 @@ module QA module Settings class Advanced < Page::Base include Component::ConfirmModal + include Component::NamespaceSelect view 'app/views/projects/edit.html.haml' do element :project_path_field element :change_path_button end - view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue" do - element :namespaces_list - element :namespaces_list_groups - element :namespaces_list_item - end - view 'app/views/projects/settings/_archive.html.haml' do element :archive_project_link element :unarchive_project_link @@ -43,14 +38,6 @@ module QA click_element :change_path_button end - def select_namespace(item) - click_element :namespaces_list - - within_element(:namespaces_list) do - find_element(:namespaces_list_item, text: item).click - end - end - def transfer_project!(project_name, namespace) QA::Runtime::Logger.info "Transferring project: #{project_name} to namespace: #{namespace}" diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb index 881bc5bc7c3..2db93ac60ea 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb @@ -31,7 +31,7 @@ module QA testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do Page::Group::Menu.perform(&:click_group_general_settings_item) Page::Group::Settings::General.perform do |general| - general.transfer_group(target_group.path) + general.transfer_group(target_group.path, sub_group_for_transfer.path) sub_group_for_transfer.sandbox = target_group sub_group_for_transfer.reload! diff --git a/spec/frontend/groups/components/transfer_group_form_spec.js b/spec/frontend/groups/components/transfer_group_form_spec.js new file mode 100644 index 00000000000..65fa4f49ac9 --- /dev/null +++ b/spec/frontend/groups/components/transfer_group_form_spec.js @@ -0,0 +1,131 @@ +import { GlAlert, GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import Component from '~/groups/components/transfer_group_form.vue'; +import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue'; +import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue'; + +describe('Transfer group form', () => { + let wrapper; + + const confirmButtonText = 'confirm'; + const confirmationPhrase = 'confirmation-phrase'; + const paidGroupHelpLink = 'some/fake/link'; + const groups = [ + { + id: 1, + humanName: 'Group 1', + }, + { + id: 2, + humanName: 'Group 2', + }, + ]; + + const defaultProps = { + parentGroups: { groups }, + paidGroupHelpLink, + isPaidGroup: false, + confirmationPhrase, + confirmButtonText, + }; + + const createComponent = (propsData = {}) => + shallowMountExtended(Component, { + propsData: { + ...defaultProps, + ...propsData, + }, + stubs: { GlSprintf }, + }); + + const findAlert = () => wrapper.findComponent(GlAlert); + const findConfirmDanger = () => wrapper.findComponent(ConfirmDanger); + const findNamespaceSelect = () => wrapper.findComponent(NamespaceSelect); + const findHiddenInput = () => wrapper.find('[name="new_parent_group_id"]'); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('default', () => { + beforeEach(() => { + wrapper = createComponent(); + }); + + it('renders the namespace select component', () => { + expect(findNamespaceSelect().exists()).toBe(true); + }); + + it('sets the namespace select properties', () => { + expect(findNamespaceSelect().props()).toMatchObject({ + defaultText: 'Select parent group', + fullWidth: false, + includeHeaders: false, + emptyNamespaceTitle: 'No parent group', + includeEmptyNamespace: true, + data: { groups }, + }); + }); + + it('renders the hidden input field', () => { + expect(findHiddenInput().exists()).toBe(true); + expect(findHiddenInput().attributes('value')).toBeUndefined(); + }); + + it('does not render the alert message', () => { + expect(findAlert().exists()).toBe(false); + }); + + it('renders the confirm danger component', () => { + expect(findConfirmDanger().exists()).toBe(true); + }); + + it('sets the confirm danger properties', () => { + expect(findConfirmDanger().props()).toMatchObject({ + buttonClass: 'qa-transfer-button', + disabled: true, + buttonText: confirmButtonText, + phrase: confirmationPhrase, + }); + }); + }); + + describe('with a selected project', () => { + const [firstGroup] = groups; + beforeEach(() => { + wrapper = createComponent(); + findNamespaceSelect().vm.$emit('select', firstGroup); + }); + + it('sets the confirm danger disabled property to false', () => { + expect(findConfirmDanger().props()).toMatchObject({ disabled: false }); + }); + + it('sets the hidden input field', () => { + expect(findHiddenInput().exists()).toBe(true); + expect(parseInt(findHiddenInput().attributes('value'), 10)).toBe(firstGroup.id); + }); + + it('emits "confirm" event when the danger modal is confirmed', () => { + expect(wrapper.emitted('confirm')).toBeUndefined(); + + findConfirmDanger().vm.$emit('confirm'); + + expect(wrapper.emitted('confirm')).toHaveLength(1); + }); + }); + + describe('isPaidGroup = true', () => { + beforeEach(() => { + wrapper = createComponent({ isPaidGroup: true }); + }); + + it('disables the transfer button', () => { + expect(findConfirmDanger().props()).toMatchObject({ disabled: true }); + }); + + it('hides the namespace selector button', () => { + expect(findNamespaceSelect().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/groups/transfer_edit_spec.js b/spec/frontend/groups/transfer_edit_spec.js deleted file mode 100644 index bc070920d02..00000000000 --- a/spec/frontend/groups/transfer_edit_spec.js +++ /dev/null @@ -1,31 +0,0 @@ -import $ from 'jquery'; - -import { loadHTMLFixture } from 'helpers/fixtures'; -import setupTransferEdit from '~/groups/transfer_edit'; - -describe('setupTransferEdit', () => { - const formSelector = '.js-group-transfer-form'; - const targetSelector = '#new_parent_group_id'; - - beforeEach(() => { - loadHTMLFixture('groups/edit.html'); - setupTransferEdit(formSelector, targetSelector); - }); - - it('disables submit button on load', () => { - expect($(formSelector).find(':submit').prop('disabled')).toBe(true); - }); - - it('enables submit button when selection changes to non-empty value', () => { - const lastValue = $(formSelector).find(targetSelector).find('.dropdown-content li').last(); - $(formSelector).find(targetSelector).val(lastValue).trigger('change'); - - expect($(formSelector).find(':submit').prop('disabled')).toBeFalsy(); - }); - - it('disables submit button when selection changes to empty value', () => { - $(formSelector).find(targetSelector).val('').trigger('change'); - - expect($(formSelector).find(':submit').prop('disabled')).toBe(true); - }); -}); diff --git a/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js index 8f07f63993d..cebb6279178 100644 --- a/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js +++ b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js @@ -1,7 +1,9 @@ +import { nextTick } from 'vue'; import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import NamespaceSelect, { i18n, + EMPTY_NAMESPACE_ID, } from '~/vue_shared/components/namespace_select/namespace_select.vue'; import { user, group, namespaces } from './mock_data'; @@ -36,6 +38,12 @@ describe('Namespace Select', () => { expect(findDropdown().exists()).toBe(true); }); + it('can override the default text', () => { + const textOverride = 'Select an option'; + wrapper = createComponent({ defaultText: textOverride }); + expect(selectedDropdownItemText()).toBe(textOverride); + }); + it('renders each dropdown item', () => { const items = findDropdownItems().wrappers; expect(items).toHaveLength(flatNamespaces().length); @@ -57,6 +65,11 @@ describe('Namespace Select', () => { expect(wrappersText(headers)).toEqual([i18n.GROUPS, i18n.USERS]); }); + it('can hide the group / user headers', () => { + wrapper = createComponent({ includeHeaders: false }); + expect(findSectionHeaders()).toHaveLength(0); + }); + it('sets the dropdown to full width', () => { expect(findDropdownAttributes('block')).toBeUndefined(); @@ -83,4 +96,29 @@ describe('Namespace Select', () => { expect(wrapper.emitted('select')).toEqual([args]); }); }); + + describe('with an empty namespace option', () => { + const emptyNamespaceTitle = 'No namespace selected'; + + beforeEach(async () => { + wrapper = createComponent({ + includeEmptyNamespace: true, + emptyNamespaceTitle, + }); + await nextTick(); + }); + + it('includes the empty namespace', () => { + const first = findDropdownItems().at(0); + expect(first.text()).toBe(emptyNamespaceTitle); + }); + + it('emits the `select` event when a namespace is selected', () => { + findDropdownItems().at(0).vm.$emit('click'); + + expect(wrapper.emitted('select')).toEqual([ + [{ id: EMPTY_NAMESPACE_ID, humanName: emptyNamespaceTitle }], + ]); + }); + }); }); diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb index c410ba4d116..600682d30ad 100644 --- a/spec/lib/gitlab/changelog/config_spec.rb +++ b/spec/lib/gitlab/changelog/config_spec.rb @@ -31,6 +31,20 @@ RSpec.describe Gitlab::Changelog::Config do described_class.from_git(project) end + + context 'when changelog is empty' do + it 'returns the default configuration' do + allow(project.repository) + .to receive(:changelog_config) + .and_return("") + + expect(described_class) + .to receive(:new) + .with(project) + + described_class.from_git(project) + end + end end describe '.from_hash' do diff --git a/spec/views/groups/settings/_transfer.html.haml_spec.rb b/spec/views/groups/settings/_transfer.html.haml_spec.rb deleted file mode 100644 index 911eb5b7ab3..00000000000 --- a/spec/views/groups/settings/_transfer.html.haml_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'groups/settings/_transfer.html.haml' do - describe 'render' do - it 'enables the Select parent group dropdown and does not show an alert for a group' do - group = build(:group) - - render 'groups/settings/transfer', group: group - - expect(rendered).to have_button 'Select parent group' - expect(rendered).not_to have_button 'Select parent group', disabled: true - expect(rendered).not_to have_text "This group can't be transfered because it is linked to a subscription." - end - end -end |