Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-10 21:10:16 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-10 21:10:16 +0300
commitcba8ff64401258aa68eebd7603d4022884ed0a45 (patch)
tree39d46d3dc50523a36816942b8cf079daf93dfb70
parent8f143a46faf2e7b594301512757edf372c294a0c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG.md15
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue2
-rw-r--r--app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue93
-rw-r--r--app/assets/javascripts/members/constants.js48
-rw-r--r--app/assets/javascripts/members/utils.js13
-rw-r--r--app/assets/javascripts/pipelines/components/graph/action_component.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue4
-rw-r--r--app/finders/alert_management/alerts_finder.rb7
-rw-r--r--app/graphql/resolvers/alert_management/alert_resolver.rb5
-rw-r--r--app/graphql/types/alert_management/domain_filter_enum.rb13
-rw-r--r--app/models/alert_management/alert.rb2
-rw-r--r--app/serializers/vulnerabilities/feedback_entity.rb74
-rw-r--r--app/serializers/vulnerabilities/finding_diff_serializer.rb7
-rw-r--r--app/serializers/vulnerabilities/finding_entity.rb57
-rw-r--r--app/serializers/vulnerabilities/finding_reports_comparer_entity.rb12
-rw-r--r--app/serializers/vulnerabilities/identifier_entity.rb8
-rw-r--r--app/serializers/vulnerabilities/request_entity.rb8
-rw-r--r--app/serializers/vulnerabilities/response_entity.rb8
-rw-r--r--app/serializers/vulnerabilities/scanner_entity.rb7
-rw-r--r--app/workers/repository_import_worker.rb2
-rw-r--r--changelogs/unreleased/255103-blob-switcher-active.yml5
-rw-r--r--changelogs/unreleased/285076-400-bad-request-during-authentication-due-to-password-format-lengt.yml5
-rw-r--r--changelogs/unreleased/288752-registry-details-relative-url-fix.yml5
-rw-r--r--changelogs/unreleased/290006-error-500-on-members-page-after-invitation-sent-via-api.yml5
-rw-r--r--changelogs/unreleased/291160-merge-request-doesn-t-fully-render-when-user-is-a-tool-admin-if-no.yml5
-rw-r--r--changelogs/unreleased/31528-index-cleanup.yml5
-rw-r--r--changelogs/unreleased/filter-by-monitoring-tool.yml5
-rw-r--r--changelogs/unreleased/ldap-secret-command.yml5
-rw-r--r--changelogs/unreleased/sh-fail-import-errors.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-233862.yml5
-rw-r--r--changelogs/unreleased/sh-require-ruby-2-7.yml5
-rw-r--r--changelogs/unreleased/truncate_security_findings_table.yml5
-rw-r--r--config/feature_categories.yml3
-rw-r--r--config/gitlab.yml.example3
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--db/migrate/20201106135608_remove_redundant_pipelines_index.rb17
-rw-r--r--db/post_migrate/20201207151651_truncate_security_findings_table_2.rb7
-rw-r--r--db/schema_migrations/202011061356081
-rw-r--r--db/schema_migrations/202012071516511
-rw-r--r--db/structure.sql2
-rw-r--r--doc/api/award_emoji.md27
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql30
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json65
-rw-r--r--doc/api/graphql/reference/index.md10
-rw-r--r--doc/api/merge_trains.md2
-rw-r--r--doc/ci/caching/index.md2
-rw-r--r--doc/ci/examples/authenticating-with-hashicorp-vault/index.md2
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md4
-rw-r--r--doc/ci/introduction/index.md2
-rw-r--r--doc/ci/pipelines/job_artifacts.md2
-rw-r--r--doc/ci/pipelines/settings.md2
-rw-r--r--doc/ci/triggers/README.md6
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/variables/predefined_variables.md2
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md2
-rw-r--r--doc/ci/yaml/README.md6
-rw-r--r--doc/development/api_graphql_styleguide.md78
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md12
-rw-r--r--doc/development/documentation/site_architecture/index.md5
-rw-r--r--doc/development/documentation/structure.md2
-rw-r--r--doc/development/documentation/testing.md8
-rw-r--r--doc/development/documentation/workflow.md6
-rw-r--r--doc/ssh/README.md5
-rw-r--r--doc/user/group/img/add_new_members_v13_6.pngbin43257 -> 0 bytes
-rw-r--r--doc/user/group/img/add_new_members_v13_7.pngbin0 -> 58897 bytes
-rw-r--r--doc/user/group/img/manual_permissions_v13_6.pngbin70840 -> 0 bytes
-rw-r--r--doc/user/group/img/manual_permissions_v13_7.pngbin0 -> 69289 bytes
-rw-r--r--doc/user/group/index.md4
-rw-r--r--doc/user/project/issues/associate_zoom_meeting.md22
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md23
-rw-r--r--lib/gitlab/auth/ldap/config.rb34
-rw-r--r--lib/gitlab/encrypted_ldap_command.rb104
-rw-r--r--lib/tasks/gitlab/ldap.rake18
-rw-r--r--locale/gitlab.pot27
-rw-r--r--spec/features/groups/members/sort_members_spec.rb36
-rw-r--r--spec/finders/alert_management/alerts_finder_spec.rb34
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js87
-rw-r--r--spec/frontend/members/utils_spec.js28
-rw-r--r--spec/graphql/resolvers/alert_management/alert_resolver_spec.rb10
-rw-r--r--spec/graphql/types/alert_management/domain_filter_enum_spec.rb11
-rw-r--r--spec/tasks/gitlab/ldap_rake_spec.rb109
-rw-r--r--spec/workers/repository_import_worker_spec.rb2
83 files changed, 1005 insertions, 310 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7907185aa10..5383491ffe5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 13.6.3 (2020-12-10)
+
+### Fixed (5 changes)
+
+- Fix error 500s creating projects concurrently. !48571
+- Fix container_registry url for relative urls. !48661
+- Resolve Members page 500 error after Invitation sent via API. !48937
+- Add different string encoding method in rack middleware. !49044
+- Fix MR rendering issue when user is tool admin and not project member. !49258
+
+### Changed (1 change)
+
+- Update Rake check and docs to require Ruby 2.7. !48552
+
+
## 13.6.2 (2020-12-07)
### Security (10 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 50b0d1825bd..b3961b1f317 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-ee8cb3bd753698786693f440f0de4ebb04f065a9
+873c388ed3c1c458c34f349e31d122dbe3813bc2
diff --git a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
index 902dd0b8eec..a5b594fbd88 100644
--- a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
+++ b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
@@ -50,7 +50,6 @@ export default {
:aria-label="$options.SIMPLE_BLOB_VIEWER_TITLE"
:title="$options.SIMPLE_BLOB_VIEWER_TITLE"
:selected="isSimpleViewer"
- :class="{ active: isSimpleViewer }"
icon="code"
category="primary"
variant="default"
@@ -61,7 +60,6 @@ export default {
:aria-label="$options.RICH_BLOB_VIEWER_TITLE"
:title="$options.RICH_BLOB_VIEWER_TITLE"
:selected="isRichViewer"
- :class="{ active: isRichViewer }"
icon="document"
category="primary"
variant="default"
diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
index e2fbb074fcd..de7fbc4241c 100644
--- a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
+++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
@@ -1,66 +1,77 @@
<script>
import { mapState } from 'vuex';
-import { GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui';
-import { parseSortParam, buildSortUrl } from '~/members/utils';
+import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { parseSortParam, buildSortHref } from '~/members/utils';
import { FIELDS } from '~/members/constants';
export default {
name: 'SortDropdown',
- components: { GlDropdown, GlDropdownItem, GlFormGroup },
+ components: { GlSorting, GlSortingItem },
computed: {
...mapState(['tableSortableFields', 'filteredSearchBar']),
sort() {
return parseSortParam(this.tableSortableFields);
},
+ activeOption() {
+ return FIELDS.find(field => field.key === this.sort.sortByKey);
+ },
+ activeOptionLabel() {
+ return this.activeOption?.label;
+ },
+ isAscending() {
+ return !this.sort.sortDesc;
+ },
filteredOptions() {
- const buildOption = (field, sortDesc) => ({
- ...(sortDesc ? field.sort.desc : field.sort.asc),
- key: field.key,
- sortDesc,
- url: buildSortUrl({
- sortBy: field.key,
- sortDesc,
- filteredSearchBarTokens: this.filteredSearchBar.tokens,
- filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ return FIELDS.filter(field => this.tableSortableFields.includes(field.key) && field.sort).map(
+ field => ({
+ key: field.key,
+ label: field.label,
+ href: buildSortHref({
+ sortBy: field.key,
+ sortDesc: false,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
}),
- });
-
- return FIELDS.filter(
- field => this.tableSortableFields.includes(field.key) && field.sort,
- ).flatMap(field => [buildOption(field, false), buildOption(field, true)]);
+ );
},
},
methods: {
- isChecked(key, sortDesc) {
- return this.sort?.sortBy === key && this.sort?.sortDesc === sortDesc;
+ isActive(key) {
+ return this.activeOption.key === key;
+ },
+ handleSortDirectionChange() {
+ visitUrl(
+ buildSortHref({
+ sortBy: this.activeOption.key,
+ sortDesc: !this.sort.sortDesc,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
+ );
},
},
};
</script>
<template>
- <gl-form-group
- :label="__('Sort by')"
- class="gl-mb-0"
- label-cols="auto"
- label-class="gl-align-self-center gl-pb-0!"
+ <gl-sorting
+ class="gl-display-flex"
+ dropdown-class="gl-w-full"
+ data-testid="members-sort-dropdown"
+ :text="activeOptionLabel"
+ :is-ascending="isAscending"
+ :sort-direction-tool-tip="__('Sort direction')"
+ @sortDirectionChange="handleSortDirectionChange"
>
- <gl-dropdown
- :text="sort.sortByLabel"
- block
- toggle-class="gl-mb-0"
- data-testid="members-sort-dropdown"
- right
+ <gl-sorting-item
+ v-for="option in filteredOptions"
+ :key="option.key"
+ :href="option.href"
+ :active="isActive(option.key)"
>
- <gl-dropdown-item
- v-for="option in filteredOptions"
- :key="option.param"
- :href="option.url"
- is-check-item
- :is-checked="isChecked(option.key, option.sortDesc)"
- >
- {{ option.label }}
- </gl-dropdown-item>
- </gl-dropdown>
- </gl-form-group>
+ {{ option.label }}
+ </gl-sorting-item>
+ </gl-sorting>
</template>
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 874e934e5b0..21af825f795 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -1,20 +1,12 @@
-import { __, s__ } from '~/locale';
-
-const ACCOUNT_SORT_ASC_LABEL = s__('Members|Account, ascending');
+import { __ } from '~/locale';
export const FIELDS = [
{
key: 'account',
label: __('Account'),
sort: {
- asc: {
- param: 'name_asc',
- label: ACCOUNT_SORT_ASC_LABEL,
- },
- desc: {
- param: 'name_desc',
- label: s__('Members|Account, descending'),
- },
+ asc: 'name_asc',
+ desc: 'name_desc',
},
},
{
@@ -29,14 +21,8 @@ export const FIELDS = [
thClass: 'col-meta',
tdClass: 'col-meta',
sort: {
- asc: {
- param: 'last_joined',
- label: s__('Members|Access granted, ascending'),
- },
- desc: {
- param: 'oldest_joined',
- label: s__('Members|Access granted, descending'),
- },
+ asc: 'last_joined',
+ desc: 'oldest_joined',
},
},
{
@@ -63,14 +49,8 @@ export const FIELDS = [
thClass: 'col-max-role',
tdClass: 'col-max-role',
sort: {
- asc: {
- param: 'access_level_asc',
- label: s__('Members|Max role, ascending'),
- },
- desc: {
- param: 'access_level_desc',
- label: s__('Members|Max role, descending'),
- },
+ asc: 'access_level_asc',
+ desc: 'access_level_desc',
},
},
{
@@ -81,15 +61,10 @@ export const FIELDS = [
},
{
key: 'lastSignIn',
+ label: __('Last sign-in'),
sort: {
- asc: {
- param: 'recent_sign_in',
- label: s__('Members|Last sign-in, ascending'),
- },
- desc: {
- param: 'oldest_sign_in',
- label: s__('Members|Last sign-in, descending'),
- },
+ asc: 'recent_sign_in',
+ desc: 'oldest_sign_in',
},
},
{
@@ -101,9 +76,8 @@ export const FIELDS = [
];
export const DEFAULT_SORT = {
- sortBy: 'account',
+ sortByKey: 'account',
sortDesc: false,
- sortByLabel: ACCOUNT_SORT_ASC_LABEL,
};
export const AVATAR_SIZE = 48;
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
index 5c58c4a9f6c..bf1fc2d7515 100644
--- a/app/assets/javascripts/members/utils.js
+++ b/app/assets/javascripts/members/utils.js
@@ -51,23 +51,20 @@ export const parseSortParam = sortableFields => {
const sortParam = getParameterByName('sort');
const sortedField = FIELDS.filter(field => sortableFields.includes(field.key)).find(
- field => field.sort?.asc?.param === sortParam || field.sort?.desc?.param === sortParam,
+ field => field.sort?.asc === sortParam || field.sort?.desc === sortParam,
);
if (!sortedField) {
return DEFAULT_SORT;
}
- const isDesc = sortedField?.sort?.desc?.param === sortParam;
-
return {
- sortBy: sortedField.key,
- sortDesc: isDesc,
- sortByLabel: isDesc ? sortedField?.sort?.desc?.label : sortedField?.sort?.asc?.label,
+ sortByKey: sortedField.key,
+ sortDesc: sortedField?.sort?.desc === sortParam,
};
};
-export const buildSortUrl = ({
+export const buildSortHref = ({
sortBy,
sortDesc,
filteredSearchBarTokens,
@@ -79,7 +76,7 @@ export const buildSortUrl = ({
return '';
}
- const sortParam = sortDesc ? sortDefinition.desc.param : sortDefinition.asc.param;
+ const sortParam = sortDesc ? sortDefinition.desc : sortDefinition.asc;
const filterParams =
filteredSearchBarTokens?.reduce((accumulator, token) => {
diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue
index e35817a4dbb..4e9b21a5c55 100644
--- a/app/assets/javascripts/pipelines/components/graph/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue
@@ -91,6 +91,6 @@ export default {
@click.stop="onClickAction"
>
<gl-loading-icon v-if="isLoading" class="js-action-icon-loading" />
- <gl-icon v-else :name="actionIcon" class="gl-mr-0!" />
+ <gl-icon v-else :name="actionIcon" class="gl-mr-0!" :aria-label="actionIcon" />
</gl-button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index d775a093f5f..07bd6019b80 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -63,5 +63,7 @@ export default {
};
</script>
<template>
- <span :class="cssClass"> <gl-icon :name="icon" :size="size" :class="cssClasses" /> </span>
+ <span :class="cssClass">
+ <gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" />
+ </span>
</template>
diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb
index 1d6f790af31..be3b329fb6f 100644
--- a/app/finders/alert_management/alerts_finder.rb
+++ b/app/finders/alert_management/alerts_finder.rb
@@ -18,6 +18,7 @@ module AlertManagement
return AlertManagement::Alert.none unless authorized?
collection = project.alert_management_alerts
+ collection = by_domain(collection)
collection = by_status(collection)
collection = by_iid(collection)
collection = by_assignee(collection)
@@ -30,6 +31,10 @@ module AlertManagement
attr_reader :current_user, :project, :params
+ def by_domain(collection)
+ collection.with_operations_alerts
+ end
+
def by_iid(collection)
return collection unless params[:iid]
@@ -59,3 +64,5 @@ module AlertManagement
end
end
end
+
+AlertManagement::AlertsFinder.prepend_if_ee('EE::AlertManagement::AlertsFinder')
diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb
index c3219d9cdc3..b115bd80113 100644
--- a/app/graphql/resolvers/alert_management/alert_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_resolver.rb
@@ -18,6 +18,11 @@ module Resolvers
description: 'Sort alerts by this criteria',
required: false
+ argument :domain, Types::AlertManagement::DomainFilterEnum,
+ description: 'Filter query for given domain',
+ required: true,
+ default_value: 'operations'
+
argument :search, GraphQL::STRING_TYPE,
description: 'Search query for title, description, service, or monitoring_tool.',
required: false
diff --git a/app/graphql/types/alert_management/domain_filter_enum.rb b/app/graphql/types/alert_management/domain_filter_enum.rb
new file mode 100644
index 00000000000..58dbc8bb2cf
--- /dev/null
+++ b/app/graphql/types/alert_management/domain_filter_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module AlertManagement
+ class DomainFilterEnum < BaseEnum
+ graphql_name 'AlertManagementDomainFilter'
+ description 'Filters the alerts based on given domain'
+
+ value 'operations', description: 'Alerts for operations domain '
+ value 'threat_monitoring', description: 'Alerts for threat monitoring domain'
+ end
+ end
+end
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index fa9b9fc9e2d..7090d9f4ea1 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -127,6 +127,8 @@ module AlertManagement
scope :open, -> { with_status(open_statuses) }
scope :not_resolved, -> { without_status(:resolved) }
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
+ scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) }
+ scope :with_operations_alerts, -> { where(domain: :operations) }
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) }
diff --git a/app/serializers/vulnerabilities/feedback_entity.rb b/app/serializers/vulnerabilities/feedback_entity.rb
new file mode 100644
index 00000000000..19d99e2d26a
--- /dev/null
+++ b/app/serializers/vulnerabilities/feedback_entity.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::FeedbackEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :id
+ expose :created_at
+ expose :project_id
+ expose :author, using: UserEntity
+ expose :comment_details, if: -> (feedback, _) { feedback.has_comment? } do
+ expose :comment
+ expose :comment_timestamp
+ expose :comment_author, using: UserEntity
+ end
+
+ expose :pipeline, if: -> (feedback, _) { feedback.pipeline.present? } do
+ expose :id do |feedback|
+ feedback.pipeline.id
+ end
+
+ expose :path do |feedback|
+ project_pipeline_path(feedback.pipeline.project, feedback.pipeline)
+ end
+ end
+
+ expose :issue_iid, if: -> (feedback, _) { feedback.issue.present? } do |feedback|
+ feedback.issue.iid
+ end
+
+ expose :issue_url, if: -> (_, _) { can_read_issue? } do |feedback|
+ project_issue_url(feedback.project, feedback.issue)
+ end
+
+ expose :merge_request_iid, if: -> (feedback, _) { feedback.merge_request.present? } do |feedback|
+ feedback.merge_request.iid
+ end
+
+ expose :merge_request_path, if: -> (_, _) { can_read_merge_request? } do |feedback|
+ project_merge_request_path(feedback.project, feedback.merge_request)
+ end
+
+ expose :destroy_vulnerability_feedback_dismissal_path, if: ->(_, _) { can_destroy_feedback? }
+
+ expose :category
+ expose :feedback_type
+ expose :branch do |feedback|
+ feedback&.pipeline&.ref
+ end
+ expose :project_fingerprint
+
+ alias_method :feedback, :object
+
+ private
+
+ def destroy_vulnerability_feedback_dismissal_path
+ project_vulnerability_feedback_path(feedback.project, feedback)
+ end
+
+ def can_destroy_feedback?
+ can?(current_user, :destroy_vulnerability_feedback, feedback)
+ end
+
+ def can_read_issue?
+ feedback.issue.present? && can?(current_user, :read_issue, feedback.issue)
+ end
+
+ def can_read_merge_request?
+ feedback.merge_request.present? && can?(current_user, :read_merge_request, feedback.merge_request)
+ end
+
+ def current_user
+ return request.current_user if request.respond_to?(:current_user)
+ end
+end
diff --git a/app/serializers/vulnerabilities/finding_diff_serializer.rb b/app/serializers/vulnerabilities/finding_diff_serializer.rb
new file mode 100644
index 00000000000..19777873389
--- /dev/null
+++ b/app/serializers/vulnerabilities/finding_diff_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::FindingDiffSerializer < BaseSerializer
+ include WithPagination
+
+ entity Vulnerabilities::FindingReportsComparerEntity
+end
diff --git a/app/serializers/vulnerabilities/finding_entity.rb b/app/serializers/vulnerabilities/finding_entity.rb
new file mode 100644
index 00000000000..40052896c26
--- /dev/null
+++ b/app/serializers/vulnerabilities/finding_entity.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::FindingEntity < Grape::Entity
+ include RequestAwareEntity
+ include VulnerabilitiesHelper
+
+ expose :id, :report_type, :name, :severity, :confidence
+ expose :scanner, using: Vulnerabilities::ScannerEntity
+ expose :identifiers, using: Vulnerabilities::IdentifierEntity
+ expose :project_fingerprint
+ expose :create_jira_issue_url do |occurrence|
+ create_jira_issue_url_for(occurrence)
+ end
+ expose :create_vulnerability_feedback_issue_path do |occurrence|
+ create_vulnerability_feedback_issue_path(occurrence.project)
+ end
+ expose :create_vulnerability_feedback_merge_request_path do |occurrence|
+ create_vulnerability_feedback_merge_request_path(occurrence.project)
+ end
+ expose :create_vulnerability_feedback_dismissal_path do |occurrence|
+ create_vulnerability_feedback_dismissal_path(occurrence.project)
+ end
+
+ expose :project, using: ::ProjectEntity
+ expose :dismissal_feedback, using: Vulnerabilities::FeedbackEntity
+ expose :issue_feedback, using: Vulnerabilities::FeedbackEntity
+ expose :merge_request_feedback, using: Vulnerabilities::FeedbackEntity
+
+ expose :metadata, merge: true, if: ->(occurrence, _) { occurrence.raw_metadata } do
+ expose :description
+ expose :links
+ expose :location
+ expose :remediations
+ expose :solution
+ expose(:evidence) { |model, _| model.evidence[:summary] }
+ expose(:request, using: Vulnerabilities::RequestEntity) { |model, _| model.evidence[:request] }
+ expose(:response, using: Vulnerabilities::ResponseEntity) { |model, _| model.evidence[:response] }
+ expose(:evidence_source) { |model, _| model.evidence[:source] }
+ expose(:supporting_messages) { |model, _| model.evidence[:supporting_messages]}
+ expose(:assets) { |model, _| model.assets }
+ end
+
+ expose :state
+ expose :scan
+
+ expose :blob_path do |occurrence|
+ occurrence.present.blob_path
+ end
+
+ alias_method :occurrence, :object
+
+ def current_user
+ return request.current_user if request.respond_to?(:current_user)
+ end
+end
+
+Vulnerabilities::FindingEntity.include_if_ee('::EE::ProjectsHelper')
diff --git a/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb b/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb
new file mode 100644
index 00000000000..f8529ba56c3
--- /dev/null
+++ b/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::FindingReportsComparerEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :base_report_created_at
+ expose :base_report_out_of_date
+ expose :head_report_created_at
+ expose :added, using: Vulnerabilities::FindingEntity
+ expose :fixed, using: Vulnerabilities::FindingEntity
+ expose :existing, using: Vulnerabilities::FindingEntity
+end
diff --git a/app/serializers/vulnerabilities/identifier_entity.rb b/app/serializers/vulnerabilities/identifier_entity.rb
new file mode 100644
index 00000000000..79ae4623087
--- /dev/null
+++ b/app/serializers/vulnerabilities/identifier_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::IdentifierEntity < Grape::Entity
+ expose :external_type
+ expose :external_id
+ expose :name
+ expose :url
+end
diff --git a/app/serializers/vulnerabilities/request_entity.rb b/app/serializers/vulnerabilities/request_entity.rb
new file mode 100644
index 00000000000..f1da4525889
--- /dev/null
+++ b/app/serializers/vulnerabilities/request_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::RequestEntity < Grape::Entity
+ expose :headers
+ expose :method
+ expose :url
+ expose :body
+end
diff --git a/app/serializers/vulnerabilities/response_entity.rb b/app/serializers/vulnerabilities/response_entity.rb
new file mode 100644
index 00000000000..64ac006cb16
--- /dev/null
+++ b/app/serializers/vulnerabilities/response_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::ResponseEntity < Grape::Entity
+ expose :headers
+ expose :reason_phrase
+ expose :status_code
+ expose :body
+end
diff --git a/app/serializers/vulnerabilities/scanner_entity.rb b/app/serializers/vulnerabilities/scanner_entity.rb
new file mode 100644
index 00000000000..cdcbc01d867
--- /dev/null
+++ b/app/serializers/vulnerabilities/scanner_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Vulnerabilities::ScannerEntity < Grape::Entity
+ expose :external_id
+ expose :name
+ expose :vendor
+end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 51fe60e25fc..90764d7374d 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -30,7 +30,7 @@ class RepositoryImportWorker # rubocop:disable Scalability/IdempotentWorker
return if service.async?
if result[:status] == :error
- fail_import(result[:message]) if template_import?
+ fail_import(result[:message])
raise result[:message]
end
diff --git a/changelogs/unreleased/255103-blob-switcher-active.yml b/changelogs/unreleased/255103-blob-switcher-active.yml
new file mode 100644
index 00000000000..ece9b7673a5
--- /dev/null
+++ b/changelogs/unreleased/255103-blob-switcher-active.yml
@@ -0,0 +1,5 @@
+---
+title: Fix outline on selected button in Snippets Rendered/Source buttons
+merge_request: 49676
+author:
+type: fixed
diff --git a/changelogs/unreleased/285076-400-bad-request-during-authentication-due-to-password-format-lengt.yml b/changelogs/unreleased/285076-400-bad-request-during-authentication-due-to-password-format-lengt.yml
deleted file mode 100644
index a2dfc320f49..00000000000
--- a/changelogs/unreleased/285076-400-bad-request-during-authentication-due-to-password-format-lengt.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add different string encoding method in rack middleware
-merge_request: 49044
-author:
-type: fixed
diff --git a/changelogs/unreleased/288752-registry-details-relative-url-fix.yml b/changelogs/unreleased/288752-registry-details-relative-url-fix.yml
deleted file mode 100644
index 2d315d396ea..00000000000
--- a/changelogs/unreleased/288752-registry-details-relative-url-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix container_registry url for relative urls
-merge_request: 48661
-author:
-type: fixed
diff --git a/changelogs/unreleased/290006-error-500-on-members-page-after-invitation-sent-via-api.yml b/changelogs/unreleased/290006-error-500-on-members-page-after-invitation-sent-via-api.yml
deleted file mode 100644
index 8d393e47a61..00000000000
--- a/changelogs/unreleased/290006-error-500-on-members-page-after-invitation-sent-via-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Members page 500 error after Invitation sent via API
-merge_request: 48937
-author:
-type: fixed
diff --git a/changelogs/unreleased/291160-merge-request-doesn-t-fully-render-when-user-is-a-tool-admin-if-no.yml b/changelogs/unreleased/291160-merge-request-doesn-t-fully-render-when-user-is-a-tool-admin-if-no.yml
deleted file mode 100644
index 654119b2342..00000000000
--- a/changelogs/unreleased/291160-merge-request-doesn-t-fully-render-when-user-is-a-tool-admin-if-no.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix MR rendering issue when user is tool admin and not project member
-merge_request: 49258
-author:
-type: fixed
diff --git a/changelogs/unreleased/31528-index-cleanup.yml b/changelogs/unreleased/31528-index-cleanup.yml
new file mode 100644
index 00000000000..69e9246ad33
--- /dev/null
+++ b/changelogs/unreleased/31528-index-cleanup.yml
@@ -0,0 +1,5 @@
+---
+title: Remove redundant index
+merge_request: 47072
+author:
+type: performance
diff --git a/changelogs/unreleased/filter-by-monitoring-tool.yml b/changelogs/unreleased/filter-by-monitoring-tool.yml
new file mode 100644
index 00000000000..c110c9b16af
--- /dev/null
+++ b/changelogs/unreleased/filter-by-monitoring-tool.yml
@@ -0,0 +1,5 @@
+---
+title: Allow alerts to be filtered by monitoring tool
+merge_request: 48699
+author:
+type: added
diff --git a/changelogs/unreleased/ldap-secret-command.yml b/changelogs/unreleased/ldap-secret-command.yml
new file mode 100644
index 00000000000..a1c0617867c
--- /dev/null
+++ b/changelogs/unreleased/ldap-secret-command.yml
@@ -0,0 +1,5 @@
+---
+title: Add encrypted ldap secrets support
+merge_request: 45712
+author:
+type: added
diff --git a/changelogs/unreleased/sh-fail-import-errors.yml b/changelogs/unreleased/sh-fail-import-errors.yml
new file mode 100644
index 00000000000..f114b73241f
--- /dev/null
+++ b/changelogs/unreleased/sh-fail-import-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Fail import state whenever repository import fails
+merge_request: 49682
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-233862.yml b/changelogs/unreleased/sh-fix-issue-233862.yml
deleted file mode 100644
index 081c3574ec4..00000000000
--- a/changelogs/unreleased/sh-fix-issue-233862.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix error 500s creating projects concurrently
-merge_request: 48571
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-require-ruby-2-7.yml b/changelogs/unreleased/sh-require-ruby-2-7.yml
deleted file mode 100644
index 7f7465d5cac..00000000000
--- a/changelogs/unreleased/sh-require-ruby-2-7.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Rake check and docs to require Ruby 2.7
-merge_request: 48552
-author:
-type: changed
diff --git a/changelogs/unreleased/truncate_security_findings_table.yml b/changelogs/unreleased/truncate_security_findings_table.yml
new file mode 100644
index 00000000000..4e8bb60b296
--- /dev/null
+++ b/changelogs/unreleased/truncate_security_findings_table.yml
@@ -0,0 +1,5 @@
+---
+title: Truncate the `security_findings` table
+merge_request: 49385
+author:
+type: added
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 9b272e494eb..2b6f8fc51db 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -43,7 +43,6 @@
- dynamic_application_security_testing
- editor_extension
- epics
-- epic_tracking
- error_tracking
- feature_flags
- five_minute_production_app
@@ -73,7 +72,6 @@
- live_preview
- load_testing
- logging
-- malware_scanning
- memory
- merge_trains
- metrics
@@ -84,7 +82,6 @@
- package_registry
- pages
- pipeline_authoring
-- pki_management
- planning_analytics
- product_analytics
- projects
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 801aad50ddb..291431aa23f 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -620,6 +620,9 @@ production: &base
enabled: false
prevent_ldap_sign_in: false
+ # File location to read encrypted secrets from
+ # secret_file: /mnt/gitlab/ldap.yaml.enc # Default: shared/encrypted_settings/ldap.yaml.enc
+
# This setting controls the number of seconds between LDAP permission checks
# for each user. After this time has expired for a given user, their next
# interaction with GitLab (a click in the web UI, a git pull, etc.) will be
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 2c964e597ff..97c0e051f1f 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -13,6 +13,7 @@ Settings.encrypted_settings['path'] = Settings.absolute(Settings.encrypted_setti
Settings['ldap'] ||= Settingslogic.new({})
Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil?
Settings.ldap['prevent_ldap_sign_in'] = false if Settings.ldap['prevent_ldap_sign_in'].blank?
+Settings.ldap['secret_file'] = Settings.absolute(Settings.ldap['secret_file'] || File.join(Settings.encrypted_settings['path'], "ldap.yaml.enc"))
Gitlab.ee do
Settings.ldap['sync_time'] = 3600 if Settings.ldap['sync_time'].nil?
diff --git a/db/migrate/20201106135608_remove_redundant_pipelines_index.rb b/db/migrate/20201106135608_remove_redundant_pipelines_index.rb
new file mode 100644
index 00000000000..03e757c0ec1
--- /dev/null
+++ b/db/migrate/20201106135608_remove_redundant_pipelines_index.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveRedundantPipelinesIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :ci_pipelines, :index_ci_pipelines_on_project_id_and_created_at
+ end
+
+ def down
+ add_concurrent_index :ci_pipelines, [:project_id, :created_at]
+ end
+end
diff --git a/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb b/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb
new file mode 100644
index 00000000000..2ac6941be6d
--- /dev/null
+++ b/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_relative Rails.root.join('db', 'post_migrate', '20201102152945_truncate_security_findings_table.rb')
+
+# This is the second time we are truncating this table
+# so the migration class name has choosen like this for this reason.
+class TruncateSecurityFindingsTable2 < TruncateSecurityFindingsTable; end
diff --git a/db/schema_migrations/20201106135608 b/db/schema_migrations/20201106135608
new file mode 100644
index 00000000000..ce6570d9dbf
--- /dev/null
+++ b/db/schema_migrations/20201106135608
@@ -0,0 +1 @@
+52c8fb75035a08e212db52d032638a0c8f9d91306bfb8015fb3fc403a1cff1ec \ No newline at end of file
diff --git a/db/schema_migrations/20201207151651 b/db/schema_migrations/20201207151651
new file mode 100644
index 00000000000..92d8fbec6bd
--- /dev/null
+++ b/db/schema_migrations/20201207151651
@@ -0,0 +1 @@
+93def9138efddc9cd0ace5524dc5cf6cdc0221977083324c5c0ad3cf3fb75e55 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index ea21f9a9866..8883f6cf214 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -20990,8 +20990,6 @@ CREATE INDEX index_ci_pipelines_on_merge_request_id ON ci_pipelines USING btree
CREATE INDEX index_ci_pipelines_on_pipeline_schedule_id ON ci_pipelines USING btree (pipeline_schedule_id);
-CREATE INDEX index_ci_pipelines_on_project_id_and_created_at ON ci_pipelines USING btree (project_id, created_at);
-
CREATE INDEX index_ci_pipelines_on_project_id_and_id_desc ON ci_pipelines USING btree (project_id, id DESC);
CREATE UNIQUE INDEX index_ci_pipelines_on_project_id_and_iid ON ci_pipelines USING btree (project_id, iid) WHERE (iid IS NOT NULL);
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 72d70c966d5..32ac6ef26ea 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -10,21 +10,22 @@ info: To determine the technical writer assigned to the Stage/Group associated w
An [awarded emoji](../user/award_emojis.md) tells a thousand words.
-Emoji can be awarded on the following (known as "awardables"):
+We call GitLab objects on which you can award an emoji "awardables". You can award emojis on the following:
+- [Epics](../user/group/epics/index.md) ([API](epics.md)).
- [Issues](../user/project/issues/index.md) ([API](issues.md)).
- [Merge requests](../user/project/merge_requests/index.md) ([API](merge_requests.md)).
- [Snippets](../user/snippets.md) ([API](snippets.md)).
-Emoji can also [be awarded](../user/award_emojis.md#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
+Emojis can also [be awarded](../user/award_emojis.md#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
## Issues, merge requests, and snippets
See [Award Emoji on Comments](#award-emoji-on-comments) for information on using these endpoints with comments.
-### List an awardable's award emoji
+### List an awardable's award emojis
-Get a list of all award emoji for a specified awardable.
+Get a list of all award emojis for a specified awardable.
```plaintext
GET /projects/:id/issues/:issue_iid/award_emoji
@@ -174,10 +175,9 @@ Example Response:
### Delete an award emoji
-Sometimes it's just not meant to be and you'll have to remove the award.
+Sometimes it's just not meant to be and you need to remove the award.
-NOTE:
-Only available to administrators or the author of the award.
+Only an administrator or the author of the award can delete an award emoji.
```plaintext
DELETE /projects/:id/issues/:issue_iid/award_emoji/:award_id
@@ -202,13 +202,13 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
Comments (also known as notes) are a sub-resource of issues, merge requests, and snippets.
NOTE:
-The examples below describe working with award emoji on comments for an issue, but can be
-easily adapted for comments on a merge request or on a snippet. Therefore, you have to replace
+The examples below describe working with award emojis on an issue's comments, but can be
+adapted to comments on merge requests and snippets. Therefore, you have to replace
`issue_iid` either with `merge_request_iid` or with the `snippet_id`.
-### List a comment's award emoji
+### List a comment's award emojis
-Get all award emoji for a comment (note).
+Get all award emojis for a comment (note).
```plaintext
GET /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji
@@ -341,10 +341,9 @@ Example response:
### Delete an award emoji from a comment
-Sometimes it's just not meant to be and you'll have to remove the award.
+Sometimes it's just not meant to be and you need to remove the award.
-NOTE:
-Only available to administrators or the author of the award.
+Only an administrator or the author of the award can delete an award emoji.
```plaintext
DELETE /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji/:award_id
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 4eab0d7797c..c4f670c8a87 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -591,6 +591,21 @@ type AlertManagementAlertStatusCountsType {
}
"""
+Filters the alerts based on given domain
+"""
+enum AlertManagementDomainFilter {
+ """
+ Alerts for operations domain
+ """
+ operations
+
+ """
+ Alerts for threat monitoring domain
+ """
+ threat_monitoring
+}
+
+"""
An endpoint and credentials used to accept alerts for a project
"""
type AlertManagementHttpIntegration implements AlertManagementIntegration {
@@ -15736,6 +15751,11 @@ type Pipeline {
before: String
"""
+ Filter query for given domain
+ """
+ domain: AlertManagementDomainFilter! = operations
+
+ """
Returns the first _n_ elements from the list.
"""
first: Int
@@ -16028,6 +16048,11 @@ type Project {
assigneeUsername: String
"""
+ Filter query for given domain
+ """
+ domain: AlertManagementDomainFilter! = operations
+
+ """
IID of the alert. For example, "1"
"""
iid: String
@@ -24959,6 +24984,11 @@ type Vulnerability implements Noteable {
reportType: VulnerabilityReportType
"""
+ Timestamp of when the vulnerability state was changed to resolved
+ """
+ resolvedAt: Time
+
+ """
Indicates whether the vulnerability is fixed on the default branch or not
"""
resolvedOnDefaultBranch: Boolean!
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 1eeda974bfb..b64bba6bdaa 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -1481,6 +1481,29 @@
"possibleTypes": null
},
{
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "description": "Filters the alerts based on given domain",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "operations",
+ "description": "Alerts for operations domain ",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "threat_monitoring",
+ "description": "Alerts for threat monitoring domain",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegration",
"description": "An endpoint and credentials used to accept alerts for a project",
@@ -47849,6 +47872,20 @@
"defaultValue": null
},
{
+ "name": "domain",
+ "description": "Filter query for given domain",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "ofType": null
+ }
+ },
+ "defaultValue": "operations"
+ },
+ {
"name": "search",
"description": "Search query for title, description, service, or monitoring_tool.",
"type": {
@@ -47953,6 +47990,20 @@
"defaultValue": null
},
{
+ "name": "domain",
+ "description": "Filter query for given domain",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "ofType": null
+ }
+ },
+ "defaultValue": "operations"
+ },
+ {
"name": "search",
"description": "Search query for title, description, service, or monitoring_tool.",
"type": {
@@ -72625,6 +72676,20 @@
"deprecationReason": null
},
{
+ "name": "resolvedAt",
+ "description": "Timestamp of when the vulnerability state was changed to resolved",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "resolvedOnDefaultBranch",
"description": "Indicates whether the vulnerability is fixed on the default branch or not",
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 09f6e3242a0..bf8c45cead7 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -3754,6 +3754,7 @@ Represents a vulnerability.
| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. |
| `project` | Project | The project on which the vulnerability was found |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING) |
+| `resolvedAt` | Time | Timestamp of when the vulnerability state was changed to resolved |
| `resolvedOnDefaultBranch` | Boolean! | Indicates whether the vulnerability is fixed on the default branch or not |
| `scanner` | VulnerabilityScanner | Scanner metadata for the vulnerability. |
| `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) |
@@ -4018,6 +4019,15 @@ Values for sorting alerts.
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
+### AlertManagementDomainFilter
+
+Filters the alerts based on given domain.
+
+| Value | Description |
+| ----- | ----------- |
+| `operations` | Alerts for operations domain |
+| `threat_monitoring` | Alerts for threat monitoring domain |
+
### AlertManagementIntegrationType
Values of types of integrations.
diff --git a/doc/api/merge_trains.md b/doc/api/merge_trains.md
index 3891dc12a2e..d8c0acb3896 100644
--- a/doc/api/merge_trains.md
+++ b/doc/api/merge_trains.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Merge Trains API **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36146) in GitLab 12.9.
-> - Using this API you can consume GitLab's [Merge Train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md) entries.
+> - Using this API you can consume [Merge Train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md) entries.
Every API call to merge trains must be authenticated with Developer or higher [permissions](../user/permissions.md).
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 2eb8ad47951..e8b22a24017 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -541,7 +541,7 @@ next run of the pipeline, the cache is stored in a different location.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41249) in GitLab 10.4.
If you want to avoid editing `.gitlab-ci.yml`, you can clear the cache
-via GitLab's UI:
+via the GitLab UI:
1. Navigate to your project's **CI/CD > Pipelines** page.
1. Click on the **Clear runner caches** button to clean up the cache.
diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
index 81d22167b96..b7f59761889 100644
--- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
+++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
@@ -152,7 +152,7 @@ EOF
This example uses [bound_claims](https://www.vaultproject.io/api/auth/jwt#bound_claims) to specify that only a JWT with matching values for the specified claims will be allowed to authenticate.
-Combined with GitLab's [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
+Combined with [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
[token_explicit_max_ttl](https://www.vaultproject.io/api/auth/jwt#token_explicit_max_ttl) specifies that the token issued by Vault, upon successful authentication, has a hard lifetime limit of 60 seconds.
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index d08158f8bc0..2405ce31413 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -431,7 +431,7 @@ fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/lat
To deploy our build artifacts, we need to install the [AWS CLI](https://aws.amazon.com/cli/) on
the shared runner. The shared runner also needs to be able to authenticate with your AWS
account to deploy the artifacts. By convention, AWS CLI will look for `AWS_ACCESS_KEY_ID`
-and `AWS_SECRET_ACCESS_KEY`. GitLab's CI gives us a way to pass the variables we
+and `AWS_SECRET_ACCESS_KEY`. GitLab CI/CD gives us a way to pass the variables we
set up in the prior section using the `variables` portion of the `deploy` job. At the end,
we add directives to ensure deployment `only` happens on pushes to `master`. This way, every
single branch still runs through CI, and only merging (or committing directly) to master will
@@ -509,7 +509,7 @@ Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](https://www.darknova.io).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code.
-Errors can be easily debugged through GitLab's build logs, and within minutes of a successful commit,
+Errors can be easily debugged through GitLab build logs, and within minutes of a successful commit,
you can see the changes live on your game.
Setting up Continuous Integration and Continuous Deployment from the start with Dark Nova enables
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index b69103ee8f0..1447c3d5730 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -116,7 +116,7 @@ After you're happy with your implementation:
![GitLab workflow example](img/gitlab_workflow_example_11_9.png)
GitLab CI/CD is capable of doing a lot more, but this workflow
-exemplifies GitLab's ability to track the entire process,
+exemplifies the ability of GitLab to track the entire process,
without the need for an external tool to deliver your software.
And, most usefully, you can visualize all the steps through
the GitLab UI.
diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md
index 56e9b285e3c..0c1787e9818 100644
--- a/doc/ci/pipelines/job_artifacts.md
+++ b/doc/ci/pipelines/job_artifacts.md
@@ -58,7 +58,7 @@ For more examples on artifacts, follow the [artifacts reference in
> - Requires GitLab Runner 11.2 and above.
The `artifacts:reports` keyword is used for collecting test reports, code quality
-reports, and security reports from jobs. It also exposes these reports in GitLab's
+reports, and security reports from jobs. It also exposes these reports in the GitLab
UI (merge requests, pipeline views, and security dashboards).
The test reports are collected regardless of the job results (success or failure).
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index a346f5ccbc6..5a758bccd62 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -26,7 +26,7 @@ There are two options. Using:
- `git clone`, which is slower since it clones the repository from scratch
for every job, ensuring that the local working copy is always pristine.
-- `git fetch`, which is GitLab's default and faster as it re-uses the local working copy (falling
+- `git fetch`, which is default in GitLab and faster as it re-uses the local working copy (falling
back to clone if it doesn't exist).
This is recommended, especially for [large repositories](../large_repositories/index.md#git-strategy).
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 49e1ebdc46b..2e7ec58f048 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -114,7 +114,7 @@ The action is irreversible.
## Triggering a pipeline
-To trigger a job you need to send a `POST` request to GitLab's API endpoint:
+To trigger a job you need to send a `POST` request to the GitLab API endpoint:
```plaintext
POST /projects/:id/trigger/pipeline
@@ -126,7 +126,7 @@ branches or tags. The `:id` of a project can be found by
[querying the API](../../api/projects.md) or by visiting the **CI/CD**
settings page which provides self-explanatory examples.
-When a rerun of a pipeline is triggered, the information is exposed in GitLab's
+When a rerun of a pipeline is triggered, the information is exposed in the GitLab
UI under the **Jobs** page and the jobs are marked as triggered 'by API'.
![Marked rebuilds as on jobs page](img/builds_page.png)
@@ -260,7 +260,7 @@ branch of project with ID `9` every night at `00:30`:
30 0 * * * curl --request POST --form token=TOKEN --form ref=master "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
```
-This behavior can also be achieved through GitLab's UI with
+This behavior can also be achieved through the GitLab UI with
[pipeline schedules](../pipelines/schedules.md).
## Legacy triggers
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index b40aa36294f..f4ca51be151 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -415,7 +415,7 @@ You can define per-project or per-group variables that are set in the pipeline e
We recommend using group environment variables to store secrets (like passwords, SSH keys, and credentials) for Premium users who:
- Do **not** use an external key store.
-- Use GitLab's [integration with HashiCorp Vault](../secrets/index.md).
+- Use the GitLab [integration with HashiCorp Vault](../secrets/index.md).
Group-level variables can be added by:
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 25b603623b3..9d273c006a4 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -118,7 +118,7 @@ Kubernetes-specific environment variables are detailed in the
| `CI_PROJECT_TITLE` | 12.4 | all | The human-readable project name as displayed in the GitLab web interface. |
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address to access project |
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility (internal, private, public) |
-| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry. This variable includes a `:port` value if one has been specified in the registry configuration. |
+| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of the GitLab Container Registry. This variable includes a `:port` value if one has been specified in the registry configuration. |
| `CI_REGISTRY_IMAGE` | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| `CI_REGISTRY_PASSWORD` | 9.0 | all | The password to use to push containers to the GitLab Container Registry, for the current project. |
| `CI_REGISTRY_USER` | 9.0 | all | The username to use to push containers to the GitLab Container Registry, for the current project. |
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index c551763ca5a..f2dc58bc144 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -24,7 +24,7 @@ There are two places defined variables can be used. On the:
| Definition | Can be expanded? | Expansion place | Description |
|:-------------------------------------------|:-----------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<br/><br/>Supported are all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules).<br/><br/>Not supported are variables defined in the GitLab Runner `config.toml` and variables created in the job's `script`. |
+| `environment:url` | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab.<br/><br/>Supported are all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules).<br/><br/>Not supported are variables defined in the GitLab Runner `config.toml` and variables created in the job's `script`. |
| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
| `resource_group` | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index bd975b1f690..b1f0dae67d3 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -503,7 +503,7 @@ so you can only `include` public projects or templates.
For example:
```yaml
-# File sourced from GitLab's template collection
+# File sourced from the GitLab template collection
include:
- template: Auto-DevOps.gitlab-ci.yml
```
@@ -2472,7 +2472,7 @@ environment. A new `stop_review_app` job is listed under `on_stop`.
After the `review_app` job is finished, it triggers the
`stop_review_app` job based on what is defined under `when`. In this case,
it is set to `manual`, so it needs a [manual action](#whenmanual) from
-GitLab's user interface to run.
+the GitLab UI to run.
Also in the example, `GIT_STRATEGY` is set to `none`. If the
`stop_review_app` job is [automatically triggered](../environments/index.md#automatically-stopping-an-environment),
@@ -3174,7 +3174,7 @@ in GitLab 13.4.
The [`artifacts:reports` keyword](../pipelines/job_artifacts.md#artifactsreports)
is used for collecting test reports, code quality reports, and security reports from jobs.
-It also exposes these reports in GitLab's UI (merge requests, pipeline views, and security dashboards).
+It also exposes these reports in the GitLab UI (merge requests, pipeline views, and security dashboards).
These are the available report types:
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 8a3fe93c4b1..4e3b505d5d4 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -785,7 +785,7 @@ end
You should never re-use resolvers directly. Resolvers have a complex life-cycle, with
authorization, readiness and resolution orchestrated by the framework, and at
-each stage lazy values can be returned to take advantage of batching
+each stage [lazy values](#laziness) can be returned to take advantage of batching
opportunities. Never instantiate a resolver or a mutation in application code.
Instead, the units of code reuse are much the same as in the rest of the
@@ -1262,7 +1262,7 @@ should look like this:
### Mounting the mutation
To make the mutation available it must be defined on the mutation
-type that lives in `graphql/types/mutation_types`. The
+type that is stored in `graphql/types/mutation_types`. The
`mount_mutation` helper method defines a field based on the
GraphQL-name of the mutation:
@@ -1532,32 +1532,66 @@ field :created_at, Types::TimeType, null: true, description: 'Timestamp of when
## Testing
-_full stack_ tests for a graphql query or mutation live in
+### Writing unit tests
+
+Before creating unit tests, review the following examples:
+
+- [`spec/graphql/resolvers/users_resolver_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/resolvers/users_resolver_spec.rb)
+- [`spec/graphql/mutations/issues/create_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/mutations/issues/create_spec.rb)
+
+It's faster to test as much of the logic from your GraphQL queries and mutations
+with unit tests, which are stored in `spec/graphql`.
+
+Use unit tests to verify that:
+
+- Types have the expected fields.
+- Resolvers and mutations apply authorizations and return expected data.
+- Edge cases are handled correctly.
+
+### Writing integration tests
+
+Integration tests check the full stack for a GraphQL query or mutation and are stored in
`spec/requests/api/graphql`.
-When adding a query, the `a working graphql query` shared example can
-be used to test if the query renders valid results.
+For speed, you should test most logic in unit tests instead of integration tests.
+However, integration tests that check if data is returned verify the following
+additional items:
+
+- The mutation is actually queryable within the schema (was mounted in `MutationType`).
+- The data returned by a resolver or mutation correctly matches the
+ [return types](https://graphql-ruby.org/fields/introduction.html#field-return-type) of
+ the fields and resolves without errors.
-Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query
-including all available fields can be constructed. This makes it easy
-to add a test rendering all possible fields for a query.
+Integration tests can also verify the following items, because they invoke the
+full stack:
+
+- An argument or scalar's [`prepare`](#validating-arguments) applies correctly.
+- Logic in a resolver or mutation's [`#ready?` method](#correct-use-of-resolverready) applies correctly.
+- An [argument's `default_value`](https://graphql-ruby.org/fields/arguments.html) applies correctly.
+- Objects resolve performantly and there are no N+1 issues.
+
+When adding a query, you can use the `a working graphql query` shared example to test if the query
+renders valid results.
+
+You can construct a query including all available fields using the `GraphqlHelpers#all_graphql_fields_for`
+helper. This makes it easy to add a test rendering all possible fields for a query.
If you're adding a field to a query that supports pagination and sorting,
visit [Testing](graphql_guide/pagination.md#testing) for details.
-To test GraphQL mutation requests, `GraphqlHelpers` provides 2
+To test GraphQL mutation requests, `GraphqlHelpers` provides two
helpers: `graphql_mutation` which takes the name of the mutation, and
a hash with the input for the mutation. This returns a struct with
a mutation query, and prepared variables.
-This struct can then be passed to the `post_graphql_mutation` helper,
+You can then pass this struct to the `post_graphql_mutation` helper,
that posts the request with the correct parameters, like a GraphQL
client would do.
-To access the response of a mutation, the `graphql_mutation_response`
-helper is available.
+To access the response of a mutation, you can use the `graphql_mutation_response`
+helper.
-Using these helpers, we can build specs like this:
+Using these helpers, you can build specs like this:
```ruby
let(:mutation) do
@@ -1629,7 +1663,7 @@ end
- Mimic the folder structure of `app/graphql/types`:
For example, tests for fields on `Types::Ci::PipelineType`
- in `app/graphql/types/ci/pipeline_type.rb` should live in
+ in `app/graphql/types/ci/pipeline_type.rb` should be stored in
`spec/requests/api/graphql/ci/pipeline_spec.rb` regardless of the query being
used to fetch the pipeline data.
@@ -1702,3 +1736,19 @@ For guidance, see the [GraphQL API](documentation/graphql_styleguide.md) page.
## Include a changelog entry
All client-facing changes **must** include a [changelog entry](changelog.md).
+
+## Laziness
+
+One important technique unique to GraphQL for managing performance is
+using **lazy** values. Lazy values represent the promise of a result,
+allowing their action to be run later, which enables batching of queries in
+different parts of the query tree. The main example of lazy values in our code is
+the [GraphQL BatchLoader](graphql_guide/batchloader.md).
+
+To manage lazy values directly, read `Gitlab::Graphql::Lazy`, and in
+particular `Gitlab::Graphql::Laziness`. This contains `#force` and
+`#delay`, which help implement the basic operations of creation and
+elimination of laziness, where needed.
+
+For dealing with lazy values without forcing them, use
+`Gitlab::Graphql::Lazy.with_value`.
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index d0e75c47a07..fcf4662502f 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -101,11 +101,11 @@ The global nav has 3 components:
The available sections are described on the table below:
-| Section | Description |
-| ------------- | ------------------------------------------ |
-| User | Documentation for the GitLab's user UI. |
-| Administrator | Documentation for the GitLab's Admin Area. |
-| Contributor | Documentation for developing GitLab. |
+| Section | Description |
+| ------------- | ------------------------------------ |
+| User | Documentation for the GitLab UI. |
+| Administrator | Documentation for the Admin Area. |
+| Contributor | Documentation for developing GitLab. |
The majority of the links available on the nav were added according to the UI.
The match is not perfect, as for some UI nav items the documentation doesn't
@@ -250,7 +250,7 @@ below the doc link:
```
All nav links are clickable. If the higher-level link does not have a link
-of its own, it must link to its first sub-item link, mimicking GitLab's navigation.
+of its own, it must link to its first sub-item link, mimicking the navigation in GitLab.
This must be avoided so that we don't have duplicated links nor two `.active` links
at the same time.
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index c72a4dcf1d5..be25a083948 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -2,7 +2,6 @@
stage: none
group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-description: "Learn how GitLab's documentation website is architectured."
---
# Documentation site architecture
@@ -14,8 +13,8 @@ static site generator.
## Architecture
-While the source of the documentation content is stored in GitLab's respective product
-repositories, the source that is used to build the documentation
+While the source of the documentation content is stored in the repositories for
+each GitLab product, the source that is used to build the documentation
site _from that content_ is located at <https://gitlab.com/gitlab-org/gitlab-docs>.
The following diagram illustrates the relationship between the repositories
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index 68bd8fbb0df..d73494699cb 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -9,7 +9,7 @@ description: What to include in GitLab documentation pages.
Use these standards to contribute content to the GitLab documentation.
-Before getting started, familiarize yourself with [GitLab's Documentation guidelines](index.md)
+Before getting started, familiarize yourself with [Documentation guidelines for GitLab](index.md)
and the [Documentation Style Guide](styleguide/index.md).
## Components of a documentation page
diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md
index bafef89f954..ce02c01d874 100644
--- a/doc/development/documentation/testing.md
+++ b/doc/development/documentation/testing.md
@@ -31,8 +31,8 @@ run these tests on your local computer. This has the advantage of:
- Speeding up the feedback loop. You can know of any problems with the changes in your branch
without waiting for a CI/CD pipeline to run.
-- Lowering costs. Running tests locally is cheaper than running tests on GitLab's cloud
- infrastructure.
+- Lowering costs. Running tests locally is cheaper than running tests on the cloud
+ infrastructure GitLab uses.
To run tests locally, it's important to:
@@ -106,9 +106,9 @@ To run the `ui-docs-links` test locally:
```
If you receive an error the first time you run this test, run `bundle install`, which
-installs GitLab's dependencies, and try again.
+installs the dependencies for GitLab, and try again.
-If you don't want to install all of GitLab's dependencies to test the links, you can:
+If you don't want to install all of the dependencies to test the links, you can:
1. Open the `gitlab` directory in a terminal window.
1. Install `haml-lint`:
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 87de8b9cdf6..e809ca84707 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Documentation process
The process for creating and maintaining GitLab product documentation allows
-anyone to contribute a merge request or create an issue for GitLab's
+anyone to contribute a merge request or create an issue for GitLab
documentation.
Documentation updates relating to new features or feature enhancements must
@@ -60,7 +60,7 @@ To update GitLab documentation:
- The [Structure and template](structure.md) page.
- The [Style Guide](styleguide/index.md).
- The [Markdown Guide](https://about.gitlab.com/handbook/markdown-guide/).
-1. Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
+1. Follow the [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
NOTE:
Work in a fork if you do not have Developer access to the GitLab project.
@@ -79,7 +79,7 @@ To request help:
- If urgent help is required, directly assign the Technical Writer in the issue or in the merge request.
- If non-urgent help is required, ping the Technical Writer in the issue or merge request.
-If you are a member of GitLab's Slack workspace, you can request help in `#docs`.
+If you are a member of the GitLab Slack workspace, you can request help in `#docs`.
### Reviewing and merging
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 5c4497fc49a..f34e38fb7ca 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -232,6 +232,11 @@ NOTE:
If you manually copied your public SSH key make sure you copied the entire
key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email address.
+## Two-factor Authentication (2FA)
+
+You can set up two-factor authentication (2FA) for
+[Git over SSH](../security/two_factor_authentication.md#two-factor-authentication-2fa-for-git-over-ssh-operations).
+
## Testing that everything is set up correctly
To test whether your SSH key was added correctly, run the following
diff --git a/doc/user/group/img/add_new_members_v13_6.png b/doc/user/group/img/add_new_members_v13_6.png
deleted file mode 100644
index 4255eeb72c7..00000000000
--- a/doc/user/group/img/add_new_members_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/img/add_new_members_v13_7.png b/doc/user/group/img/add_new_members_v13_7.png
new file mode 100644
index 00000000000..7e06bdf8fd1
--- /dev/null
+++ b/doc/user/group/img/add_new_members_v13_7.png
Binary files differ
diff --git a/doc/user/group/img/manual_permissions_v13_6.png b/doc/user/group/img/manual_permissions_v13_6.png
deleted file mode 100644
index 6d26061b049..00000000000
--- a/doc/user/group/img/manual_permissions_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/img/manual_permissions_v13_7.png b/doc/user/group/img/manual_permissions_v13_7.png
new file mode 100644
index 00000000000..fcea0121915
--- /dev/null
+++ b/doc/user/group/img/manual_permissions_v13_7.png
Binary files differ
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 998e39fa62c..b09fb1aae86 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -130,7 +130,7 @@ give a user access to all projects in the group with one action.
Add members to a group by navigating to the group's dashboard and clicking **Members**.
-![add members to group](img/add_new_members_v13_6.png)
+![add members to group](img/add_new_members_v13_7.png)
Select the [permission level](../permissions.md#permissions), and add the new member. You can also set the expiring date for that user; this is the date on which they will no longer have access to your group.
@@ -377,7 +377,7 @@ In GitLab [8.15](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/822) and
1. Select the pencil icon in the row for the user you are editing.
1. Select the brown `Edit permissions` button in the modal.
-![Setting manual permissions](img/manual_permissions_v13_6.png)
+![Setting manual permissions](img/manual_permissions_v13_7.png)
Now you will be able to edit the user's permissions from the **Members** page.
diff --git a/doc/user/project/issues/associate_zoom_meeting.md b/doc/user/project/issues/associate_zoom_meeting.md
index 9c4e6fd9acb..d81fe19c5b9 100644
--- a/doc/user/project/issues/associate_zoom_meeting.md
+++ b/doc/user/project/issues/associate_zoom_meeting.md
@@ -10,13 +10,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
In order to communicate synchronously for incidents management,
GitLab allows to associate a Zoom meeting with an issue.
-Once you start a Zoom call for a fire-fight, you need a way to
-associate the conference call with an issue, so that your team
-members can join swiftly without requesting a link.
+After you start a Zoom call for a fire-fight, you need a way to
+associate the conference call with an issue. This is so that your
+team members can join swiftly without requesting a link.
-## Adding a zoom meeting to an issue
+## Adding a Zoom meeting to an issue
-To associate a zoom meeting with an issue, you can use GitLab's
+To associate a Zoom meeting with an issue, you can use GitLab
[quick actions](../quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
In an issue, leave a comment using the `/zoom` quick action followed by a valid Zoom link:
@@ -26,23 +26,23 @@ In an issue, leave a comment using the `/zoom` quick action followed by a valid
```
If the Zoom meeting URL is valid and you have at least [Reporter permissions](../../permissions.md),
-a system alert will notify you that the addition of the meeting URL was successful.
-The issue's description will be automatically edited to include the Zoom link, and a button will
-appear right under the issue's title.
+a system alert notifies you of its successful addition.
+The issue's description is automatically edited to include the Zoom link, and a button
+appears right under the issue's title.
![Link Zoom Call in Issue](img/zoom-quickaction-button.png)
You are only allowed to attach a single Zoom meeting to an issue. If you attempt
-to add a second Zoom meeting using the `/zoom` quick action, it won't work, you
+to add a second Zoom meeting using the `/zoom` quick action, it doesn't work. You
need to [remove it](#removing-an-existing-zoom-meeting-from-an-issue) first.
## Removing an existing Zoom meeting from an issue
-Similarly to adding a zoom meeting, you can remove it with a quick action:
+Similarly to adding a Zoom meeting, you can remove it with a quick action:
```shell
/remove_zoom
```
If you have at least [Reporter permissions](../../permissions.md),
-a system alert will notify you that the meeting URL was successfully removed.
+a system alert notifies you that the meeting URL was successfully removed.
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index 3af651628e8..fd7c58f12b9 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -203,18 +203,17 @@ To properly configure submodules with GitLab CI/CD, read the
With the update permission model we also extended the support for accessing
Container Registries for private projects.
-> **Notes:**
->
-> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
-> for permissions. This makes the `image:` directive not work with private
-> projects automatically and it needs to be configured manually on the GitLab Runner host
-> with a predefined account (for example administrator's personal account with
-> access token created explicitly for this purpose). This issue is resolved with
-> latest changes in GitLab Runner 1.8 which receives GitLab credentials with
-> build data.
-> - Starting from GitLab 8.12, if you have [2FA](../profile/account/two_factor_authentication.md) enabled in your account, you need
-> to pass a [personal access token](../profile/personal_access_tokens.md) instead of your password in order to
-> login to GitLab's Container Registry.
+GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
+for permissions. This makes the `image:` directive not work with private
+projects automatically and it needs to be configured manually on the GitLab Runner host
+with a predefined account (for example administrator's personal account with
+access token created explicitly for this purpose). This issue is resolved with
+latest changes in GitLab Runner 1.8 which receives GitLab credentials with
+build data.
+
+Starting from GitLab 8.12, if you have [2FA](../profile/account/two_factor_authentication.md) enabled in your account, you need
+to pass a [personal access token](../profile/personal_access_tokens.md) instead of your password in order to
+login to the Container Registry.
Your jobs can access all container images that you would normally have access
to. The only implication is that you can push to the Container Registry of the
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 88cc840c395..f5931a1d5eb 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -53,6 +53,10 @@ module Gitlab
raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}")
end
+ def self.encrypted_secrets
+ Settings.encrypted(Gitlab.config.ldap.secret_file)
+ end
+
def initialize(provider)
if self.class.valid_provider?(provider)
@provider = provider
@@ -89,8 +93,8 @@ module Gitlab
if has_auth?
opts.merge!(
- bind_dn: options['bind_dn'],
- password: options['password']
+ bind_dn: auth_username,
+ password: auth_password
)
end
@@ -155,7 +159,7 @@ module Gitlab
end
def has_auth?
- options['password'] || options['bind_dn']
+ auth_password || auth_username
end
def allow_username_or_email_login
@@ -267,12 +271,32 @@ module Gitlab
{
auth: {
method: :simple,
- username: options['bind_dn'],
- password: options['password']
+ username: auth_username,
+ password: auth_password
}
}
end
+ def secrets
+ @secrets ||= self.class.encrypted_secrets[@provider.delete_prefix('ldap').to_sym]
+ rescue => e
+ Gitlab::AppLogger.error "LDAP encrypted secrets are invalid: #{e.inspect}"
+
+ nil
+ end
+
+ def auth_password
+ return options['password'] if options['password']
+
+ secrets&.fetch(:password, nil)&.chomp
+ end
+
+ def auth_username
+ return options['bind_dn'] if options['bind_dn']
+
+ secrets&.fetch(:bind_dn, nil)&.chomp
+ end
+
def omniauth_user_filter
uid_filter = Net::LDAP::Filter.eq(uid, '%{username}')
diff --git a/lib/gitlab/encrypted_ldap_command.rb b/lib/gitlab/encrypted_ldap_command.rb
new file mode 100644
index 00000000000..cdb3e268b51
--- /dev/null
+++ b/lib/gitlab/encrypted_ldap_command.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+# rubocop:disable Rails/Output
+module Gitlab
+ class EncryptedLdapCommand
+ class << self
+ def write(contents)
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ validate_contents(contents)
+ encrypted.write(contents)
+
+ puts "File encrypted and saved."
+ rescue Interrupt
+ puts "Aborted changing file: nothing saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ end
+
+ def edit
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ if ENV["EDITOR"].blank?
+ puts 'No $EDITOR specified to open file. Please provide one when running the command:'
+ puts 'gitlab-rake gitlab:ldap:secret:edit EDITOR=vim'
+ return
+ end
+
+ temp_file = Tempfile.new(File.basename(encrypted.content_path), File.dirname(encrypted.content_path))
+ contents_changed = false
+
+ encrypted.change do |contents|
+ contents = encrypted_file_template unless File.exist?(encrypted.content_path)
+ File.write(temp_file.path, contents)
+ system(ENV['EDITOR'], temp_file.path)
+ changes = File.read(temp_file.path)
+ contents_changed = contents != changes
+ validate_contents(changes)
+ changes
+ end
+
+ puts "Contents were unchanged." unless contents_changed
+ puts "File encrypted and saved."
+ rescue Interrupt
+ puts "Aborted changing file: nothing saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ ensure
+ temp_file&.unlink
+ end
+
+ def show
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ puts encrypted.read.presence || "File '#{encrypted.content_path}' does not exist. Use `gitlab-rake gitlab:ldap:secret:edit` to change that."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ end
+
+ private
+
+ def validate_config(encrypted)
+ dir_path = File.dirname(encrypted.content_path)
+
+ unless File.exist?(dir_path)
+ puts "Directory #{dir_path} does not exist. Create the directory and try again."
+ return false
+ end
+
+ if encrypted.key.nil?
+ puts "Missing encryption key encrypted_settings_key_base."
+ return false
+ end
+
+ true
+ end
+
+ def validate_contents(contents)
+ begin
+ config = YAML.safe_load(contents, permitted_classes: [Symbol])
+ error_contents = "Did not include any key-value pairs" unless config.is_a?(Hash)
+ rescue Psych::Exception => e
+ error_contents = e.message
+ end
+
+ puts "WARNING: Content was not a valid LDAP secret yml file. #{error_contents}" if error_contents
+
+ contents
+ end
+
+ def encrypted_file_template
+ <<~YAML
+ # main:
+ # password: '123'
+ # user_dn: 'gitlab-adm'
+ YAML
+ end
+ end
+ end
+end
+# rubocop:enable Rails/Output
diff --git a/lib/tasks/gitlab/ldap.rake b/lib/tasks/gitlab/ldap.rake
index 0459de27c96..fe7920c621f 100644
--- a/lib/tasks/gitlab/ldap.rake
+++ b/lib/tasks/gitlab/ldap.rake
@@ -36,5 +36,23 @@ namespace :gitlab do
puts "Successfully updated #{plural_updated_count} out of #{plural_id_count} total"
end
end
+
+ namespace :secret do
+ desc 'GitLab | LDAP | Secret | Write LDAP secrets'
+ task write: [:environment] do
+ content = STDIN.tty? ? STDIN.gets : STDIN.read
+ Gitlab::EncryptedLdapCommand.write(content)
+ end
+
+ desc 'GitLab | LDAP | Secret | Edit LDAP secrets'
+ task edit: [:environment] do
+ Gitlab::EncryptedLdapCommand.edit
+ end
+
+ desc 'GitLab | LDAP | Secret | Show LDAP secrets'
+ task show: [:environment] do
+ Gitlab::EncryptedLdapCommand.show
+ end
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a32913e5a07..a5ab8eb66ee 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -15995,6 +15995,9 @@ msgstr ""
msgid "Last seen"
msgstr ""
+msgid "Last sign-in"
+msgstr ""
+
msgid "Last successful sync"
msgstr ""
@@ -16969,18 +16972,6 @@ msgstr ""
msgid "Members|2FA"
msgstr ""
-msgid "Members|Access granted, ascending"
-msgstr ""
-
-msgid "Members|Access granted, descending"
-msgstr ""
-
-msgid "Members|Account, ascending"
-msgstr ""
-
-msgid "Members|Account, descending"
-msgstr ""
-
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
msgstr ""
@@ -17044,21 +17035,9 @@ msgstr ""
msgid "Members|LDAP override enabled."
msgstr ""
-msgid "Members|Last sign-in, ascending"
-msgstr ""
-
-msgid "Members|Last sign-in, descending"
-msgstr ""
-
msgid "Members|Leave \"%{source}\""
msgstr ""
-msgid "Members|Max role, ascending"
-msgstr ""
-
-msgid "Members|Max role, descending"
-msgstr ""
-
msgid "Members|Membership"
msgstr ""
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index a7a6104a1f7..68a748aa76a 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -17,14 +17,20 @@ RSpec.describe 'Groups > Members > Sort members', :js do
end
context 'when `group_members_filtered_search` feature flag is enabled' do
- dropdown_toggle_selector = '[data-testid="members-sort-dropdown"] > button'
+ def expect_sort_by(text, sort_direction)
+ within('[data-testid="members-sort-dropdown"]') do
+ expect(page).to have_css('button[aria-haspopup="true"]', text: text)
+ expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
+ end
+ end
- it 'sorts account by default' do
+ it 'sorts by account by default' do
visit_members_list(sort: nil)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Account, ascending')
+
+ expect_sort_by('Account', :asc)
end
it 'sorts by max role ascending' do
@@ -32,7 +38,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Max role, ascending')
+
+ expect_sort_by('Max role', :asc)
end
it 'sorts by max role descending' do
@@ -40,7 +47,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Max role, descending')
+
+ expect_sort_by('Max role', :desc)
end
it 'sorts by access granted ascending' do
@@ -48,7 +56,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Access granted, ascending')
+
+ expect_sort_by('Access granted', :asc)
end
it 'sorts by access granted descending' do
@@ -56,7 +65,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Access granted, descending')
+
+ expect_sort_by('Access granted', :desc)
end
it 'sorts by account ascending' do
@@ -64,7 +74,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Account, ascending')
+
+ expect_sort_by('Account', :asc)
end
it 'sorts by account descending' do
@@ -72,7 +83,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Account, descending')
+
+ expect_sort_by('Account', :desc)
end
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
@@ -80,7 +92,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Last sign-in, ascending')
+
+ expect_sort_by('Last sign-in', :asc)
end
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
@@ -88,7 +101,8 @@ RSpec.describe 'Groups > Members > Sort members', :js do
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Last sign-in, descending')
+
+ expect_sort_by('Last sign-in', :desc)
end
end
diff --git a/spec/finders/alert_management/alerts_finder_spec.rb b/spec/finders/alert_management/alerts_finder_spec.rb
index e74f3ac68ed..87a5da38dd1 100644
--- a/spec/finders/alert_management/alerts_finder_spec.rb
+++ b/spec/finders/alert_management/alerts_finder_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
let_it_be(:resolved_alert) { create(:alert_management_alert, :all_fields, :resolved, project: project, ended_at: 1.year.ago, events: 2, severity: :high) }
let_it_be(:ignored_alert) { create(:alert_management_alert, :all_fields, :ignored, project: project, events: 1, severity: :critical) }
let_it_be(:triggered_alert) { create(:alert_management_alert, :all_fields) }
+ let_it_be(:threat_monitroing_alert) { create(:alert_management_alert, domain: 'threat_monitoring') }
+
let(:params) { {} }
describe '#execute' do
@@ -22,6 +24,26 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
project.add_developer(current_user)
end
+ context 'domain' do
+ context 'domain is threat management' do
+ let(:params) { { domain: 'threat_management' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+
+ context 'domain is unknown' do
+ let(:params) { { domain: 'unkown' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+
+ context 'domain is missing' do
+ let(:params) { {} }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+ end
+
context 'empty params' do
it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
end
@@ -233,12 +255,6 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
it { is_expected.to be_empty }
end
-
- context 'empty search' do
- let(:params) { { search: ' ' } }
-
- it { is_expected.not_to include(alert) }
- end
end
context 'assignee username given' do
@@ -257,12 +273,6 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
it { is_expected.to be_empty }
end
-
- context 'with empty assignee_username' do
- let(:username) { ' ' }
-
- it { is_expected.not_to include(alert) }
- end
end
end
end
diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
index 22c1eea1653..6fe67aded3d 100644
--- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -1,8 +1,8 @@
import { mount, createLocalVue } from '@vue/test-utils';
-import { within } from '@testing-library/dom';
import Vuex from 'vuex';
-import { GlDropdownItem } from '@gitlab/ui';
+import { GlSorting, GlSortingItem } from '@gitlab/ui';
import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
+import * as urlUtilities from '~/lib/utils/url_utility';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -34,10 +34,13 @@ describe('SortDropdown', () => {
});
};
+ const findSortingComponent = () => wrapper.find(GlSorting);
+ const findSortDirectionToggle = () =>
+ findSortingComponent().find('button[title="Sort direction"]');
const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
const findDropdownItemByText = text =>
wrapper
- .findAll(GlDropdownItem)
+ .findAll(GlSortingItem)
.wrappers.find(dropdownItemWrapper => dropdownItemWrapper.text() === text);
describe('dropdown options', () => {
@@ -54,37 +57,21 @@ describe('SortDropdown', () => {
const expectedDropdownItems = [
{
- label: 'Account, ascending',
+ label: 'Account',
url: `${EXPECTED_BASE_URL}name_asc`,
},
{
- label: 'Account, descending',
- url: `${EXPECTED_BASE_URL}name_desc`,
- },
- {
- label: 'Access granted, ascending',
+ label: 'Access granted',
url: `${EXPECTED_BASE_URL}last_joined`,
},
{
- label: 'Access granted, descending',
- url: `${EXPECTED_BASE_URL}oldest_joined`,
- },
- {
- label: 'Max role, ascending',
+ label: 'Max role',
url: `${EXPECTED_BASE_URL}access_level_asc`,
},
{
- label: 'Max role, descending',
- url: `${EXPECTED_BASE_URL}access_level_desc`,
- },
- {
- label: 'Last sign-in, ascending',
+ label: 'Last sign-in',
url: `${EXPECTED_BASE_URL}recent_sign_in`,
},
- {
- label: 'Last sign-in, descending',
- url: `${EXPECTED_BASE_URL}oldest_sign_in`,
- },
];
createComponent();
@@ -102,7 +89,7 @@ describe('SortDropdown', () => {
createComponent();
- expect(findDropdownItemByText('Max role, ascending').props('isChecked')).toBe(true);
+ expect(findDropdownItemByText('Max role').vm.$attrs.active).toBe(true);
});
});
@@ -112,10 +99,11 @@ describe('SortDropdown', () => {
window.location = new URL(URL_HOST);
});
- it('defaults to sorting by "Account, ascending"', () => {
+ it('defaults to sorting by "Account" in ascending order', () => {
createComponent();
- expect(findDropdownToggle().text()).toBe('Account, ascending');
+ expect(findSortingComponent().props('isAscending')).toBe(true);
+ expect(findDropdownToggle().text()).toBe('Account');
});
it('sets text as selected sort option', () => {
@@ -123,13 +111,52 @@ describe('SortDropdown', () => {
createComponent();
- expect(findDropdownToggle().text()).toBe('Max role, ascending');
+ expect(findDropdownToggle().text()).toBe('Max role');
});
});
- it('renders dropdown label', () => {
- createComponent();
+ describe('sort direction toggle', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+
+ jest.spyOn(urlUtilities, 'visitUrl');
+ });
+
+ describe('when current sort direction is ascending', () => {
+ beforeEach(() => {
+ window.location.search = '?sort=access_level_asc';
+
+ createComponent();
+ });
+
+ describe('when sort direction toggle is clicked', () => {
+ beforeEach(() => {
+ findSortDirectionToggle().trigger('click');
+ });
+
+ it('sorts in descending order', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_desc`);
+ });
+ });
+ });
- expect(within(wrapper.element).queryByText('Sort by')).not.toBe(null);
+ describe('when current sort direction is descending', () => {
+ beforeEach(() => {
+ window.location.search = '?sort=access_level_desc';
+
+ createComponent();
+ });
+
+ describe('when sort direction toggle is clicked', () => {
+ beforeEach(() => {
+ findSortDirectionToggle().trigger('click');
+ });
+
+ it('sorts in ascending order', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_asc`);
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
index 2dcd084b241..7bbfddf8fc6 100644
--- a/spec/frontend/members/utils_spec.js
+++ b/spec/frontend/members/utils_spec.js
@@ -8,7 +8,7 @@ import {
canUpdate,
canOverride,
parseSortParam,
- buildSortUrl,
+ buildSortHref,
} from '~/members/utils';
import { DEFAULT_SORT } from '~/members/constants';
import { member as memberMock, group, invite } from './mock_data';
@@ -148,14 +148,14 @@ describe('Members Utils', () => {
describe.each`
sortParam | expected
- ${'name_asc'} | ${{ sortBy: 'account', sortDesc: false, sortByLabel: 'Account, ascending' }}
- ${'name_desc'} | ${{ sortBy: 'account', sortDesc: true, sortByLabel: 'Account, descending' }}
- ${'last_joined'} | ${{ sortBy: 'granted', sortDesc: false, sortByLabel: 'Access granted, ascending' }}
- ${'oldest_joined'} | ${{ sortBy: 'granted', sortDesc: true, sortByLabel: 'Access granted, descending' }}
- ${'access_level_asc'} | ${{ sortBy: 'maxRole', sortDesc: false, sortByLabel: 'Max role, ascending' }}
- ${'access_level_desc'} | ${{ sortBy: 'maxRole', sortDesc: true, sortByLabel: 'Max role, descending' }}
- ${'recent_sign_in'} | ${{ sortBy: 'lastSignIn', sortDesc: false, sortByLabel: 'Last sign-in, ascending' }}
- ${'oldest_sign_in'} | ${{ sortBy: 'lastSignIn', sortDesc: true, sortByLabel: 'Last sign-in, descending' }}
+ ${'name_asc'} | ${{ sortByKey: 'account', sortDesc: false }}
+ ${'name_desc'} | ${{ sortByKey: 'account', sortDesc: true }}
+ ${'last_joined'} | ${{ sortByKey: 'granted', sortDesc: false }}
+ ${'oldest_joined'} | ${{ sortByKey: 'granted', sortDesc: true }}
+ ${'access_level_asc'} | ${{ sortByKey: 'maxRole', sortDesc: false }}
+ ${'access_level_desc'} | ${{ sortByKey: 'maxRole', sortDesc: true }}
+ ${'recent_sign_in'} | ${{ sortByKey: 'lastSignIn', sortDesc: false }}
+ ${'oldest_sign_in'} | ${{ sortByKey: 'lastSignIn', sortDesc: true }}
`('when `sort` query string param is `$sortParam`', ({ sortParam, expected }) => {
it(`returns ${JSON.stringify(expected)}`, async () => {
window.location.search = `?sort=${sortParam}`;
@@ -167,7 +167,7 @@ describe('Members Utils', () => {
});
});
- describe('buildSortUrl', () => {
+ describe('buildSortHref', () => {
beforeEach(() => {
delete window.location;
window.location = new URL(URL_HOST);
@@ -176,7 +176,7 @@ describe('Members Utils', () => {
describe('when field passed in `sortBy` argument does not have `sort` key defined', () => {
it('returns an empty string', () => {
expect(
- buildSortUrl({
+ buildSortHref({
sortBy: 'source',
sortDesc: false,
filteredSearchBarTokens: [],
@@ -189,7 +189,7 @@ describe('Members Utils', () => {
describe('when there are no filter params set', () => {
it('sets `sort` param', () => {
expect(
- buildSortUrl({
+ buildSortHref({
sortBy: 'account',
sortDesc: false,
filteredSearchBarTokens: [],
@@ -204,7 +204,7 @@ describe('Members Utils', () => {
window.location.search = '?two_factor=enabled&with_inherited_permissions=exclude';
expect(
- buildSortUrl({
+ buildSortHref({
sortBy: 'account',
sortDesc: false,
filteredSearchBarTokens: ['two_factor', 'with_inherited_permissions'],
@@ -219,7 +219,7 @@ describe('Members Utils', () => {
window.location.search = '?search=foobar';
expect(
- buildSortUrl({
+ buildSortHref({
sortBy: 'account',
sortDesc: false,
filteredSearchBarTokens: ['two_factor', 'with_inherited_permissions'],
diff --git a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
index 42830f0024d..73f878bb745 100644
--- a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
+++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
@@ -38,6 +38,16 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do
it { is_expected.to contain_exactly(ignored_alert) }
end
+ context 'filtering by domain' do
+ let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
+ let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
+
+ let(:args) { { domain: 'operations' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert, alert3) }
+ end
+
describe 'sorting' do
# Other sorting examples in spec/finders/alert_management/alerts_finder_spec.rb
context 'when sorting by events count' do
diff --git a/spec/graphql/types/alert_management/domain_filter_enum_spec.rb b/spec/graphql/types/alert_management/domain_filter_enum_spec.rb
new file mode 100644
index 00000000000..2111a33b8b4
--- /dev/null
+++ b/spec/graphql/types/alert_management/domain_filter_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['AlertManagementDomainFilter'] do
+ specify { expect(described_class.graphql_name).to eq('AlertManagementDomainFilter') }
+
+ it 'exposes all the severity values' do
+ expect(described_class.values.keys).to include(*%w[threat_monitoring operations])
+ end
+end
diff --git a/spec/tasks/gitlab/ldap_rake_spec.rb b/spec/tasks/gitlab/ldap_rake_spec.rb
index 275fcb98dd0..5286cd98944 100644
--- a/spec/tasks/gitlab/ldap_rake_spec.rb
+++ b/spec/tasks/gitlab/ldap_rake_spec.rb
@@ -13,3 +13,112 @@ RSpec.describe 'gitlab:ldap:rename_provider rake task' do
run_rake_task('gitlab:ldap:rename_provider', 'ldapmain', 'ldapfoo')
end
end
+
+RSpec.describe 'gitlab:ldap:secret rake tasks' do
+ let(:ldap_secret_file) { 'tmp/tests/ldapenc/ldap_secret.yaml.enc' }
+
+ before do
+ Rake.application.rake_require 'tasks/gitlab/ldap'
+ stub_env('EDITOR', 'cat')
+ stub_warn_user_is_not_gitlab
+ FileUtils.mkdir_p('tmp/tests/ldapenc/')
+ allow(Gitlab.config.ldap).to receive(:secret_file).and_return(ldap_secret_file)
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ end
+
+ after do
+ FileUtils.rm_rf(Rails.root.join('tmp/tests/ldapenc'))
+ end
+
+ describe ':show' do
+ it 'displays error when file does not exist' do
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/File .* does not exist. Use `gitlab-rake gitlab:ldap:secret:edit` to change that./).to_stdout
+ end
+
+ it 'displays error when key does not exist' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when key is changed' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
+ end
+
+ it 'outputs the unencrypted content when present' do
+ encrypted = Settings.encrypted(ldap_secret_file)
+ encrypted.write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/somevalue/).to_stdout
+ end
+ end
+
+ describe 'edit' do
+ it 'creates encrypted file' do
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/File encrypted and saved./).to_stdout
+ expect(File.exist?(ldap_secret_file)).to be true
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/password: '123'/)
+ end
+
+ it 'displays error when key does not exist' do
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when key is changed' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
+ end
+
+ it 'displays error when write directory does not exist' do
+ FileUtils.rm_rf(Rails.root.join('tmp/tests/ldapenc'))
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Directory .* does not exist./).to_stdout
+ end
+
+ it 'shows a warning when content is invalid' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/WARNING: Content was not a valid LDAP secret yml file/).to_stdout
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/somevalue/)
+ end
+
+ it 'displays error when $EDITOR is not set' do
+ stub_env('EDITOR', nil)
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/No \$EDITOR specified to open file. Please provide one when running the command/).to_stdout
+ end
+ end
+
+ describe 'write' do
+ before do
+ allow(STDIN).to receive(:tty?).and_return(false)
+ allow(STDIN).to receive(:read).and_return('testvalue')
+ end
+
+ it 'creates encrypted file from stdin' do
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/File encrypted and saved./).to_stdout
+ expect(File.exist?(ldap_secret_file)).to be true
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/testvalue/)
+ end
+
+ it 'displays error when key does not exist' do
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when write directory does not exist' do
+ FileUtils.rm_rf('tmp/tests/ldapenc/')
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Directory .* does not exist./).to_stdout
+ end
+
+ it 'shows a warning when content is invalid' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/WARNING: Content was not a valid LDAP secret yml file/).to_stdout
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/somevalue/)
+ end
+ end
+end
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 4a80f4f9da6..82d975cb85a 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -58,6 +58,7 @@ RSpec.describe RepositoryImportWorker do
subject.perform(project.id)
end.to raise_error(RuntimeError, error)
expect(import_state.reload.jid).not_to be_nil
+ expect(import_state.status).to eq('failed')
end
it 'updates the error on Import/Export' do
@@ -74,6 +75,7 @@ RSpec.describe RepositoryImportWorker do
end.to raise_error(RuntimeError, error)
expect(import_state.reload.last_error).not_to be_nil
+ expect(import_state.status).to eq('failed')
end
end