diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-14 09:18:27 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-14 09:18:27 +0300 |
commit | c192f26df39e9a2ab122c2d097b86e461599bde8 (patch) | |
tree | 405f7ce164bcf33ce178699048b137a8712990a0 /app | |
parent | 361def36660a93176d8cb32fb47412dbb1100c2b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
10 files changed, 234 insertions, 53 deletions
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue index b167fff26b0..f790c7b1430 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue @@ -1,39 +1,74 @@ <script> -import { GlModal } from '@gitlab/ui'; -import { __, n__ } from '~/locale'; +import { GlLink, GlModal, GlSprintf } from '@gitlab/ui'; +import { __ } from '~/locale'; import { + DELETE_MODAL_CONTENT, + DELETE_MODAL_TITLE, + DELETE_PACKAGES_MODAL_DESCRIPTION, DELETE_PACKAGES_MODAL_TITLE, DELETE_PACKAGE_MODAL_PRIMARY_ACTION, + DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT, + DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT, + DELETE_PACKAGE_WITH_REQUEST_FORWARDING_PRIMARY_ACTION, + DELETE_PACKAGES_WITH_REQUEST_FORWARDING_PRIMARY_ACTION, + REQUEST_FORWARDING_HELP_PAGE_PATH, } from '~/packages_and_registries/package_registry/constants'; export default { name: 'DeleteModal', i18n: { - DELETE_PACKAGES_MODAL_TITLE, + DELETE_MODAL_CONTENT, + DELETE_PACKAGES_MODAL_DESCRIPTION, }, components: { + GlLink, GlModal, + GlSprintf, }, props: { itemsToBeDeleted: { type: Array, required: true, }, + showRequestForwardingContent: { + type: Boolean, + required: false, + default: false, + }, }, computed: { - description() { - return n__( - 'PackageRegistry|You are about to delete 1 package. This operation is irreversible.', - `PackageRegistry|You are about to delete %d packages. This operation is irreversible.`, - this.itemsToBeDeleted.length, - ); + itemToBeDeleted() { + if (this.itemsToBeDeleted.length === 1) { + const [itemToBeDeleted] = this.itemsToBeDeleted; + return itemToBeDeleted; + } + return null; + }, + title() { + return this.itemToBeDeleted ? DELETE_MODAL_TITLE : DELETE_PACKAGES_MODAL_TITLE; + }, + packagesDeletePrimaryActionProps() { + let text = DELETE_PACKAGE_MODAL_PRIMARY_ACTION; + + if (this.showRequestForwardingContent) { + if (this.itemToBeDeleted) { + text = DELETE_PACKAGE_WITH_REQUEST_FORWARDING_PRIMARY_ACTION; + } else { + text = DELETE_PACKAGES_WITH_REQUEST_FORWARDING_PRIMARY_ACTION; + } + } + return { + text, + attributes: { variant: 'danger', category: 'primary' }, + }; + }, + requestForwardingContentMessage() { + return this.itemToBeDeleted + ? DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT + : DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT; }, }, modal: { - packagesDeletePrimaryAction: { - text: DELETE_PACKAGE_MODAL_PRIMARY_ACTION, - attributes: { variant: 'danger', category: 'primary' }, - }, cancelAction: { text: __('Cancel'), }, @@ -43,6 +78,9 @@ export default { this.$refs.deleteModal.show(); }, }, + links: { + REQUEST_FORWARDING_HELP_PAGE_PATH, + }, }; </script> @@ -51,12 +89,34 @@ export default { ref="deleteModal" size="sm" modal-id="delete-packages-modal" - :action-primary="$options.modal.packagesDeletePrimaryAction" + :action-primary="packagesDeletePrimaryActionProps" :action-cancel="$options.modal.cancelAction" - :title="$options.i18n.DELETE_PACKAGES_MODAL_TITLE" + :title="title" @primary="$emit('confirm')" @cancel="$emit('cancel')" > - <span>{{ description }}</span> + <p v-if="showRequestForwardingContent"> + <gl-sprintf :message="requestForwardingContentMessage"> + <template #docLink="{ content }"> + <gl-link :href="$options.links.REQUEST_FORWARDING_HELP_PAGE_PATH">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + <p v-else> + <gl-sprintf v-if="itemToBeDeleted" :message="$options.i18n.DELETE_MODAL_CONTENT"> + <template #version> + <strong>{{ itemToBeDeleted.version }}</strong> + </template> + + <template #name> + <strong>{{ itemToBeDeleted.name }}</strong> + </template> + </gl-sprintf> + <gl-sprintf v-else :message="$options.i18n.DELETE_PACKAGES_MODAL_DESCRIPTION"> + <template #count> + {{ itemsToBeDeleted.length }} + </template> + </gl-sprintf> + </p> </gl-modal> </template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue index 486ab4fdc99..effed4891d8 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue @@ -1,7 +1,6 @@ <script> import { GlAlert } from '@gitlab/ui'; import { s__, sprintf, n__ } from '~/locale'; -import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue'; import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue'; import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue'; import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; @@ -14,16 +13,24 @@ import { CANCEL_DELETE_PACKAGE_TRACKING_ACTION, CANCEL_DELETE_PACKAGES_TRACKING_ACTION, PACKAGE_ERROR_STATUS, + PACKAGE_TYPE_MAVEN, + PACKAGE_TYPE_NPM, + PACKAGE_TYPE_PYPI, } from '~/packages_and_registries/package_registry/constants'; import { packageTypeToTrackCategory } from '~/packages_and_registries/package_registry/utils'; import Tracking from '~/tracking'; +const forwardingFieldToPackageTypeMapping = { + mavenPackageRequestsForwarding: PACKAGE_TYPE_MAVEN, + npmPackageRequestsForwarding: PACKAGE_TYPE_NPM, + pypiPackageRequestsForwarding: PACKAGE_TYPE_PYPI, +}; + export default { name: 'PackagesList', components: { GlAlert, DeleteModal, - DeletePackageModal, PackagesListLoader, PackagesListRow, RegistryList, @@ -44,16 +51,27 @@ export default { type: Object, required: true, }, + groupSettings: { + type: Object, + required: false, + default: () => ({}), + }, }, data() { return { - itemToBeDeleted: null, itemsToBeDeleted: [], errorPackages: [], }; }, computed: { + itemToBeDeleted() { + if (this.itemsToBeDeleted.length === 1) { + const [itemToBeDeleted] = this.itemsToBeDeleted; + return itemToBeDeleted; + } + return null; + }, listTitle() { return n__('%d package', '%d packages', this.list.length); }, @@ -77,6 +95,15 @@ export default { showErrorPackageAlert() { return this.errorPackages.length > 0; }, + packageTypesWithForwardingEnabled() { + return Object.keys(this.groupSettings) + .filter((field) => this.groupSettings[field]) + .map((field) => forwardingFieldToPackageTypeMapping[field]); + }, + isRequestForwardingEnabled() { + const selectedPackageTypes = new Set(this.itemsToBeDeleted.map((item) => item.packageType)); + return this.packageTypesWithForwardingEnabled.some((type) => selectedPackageTypes.has(type)); + }, }, watch: { list(newVal) { @@ -88,40 +115,36 @@ export default { this.list.length > 0 ? this.list.filter((pkg) => pkg.status === PACKAGE_ERROR_STATUS) : []; }, methods: { - setItemToBeDeleted(item) { - this.itemToBeDeleted = { ...item }; - this.track(REQUEST_DELETE_PACKAGE_TRACKING_ACTION); - }, setItemsToBeDeleted(items) { + this.itemsToBeDeleted = items; if (items.length === 1) { - const [item] = items; - this.setItemToBeDeleted(item); - return; + this.track(REQUEST_DELETE_PACKAGE_TRACKING_ACTION); + } else { + this.track(REQUEST_DELETE_PACKAGES_TRACKING_ACTION); } - this.itemsToBeDeleted = items; - this.track(REQUEST_DELETE_PACKAGES_TRACKING_ACTION); this.$refs.deletePackagesModal.show(); }, deleteItemsConfirmation() { this.$emit('delete', this.itemsToBeDeleted); - this.track(DELETE_PACKAGES_TRACKING_ACTION); + + if (this.itemToBeDeleted) { + this.track(DELETE_PACKAGE_TRACKING_ACTION); + } else { + this.track(DELETE_PACKAGES_TRACKING_ACTION); + } + this.itemsToBeDeleted = []; }, deleteItemsCanceled() { - this.track(CANCEL_DELETE_PACKAGES_TRACKING_ACTION); + if (this.itemToBeDeleted) { + this.track(CANCEL_DELETE_PACKAGE_TRACKING_ACTION); + } else { + this.track(CANCEL_DELETE_PACKAGES_TRACKING_ACTION); + } this.itemsToBeDeleted = []; }, - deleteItemConfirmation() { - this.$emit('delete', [this.itemToBeDeleted]); - this.track(DELETE_PACKAGE_TRACKING_ACTION); - this.itemToBeDeleted = null; - }, - deleteItemCanceled() { - this.track(CANCEL_DELETE_PACKAGE_TRACKING_ACTION); - this.itemToBeDeleted = null; - }, showConfirmationModal() { - this.setItemToBeDeleted(this.errorPackages[0]); + this.setItemsToBeDeleted([this.errorPackages[0]]); }, }, i18n: { @@ -165,21 +188,16 @@ export default { :first="first" :package-entity="item" :selected="isSelected(item)" - @delete="setItemToBeDeleted(item)" + @delete="setItemsToBeDeleted([item])" @select="selectItem(item)" /> </template> </registry-list> - <delete-package-modal - :item-to-be-deleted="itemToBeDeleted" - @ok="deleteItemConfirmation" - @cancel="deleteItemCanceled" - /> - <delete-modal ref="deletePackagesModal" :items-to-be-deleted="itemsToBeDeleted" + :show-request-forwarding-content="isRequestForwardingEnabled" @confirm="deleteItemsConfirmation" @cancel="deleteItemsCanceled" /> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js index a65c0e9ab2d..ad5edcd7602 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js +++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js @@ -126,6 +126,21 @@ export const DELETE_PACKAGES_SUCCESS_MESSAGE = s__('PackageRegistry|Packages del export const DELETE_PACKAGES_MODAL_TITLE = s__('PackageRegistry|Delete packages'); export const DELETE_PACKAGE_MODAL_PRIMARY_ACTION = s__('PackageRegistry|Permanently delete'); +export const DELETE_PACKAGES_MODAL_DESCRIPTION = s__( + 'PackageRegistry|You are about to delete %{count} packages. This operation is irreversible.', +); +export const DELETE_PACKAGE_WITH_REQUEST_FORWARDING_PRIMARY_ACTION = s__( + 'PackageRegistry|Yes, delete package', +); +export const DELETE_PACKAGES_WITH_REQUEST_FORWARDING_PRIMARY_ACTION = s__( + 'PackageRegistry|Yes, delete selected packages', +); +export const DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT = s__( + 'PackageRegistry|Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete the package anyway? %{docLinkStart}What are the risks?%{docLinkEnd}', +); +export const DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT = s__( + 'PackageRegistry|Some of the selected package formats allow request forwarding. Deleting a package while request forwarding is enabled for the project can pose a security risk. Do you want to proceed with deleting the selected packages? %{docLinkStart}What are the risks?%{docLinkEnd}', +); export const DELETE_PACKAGE_TEXT = s__('PackageRegistry|Delete package'); export const DELETE_PACKAGE_SUCCESS_MESSAGE = s__('PackageRegistry|Package deleted successfully'); @@ -211,5 +226,9 @@ export const NUGET_HELP_PATH = helpPagePath('user/packages/nuget_repository/inde export const PYPI_HELP_PATH = helpPagePath('user/packages/pypi_repository/index'); export const COMPOSER_HELP_PATH = helpPagePath('user/packages/composer_repository/index'); export const PERSONAL_ACCESS_TOKEN_HELP_URL = helpPagePath('user/profile/personal_access_tokens'); +export const REQUEST_FORWARDING_HELP_PAGE_PATH = helpPagePath( + 'user/packages/package_registry/supported_functionality', + { anchor: 'deleting-packages' }, +); export const GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE = 10; diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_group_settings.fragment.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_group_settings.fragment.graphql new file mode 100644 index 00000000000..db05f497b7f --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_group_settings.fragment.graphql @@ -0,0 +1,8 @@ +fragment GroupPackageSettings on Group { + id + packageSettings { + mavenPackageRequestsForwarding + npmPackageRequestsForwarding + pypiPackageRequestsForwarding + } +} diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql index 5bde5f08e56..f25f24cbc5f 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql @@ -1,4 +1,5 @@ #import "~/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql" +#import "~/packages_and_registries/package_registry/graphql/fragments/package_group_settings.fragment.graphql" #import "~/graphql_shared/fragments/page_info.fragment.graphql" query getPackages( @@ -32,6 +33,9 @@ query getPackages( ...PageInfo } } + group { + ...GroupPackageSettings + } } group(fullPath: $fullPath) @include(if: $isGroupPage) { id @@ -52,5 +56,6 @@ query getPackages( ...PageInfo } } + ...GroupPackageSettings } } diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue index 31380d4f925..044ce4e6413 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue @@ -34,20 +34,20 @@ export default { inject: ['emptyListIllustration', 'isGroupPage', 'fullPath', 'settingsPath'], data() { return { - packages: {}, + packagesResource: {}, sort: '', filters: {}, mutationLoading: false, }; }, apollo: { - packages: { + packagesResource: { query: getPackagesQuery, variables() { return this.queryVariables; }, update(data) { - return data[this.graphqlResource]?.packages ?? {}; + return data[this.graphqlResource] ?? {}; }, skip() { return !this.sort; @@ -55,6 +55,14 @@ export default { }, }, computed: { + packages() { + return this.packagesResource?.packages ?? {}; + }, + groupSettings() { + return this.isGroupPage + ? this.packagesResource?.packageSettings ?? {} + : this.packagesResource?.group?.packageSettings ?? {}; + }, queryVariables() { return { isGroupPage: this.isGroupPage, @@ -87,7 +95,7 @@ export default { : this.$options.i18n.noResultsTitle; }, isLoading() { - return this.$apollo.queries.packages.loading || this.mutationLoading; + return this.$apollo.queries.packagesResource.loading || this.mutationLoading; }, refetchQueriesData() { return [ @@ -127,7 +135,7 @@ export default { after: this.pageInfo?.endCursor, }; - this.$apollo.queries.packages.fetchMore({ + this.$apollo.queries.packagesResource.fetchMore({ variables, updateQuery: this.updateQuery, }); @@ -140,7 +148,7 @@ export default { before: this.pageInfo?.startCursor, }; - this.$apollo.queries.packages.fetchMore({ + this.$apollo.queries.packagesResource.fetchMore({ variables, updateQuery: this.updateQuery, }); @@ -184,6 +192,7 @@ export default { > <template #default="{ deletePackages }"> <package-list + :group-settings="groupSettings" :list="packages.nodes" :is-loading="isLoading" :page-info="pageInfo" diff --git a/app/models/abuse/trust_score.rb b/app/models/abuse/trust_score.rb new file mode 100644 index 00000000000..9ad7c9b14b1 --- /dev/null +++ b/app/models/abuse/trust_score.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Abuse + class TrustScore < ApplicationRecord + MAX_EVENTS = 100 + + self.table_name = 'abuse_trust_scores' + + enum source: Enums::Abuse::Source.sources + + belongs_to :user + + validates :user, presence: true + validates :score, presence: true + validates :source, presence: true + + before_create :assign_correlation_id + after_commit :remove_old_scores + + private + + def assign_correlation_id + self.correlation_id_value ||= (Labkit::Correlation::CorrelationId.current_id || '') + end + + def remove_old_scores + count = user.trust_scores_for_source(source).count + return unless count > MAX_EVENTS + + TrustScore.delete( + user.trust_scores_for_source(source) + .order(created_at: :asc) + .limit(count - MAX_EVENTS) + ) + end + end +end diff --git a/app/models/concerns/enums/abuse/source.rb b/app/models/concerns/enums/abuse/source.rb new file mode 100644 index 00000000000..80703126aae --- /dev/null +++ b/app/models/concerns/enums/abuse/source.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Enums + module Abuse + module Source + def self.sources + { + spamcheck: 0, + virus_total: 1, + arkose_custom_score: 2, + arkose_global_score: 3, + telesign: 4, + pvs: 5 + } + end + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 044c9d3c24b..738e1eba982 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -219,6 +219,7 @@ class User < ApplicationRecord has_many :abuse_reports, dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent has_many :spam_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :abuse_trust_scores, class_name: 'Abuse::TrustScore', foreign_key: :user_id has_many :builds, class_name: 'Ci::Build' has_many :pipelines, class_name: 'Ci::Pipeline' has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -2210,6 +2211,10 @@ class User < ApplicationRecord namespace_commit_emails.find_by(namespace: project.root_namespace) end + def trust_scores_for_source(source) + abuse_trust_scores.where(source: source) + end + protected # override, from Devise::Validatable diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb index 792f1728b88..6248baea4ea 100644 --- a/app/services/merge_requests/rebase_service.rb +++ b/app/services/merge_requests/rebase_service.rb @@ -63,3 +63,5 @@ module MergeRequests end end end + +::MergeRequests::RebaseService.prepend_mod |