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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-08-21 09:09:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-21 09:09:48 +0300
commitf9441cac3defdec8bdc34cefe7b4364fb49c0aff (patch)
tree372bbc9d0f33acd2c16095d713734ed85277bbfd /app
parent98e4ee99fe0ae9a8563d223c5cb7f0752d4a2604 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue16
-rw-r--r--app/assets/javascripts/admin/abuse_report/components/history_items.vue10
-rw-r--r--app/assets/javascripts/admin/abuse_report/components/reported_content.vue13
-rw-r--r--app/assets/javascripts/admin/abuse_report/components/user_details.vue40
-rw-r--r--app/assets/javascripts/admin/abuse_report/constants.js4
-rw-r--r--app/finders/organizations/groups_finder.rb59
-rw-r--r--app/graphql/resolvers/organizations/groups_resolver.rb37
-rw-r--r--app/graphql/resolvers/organizations/organization_resolver.rb22
-rw-r--r--app/graphql/types/organizations/group_sort_enum.rb24
-rw-r--r--app/graphql/types/organizations/organization_type.rb33
-rw-r--r--app/graphql/types/query_type.rb6
-rw-r--r--app/models/abuse_report.rb14
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/organizations/organization.rb2
-rw-r--r--app/serializers/admin/abuse_report_details_entity.rb50
-rw-r--r--app/serializers/admin/reported_content_entity.rb38
16 files changed, 303 insertions, 66 deletions
diff --git a/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue b/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue
index 1490d7e64f5..fb6bc38848c 100644
--- a/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue
@@ -31,6 +31,11 @@ export default {
alert: { ...alertDefaults },
};
},
+ computed: {
+ similarOpenReports() {
+ return this.abuseReport.user?.similarOpenReports || [];
+ },
+ },
methods: {
showAlert(variant, message) {
this.alert.visible = true;
@@ -49,6 +54,7 @@ export default {
<gl-alert v-if="alert.visible" :variant="alert.variant" class="gl-mt-4" @dismiss="closeAlert">{{
alert.message
}}</gl-alert>
+
<report-header
v-if="abuseReport.user"
:user="abuseReport.user"
@@ -56,7 +62,13 @@ export default {
@showAlert="showAlert"
/>
<user-details v-if="abuseReport.user" :user="abuseReport.user" />
- <reported-content :report="abuseReport.report" :reporter="abuseReport.reporter" />
- <history-items :report="abuseReport.report" :reporter="abuseReport.reporter" />
+
+ <reported-content :report="abuseReport.report" data-testid="reported-content" />
+
+ <div v-for="report in similarOpenReports" :key="report.id" data-testid="similar-open-reports">
+ <reported-content :report="report" />
+ </div>
+
+ <history-items :report="abuseReport.report" />
</section>
</template>
diff --git a/app/assets/javascripts/admin/abuse_report/components/history_items.vue b/app/assets/javascripts/admin/abuse_report/components/history_items.vue
index 28b66db84a2..619a8bcfe92 100644
--- a/app/assets/javascripts/admin/abuse_report/components/history_items.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/history_items.vue
@@ -16,13 +16,11 @@ export default {
type: Object,
required: true,
},
- reporter: {
- type: Object,
- required: false,
- default: null,
- },
},
computed: {
+ reporter() {
+ return this.report.reporter;
+ },
reporterName() {
return this.reporter?.name || this.$options.i18n.deletedReporter;
},
@@ -35,7 +33,7 @@ export default {
<!-- The styles `issuable-discussion`, `timeline`, `main-notes-list` and `notes` used below
are declared in app/assets/stylesheets/pages/notes.scss -->
<section class="gl-pt-6 issuable-discussion">
- <h2 class="gl-font-size-h1 gl-mt-0 gl-mb-2">{{ $options.i18n.activity }}</h2>
+ <h2 class="gl-font-lg gl-mt-0 gl-mb-2">{{ $options.i18n.activity }}</h2>
<ul class="timeline main-notes-list notes">
<history-item icon="warning">
<div class="gl-display-flex gl-xs-flex-direction-column">
diff --git a/app/assets/javascripts/admin/abuse_report/components/reported_content.vue b/app/assets/javascripts/admin/abuse_report/components/reported_content.vue
index f4f0fcac58f..84d6f25ac05 100644
--- a/app/assets/javascripts/admin/abuse_report/components/reported_content.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/reported_content.vue
@@ -26,11 +26,6 @@ export default {
type: Object,
required: true,
},
- reporter: {
- type: Object,
- required: false,
- default: null,
- },
},
data() {
return {
@@ -38,6 +33,9 @@ export default {
};
},
computed: {
+ reporter() {
+ return this.report.reporter;
+ },
reporterName() {
return this.reporter?.name || this.$options.i18n.deletedReporter;
},
@@ -67,11 +65,12 @@ export default {
<template>
<div class="gl-pt-6">
<div
- class="gl-pb-3 gl-display-flex gl-justify-content-space-between gl-xs-flex-direction-column"
+ class="gl-pb-3 gl-display-flex gl-justify-content-space-between gl-xs-flex-direction-column gl-align-items-center"
>
- <h2 class="gl-font-size-h1 gl-mt-0 gl-mb-2">
+ <h2 class="gl-font-lg gl-mt-2 gl-mb-2">
{{ $options.i18n.reportTypes[reportType] }}
</h2>
+
<div
class="gl-display-flex gl-align-items-stretch gl-xs-flex-direction-column gl-mt-3 gl-sm-mt-0"
>
diff --git a/app/assets/javascripts/admin/abuse_report/components/user_details.vue b/app/assets/javascripts/admin/abuse_report/components/user_details.vue
index 3dc03a8748f..fe0add1ba8d 100644
--- a/app/assets/javascripts/admin/abuse_report/components/user_details.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/user_details.vue
@@ -39,19 +39,27 @@ export default {
<template>
<div class="gl-mt-6">
- <user-detail data-testid="createdAt" :label="$options.i18n.createdAt">
+ <user-detail data-testid="created-at" :label="$options.i18n.createdAt">
<time-ago-tooltip :time="user.createdAt" />
</user-detail>
+
<user-detail data-testid="email" :label="$options.i18n.email">
<gl-link :href="`mailto:${user.email}`">{{ user.email }}</gl-link>
</user-detail>
+
<user-detail data-testid="plan" :label="$options.i18n.plan" :value="user.plan" />
+
<user-detail
data-testid="verification"
:label="$options.i18n.verification"
:value="verificationState"
/>
- <user-detail v-if="user.creditCard" data-testid="creditCard" :label="$options.i18n.creditCard">
+
+ <user-detail
+ v-if="user.creditCard"
+ data-testid="credit-card-verification"
+ :label="$options.i18n.creditCard"
+ >
<gl-sprintf :message="$options.i18n.registeredWith">
<template #name>{{ user.creditCard.name }}</template>
</gl-sprintf>
@@ -65,17 +73,18 @@ export default {
</template>
</gl-sprintf>
</user-detail>
+
<user-detail
- v-if="user.otherReports.length"
- data-testid="otherReports"
- :label="$options.i18n.otherReports"
+ v-if="user.pastClosedReports.length"
+ data-testid="past-closed-reports"
+ :label="$options.i18n.pastReports"
>
<div
- v-for="(report, index) in user.otherReports"
+ v-for="(report, index) in user.pastClosedReports"
:key="index"
- :data-testid="`other-report-${index}`"
+ :data-testid="`past-report-${index}`"
>
- <gl-sprintf :message="$options.i18n.otherReport">
+ <gl-sprintf :message="$options.i18n.reportedFor">
<template #reportLink="{ content }">
<gl-link :href="report.reportPath">{{ content }}</gl-link>
</template>
@@ -86,28 +95,33 @@ export default {
</gl-sprintf>
</div>
</user-detail>
+
<user-detail
- data-testid="normalLocation"
+ data-testid="normal-location"
:label="$options.i18n.normalLocation"
:value="user.mostUsedIp || user.lastSignInIp"
/>
+
<user-detail
- data-testid="lastSignInIp"
+ data-testid="last-sign-in-ip"
:label="$options.i18n.lastSignInIp"
:value="user.lastSignInIp"
/>
+
<user-detail
- data-testid="snippets"
+ data-testid="user-snippets-count"
:label="$options.i18n.snippets"
:value="$options.i18n.snippetsCount(user.snippetsCount)"
/>
+
<user-detail
- data-testid="groups"
+ data-testid="user-groups-count"
:label="$options.i18n.groups"
:value="$options.i18n.groupsCount(user.groupsCount)"
/>
+
<user-detail
- data-testid="notes"
+ data-testid="user-notes-count"
:label="$options.i18n.notes"
:value="$options.i18n.notesCount(user.notesCount)"
/>
diff --git a/app/assets/javascripts/admin/abuse_report/constants.js b/app/assets/javascripts/admin/abuse_report/constants.js
index b290581598a..6cae6b24f20 100644
--- a/app/assets/javascripts/admin/abuse_report/constants.js
+++ b/app/assets/javascripts/admin/abuse_report/constants.js
@@ -58,7 +58,7 @@ export const USER_DETAILS_I18N = {
plan: s__('AbuseReport|Tier'),
verification: s__('AbuseReport|Verification'),
creditCard: s__('AbuseReport|Credit card'),
- otherReports: s__('AbuseReport|Abuse reports'),
+ pastReports: s__('AbuseReport|Past abuse reports'),
normalLocation: s__('AbuseReport|Normal location'),
lastSignInIp: s__('AbuseReport|Last login'),
snippets: s__('AbuseReport|Snippets'),
@@ -72,7 +72,7 @@ export const USER_DETAILS_I18N = {
phone: s__('AbuseReport|Phone'),
creditCard: s__('AbuseReport|Credit card'),
},
- otherReport: s__(
+ reportedFor: s__(
'AbuseReport|%{reportLinkStart}Reported%{reportLinkEnd} for %{category} %{timeAgo}.',
),
registeredWith: s__('AbuseReport|Registered with name %{name}.'),
diff --git a/app/finders/organizations/groups_finder.rb b/app/finders/organizations/groups_finder.rb
new file mode 100644
index 00000000000..2b59a3106a3
--- /dev/null
+++ b/app/finders/organizations/groups_finder.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+# Organizations::GroupsFinder
+#
+# Used to find Groups within an Organization
+module Organizations
+ class GroupsFinder
+ # @param organization [Organizations::Organization]
+ # @param current_user [User]
+ # @param params [{ sort: { field: [String], direction: [String] }, search: [String] }]
+ def initialize(organization:, current_user:, params: {})
+ @organization = organization
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ return Group.none if organization.nil? || !authorized?
+
+ filter_groups(all_accessible_groups)
+ .then { |groups| sort(groups) }
+ .then(&:with_route)
+ end
+
+ private
+
+ attr_reader :organization, :params, :current_user
+
+ def all_accessible_groups
+ current_user.authorized_groups.in_organization(organization)
+ end
+
+ def filter_groups(groups)
+ by_search(groups)
+ end
+
+ def by_search(groups)
+ return groups unless params[:search].present?
+
+ groups.search(params[:search])
+ end
+
+ def sort(groups)
+ return default_sort_order(groups) if params[:sort].blank?
+
+ field = params[:sort][:field]
+ direction = params[:sort][:direction]
+ groups.reorder(field => direction) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def default_sort_order(groups)
+ groups.sort_by_attribute('name_asc')
+ end
+
+ def authorized?
+ Ability.allowed?(current_user, :read_organization, organization)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/organizations/groups_resolver.rb b/app/graphql/resolvers/organizations/groups_resolver.rb
new file mode 100644
index 00000000000..0f50713b9b4
--- /dev/null
+++ b/app/graphql/resolvers/organizations/groups_resolver.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Organizations
+ class GroupsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include ResolvesGroups
+
+ type Types::GroupType.connection_type, null: true
+
+ authorize :read_group
+
+ argument :search,
+ GraphQL::Types::String,
+ required: false,
+ description: 'Search query for group name or full path.',
+ alpha: { milestone: '16.4' }
+
+ argument :sort,
+ Types::Organizations::GroupSortEnum,
+ description: 'Criteria to sort organization groups by.',
+ required: false,
+ default_value: { field: 'name', direction: :asc },
+ alpha: { milestone: '16.4' }
+
+ private
+
+ def resolve_groups(**args)
+ return Group.none if Feature.disabled?(:resolve_organization_groups, context[:current_user])
+
+ ::Organizations::GroupsFinder
+ .new(organization: object, current_user: context[:current_user], params: args)
+ .execute
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/organizations/organization_resolver.rb b/app/graphql/resolvers/organizations/organization_resolver.rb
new file mode 100644
index 00000000000..9194d9a32c5
--- /dev/null
+++ b/app/graphql/resolvers/organizations/organization_resolver.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Organizations
+ class OrganizationResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :read_organization
+
+ type Types::Organizations::OrganizationType, null: true
+
+ argument :id,
+ Types::GlobalIDType[::Organizations::Organization],
+ required: true,
+ description: 'ID of the organization.'
+
+ def resolve(id:)
+ authorized_find!(id: id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/organizations/group_sort_enum.rb b/app/graphql/types/organizations/group_sort_enum.rb
new file mode 100644
index 00000000000..8fb2f553539
--- /dev/null
+++ b/app/graphql/types/organizations/group_sort_enum.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ module Organizations
+ class GroupSortEnum < BaseEnum
+ graphql_name 'OrganizationGroupSort'
+ description 'Values for sorting organization groups'
+
+ sortable_fields = ['ID', 'Name', 'Path', 'Updated at', 'Created at']
+
+ sortable_fields.each do |field|
+ value "#{field.upcase.tr(' ', '_')}_ASC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :asc },
+ description: "#{field} in ascending order.",
+ alpha: { milestone: '16.4' }
+
+ value "#{field.upcase.tr(' ', '_')}_DESC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :desc },
+ description: "#{field} in descending order.",
+ alpha: { milestone: '16.4' }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/organizations/organization_type.rb b/app/graphql/types/organizations/organization_type.rb
new file mode 100644
index 00000000000..791fddc5266
--- /dev/null
+++ b/app/graphql/types/organizations/organization_type.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Types
+ module Organizations
+ class OrganizationType < BaseObject
+ graphql_name 'Organization'
+
+ authorize :read_organization
+
+ field :groups,
+ Types::GroupType.connection_type,
+ null: false,
+ description: 'Groups within this organization that the user has access to.',
+ alpha: { milestone: '16.4' },
+ resolver: ::Resolvers::Organizations::GroupsResolver
+ field :id,
+ GraphQL::Types::ID,
+ null: false,
+ description: 'ID of the organization.',
+ alpha: { milestone: '16.4' }
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the organization.',
+ alpha: { milestone: '16.4' }
+ field :path,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Path of the organization.',
+ alpha: { milestone: '16.4' }
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 38b8973034d..e3dd0211029 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -96,6 +96,12 @@ module Types
required: true,
description: 'Global ID of the note.'
end
+ field :organization,
+ Types::Organizations::OrganizationType,
+ null: true,
+ resolver: Resolvers::Organizations::OrganizationResolver,
+ description: "Find an organization.",
+ alpha: { milestone: '16.4' }
field :package,
description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.',
resolver: Resolvers::PackageDetailsResolver
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 75c90d370c3..afac53762a7 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -61,8 +61,8 @@ class AbuseReport < ApplicationRecord
validates :screenshot, file_size: { maximum: MAX_FILE_SIZE }
validate :validate_screenshot_is_image
- scope :by_user_id, ->(id) { where(user_id: id) }
- scope :by_reporter_id, ->(id) { where(reporter_id: id) }
+ scope :by_user_id, ->(user_id) { where(user_id: user_id) }
+ scope :by_reporter_id, ->(reporter_id) { where(reporter_id: reporter_id) }
scope :by_category, ->(category) { where(category: category) }
scope :with_users, -> { includes(:reporter, :user) }
@@ -141,8 +141,14 @@ class AbuseReport < ApplicationRecord
end
end
- def other_reports_for_user
- user.abuse_reports.id_not_in(id)
+ def past_closed_reports_for_user
+ user.abuse_reports.closed.id_not_in(id)
+ end
+
+ def similar_open_reports_for_user
+ return AbuseReport.none unless open?
+
+ user.abuse_reports.open.by_category(category).id_not_in(id).includes(:reporter)
end
private
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index a7d03c3688a..be39b894ef9 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -167,6 +167,7 @@ class Namespace < ApplicationRecord
scope :include_route, -> { includes(:route) }
scope :by_parent, -> (parent) { where(parent_id: parent) }
scope :filter_by_path, -> (query) { where('lower(path) = :query', query: query.downcase) }
+ scope :in_organization, -> (organization) { where(organization: organization) }
scope :with_statistics, -> do
joins('LEFT JOIN project_statistics ps ON ps.namespace_id = namespaces.id')
diff --git a/app/models/organizations/organization.rb b/app/models/organizations/organization.rb
index 9f2119949fb..489fd6e0da7 100644
--- a/app/models/organizations/organization.rb
+++ b/app/models/organizations/organization.rb
@@ -38,7 +38,7 @@ module Organizations
end
def user?(user)
- users.exists?(user.id)
+ organization_users.exists?(user: user)
end
private
diff --git a/app/serializers/admin/abuse_report_details_entity.rb b/app/serializers/admin/abuse_report_details_entity.rb
index 3efb8508e5e..8a67aabda9e 100644
--- a/app/serializers/admin/abuse_report_details_entity.rb
+++ b/app/serializers/admin/abuse_report_details_entity.rb
@@ -8,17 +8,21 @@ module Admin
expose :details, merge: true do |report|
UserEntity.represent(report.user, only: [:name, :username, :avatar_url, :email, :created_at, :last_activity_on])
end
+
expose :path do |report|
user_path(report.user)
end
+
expose :admin_path do |report|
admin_user_path(report.user)
end
+
expose :plan do |report|
if Gitlab::CurrentSettings.current_application_settings.try(:should_check_namespace_plan?)
report.user.namespace&.actual_plan&.title
end
end
+
expose :verification_state do
expose :email do |report|
report.user.confirmed?
@@ -30,6 +34,7 @@ module Admin
report.user.credit_card_validation.present?
end
end
+
expose :credit_card, if: ->(report) { report.user.credit_card_validation&.holder_name } do
expose :name do |report|
report.user.credit_card_validation.holder_name
@@ -41,55 +46,38 @@ module Admin
card_match_admin_user_path(report.user) if Gitlab.ee?
end
end
- expose :other_reports do |report|
- AbuseReportEntity.represent(report.other_reports_for_user, only: [:created_at, :category, :report_path])
+
+ expose :past_closed_reports do |report|
+ AbuseReportEntity.represent(report.past_closed_reports_for_user, only: [:created_at, :category, :report_path])
+ end
+
+ expose :similar_open_reports, if: ->(report) { report.open? } do |report|
+ ReportedContentEntity.represent(report.similar_open_reports_for_user)
end
+
expose :most_used_ip do |report|
AuthenticationEvent.most_used_ip_address_for_user(report.user)
end
+
expose :last_sign_in_ip do |report|
report.user.last_sign_in_ip
end
+
expose :snippets_count do |report|
report.user.snippets.count
end
+
expose :groups_count do |report|
report.user.groups.count
end
+
expose :notes_count do |report|
report.user.notes.count
end
end
- expose :reporter, if: ->(report) { report.reporter } do
- expose :details, merge: true do |report|
- UserEntity.represent(report.reporter, only: [:name, :username, :avatar_url])
- end
- expose :path do |report|
- user_path(report.reporter)
- end
- end
-
- expose :report do
- expose :status
- expose :message
- expose :created_at, as: :reported_at
- expose :category
- expose :report_type, as: :type
- expose :reported_content, as: :content
- expose :reported_from_url, as: :url
- expose :screenshot_path, as: :screenshot
-
- # Kept for backwards compatibility.
- # TODO: See https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/167?work_item_iid=443
- # In 16.4 remove or re-use this field after frontend has migrated to using moderate_user_path
- expose :update_path do |report|
- admin_abuse_report_path(report)
- end
-
- expose :moderate_user_path do |report|
- moderate_user_admin_abuse_report_path(report)
- end
+ expose :report do |report|
+ ReportedContentEntity.represent(report)
end
end
end
diff --git a/app/serializers/admin/reported_content_entity.rb b/app/serializers/admin/reported_content_entity.rb
new file mode 100644
index 00000000000..0e86a1434f8
--- /dev/null
+++ b/app/serializers/admin/reported_content_entity.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Admin
+ class ReportedContentEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :id
+ expose :status
+ expose :message
+ expose :created_at, as: :reported_at
+ expose :category
+ expose :report_type, as: :type
+ expose :reported_content, as: :content
+ expose :reported_from_url, as: :url
+ expose :screenshot_path, as: :screenshot
+
+ expose :reporter, if: ->(report) { report.reporter } do
+ expose :details, merge: true do |report|
+ UserEntity.represent(report.reporter, only: [:name, :username, :avatar_url])
+ end
+
+ expose :path do |report|
+ user_path(report.reporter)
+ end
+ end
+
+ # Kept for backwards compatibility.
+ # TODO: See https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/167?work_item_iid=443
+ # In 16.4 remove or re-use this field after frontend has migrated to using moderate_user_path
+ expose :update_path do |report|
+ admin_abuse_report_path(report)
+ end
+
+ expose :moderate_user_path do |report|
+ moderate_user_admin_abuse_report_path(report)
+ end
+ end
+end