Add latest changes from gitlab-org/security/gitlab@14-10-stable-ee
This commit is contained in:
parent
9fdc4213b6
commit
d547692176
|
|
@ -1,4 +1,4 @@
|
|||
export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug'];
|
||||
export const projectKeys = ['id', 'name', 'organizationName', 'organizationSlug', 'slug'];
|
||||
|
||||
export const transformFrontendSettings = ({
|
||||
apiHost,
|
||||
|
|
@ -9,6 +9,7 @@ export const transformFrontendSettings = ({
|
|||
}) => {
|
||||
const project = selectedProject
|
||||
? {
|
||||
sentry_project_id: selectedProject.id,
|
||||
slug: selectedProject.slug,
|
||||
name: selectedProject.name,
|
||||
organization_name: selectedProject.organizationName,
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ export default class AccessDropdown {
|
|||
return `
|
||||
<li>
|
||||
<a href="#" class="${isActiveClass}">
|
||||
<strong>${key.title}</strong>
|
||||
<strong>${escape(key.title)}</strong>
|
||||
<p>
|
||||
${sprintf(
|
||||
__('Owned by %{image_tag}'),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle
|
|||
respond_to :json
|
||||
|
||||
before_action :authorize_read_sentry_issue!
|
||||
before_action :authorize_update_sentry_issue!, only: %i[update]
|
||||
before_action :set_issue_id, only: :details
|
||||
|
||||
before_action only: [:index] do
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ module Projects
|
|||
:integrated,
|
||||
:api_host,
|
||||
:token,
|
||||
project: [:slug, :name, :organization_slug, :organization_name]
|
||||
project: [:slug, :name, :organization_slug, :organization_name, :sentry_project_id]
|
||||
],
|
||||
|
||||
grafana_integration_attributes: [:token, :grafana_url, :enabled],
|
||||
|
|
|
|||
|
|
@ -159,27 +159,6 @@ module IntegrationsHelper
|
|||
!Gitlab.com?
|
||||
end
|
||||
|
||||
def jira_issue_breadcrumb_link(issue_reference)
|
||||
link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
|
||||
icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2'
|
||||
[icon, html_escape(issue_reference)].join.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def zentao_issue_breadcrumb_link(issue)
|
||||
link_to issue[:web_url], { target: '_blank', rel: 'noopener noreferrer', class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
|
||||
icon = image_tag image_path('logos/zentao.svg'), width: 15, height: 15, class: 'gl-mr-2'
|
||||
[icon, html_escape(issue[:id])].join.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def zentao_issues_show_data
|
||||
{
|
||||
issues_show_path: project_integrations_zentao_issue_path(@project, params[:id], format: :json),
|
||||
issues_list_path: project_integrations_zentao_issues_path(@project)
|
||||
}
|
||||
end
|
||||
|
||||
extend self
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ module ProjectsHelper
|
|||
setting.organization_slug.blank?
|
||||
|
||||
{
|
||||
sentry_project_id: setting.sentry_project_id,
|
||||
name: setting.project_name,
|
||||
organization_name: setting.organization_name,
|
||||
organization_slug: setting.organization_slug,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Clusters
|
||||
module Applications
|
||||
class Runner < ApplicationRecord
|
||||
VERSION = '0.39.0'
|
||||
VERSION = '0.39.2'
|
||||
|
||||
self.table_name = 'clusters_applications_runners'
|
||||
|
||||
|
|
|
|||
|
|
@ -125,17 +125,22 @@ module ErrorTracking
|
|||
|
||||
def issue_details(opts = {})
|
||||
with_reactive_cache('issue_details', opts.stringify_keys) do |result|
|
||||
ensure_issue_belongs_to_project!(result[:issue].project_id)
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def issue_latest_event(opts = {})
|
||||
with_reactive_cache('issue_latest_event', opts.stringify_keys) do |result|
|
||||
ensure_issue_belongs_to_project!(result[:latest_event].project_id)
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def update_issue(opts = {})
|
||||
issue_to_be_updated = sentry_client.issue_details(issue_id: opts[:issue_id])
|
||||
ensure_issue_belongs_to_project!(issue_to_be_updated.project_id)
|
||||
|
||||
handle_exceptions do
|
||||
{ updated: sentry_client.update_issue(opts) }
|
||||
end
|
||||
|
|
@ -177,6 +182,25 @@ module ErrorTracking
|
|||
|
||||
private
|
||||
|
||||
def ensure_issue_belongs_to_project!(project_id_from_api)
|
||||
raise 'The Sentry issue appers to be outside of the configured Sentry project' if Integer(project_id_from_api) != ensure_sentry_project_id!
|
||||
end
|
||||
|
||||
def ensure_sentry_project_id!
|
||||
return sentry_project_id if sentry_project_id.present?
|
||||
|
||||
raise("Couldn't find project: #{organization_name} / #{project_name} on Sentry") if sentry_project.nil?
|
||||
|
||||
update!(sentry_project_id: sentry_project.id)
|
||||
sentry_project_id
|
||||
end
|
||||
|
||||
def sentry_project
|
||||
strong_memoize(:sentry_project) do
|
||||
sentry_client.projects.find { |project| project.name == project_name && project.organization_name == organization_name }
|
||||
end
|
||||
end
|
||||
|
||||
def add_gitlab_issue_details(issue)
|
||||
issue.gitlab_commit = match_gitlab_commit(issue.first_release_version)
|
||||
issue.gitlab_commit_path = project_commit_path(project, issue.gitlab_commit) if issue.gitlab_commit
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ class ProjectPolicy < BasePolicy
|
|||
enable :read_deployment
|
||||
enable :read_merge_request
|
||||
enable :read_sentry_issue
|
||||
enable :update_sentry_issue
|
||||
enable :read_prometheus
|
||||
enable :read_metrics_dashboard_annotation
|
||||
enable :metrics_dashboard
|
||||
|
|
@ -413,6 +412,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :admin_feature_flags_user_lists
|
||||
enable :update_escalation_status
|
||||
enable :read_secure_files
|
||||
enable :update_sentry_issue
|
||||
end
|
||||
|
||||
rule { can?(:developer_access) & user_confirmed? }.policy do
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class MemberUserEntity < UserEntity
|
|||
user.blocked?
|
||||
end
|
||||
|
||||
expose :two_factor_enabled do |user|
|
||||
expose :two_factor_enabled, if: -> (user) { current_user_can_manage_members? || current_user?(user) } do |user|
|
||||
user.two_factor_enabled?
|
||||
end
|
||||
|
||||
|
|
@ -25,6 +25,18 @@ class MemberUserEntity < UserEntity
|
|||
user.status.emoji
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_user_can_manage_members?
|
||||
return false unless options[:source]
|
||||
|
||||
Ability.allowed?(options[:current_user], :"admin_#{options[:source].to_ability_name}_member", options[:source])
|
||||
end
|
||||
|
||||
def current_user?(user)
|
||||
options[:current_user] == user
|
||||
end
|
||||
end
|
||||
|
||||
MemberUserEntity.prepend_mod_with('MemberUserEntity')
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ module Projects
|
|||
api_url: api_url,
|
||||
enabled: settings[:enabled],
|
||||
project_name: settings.dig(:project, :name),
|
||||
organization_name: settings.dig(:project, :organization_name)
|
||||
organization_name: settings.dig(:project, :organization_name),
|
||||
sentry_project_id: settings.dig(:project, :sentry_project_id)
|
||||
}
|
||||
}
|
||||
params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSentryProjectIdToProjectErrorTrackingSettings < Gitlab::Database::Migration[2.0]
|
||||
def change
|
||||
add_column :project_error_tracking_settings, :sentry_project_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
1f03beba0775e2a4eead512819592f590b02b70096cee250dfcdf426440cb5f5
|
||||
|
|
@ -19156,7 +19156,8 @@ CREATE TABLE project_error_tracking_settings (
|
|||
encrypted_token_iv character varying,
|
||||
project_name character varying,
|
||||
organization_name character varying,
|
||||
integrated boolean DEFAULT true NOT NULL
|
||||
integrated boolean DEFAULT true NOT NULL,
|
||||
sentry_project_id bigint
|
||||
);
|
||||
|
||||
CREATE TABLE project_export_jobs (
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ button and a link to the GitLab issue displays within the error detail section.
|
|||
|
||||
## Taking Action on errors
|
||||
|
||||
You can take action on Sentry Errors from within the GitLab UI.
|
||||
You can take action on Sentry Errors from within the GitLab UI. Marking errors ignored or resolved require at least Developer role.
|
||||
|
||||
### Ignoring errors
|
||||
|
||||
|
|
|
|||
|
|
@ -82,8 +82,14 @@ module API
|
|||
params.delete(:label_id)
|
||||
params.delete(:name)
|
||||
|
||||
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
|
||||
render_validation_error!(label) unless label.valid?
|
||||
update_params = declared_params(include_missing: false)
|
||||
|
||||
if update_params.present?
|
||||
authorize! :admin_label, label
|
||||
|
||||
label = ::Labels::UpdateService.new(update_params).execute(label)
|
||||
render_validation_error!(label) unless label.valid?
|
||||
end
|
||||
|
||||
if parent.is_a?(Project) && update_priority
|
||||
if priority.nil?
|
||||
|
|
@ -97,10 +103,10 @@ module API
|
|||
end
|
||||
|
||||
def delete_label(parent)
|
||||
authorize! :admin_label, parent
|
||||
|
||||
label = find_label(parent, params_id_or_title, include_ancestor_groups: false)
|
||||
|
||||
authorize! :admin_label, label
|
||||
|
||||
destroy_conditionally!(label)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,20 +10,8 @@ module BulkImports
|
|||
<<-'GRAPHQL'
|
||||
query($full_path: ID!) {
|
||||
project(fullPath: $full_path) {
|
||||
description
|
||||
visibility
|
||||
archived
|
||||
created_at: createdAt
|
||||
shared_runners_enabled: sharedRunnersEnabled
|
||||
container_registry_enabled: containerRegistryEnabled
|
||||
only_allow_merge_if_pipeline_succeeds: onlyAllowMergeIfPipelineSucceeds
|
||||
only_allow_merge_if_all_discussions_are_resolved: onlyAllowMergeIfAllDiscussionsAreResolved
|
||||
request_access_enabled: requestAccessEnabled
|
||||
printing_merge_request_link_enabled: printingMergeRequestLinkEnabled
|
||||
remove_source_branch_after_merge: removeSourceBranchAfterMerge
|
||||
autoclose_referenced_issues: autocloseReferencedIssues
|
||||
suggestion_commit_message: suggestionCommitMessage
|
||||
wiki_enabled: wikiEnabled
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
|
|
|
|||
|
|
@ -7,16 +7,18 @@ module BulkImports
|
|||
PROJECT_IMPORT_TYPE = 'gitlab_project_migration'
|
||||
|
||||
def transform(context, data)
|
||||
project = {}
|
||||
entity = context.entity
|
||||
visibility = data.delete('visibility')
|
||||
|
||||
data['name'] = entity.destination_name
|
||||
data['path'] = entity.destination_name.parameterize
|
||||
data['import_type'] = PROJECT_IMPORT_TYPE
|
||||
data['visibility_level'] = Gitlab::VisibilityLevel.string_options[visibility] if visibility.present?
|
||||
data['namespace_id'] = Namespace.find_by_full_path(entity.destination_namespace)&.id if entity.destination_namespace.present?
|
||||
project[:name] = entity.destination_name
|
||||
project[:path] = entity.destination_name.parameterize
|
||||
project[:created_at] = data['created_at']
|
||||
project[:import_type] = PROJECT_IMPORT_TYPE
|
||||
project[:visibility_level] = Gitlab::VisibilityLevel.string_options[visibility] if visibility.present?
|
||||
project[:namespace_id] = Namespace.find_by_full_path(entity.destination_namespace)&.id if entity.destination_namespace.present?
|
||||
|
||||
data.transform_keys!(&:to_sym)
|
||||
project
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ module ErrorTracking
|
|||
stack_trace = parse_stack_trace(event)
|
||||
|
||||
Gitlab::ErrorTracking::ErrorEvent.new(
|
||||
project_id: event['projectID'],
|
||||
issue_id: event['groupID'],
|
||||
date_received: event['dateReceived'],
|
||||
stack_trace_entries: stack_trace
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Gitlab
|
|||
class ErrorEvent
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :issue_id, :date_received, :stack_trace_entries, :gitlab_project
|
||||
attr_accessor :issue_id, :date_received, :stack_trace_entries, :gitlab_project, :project_id
|
||||
|
||||
def self.declarative_policy_class
|
||||
'ErrorTracking::BasePolicy'
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ module Gitlab
|
|||
DEFAULT_MAX_BYTES = 10.gigabytes.freeze
|
||||
TIMEOUT_LIMIT = 210.seconds
|
||||
|
||||
ServiceError = Class.new(StandardError)
|
||||
|
||||
def initialize(archive_path:, max_bytes: self.class.max_bytes)
|
||||
@archive_path = archive_path
|
||||
@max_bytes = max_bytes
|
||||
|
|
@ -29,6 +31,8 @@ module Gitlab
|
|||
pgrp = nil
|
||||
valid_archive = true
|
||||
|
||||
validate_archive_path
|
||||
|
||||
Timeout.timeout(TIMEOUT_LIMIT) do
|
||||
stdin, stdout, stderr, wait_thr = Open3.popen3(command, pgroup: true)
|
||||
stdin.close
|
||||
|
|
@ -78,15 +82,29 @@ module Gitlab
|
|||
false
|
||||
end
|
||||
|
||||
def validate_archive_path
|
||||
Gitlab::Utils.check_path_traversal!(@archive_path)
|
||||
|
||||
raise(ServiceError, 'Archive path is not a string') unless @archive_path.is_a?(String)
|
||||
raise(ServiceError, 'Archive path is a symlink') if File.lstat(@archive_path).symlink?
|
||||
raise(ServiceError, 'Archive path is not a file') unless File.file?(@archive_path)
|
||||
end
|
||||
|
||||
def command
|
||||
"gzip -dc #{@archive_path} | wc -c"
|
||||
end
|
||||
|
||||
def log_error(error)
|
||||
archive_size = begin
|
||||
File.size(@archive_path)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
Gitlab::Import::Logger.info(
|
||||
message: error,
|
||||
import_upload_archive_path: @archive_path,
|
||||
import_upload_archive_size: File.size(@archive_path)
|
||||
import_upload_archive_size: archive_size
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -297,40 +297,54 @@ RSpec.describe Projects::ErrorTrackingController do
|
|||
put :update, params: issue_params(issue_id: issue_id, status: 'resolved', format: :json)
|
||||
end
|
||||
|
||||
before do
|
||||
expect(ErrorTracking::IssueUpdateService)
|
||||
.to receive(:new).with(project, user, permitted_params)
|
||||
.and_return(issue_update_service)
|
||||
end
|
||||
|
||||
describe 'format json' do
|
||||
context 'update result is successful' do
|
||||
context 'when user is a reporter' do
|
||||
before do
|
||||
expect(issue_update_service).to receive(:execute)
|
||||
.and_return(status: :success, updated: true, closed_issue_iid: non_existing_record_iid)
|
||||
|
||||
update_issue
|
||||
project.add_reporter(user)
|
||||
end
|
||||
|
||||
it 'returns a success' do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('error_tracking/update_issue')
|
||||
it 'returns 404 error' do
|
||||
update_issue
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'update result is erroneous' do
|
||||
let(:error_message) { 'error message' }
|
||||
|
||||
context 'when user has access to update' do
|
||||
before do
|
||||
expect(issue_update_service).to receive(:execute)
|
||||
.and_return(status: :error, message: error_message)
|
||||
|
||||
update_issue
|
||||
expect(ErrorTracking::IssueUpdateService)
|
||||
.to receive(:new).with(project, user, permitted_params)
|
||||
.and_return(issue_update_service)
|
||||
end
|
||||
|
||||
it 'returns 400 with message' do
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(error_message)
|
||||
context 'when update result is successful' do
|
||||
before do
|
||||
expect(issue_update_service).to receive(:execute)
|
||||
.and_return(status: :success, updated: true, closed_issue_iid: non_existing_record_iid)
|
||||
|
||||
update_issue
|
||||
end
|
||||
|
||||
it 'returns a success' do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('error_tracking/update_issue')
|
||||
end
|
||||
end
|
||||
|
||||
context 'update result is erroneous' do
|
||||
let(:error_message) { 'error message' }
|
||||
|
||||
before do
|
||||
expect(issue_update_service).to receive(:execute)
|
||||
.and_return(status: :error, message: error_message)
|
||||
|
||||
update_issue
|
||||
end
|
||||
|
||||
it 'returns 400 with message' do
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(error_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ RSpec.describe 'Database schema' do
|
|||
oauth_applications: %w[owner_id],
|
||||
product_analytics_events_experimental: %w[event_id txn_id user_id],
|
||||
project_build_artifacts_size_refreshes: %w[last_job_artifact_id],
|
||||
project_error_tracking_settings: %w[sentry_project_id],
|
||||
project_group_links: %w[group_id],
|
||||
project_statistics: %w[namespace_id],
|
||||
projects: %w[creator_id ci_id mirror_user_id],
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ FactoryBot.define do
|
|||
message { 'message' }
|
||||
culprit { 'culprit' }
|
||||
external_url { 'http://example.com/id' }
|
||||
project_id { 'project1' }
|
||||
project_id { '111111' }
|
||||
project_name { 'project name' }
|
||||
project_slug { 'project_name' }
|
||||
short_id { 'ID' }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FactoryBot.define do
|
|||
project_name { 'Sentry Project' }
|
||||
organization_name { 'Sentry Org' }
|
||||
integrated { false }
|
||||
sentry_project_id { 10 }
|
||||
|
||||
trait :disabled do
|
||||
enabled { false }
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
},
|
||||
"user": {
|
||||
"allOf": [
|
||||
{ "$ref": "member_user.json" }
|
||||
{ "$ref": "member_user_default.json" }
|
||||
]
|
||||
},
|
||||
"state": { "type": "integer" },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"name",
|
||||
"username",
|
||||
"created_at",
|
||||
"last_activity_on",
|
||||
"avatar_url",
|
||||
"web_url",
|
||||
"blocked",
|
||||
"show_status"
|
||||
],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"name": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"created_at": { "type": ["string"] },
|
||||
"avatar_url": { "type": ["string", "null"] },
|
||||
"web_url": { "type": "string" },
|
||||
"blocked": { "type": "boolean" },
|
||||
"two_factor_enabled": { "type": "boolean" },
|
||||
"availability": { "type": ["string", "null"] },
|
||||
"last_activity_on": { "type": ["string", "null"] },
|
||||
"status": {
|
||||
"type": "object",
|
||||
"required": ["emoji"],
|
||||
"properties": {
|
||||
"emoji": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"show_status": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
|
|
@ -154,4 +154,21 @@ describe('AccessDropdown', () => {
|
|||
expect(template).not.toContain(user.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deployKeyRowHtml', () => {
|
||||
const deployKey = {
|
||||
id: 1,
|
||||
title: 'title <script>alert(document.domain)</script>',
|
||||
fullname: 'fullname <script>alert(document.domain)</script>',
|
||||
avatar_url: '',
|
||||
username: '',
|
||||
};
|
||||
|
||||
it('escapes deploy key title and fullname', () => {
|
||||
const template = dropdown.deployKeyRowHtml(deployKey);
|
||||
|
||||
expect(template).not.toContain(deployKey.title);
|
||||
expect(template).not.toContain(deployKey.fullname);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -149,19 +149,4 @@ RSpec.describe IntegrationsHelper do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#jira_issue_breadcrumb_link' do
|
||||
let(:issue_reference) { nil }
|
||||
|
||||
subject { helper.jira_issue_breadcrumb_link(issue_reference) }
|
||||
|
||||
context 'when issue_reference contains HTML' do
|
||||
let(:issue_reference) { "<script>alert('XSS')</script>" }
|
||||
|
||||
it 'escapes issue reference' do
|
||||
is_expected.not_to include(issue_reference)
|
||||
is_expected.to include(html_escape(issue_reference))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ RSpec.describe ProjectsHelper do
|
|||
context 'api_url present' do
|
||||
let(:json) do
|
||||
{
|
||||
sentry_project_id: error_tracking_setting.sentry_project_id,
|
||||
name: error_tracking_setting.project_name,
|
||||
organization_name: error_tracking_setting.organization_name,
|
||||
organization_slug: error_tracking_setting.organization_slug,
|
||||
|
|
|
|||
|
|
@ -25,18 +25,7 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline do
|
|||
let(:project_data) do
|
||||
{
|
||||
'visibility' => 'private',
|
||||
'created_at' => 10.days.ago,
|
||||
'archived' => false,
|
||||
'shared_runners_enabled' => true,
|
||||
'container_registry_enabled' => true,
|
||||
'only_allow_merge_if_pipeline_succeeds' => true,
|
||||
'only_allow_merge_if_all_discussions_are_resolved' => true,
|
||||
'request_access_enabled' => true,
|
||||
'printing_merge_request_link_enabled' => true,
|
||||
'remove_source_branch_after_merge' => true,
|
||||
'autoclose_referenced_issues' => true,
|
||||
'suggestion_commit_message' => 'message',
|
||||
'wiki_enabled' => true
|
||||
'created_at' => '2016-08-12T09:41:03'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -58,17 +47,8 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline do
|
|||
|
||||
expect(imported_project).not_to be_nil
|
||||
expect(imported_project.group).to eq(group)
|
||||
expect(imported_project.suggestion_commit_message).to eq('message')
|
||||
expect(imported_project.archived?).to eq(project_data['archived'])
|
||||
expect(imported_project.shared_runners_enabled?).to eq(project_data['shared_runners_enabled'])
|
||||
expect(imported_project.container_registry_enabled?).to eq(project_data['container_registry_enabled'])
|
||||
expect(imported_project.only_allow_merge_if_pipeline_succeeds?).to eq(project_data['only_allow_merge_if_pipeline_succeeds'])
|
||||
expect(imported_project.only_allow_merge_if_all_discussions_are_resolved?).to eq(project_data['only_allow_merge_if_all_discussions_are_resolved'])
|
||||
expect(imported_project.request_access_enabled?).to eq(project_data['request_access_enabled'])
|
||||
expect(imported_project.printing_merge_request_link_enabled?).to eq(project_data['printing_merge_request_link_enabled'])
|
||||
expect(imported_project.remove_source_branch_after_merge?).to eq(project_data['remove_source_branch_after_merge'])
|
||||
expect(imported_project.autoclose_referenced_issues?).to eq(project_data['autoclose_referenced_issues'])
|
||||
expect(imported_project.wiki_enabled?).to eq(project_data['wiki_enabled'])
|
||||
expect(imported_project.visibility).to eq(project_data['visibility'])
|
||||
expect(imported_project.created_at).to eq(project_data['created_at'])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer
|
|||
|
||||
let(:data) do
|
||||
{
|
||||
'name' => 'source_name',
|
||||
'visibility' => 'private'
|
||||
'visibility' => 'private',
|
||||
'created_at' => '2016-11-18T09:29:42.634Z'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -76,8 +76,21 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer
|
|||
end
|
||||
end
|
||||
|
||||
it 'converts all keys to symbols' do
|
||||
expect(transformed_data.keys).to contain_exactly(:name, :path, :import_type, :visibility_level, :namespace_id)
|
||||
context 'when data has extra keys' do
|
||||
it 'returns a fixed number of keys' do
|
||||
data = {
|
||||
'visibility' => 'private',
|
||||
'created_at' => '2016-11-18T09:29:42.634Z',
|
||||
'my_key' => 'my_key',
|
||||
'another_key' => 'another_key',
|
||||
'last_key' => 'last_key'
|
||||
}
|
||||
|
||||
transformed_data = described_class.new.transform(context, data)
|
||||
|
||||
expect(transformed_data.keys)
|
||||
.to contain_exactly(:created_at, :import_type, :name, :namespace_id, :path, :visibility_level)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -86,6 +86,65 @@ RSpec.describe Gitlab::ImportExport::DecompressedArchiveSizeValidator do
|
|||
include_examples 'logs raised exception and terminates validator process group'
|
||||
end
|
||||
end
|
||||
|
||||
context 'archive path validation' do
|
||||
let(:filesize) { nil }
|
||||
|
||||
before do
|
||||
expect(Gitlab::Import::Logger)
|
||||
.to receive(:info)
|
||||
.with(
|
||||
import_upload_archive_path: filepath,
|
||||
import_upload_archive_size: filesize,
|
||||
message: error_message
|
||||
)
|
||||
end
|
||||
|
||||
context 'when archive path is traversed' do
|
||||
let(:filepath) { '/foo/../bar' }
|
||||
let(:error_message) { 'Invalid path' }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.valid?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when archive path is not a string' do
|
||||
let(:filepath) { 123 }
|
||||
let(:error_message) { 'Archive path is not a string' }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.valid?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'which archive path is a symlink' do
|
||||
let(:filepath) { File.join(Dir.tmpdir, 'symlink') }
|
||||
let(:error_message) { 'Archive path is a symlink' }
|
||||
|
||||
before do
|
||||
FileUtils.ln_s(filepath, filepath, force: true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.valid?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when archive path is not a file' do
|
||||
let(:filepath) { Dir.mktmpdir }
|
||||
let(:filesize) { File.size(filepath) }
|
||||
let(:error_message) { 'Archive path is not a file' }
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(filepath)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.valid?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_compressed_file
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
|
||||
let(:sentry_client) { instance_double(ErrorTracking::SentryClient) }
|
||||
|
||||
subject(:setting) { build(:project_error_tracking_setting, project: project) }
|
||||
let(:sentry_project_id) { 10 }
|
||||
|
||||
subject(:setting) { build(:project_error_tracking_setting, project: project, sentry_project_id: sentry_project_id) }
|
||||
|
||||
describe 'Associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
|
|
@ -270,7 +272,7 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
end
|
||||
|
||||
describe '#issue_details' do
|
||||
let(:issue) { build(:error_tracking_sentry_detailed_error) }
|
||||
let(:issue) { build(:error_tracking_sentry_detailed_error, project_id: sentry_project_id) }
|
||||
let(:commit_id) { issue.first_release_version }
|
||||
let(:result) { subject.issue_details(opts) }
|
||||
let(:opts) { { issue_id: 1 } }
|
||||
|
|
@ -317,12 +319,33 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#issue_latest_event' do
|
||||
let(:error_event) { build(:error_tracking_sentry_error_event, project_id: sentry_project_id) }
|
||||
let(:result) { subject.issue_latest_event(opts) }
|
||||
let(:opts) { { issue_id: 1 } }
|
||||
|
||||
before do
|
||||
stub_reactive_cache(subject, error_event, {})
|
||||
synchronous_reactive_cache(subject)
|
||||
|
||||
allow(subject).to receive(:sentry_client).and_return(sentry_client)
|
||||
allow(sentry_client).to receive(:issue_latest_event).with(opts).and_return(error_event)
|
||||
end
|
||||
|
||||
it 'returns the error event' do
|
||||
expect(result[:latest_event].project_id).to eq(sentry_project_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_issue' do
|
||||
let(:result) { subject.update_issue(**opts) }
|
||||
let(:opts) { { issue_id: 1, params: {} } }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:sentry_client).and_return(sentry_client)
|
||||
allow(sentry_client).to receive(:issue_details)
|
||||
.with({ issue_id: 1 })
|
||||
.and_return(Gitlab::ErrorTracking::DetailedError.new(project_id: sentry_project_id))
|
||||
end
|
||||
|
||||
context 'when sentry response is successful' do
|
||||
|
|
@ -344,6 +367,56 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
expect(result).to eq(error: 'Unexpected Error')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sentry_project_id is not set' do
|
||||
let(:sentry_projects) do
|
||||
[
|
||||
Gitlab::ErrorTracking::Project.new(
|
||||
id: 1111,
|
||||
name: 'Some Project',
|
||||
organization_name: 'Org'
|
||||
),
|
||||
Gitlab::ErrorTracking::Project.new(
|
||||
id: sentry_project_id,
|
||||
name: setting.project_name,
|
||||
organization_name: setting.organization_name
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
context 'when sentry_project_id is not set' do
|
||||
before do
|
||||
setting.update!(sentry_project_id: nil)
|
||||
|
||||
allow(sentry_client).to receive(:projects).and_return(sentry_projects)
|
||||
allow(sentry_client).to receive(:update_issue).with(opts).and_return(true)
|
||||
end
|
||||
|
||||
it 'tries to backfill it from sentry API' do
|
||||
expect(result).to eq(updated: true)
|
||||
|
||||
expect(setting.reload.sentry_project_id).to eq(sentry_project_id)
|
||||
end
|
||||
|
||||
context 'when the project cannot be found on sentry' do
|
||||
before do
|
||||
sentry_projects.pop
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect { result }.to raise_error(/Couldn't find project/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mismatching sentry_project_id is detected' do
|
||||
it 'raises error' do
|
||||
setting.update!(sentry_project_id: sentry_project_id + 1)
|
||||
|
||||
expect { result }.to raise_error(/The Sentry issue appers to be outside/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'slugs' do
|
||||
|
|
|
|||
|
|
@ -2102,4 +2102,25 @@ RSpec.describe ProjectPolicy do
|
|||
it { is_expected.to be_disallowed(:register_project_runners) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'update_sentry_issue' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:role, :allowed) do
|
||||
:owner | true
|
||||
:maintainer | true
|
||||
:developer | true
|
||||
:reporter | false
|
||||
:guest | false
|
||||
end
|
||||
|
||||
let(:project) { public_project }
|
||||
let(:current_user) { public_send(role) }
|
||||
|
||||
with_them do
|
||||
it do
|
||||
expect(subject.can?(:update_sentry_issue)).to be(allowed)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -483,6 +483,29 @@ RSpec.describe API::Labels do
|
|||
let(:request) { api("/projects/#{project.id}/labels", user) }
|
||||
let(:params) { { name: valid_label_title_1 } }
|
||||
end
|
||||
|
||||
context 'with group label' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:group_label) { create(:group_label, title: valid_group_label_title_1, group: group) }
|
||||
|
||||
before do
|
||||
project.update!(group: group)
|
||||
end
|
||||
|
||||
it 'returns 401 if user does not have access' do
|
||||
delete api("/projects/#{project.id}/labels/#{group_label.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'returns 204 if user has access' do
|
||||
group.add_developer(user)
|
||||
|
||||
delete api("/projects/#{project.id}/labels/#{group_label.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /projects/:id/labels' do
|
||||
|
|
@ -537,6 +560,44 @@ RSpec.describe API::Labels do
|
|||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
|
||||
context 'with group label' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:group_label) { create(:group_label, title: valid_group_label_title_1, group: group) }
|
||||
|
||||
before do
|
||||
project.update!(group: group)
|
||||
end
|
||||
|
||||
it 'allows updating of group label priority' do
|
||||
put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: { priority: 5 }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['priority']).to eq(5)
|
||||
end
|
||||
|
||||
it 'returns 401 when updating other fields' do
|
||||
put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: {
|
||||
priority: 5,
|
||||
new_name: 'new label name'
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'returns 200 when user has access to the group label' do
|
||||
group.add_developer(user)
|
||||
|
||||
put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: {
|
||||
priority: 5,
|
||||
new_name: 'new label name'
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['priority']).to eq(5)
|
||||
expect(json_response['name']).to eq('new label name')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /projects/:id/labels/promote' do
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ RSpec.describe MemberUserEntity do
|
|||
let(:entity_hash) { entity.as_json }
|
||||
|
||||
it 'matches json schema' do
|
||||
expect(entity.to_json).to match_schema('entities/member_user')
|
||||
expect(entity.to_json).to match_schema('entities/member_user_default')
|
||||
end
|
||||
|
||||
it 'correctly exposes `avatar_url`' do
|
||||
|
|
@ -27,10 +27,8 @@ RSpec.describe MemberUserEntity do
|
|||
expect(entity_hash[:blocked]).to be(true)
|
||||
end
|
||||
|
||||
it 'correctly exposes `two_factor_enabled`' do
|
||||
allow(user).to receive(:two_factor_enabled?).and_return(true)
|
||||
|
||||
expect(entity_hash[:two_factor_enabled]).to be(true)
|
||||
it 'does not expose `two_factor_enabled` by default' do
|
||||
expect(entity_hash[:two_factor_enabled]).to be(nil)
|
||||
end
|
||||
|
||||
it 'correctly exposes `status.emoji`' do
|
||||
|
|
@ -44,4 +42,66 @@ RSpec.describe MemberUserEntity do
|
|||
it 'correctly exposes `last_activity_on`' do
|
||||
expect(entity_hash[:last_activity_on]).to be(user.last_activity_on)
|
||||
end
|
||||
|
||||
context 'when options includes a source' do
|
||||
let(:current_user) { create(:user) }
|
||||
let(:options) { { current_user: current_user, source: source } }
|
||||
let(:entity) { described_class.new(user, options) }
|
||||
|
||||
shared_examples 'correctly exposes user two_factor_enabled' do
|
||||
context 'when the current_user has a role lower than minimum manage member role' do
|
||||
before do
|
||||
source.add_user(current_user, Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
|
||||
it 'does not expose user two_factor_enabled' do
|
||||
expect(entity_hash[:two_factor_enabled]).to be(nil)
|
||||
end
|
||||
|
||||
it 'matches json schema' do
|
||||
expect(entity.to_json).to match_schema('entities/member_user_default')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the current user has a minimum manage member role or higher' do
|
||||
before do
|
||||
source.add_user(current_user, minimum_manage_member_role)
|
||||
end
|
||||
|
||||
it 'matches json schema' do
|
||||
expect(entity.to_json).to match_schema('entities/member_user_for_admin_member')
|
||||
end
|
||||
|
||||
it 'exposes user two_factor_enabled' do
|
||||
expect(entity_hash[:two_factor_enabled]).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the current user is self' do
|
||||
let(:current_user) { user }
|
||||
|
||||
it 'exposes user two_factor_enabled' do
|
||||
expect(entity_hash[:two_factor_enabled]).to be(false)
|
||||
end
|
||||
|
||||
it 'matches json schema' do
|
||||
expect(entity.to_json).to match_schema('entities/member_user_for_admin_member')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the source is a group' do
|
||||
let(:source) { create(:group) }
|
||||
let(:minimum_manage_member_role) { Gitlab::Access::OWNER }
|
||||
|
||||
it_behaves_like 'correctly exposes user two_factor_enabled'
|
||||
end
|
||||
|
||||
context 'when the source is a project' do
|
||||
let(:source) { create(:project) }
|
||||
let(:minimum_manage_member_role) { Gitlab::Access::MAINTAINER }
|
||||
|
||||
it_behaves_like 'correctly exposes user two_factor_enabled'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ RSpec.describe BulkImports::FileDecompressionService do
|
|||
subject { described_class.new(tmpdir: tmpdir, filename: 'symlink.gz') }
|
||||
|
||||
it 'raises an error and removes the file' do
|
||||
expect { subject.execute }.to raise_error(described_class::ServiceError, 'Invalid file')
|
||||
expect { subject.execute }
|
||||
.to raise_error(BulkImports::FileDecompressionService::ServiceError, 'File decompression error')
|
||||
|
||||
expect(File.exist?(symlink)).to eq(false)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ RSpec.describe Issues::CreateService do
|
|||
|
||||
context 'user is reporter or above' do
|
||||
before do
|
||||
project.add_reporter(user)
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'assigns the sentry error' do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ RSpec.shared_context 'sentry error tracking context' do
|
|||
before do
|
||||
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
|
||||
|
||||
project.add_reporter(user)
|
||||
project.add_developer(user)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue