Commit ca0b403f authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/security/gitlab@13-12-stable-ee

parent e9f0f9e0
...@@ -48,6 +48,7 @@ export function initMermaid(mermaid) { ...@@ -48,6 +48,7 @@ export function initMermaid(mermaid) {
useMaxWidth: true, useMaxWidth: true,
htmlLabels: false, htmlLabels: false,
}, },
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'htmlLabels'],
securityLevel: 'strict', securityLevel: 'strict',
}); });
......
...@@ -17,6 +17,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -17,6 +17,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
@todos = @todos.with_entity_associations @todos = @todos.with_entity_associations
return if redirect_out_of_range(@todos, todos_page_count(@todos)) return if redirect_out_of_range(@todos, todos_page_count(@todos))
@allowed_todos = ::Todos::AllowedTargetFilterService.new(@todos, current_user).execute
end end
def destroy def destroy
......
...@@ -34,7 +34,7 @@ module Resolvers ...@@ -34,7 +34,7 @@ module Resolvers
return Todo.none unless current_user.present? && target.present? return Todo.none unless current_user.present? && target.present?
return Todo.none if target.is_a?(User) && target != current_user return Todo.none if target.is_a?(User) && target != current_user
TodosFinder.new(current_user, todo_finder_params(args)).execute TodosFinder.new(current_user, todo_finder_params(args)).execute.with_entity_associations
end end
private private
......
...@@ -115,11 +115,9 @@ module Types ...@@ -115,11 +115,9 @@ module Types
null: true, null: true,
description: 'Runbook for the alert as defined in alert details.' description: 'Runbook for the alert as defined in alert details.'
field :todos, field :todos, description: 'To-do items of the current user for the alert.', resolver: Resolvers::TodoResolver do
Types::TodoType.connection_type, extension(::Gitlab::Graphql::TodosProjectPermissionPreloader::FieldExtension)
null: true, end
description: 'To-do items of the current user for the alert.',
resolver: Resolvers::TodoResolver
field :details_url, field :details_url,
GraphQL::STRING_TYPE, GraphQL::STRING_TYPE,
......
...@@ -55,9 +55,6 @@ module Types ...@@ -55,9 +55,6 @@ module Types
type: GraphQL::STRING_TYPE, type: GraphQL::STRING_TYPE,
null: false, null: false,
description: 'Web path of the user.' description: 'Web path of the user.'
field :todos,
resolver: Resolvers::TodoResolver,
description: 'To-do items of the user.'
field :group_memberships, field :group_memberships,
type: Types::GroupMemberType.connection_type, type: Types::GroupMemberType.connection_type,
null: true, null: true,
...@@ -81,6 +78,10 @@ module Types ...@@ -81,6 +78,10 @@ module Types
description: 'Projects starred by the user.', description: 'Projects starred by the user.',
resolver: Resolvers::UserStarredProjectsResolver resolver: Resolvers::UserStarredProjectsResolver
field :todos, resolver: Resolvers::TodoResolver, description: 'To-do items of the user.' do
extension(::Gitlab::Graphql::TodosProjectPermissionPreloader::FieldExtension)
end
# Merge request field: MRs can be authored, assigned, or assigned-for-review: # Merge request field: MRs can be authored, assigned, or assigned-for-review:
field :authored_merge_requests, field :authored_merge_requests,
resolver: Resolvers::AuthoredMergeRequestsResolver, resolver: Resolvers::AuthoredMergeRequestsResolver,
......
...@@ -262,6 +262,10 @@ module AlertManagement ...@@ -262,6 +262,10 @@ module AlertManagement
end end
end end
def to_ability_name
'alert_management_alert'
end
private private
def hook_data def hook_data
......
...@@ -84,6 +84,14 @@ class ApplicationRecord < ActiveRecord::Base ...@@ -84,6 +84,14 @@ class ApplicationRecord < ActiveRecord::Base
values = enum_mod.definition.transform_values { |v| v[:value] } values = enum_mod.definition.transform_values { |v| v[:value] }
enum(enum_mod.key => values) enum(enum_mod.key => values)
end end
def readable_by?(user)
Ability.allowed?(user, "read_#{to_ability_name}".to_sym, self)
end
def to_ability_name
model_name.element
end
end end
ApplicationRecord.prepend_mod_with('ApplicationRecordHelpers') ApplicationRecord.prepend_mod_with('ApplicationRecordHelpers')
...@@ -530,6 +530,10 @@ class Commit ...@@ -530,6 +530,10 @@ class Commit
expire_note_etag_cache_for_related_mrs expire_note_etag_cache_for_related_mrs
end end
def readable_by?(user)
Ability.allowed?(user, :read_commit, self)
end
private private
def expire_note_etag_cache_for_related_mrs def expire_note_etag_cache_for_related_mrs
......
...@@ -182,10 +182,6 @@ module DesignManagement ...@@ -182,10 +182,6 @@ module DesignManagement
File.join(DesignManagement.designs_directory, "issue-#{issue.iid}", design.filename) File.join(DesignManagement.designs_directory, "issue-#{issue.iid}", design.filename)
end end
def to_ability_name
'design'
end
def description def description
'' ''
end end
......
...@@ -720,10 +720,6 @@ class Group < Namespace ...@@ -720,10 +720,6 @@ class Group < Namespace
Gitlab::ServiceDesk.supported? && all_projects.service_desk_enabled.exists? Gitlab::ServiceDesk.supported? && all_projects.service_desk_enabled.exists?
end end
def to_ability_name
model_name.singular
end
def activity_path def activity_path
Gitlab::Routing.url_helpers.activity_group_path(self) Gitlab::Routing.url_helpers.activity_group_path(self)
end end
......
...@@ -476,23 +476,6 @@ class Issue < ApplicationRecord ...@@ -476,23 +476,6 @@ class Issue < ApplicationRecord
issue_assignees.pluck(:user_id) issue_assignees.pluck(:user_id)
end end
private
# Ensure that the metrics association is safely created and respecting the unique constraint on issue_id
override :ensure_metrics
def ensure_metrics
if !association(:metrics).loaded? || metrics.blank?
metrics_record = Issue::Metrics.safe_find_or_create_by(issue: self)
self.metrics = metrics_record
end
metrics.record!
end
def record_create_action
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author)
end
# Returns `true` if the given User can read the current Issue. # Returns `true` if the given User can read the current Issue.
# #
# This method duplicates the same check of issue_policy.rb # This method duplicates the same check of issue_policy.rb
...@@ -512,6 +495,23 @@ class Issue < ApplicationRecord ...@@ -512,6 +495,23 @@ class Issue < ApplicationRecord
end end
end end
private
# Ensure that the metrics association is safely created and respecting the unique constraint on issue_id
override :ensure_metrics
def ensure_metrics
if !association(:metrics).loaded? || metrics.blank?
metrics_record = Issue::Metrics.safe_find_or_create_by(issue: self)
self.metrics = metrics_record
end
metrics.record!
end
def record_create_action
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author)
end
# Returns `true` if this Issue is visible to everybody. # Returns `true` if this Issue is visible to everybody.
def publicly_visible? def publicly_visible?
project.public? && !confidential? && !::Gitlab::ExternalAuthorization.enabled? project.public? && !confidential? && !::Gitlab::ExternalAuthorization.enabled?
......
...@@ -378,12 +378,6 @@ class Note < ApplicationRecord ...@@ -378,12 +378,6 @@ class Note < ApplicationRecord
super super
end end
# This method is to be used for checking read permissions on a note instead of `system_note_with_references_visible_for?`
def readable_by?(user)
# note_policy accounts for #system_note_with_references_visible_for?(user) check when granting read access
Ability.allowed?(user, :read_note, self)
end
def award_emoji? def award_emoji?
can_be_award_emoji? && contains_emoji_only? can_be_award_emoji? && contains_emoji_only?
end end
...@@ -400,10 +394,6 @@ class Note < ApplicationRecord ...@@ -400,10 +394,6 @@ class Note < ApplicationRecord
note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/ note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
end end
def to_ability_name
model_name.singular
end
def noteable_ability_name def noteable_ability_name
if for_snippet? if for_snippet?
'snippet' 'snippet'
......
...@@ -1496,10 +1496,6 @@ class Project < ApplicationRecord ...@@ -1496,10 +1496,6 @@ class Project < ApplicationRecord
end end
end end
def to_ability_name
model_name.singular
end
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass
def execute_hooks(data, hooks_scope = :push_hooks) def execute_hooks(data, hooks_scope = :push_hooks)
run_after_commit_or_now do run_after_commit_or_now do
......
...@@ -5,7 +5,10 @@ class TodoPolicy < BasePolicy ...@@ -5,7 +5,10 @@ class TodoPolicy < BasePolicy
condition(:own_todo) do condition(:own_todo) do
@user && @subject.user_id == @user.id @user && @subject.user_id == @user.id
end end
condition(:can_read_target) do
@user && @subject.target&.readable_by?(@user)
end
rule { own_todo }.enable :read_todo rule { own_todo & can_read_target }.enable :read_todo
rule { own_todo }.enable :update_todo rule { own_todo & can_read_target }.enable :update_todo
end end
# frozen_string_literal: true
module Todos
class AllowedTargetFilterService
include Gitlab::Allowable
def initialize(todos, current_user)
@todos = todos
@current_user = current_user
end
def execute
Preloaders::UserMaxAccessLevelInProjectsPreloader.new(@todos.map(&:project).compact, @current_user).execute
@todos.select { |todo| can?(@current_user, :read_todo, todo) }
end
end
end
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= number_with_delimiter(todos_done_count) = number_with_delimiter(todos_done_count)
.nav-controls .nav-controls
- if @todos.any?(&:pending?) - if @allowed_todos.any?(&:pending?)
.gl-mr-3 .gl-mr-3
= link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'gl-button btn btn-default btn-loading align-items-center js-todos-mark-all', method: :delete, data: { href: destroy_all_dashboard_todos_path(todos_filter_params) } do = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'gl-button btn btn-default btn-loading align-items-center js-todos-mark-all', method: :delete, data: { href: destroy_all_dashboard_todos_path(todos_filter_params) } do
Mark all as done Mark all as done
...@@ -82,11 +82,11 @@ ...@@ -82,11 +82,11 @@
= sort_title_oldest_created = sort_title_oldest_created
.row.js-todos-all .row.js-todos-all
- if @todos.any? - if @allowed_todos.any?
.col.js-todos-list-container{ data: { qa_selector: "todos_list_container" } } .col.js-todos-list-container{ data: { qa_selector: "todos_list_container" } }
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } } .js-todos-options{ data: { per_page: @allowed_todos.count, current_page: @todos.current_page, total_pages: @todos.total_pages } }
%ul.content-list.todos-list %ul.content-list.todos-list
= render @todos = render @allowed_todos
= paginate @todos, theme: "gitlab" = paginate @todos, theme: "gitlab"
.js-nothing-here-container.empty-state.hidden .js-nothing-here-container.empty-state.hidden
.svg-content .svg-content
......
...@@ -92,6 +92,7 @@ module API ...@@ -92,6 +92,7 @@ module API
end end
get do get do
todos = paginate(find_todos.with_entity_associations) todos = paginate(find_todos.with_entity_associations)
todos = ::Todos::AllowedTargetFilterService.new(todos, current_user).execute
options = { with: Entities::Todo, current_user: current_user } options = { with: Entities::Todo, current_user: current_user }
batch_load_issuable_metadata(todos, options) batch_load_issuable_metadata(todos, options)
......
...@@ -26,13 +26,13 @@ module Gitlab ...@@ -26,13 +26,13 @@ module Gitlab
def branch_exists? def branch_exists?
strong_memoize(:is_branch) do strong_memoize(:is_branch) do
project.repository.branch_exists?(ref) branch_ref? && project.repository.branch_exists?(ref)
end end
end end
def tag_exists? def tag_exists?
strong_memoize(:is_tag) do strong_memoize(:is_tag) do
project.repository.tag_exists?(ref) tag_ref? && project.repository.tag_exists?(ref)
end end
end end
...@@ -105,6 +105,32 @@ module Gitlab ...@@ -105,6 +105,32 @@ module Gitlab
def dangling_build? def dangling_build?
%i[ondemand_dast_scan webide].include?(source) %i[ondemand_dast_scan webide].include?(source)
end end
private
# Verifies that origin_ref is a fully qualified tag reference (refs/tags/<tag-name>)
#
# Fallbacks to `true` for backward compatibility reasons
# if origin_ref is a short ref
def tag_ref?
return true if full_git_ref_name_unavailable?
Gitlab::Git.tag_ref?(origin_ref).present?
end
# Verifies that origin_ref is a fully qualified branch reference (refs/heads/<branch-name>)
#
# Fallbacks to `true` for backward compatibility reasons
# if origin_ref is a short ref
def branch_ref?
return true if full_git_ref_name_unavailable?
Gitlab::Git.branch_ref?(origin_ref).present?
end
def full_git_ref_name_unavailable?
ref == origin_ref
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Graphql
module TodosProjectPermissionPreloader
class FieldExtension < ::GraphQL::Schema::FieldExtension
def after_resolve(value:, memo:, **rest)
todos = value.to_a
Preloaders::UserMaxAccessLevelInProjectsPreloader.new(
todos.map(&:project).compact,
current_user(rest)
).execute
value
end
private
def current_user(options)
options.dig(:context, :current_user)
end
end
end
end
end
...@@ -3,16 +3,20 @@ ...@@ -3,16 +3,20 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Dashboard > Todo target states' do RSpec.describe 'Dashboard > Todo target states' do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:author) { create(:user) } let_it_be(:author) { create(:user) }
let(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
before_all do
project.add_developer(user)
end
before do before do
sign_in(user) sign_in(user)
end end
it 'on a closed issue todo has closed label' do it 'on a closed issue todo has closed label' do
issue_closed = create(:issue, state: 'closed') issue_closed = create(:issue, state: 'closed', project: project)
create_todo issue_closed create_todo issue_closed
visit dashboard_todos_path visit dashboard_todos_path
...@@ -22,7 +26,7 @@ RSpec.describe 'Dashboard > Todo target states' do ...@@ -22,7 +26,7 @@ RSpec.describe 'Dashboard > Todo target states' do
end end
it 'on an open issue todo does not have an open label' do it 'on an open issue todo does not have an open label' do
issue_open = create(:issue) issue_open = create(:issue, project: project)
create_todo issue_open create_todo issue_open
visit dashboard_todos_path visit dashboard_todos_path
...@@ -32,7 +36,7 @@ RSpec.describe 'Dashboard > Todo target states' do ...@@ -32,7 +36,7 @@ RSpec.describe 'Dashboard > Todo target states' do
end end
it 'on a merged merge request todo has merged label' do it 'on a merged merge request todo has merged label' do
mr_merged = create(:merge_request, :simple, :merged, author: user) mr_merged = create(:merge_request, :simple, :merged, author: user, source_project: project)
create_todo mr_merged create_todo mr_merged
visit dashboard_todos_path visit dashboard_todos_path
...@@ -42,7 +46,7 @@ RSpec.describe 'Dashboard > Todo target states' do ...@@ -42,7 +46,7 @@ RSpec.describe 'Dashboard > Todo target states' do
end end
it 'on a closed merge request todo has closed label' do it 'on a closed merge request todo has closed label' do
mr_closed = create(:merge_request, :simple, :closed, author: user) mr_closed = create(:merge_request, :simple, :closed, author: user, source_project: project)
create_todo mr_closed create_todo mr_closed
visit dashboard_todos_path visit dashboard_todos_path
...@@ -52,7 +56,7 @@ RSpec.describe 'Dashboard > Todo target states' do ...@@ -52,7 +56,7 @@ RSpec.describe 'Dashboard > Todo target states' do
end end
it 'on an open merge request todo does not have an open label' do it 'on an open merge request todo does not have an open label' do
mr_open = create(:merge_request, :simple, author: user) mr_open = create(:merge_request, :simple, author: user, source_project: project)
create_todo mr_open create_todo mr_open
visit dashboard_todos_path visit dashboard_todos_path
......
...@@ -128,7 +128,7 @@ RSpec.describe 'Dashboard > User filters todos', :js do ...@@ -128,7 +128,7 @@ RSpec.describe 'Dashboard > User filters todos', :js do
describe 'filter by action' do describe 'filter by action' do
before do before do
create(:todo, :build_failed, user: user_1, author: user_2, project: project_1) create(:todo, :build_failed, user: user_1, author: user_2, project: project_1, target: merge_request)
create(:todo, :marked, user: user_1, author: user_2, project: project_1, target: issue1) create(:todo, :marked, user: user_1, author: user_2, project: project_1, target: issue1)
create(:todo, :review_requested, user: user_1, author: user_2, project: project_1, target: issue1) create(:todo, :review_requested, user: user_1, author: user_2, project: project_1, target: issue1)
end end
......
...@@ -3,10 +3,16 @@ ...@@ -3,10 +3,16 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Dashboard Todos' do RSpec.describe 'Dashboard Todos' do
include DesignManagementTestHelpers
let_it_be(:user) { create(:user, username: 'john') } let_it_be(:user) { create(:user, username: 'john') }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, due_date: Date.today, title: "Fix bug") } let_it_be(:issue) { create(:issue, project: project, due_date: Date.today, title: "Fix bug") }
before_all do
project.add_developer(user)
end
context 'User does not have todos' do context 'User does not have todos' do
before do before do
...@@ -21,8 +27,8 @@ RSpec.describe 'Dashboard Todos' do ...@@ -21,8 +27,8 @@ RSpec.describe 'Dashboard Todos' do
context 'when the todo references a merge request' do context 'when the todo references a merge request' do
let(:referenced_mr) { create(:merge_request, source_project: project) } let(:referenced_mr) { create(:merge_request, source_project: project) }
let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}") } let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}", noteable: create(:issue, project: project)) }
let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note) } let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note, target: note.noteable) }
before do before do
sign_in(user) sign_in(user)
...@@ -39,9 +45,26 @@ RSpec.describe 'Dashboard Todos' do ...@@ -39,9 +45,26 @@ RSpec.describe 'Dashboard Todos' do
end end
end end
context 'User has a todo', :js do context 'user has an unauthorized todo' do
before do before do
sign_in(user)
end
it 'does not render the todo' do
unauthorized_issue = create(:issue)
create(:todo, :mentioned, user: user, project: unauthorized_issue.project, target: unauthorized_issue, author: author)
create(:todo, :mentioned, user: user, project: project, target: issue, author: author) create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
visit dashboard_todos_path
expect(page).to have_selector('.todos-list .todo', count: 1)
end
end
context 'User has a todo', :js do
let_it_be(:user_todo) { create(:todo, :mentioned, user: user, project: project, target: issue, author: author) }
before do
sign_in(user) sign_in(user)
visit dashboard_todos_path visit dashboard_todos_path
...@@ -183,7 +206,7 @@ RSpec.describe 'Dashboard Todos' do ...@@ -183,7 +206,7 @@ RSpec.describe 'Dashboard Todos' do
end end
context 'approval todo' do context 'approval todo' do
let(:merge_request) { create(:merge_request, title: "Fixes issue") } let(:merge_request) { create(:merge_request, title: "Fixes issue", source_project: project) }
before do before do
create(:todo, :approval_required, user: user, project: project, target: merge_request, author: user) create(:todo, :approval_required, user: user, project: project, target: merge_request, author: user)
...@@ -199,7 +222,7 @@ RSpec.describe 'Dashboard Todos' do ...@@ -199,7 +222,7 @@ RSpec.describe 'Dashboard Todos' do
end end
context 'review request todo' do context 'review request todo' do
let(:merge_request) { create(:merge_request, title: "Fixes issue") } let(:merge_request) { create(:merge_request, title: "Fixes issue", source_project: project) }
before do before do
create(:todo, :review_requested, user: user, project: project, target: merge_request, author: user) create(:todo, :review_requested, user: user, project: project, target: merge_request, author: user)
...@@ -355,7 +378,7 @@ RSpec.describe 'Dashboard Todos' do ...@@ -355,7 +378,7 @@ RSpec.describe 'Dashboard Todos' do
end end
context 'User has a Build Failed todo' do context 'User has a Build Failed todo' do
let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: author) } let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: author, target: create(:merge_request, source_project: project)) }
before do before do
sign_in(user) sign_in(user)
...@@ -386,6 +409,7 @@ RSpec.describe 'Dashboard Todos' do ...@@ -386,6 +409,7 @@ RSpec.describe 'Dashboard Todos' do
end end
before do before do
enable_design_management
project.add_developer(user) project.add_developer(user)
sign_in(user) sign_in(user)
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Mermaid rendering', :js do RSpec.describe 'Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) }
it 'renders Mermaid diagrams correctly' do it 'renders Mermaid diagrams correctly' do
description = <<~MERMAID description = <<~MERMAID
```mermaid ```mermaid
...@@ -14,7 +16,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -14,7 +16,6 @@ RSpec.describe 'Mermaid rendering', :js do
``` ```
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -36,7 +37,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -36,7 +37,6 @@ RSpec.describe 'Mermaid rendering', :js do
``` ```
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -64,7 +64,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -64,7 +64,6 @@ RSpec.describe 'Mermaid rendering', :js do
``` ```
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -94,7 +93,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -94,7 +93,6 @@ RSpec.describe 'Mermaid rendering', :js do
</details> </details>
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -123,7 +121,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -123,7 +121,6 @@ RSpec.describe 'Mermaid rendering', :js do
``` ```
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -144,7 +141,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -144,7 +141,6 @@ RSpec.describe 'Mermaid rendering', :js do
``` ```
MERMAID MERMAID
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -183,8 +179,6 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -183,8 +179,6 @@ RSpec.describe 'Mermaid rendering', :js do
description *= 51 description *= 51
project = create(:project, :public)
issue = create(:issue, project: project, description: description) issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
...@@ -200,6 +194,27 @@ RSpec.describe 'Mermaid rendering', :js do ...@@ -200,6 +194,27 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page).to have_selector('.js-lazy-render-mermaid-container') expect(page).to have_selector('.js-lazy-render-mermaid-container')
end end
end end
it 'does not allow HTML injection' do
description = <<~MERMAID
```mermaid
%%{init: {"flowchart": {"htmlLabels": "false"}} }%%
flowchart
A["<iframe></iframe>"]
```
MERMAID
issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue)
wait_for_requests
wait_for_mermaid
page.within('.description') do
expect(page).not_to have_xpath("//iframe")
end
end
end end
def wait_for_mermaid def wait_for_mermaid
......
...@@ -5,17 +5,23 @@ require 'spec_helper' ...@@ -5,17 +5,23 @@ require 'spec_helper'
RSpec.describe Mutations::Todos::MarkDone do RSpec.describe Mutations::Todos::MarkDone do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
before_all do
project.add_developer(current_user)
end
specify { expect(described_class).to require_graphql_authorizations(:update_todo) } specify { expect(described_class).to require_graphql_authorizations(:update_todo) }
describe '#resolve' do describe '#resolve' do
......
...@@ -5,17 +5,23 @@ require 'spec_helper' ...@@ -5,17 +5,23 @@ require 'spec_helper'
RSpec.describe Mutations::Todos::Restore do RSpec.describe Mutations::Todos::Restore do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
before_all do
project.add_developer(current_user)
end
specify { expect(described_class).to require_graphql_authorizations(:update_todo) } specify { expect(described_class).to require_graphql_authorizations(:update_todo) }
describe '#resolve' do describe '#resolve' do
......
...@@ -4,19 +4,28 @@ require 'spec_helper' ...@@ -4,19 +4,28 @@ require 'spec_helper'
RSpec.describe Resolvers::TodoResolver do RSpec.describe Resolvers::TodoResolver do
include GraphqlHelpers include GraphqlHelpers
include DesignManagementTestHelpers
specify do specify do
expect(described_class).to have_nullable_graphql_type(Types::TodoType.connection_type) expect(described_class).to have_nullable_graphql_type(Types::TodoType.connection_type)
end end
describe '#resolve' do describe '#resolve' do
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:author1) { create(:user) } let_it_be(:author1) { create(:user) }
let_it_be(:author2) { create(:user) } let_it_be(:author2) { create(:user) }
let_it_be(:merge_request_todo_pending) { create(:todo, user: current_user, target_type: 'MergeRequest', state: :pending, action: Todo::MENTIONED, author: author1) } let_it_be(:issue_todo_done) { create(:todo, user: current_user, state: :done, action: Todo::ASSIGNED, author: author2, target: issue) }
let_it_be(:issue_todo_done) { create(:todo, user: current_user, state: :done, action: Todo::ASSIGNED, author: author2) } let_it_be(:issue_todo_pending) { create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: issue) }
let_it_be(:issue_todo_pending) { create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) }
let(:merge_request) { create(:merge_request, source_project: project) }
let!(:merge_request_todo_pending) { create(:todo, user: current_user, target: merge_request, state: :pending, action: Todo::MENTIONED, author: author1) }
before_all do
project.add_developer(current_user)
end
it 'calls TodosFinder' do it 'calls TodosFinder' do
expect_next_instance_of(TodosFinder) do |finder| expect_next_instance_of(TodosFinder) do |finder|
...@@ -40,7 +49,9 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -40,7 +49,9 @@ RSpec.describe Resolvers::TodoResolver do
end end
it 'returns the todos for multiple filters' do it 'returns the todos for multiple filters' do
design_todo_pending = create(:todo, target_type: 'DesignManagement::Design', user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) enable_design_management
design = create(:design, issue: issue)
design_todo_pending = create(:todo, target: design, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
todos = resolve_todos(type: ['MergeRequest', 'DesignManagement::Design']) todos = resolve_todos(type: ['MergeRequest', 'DesignManagement::Design'])
...@@ -59,11 +70,15 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -59,11 +70,15 @@ RSpec.describe Resolvers::TodoResolver do
group3 = create(:group) group3 = create(:group)
group1.add_developer(current_user) group1.add_developer(current_user)
issue1 = create(:issue, project: create(:project, group: group1))
group2.add_developer(current_user) group2.add_developer(current_user)
issue2 = create(:issue, project: create(:project, group: group2))
group3.add_developer(current_user)
issue3 = create(:issue, project: create(:project, group: group3))
todo4 = create(:todo, group: group1, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) todo4 = create(:todo, group: group1, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: issue1)
todo5 = create(:todo, group: group2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) todo5 = create(:todo, group: group2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: issue2)
create(:todo, group: group3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) create(:todo, group: group3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: issue3)
todos = resolve_todos(group_id: [group2.id, group1.id]) todos = resolve_todos(group_id: [group2.id, group1.id])
...@@ -93,9 +108,13 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -93,9 +108,13 @@ RSpec.describe Resolvers::TodoResolver do
project2 = create(:project) project2 = create(:project)
project3 = create(:project) project3 = create(:project)
todo4 = create(:todo, project: project1, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) project1.add_developer(current_user)
todo5 = create(:todo, project: project2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) project2.add_developer(current_user)
create(:todo, project: project3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) project3.add_developer(current_user)
todo4 = create(:todo, project: project1, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: create(:issue, project: project1))
todo5 = create(:todo, project: project2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: create(:issue, project: project2))
create(:todo, project: project3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1, target: create(:issue, project: project3))
todos = resolve_todos(project_id: [project2.id, project1.id]) todos = resolve_todos(project_id: [project2.id, project1.id])
......
...@@ -136,7 +136,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do ...@@ -136,7 +136,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do
let(:command) do let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new( Gitlab::Ci::Pipeline::Chain::Command.new(
source: :push, source: :push,
origin_ref: 'mytag', origin_ref: origin_ref,
checkout_sha: project.commit.id, checkout_sha: project.commit.id,
after_sha: nil, after_sha: nil,
before_sha: nil, before_sha: nil,
...@@ -147,6 +147,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do ...@@ -147,6 +147,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do
current_user: user) current_user: user)
end end
let(:origin_ref) { 'mytag' }
before do before do
allow_any_instance_of(Repository).to receive(:tag_exists?).with('mytag').and_return(true) allow_any_instance_of(Repository).to receive(:tag_exists?).with('mytag').and_return(true)
...@@ -156,6 +158,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do ...@@ -156,6 +158,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Build do
it 'correctly indicated that this is a tagged pipeline' do it 'correctly indicated that this is a tagged pipeline' do
expect(pipeline).to be_tag expect(pipeline).to be_tag
end end
context 'when origin_ref is branch but tag ref with the same name exists' do
let(:origin_ref) { 'refs/heads/mytag' }
it 'correctly indicated that a pipeline is not tagged' do
expect(pipeline).not_to be_tag
end
end
end end
context 'when pipeline is running for a merge request' do context 'when pipeline is running for a merge request' do
......
...@@ -27,6 +27,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do ...@@ -27,6 +27,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
it { is_expected.to eq(true) } it { is_expected.to eq(true) }
end end
context 'for fully described tag ref' do
let(:origin_ref) { 'refs/tags/master' }
it { is_expected.to eq(false) }
end
context 'for fully described branch ref' do
let(:origin_ref) { 'refs/heads/master' }
it { is_expected.to eq(true) }
end
context 'for invalid branch' do context 'for invalid branch' do
let(:origin_ref) { 'something' } let(:origin_ref) { 'something' }
...@@ -43,6 +55,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do ...@@ -43,6 +55,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
it { is_expected.to eq(true) } it { is_expected.to eq(true) }
end end
context 'for fully described tag ref' do
let(:origin_ref) { 'refs/tags/v1.0.0' }
it { is_expected.to eq(true) }
end
context 'for fully described branch ref' do
let(:origin_ref) { 'refs/heads/v1.0.0' }
it { is_expected.to eq(false) }
end
context 'for invalid ref' do context 'for invalid ref' do
let(:origin_ref) { 'something' } let(:origin_ref) { 'something' }
......
...@@ -552,4 +552,10 @@ RSpec.describe DiffNote do ...@@ -552,4 +552,10 @@ RSpec.describe DiffNote do
expect(subject.on_image?).to be_truthy expect(subject.on_image?).to be_truthy
end end
end end
describe '#to_ability_name' do
subject { described_class.new.to_ability_name }
it { is_expected.to eq('note') }
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DiscussionNote do
describe '#to_ability_name' do
subject { described_class.new.to_ability_name }
it { is_expected.to eq('note') }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe LegacyDiffNote do
describe '#to_ability_name' do
subject { described_class.new.to_ability_name }
it { is_expected.to eq('note') }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe SyntheticNote do
describe '#to_ability_name' do
subject { described_class.new.to_ability_name }
it { is_expected.to eq('note') }
end
end
...@@ -9,22 +9,28 @@ RSpec.describe TodoPolicy do ...@@ -9,22 +9,28 @@ RSpec.describe TodoPolicy do
let_it_be(:user2) { create(:user) } let_it_be(:user2) { create(:user) }
let_it_be(:user3) { create(:user) } let_it_be(:user3) { create(:user) }
let_it_be(:todo1) { create(:todo, author: author, user: user1) } let_it_be(:project) { create(:project) }
let_it_be(:todo2) { create(:todo, author: author, user: user2) } let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:todo1) { create(:todo, author: author, user: user1, issue: issue) }
let_it_be(:todo2) { create(:todo, author: author, user: user2, issue: issue) }
let_it_be(:todo3) { create(:todo, author: author, user: user2) } let_it_be(:todo3) { create(:todo, author: author, user: user2) }
let_it_be(:todo4) { create(:todo, author: author, user: user3) } let_it_be(:todo4) { create(:todo, author: author, user: user3, issue: issue) }
def permissions(user, todo) def permissions(user, todo)
described_class.new(user, todo) described_class.new(user, todo)
end end
before_all do
project.add_developer(user1)
project.add_developer(user2)
end
describe 'own_todo' do describe 'own_todo' do
it 'allows owners to access their own todos' do it 'allows owners to access their own todos if they can read todo target' do
[ [
[user1, todo1], [user1, todo1],
[user2, todo2], [user2, todo2]
[user2, todo3],
[user3, todo4]
].each do |user, todo| ].each do |user, todo|
expect(permissions(user, todo)).to be_allowed(:read_todo) expect(permissions(user, todo)).to be_allowed(:read_todo)
end end
...@@ -38,7 +44,9 @@ RSpec.describe TodoPolicy do ...@@ -38,7 +44,9 @@ RSpec.describe TodoPolicy do
[user2, todo4], [user2, todo4],
[user3, todo1], [user3, todo1],
[user3, todo2], [user3, todo2],
[user3, todo3] [user3, todo3],
[user2, todo3],
[user3, todo4]
].each do |user, todo| ].each do |user, todo|
expect(permissions(user, todo)).to be_disallowed(:read_todo) expect(permissions(user, todo)).to be_disallowed(:read_todo)
end end
......
...@@ -4,12 +4,17 @@ require 'spec_helper' ...@@ -4,12 +4,17 @@ require 'spec_helper'
RSpec.describe 'Query current user todos' do RSpec.describe 'Query current user todos' do
include GraphqlHelpers include GraphqlHelpers
include DesignManagementTestHelpers
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:commit_todo) { create(:on_commit_todo, user: current_user, project: create(:project, :repository)) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:issue_todo) { create(:todo, user: current_user, target: create(:issue)) } let_it_be(:unauthorize_project) { create(:project) }
let_it_be(:merge_request_todo) { create(:todo, user: current_user, target: create(:merge_request)) } let_it_be(:commit_todo) { create(:on_commit_todo, user: current_user, project: project) }
let_it_be(:design_todo) { create(:todo, user: current_user, target: create(:design)) } let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:issue_todo) { create(:todo, project: project, user: current_user, target: issue) }
let_it_be(:merge_request_todo) { create(:todo, project: project, user: current_user, target: create(:merge_request, source_project: project)) }
let_it_be(:design_todo) { create(:todo, project: project, user: current_user, target: create(:design, issue: issue)) }
let_it_be(:unauthorized_todo) { create(:todo, user: current_user, project: unauthorize_project, target: create(:issue, project: unauthorize_project)) }
let(:fields) do let(:fields) do
<<~QUERY <<~QUERY
...@@ -23,16 +28,22 @@ RSpec.describe 'Query current user todos' do ...@@ -23,16 +28,22 @@ RSpec.describe 'Query current user todos' do
graphql_query_for('currentUser', {}, query_graphql_field('todos', {}, fields)) graphql_query_for('currentUser', {}, query_graphql_field('todos', {}, fields))
end end
before_all do
project.add_developer(current_user)
end
subject { graphql_data.dig('currentUser', 'todos', 'nodes') } subject { graphql_data.dig('currentUser', 'todos', 'nodes') }
before do before do
enable_design_management
post_graphql(query, current_user: current_user) post_graphql(query, current_user: current_user)
end end
it_behaves_like 'a working graphql query' it_behaves_like 'a working graphql query'
it 'contains the expected ids' do it 'contains the expected ids' do
is_expected.to include( is_expected.to contain_exactly(
a_hash_including('id' => commit_todo.to_global_id.to_s), a_hash_including('id' => commit_todo.to_global_id.to_s),
a_hash_including('id' => issue_todo.to_global_id.to_s), a_hash_including('id' => issue_todo.to_global_id.to_s),
a_hash_including('id' => merge_request_todo.to_global_id.to_s), a_hash_including('id' => merge_request_todo.to_global_id.to_s),
...@@ -41,11 +52,33 @@ RSpec.describe 'Query current user todos' do ...@@ -41,11 +52,33 @@ RSpec.describe 'Query current user todos' do
end end
it 'returns Todos for all target types' do it 'returns Todos for all target types' do
is_expected.to include( is_expected.to contain_exactly(
a_hash_including('targetType' => 'COMMIT'), a_hash_including('targetType' => 'COMMIT'),
a_hash_including('targetType' => 'ISSUE'), a_hash_including('targetType' => 'ISSUE'),
a_hash_including('targetType' => 'MERGEREQUEST'), a_hash_including('targetType' => 'MERGEREQUEST'),
a_hash_including('targetType' => 'DESIGN') a_hash_including('targetType' => 'DESIGN')
) )
end end
context 'when requesting a single field' do
let(:fields) do
<<~QUERY
nodes {
id
}
QUERY
end
it 'avoids N+1 queries', :request_store do
control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) }
project2 = create(:project)
project2.add_developer(current_user)
issue2 = create(:issue, project: project2)
create(:todo, user: current_user, target: issue2, project: project2)
# An additional query is made for each different group that owns a todo through a project
expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control).with_threshold(2)
end
end
end end
...@@ -5,14 +5,16 @@ require 'spec_helper' ...@@ -5,14 +5,16 @@ require 'spec_helper'
RSpec.describe 'Marking all todos done' do RSpec.describe 'Marking all todos done' do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:other_user2) { create(:user) } let_it_be(:other_user2) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:todo3) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo3) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) }
...@@ -28,6 +30,10 @@ RSpec.describe 'Marking all todos done' do ...@@ -28,6 +30,10 @@ RSpec.describe 'Marking all todos done' do
) )
end end
before_all do
project.add_developer(current_user)
end
def mutation_response def mutation_response
graphql_mutation_response(:todos_mark_all_done) graphql_mutation_response(:todos_mark_all_done)
end end
......
...@@ -5,12 +5,14 @@ require 'spec_helper' ...@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe 'Marking todos done' do RSpec.describe 'Marking todos done' do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) }
...@@ -29,6 +31,10 @@ RSpec.describe 'Marking todos done' do ...@@ -29,6 +31,10 @@ RSpec.describe 'Marking todos done' do
) )
end end
before_all do
project.add_developer(current_user)
end
def mutation_response def mutation_response
graphql_mutation_response(:todo_mark_done) graphql_mutation_response(:todo_mark_done)
end end
......
...@@ -5,12 +5,14 @@ require 'spec_helper' ...@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe 'Restoring many Todos' do RSpec.describe 'Restoring many Todos' do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) }
...@@ -31,6 +33,10 @@ RSpec.describe 'Restoring many Todos' do ...@@ -31,6 +33,10 @@ RSpec.describe 'Restoring many Todos' do
) )
end end
before_all do
project.add_developer(current_user)
end
def mutation_response def mutation_response
graphql_mutation_response(:todo_restore_many) graphql_mutation_response(:todo_restore_many)
end end
......
...@@ -5,12 +5,14 @@ require 'spec_helper' ...@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe 'Restoring Todos' do RSpec.describe 'Restoring Todos' do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:author) { create(:user) } let_it_be(:author) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done) } let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done, target: issue) }
let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending) } let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending, target: issue) }
let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) }
...@@ -29,6 +31,10 @@ RSpec.describe 'Restoring Todos' do ...@@ -29,6 +31,10 @@ RSpec.describe 'Restoring Todos' do
) )
end end
before_all do
project.add_developer(current_user)
end
def mutation_response def mutation_response
graphql_mutation_response(:todo_restore) graphql_mutation_response(:todo_restore)
end end
......
...@@ -3,18 +3,22 @@ ...@@ -3,18 +3,22 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe API::Todos do RSpec.describe API::Todos do
include DesignManagementTestHelpers
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project_1) { create(:project, :repository, group: group) } let_it_be(:project_1) { create(:project, :repository, group: group) }
let_it_be(:project_2) { create(:project) } let_it_be(:project_2) { create(:project) }
let_it_be(:author_1) { create(:user) } let_it_be(:author_1) { create(:user) }
let_it_be(:author_2) { create(:user) } let_it_be(:author_2) { create(:user) }
let_it_be(:john_doe) { create(:user, username: 'john_doe') } let_it_be(:john_doe) { create(:user, username: 'john_doe') }
let_it_be(:issue) { create(:issue, project: project_1) }
let_it_be(:merge_request) { create(:merge_request, source_project: project_1) } let_it_be(:merge_request) { create(:merge_request, source_project: project_1) }
let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) } let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) } let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: issue) }
let_it_be(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) } let_it_be(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe, target: issue) }
let_it_be(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) } let_it_be(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) }
let_it_be(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) } let_it_be(:pending_4) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe, commit_id: 'invalid_id') }
let_it_be(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe, target: issue) }
let_it_be(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') } let_it_be(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') }
let_it_be(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') } let_it_be(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') }
let_it_be(:award_emoji_3) { create(:award_emoji, awardable: pending_2.target, user: author_2, name: 'thumbsdown') } let_it_be(:award_emoji_3) { create(:award_emoji, awardable: pending_2.target, user: author_2, name: 'thumbsdown') }
...@@ -77,13 +81,13 @@ RSpec.describe API::Todos do ...@@ -77,13 +81,13 @@ RSpec.describe API::Todos do
expect(json_response[0]['target_type']).to eq('Commit') expect(json_response[0]['target_type']).to eq('Commit')
expect(json_response[1]['target_type']).to eq('Issue') expect(json_response[1]['target_type']).to eq('Issue')
expect(json_response[1]['target']['upvotes']).to eq(0) expect(json_response[1]['target']['upvotes']).to eq(1)
expect(json_response[1]['target']['downvotes']).to eq(1) expect(json_response[1]['target']['downvotes']).to eq(1)
expect(json_response[1]['target']['merge_requests_count']).to eq(0) expect(json_response[1]['target']['merge_requests_count']).to eq(0)
expect(json_response[2]['target_type']).to eq('Issue') expect(json_response[2]['target_type']).to eq('Issue')
expect(json_response[2]['target']['upvotes']).to eq(1) expect(json_response[2]['target']['upvotes']).to eq(1)
expect(json_response[2]['target']['downvotes']).to eq(0) expect(json_response[2]['target']['downvotes']).to eq(1)
expect(json_response[2]['target']['merge_requests_count']).to eq(0) expect(json_response[2]['target']['merge_requests_count']).to eq(0)
expect(json_response[3]['target_type']).to eq('MergeRequest') expect(json_response[3]['target_type']).to eq('MergeRequest')
...@@ -93,6 +97,19 @@ RSpec.describe API::Todos do ...@@ -93,6 +97,19 @@ RSpec.describe API::Todos do
expect(json_response[3]['target']['downvotes']).to eq(0) expect(json_response[3]['target']['downvotes']).to eq(0)
end end
context "when current user does not have access to one of the TODO's target" do
it 'filters out unauthorized todos' do
no_access_project = create(:project, :repository, group: group)
no_access_merge_request = create(:merge_request, source_project: no_access_project)
no_access_todo = create(:todo, project: no_access_project, author: author_2, user: john_doe, target: no_access_merge_request)
get api('/todos', john_doe)
expect(json_response.count).to eq(4)
expect(json_response.map { |t| t['id'] }).not_to include(no_access_todo.id, pending_4.id)
end
end
context 'and using the author filter' do context 'and using the author filter' do
it 'filters based on author_id param' do it 'filters based on author_id param' do
get api('/todos', john_doe), params: { author_id: author_2.id } get api('/todos', john_doe), params: { author_id: author_2.id }
...@@ -163,23 +180,31 @@ RSpec.describe API::Todos do ...@@ -163,23 +180,31 @@ RSpec.describe API::Todos do
end end
it 'avoids N+1 queries', :request_store do it 'avoids N+1 queries', :request_store do
create_issue_todo_for(john_doe)
create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request)
get api('/todos', john_doe) get api('/todos', john_doe)
control = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) } control1 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
create_issue_todo_for(john_doe)
create_mr_todo_for(john_doe, project_2)
create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: merge_request)
new_todo = create_mr_todo_for(john_doe)
merge_request_3 = create(:merge_request, :jira_branch, source_project: new_todo.project)
create(:on_commit_todo, project: new_todo.project, author: author_1, user: john_doe, target: merge_request_3)
create(:todo, project: new_todo.project, author: author_2, user: john_doe, target: merge_request_3)
merge_request_2 = create(:merge_request, source_project: project_2) expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(4)
create(:todo, project: project_2, author: author_2, user: john_doe, target: merge_request_2) control2 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
project_3 = create(:project, :repository) create_issue_todo_for(john_doe)
project_3.add_developer(john_doe) create_issue_todo_for(john_doe, project_1)
merge_request_3 = create(:merge_request, source_project: project_3) create_issue_todo_for(john_doe, project_1)
create(:todo, project: project_3, author: author_2, user: john_doe, target: merge_request_3)
create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) # Additional query only when target belongs to project from different group
create(:on_commit_todo, project: project_3, author: author_1, user: john_doe) expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control2).with_threshold(1)
expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
...@@ -201,6 +226,8 @@ RSpec.describe API::Todos do ...@@ -201,6 +226,8 @@ RSpec.describe API::Todos do
end end
before do before do
enable_design_management
api_request api_request
end end
...@@ -222,6 +249,20 @@ RSpec.describe API::Todos do ...@@ -222,6 +249,20 @@ RSpec.describe API::Todos do
) )
end end
end end
def create_mr_todo_for(user, project = nil)
new_project = project || create(:project, group: create(:group))
new_project.add_developer(user) if project.blank?
new_merge_request = create(:merge_request, source_project: new_project)
create(:todo, project: new_project, author: user, user: user, target: new_merge_request)
end
def create_issue_todo_for(user, project = nil)
new_project = project || create(:project, group: create(:group))
new_project.group.add_developer(user) if project.blank?
issue = create(:issue, project: new_project)
create(:todo, project: new_project, target: issue, author: user, user: user)
end
end end
describe 'POST /todos/:id/mark_as_done' do describe 'POST /todos/:id/mark_as_done' do
......
...@@ -1156,6 +1156,73 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -1156,6 +1156,73 @@ RSpec.describe Ci::CreatePipelineService do
end end
end end
context 'when pipeline is running for a nonexistant-branch' do
let(:gitlab_ci_yaml) { YAML.dump(test: { script: 'test' }) }
let(:ref_name) { 'refs/heads/nonexistant-branch' }
let(:pipeline) { execute_service }
it 'does not create the pipeline' do
expect(pipeline).not_to be_created_successfully
expect(pipeline.errors[:base]).to eq(['Reference not found'])
end
context 'when there is a tag with that nonexistant-branch' do
# v1.0.0 is on the test repo as a tag
let(:ref_name) { 'refs/heads/v1.0.0' }
it 'does not create the pipeline' do
expect(pipeline).not_to be_created_successfully
expect(pipeline.errors[:base]).to eq(['Reference not found'])
end
end
end
context 'when pipeline is running for a branch with the name of both a branch and a tag' do
let(:gitlab_ci_yaml) { YAML.dump(test: { script: 'test' }) }
# v1.1.0 is on the test repo as branch and tag
let(:ref_name) { 'refs/heads/v1.1.0' }
let(:pipeline) { execute_service }
it 'creates the pipeline for the branch' do
expect(pipeline).to be_created_successfully
expect(pipeline.branch?).to be true
expect(pipeline.tag?).to be false
end
end
context 'when pipeline is running for a tag with the name of both a branch and a tag' do
let(:gitlab_ci_yaml) { YAML.dump(test: { script: 'test' }) }
# v1.1.0 is on the test repo as branch and tag
let(:ref_name) { 'refs/tags/v1.1.0' }
let(:pipeline) { execute_service }
it 'creates the pipeline for the tag' do
expect(pipeline).to be_created_successfully
expect(pipeline.branch?).to be false
expect(pipeline.tag?).to be true
end
end
context 'when pipeline is running for an ambiguous ref' do
let(:gitlab_ci_yaml) { YAML.dump(test: { script: 'test' }) }
# v1.1.0 is on the test repo as branch and tag
let(:ref_name) { 'v1.1.0' }
let(:pipeline) { execute_service }
it 'does not create the pipeline' do
expect(pipeline).not_to be_created_successfully
expect(pipeline.errors[:base]).to eq(['Ref is ambiguous'])
end
end
context 'when pipeline variables are specified' do context 'when pipeline variables are specified' do
let(:variables_attributes) do let(:variables_attributes) do
[{ key: 'first', secret_value: 'world' }, [{ key: 'first', secret_value: 'world' },
......
...@@ -109,9 +109,13 @@ RSpec.describe Git::ProcessRefChangesService do ...@@ -109,9 +109,13 @@ RSpec.describe Git::ProcessRefChangesService do
.to receive(:commit) .to receive(:commit)
.and_return(project.commit) .and_return(project.commit)
allow_any_instance_of(Repository) if changes_method == :branch_changes
.to receive(:branch_exists?) allow_any_instance_of(Repository).to receive(:branch_exists?) { true }
.and_return(true) end
if changes_method == :tag_changes
allow_any_instance_of(Repository).to receive(:tag_exists?) { true }
end
end end
context 'when git_push_create_all_pipelines is disabled' do context 'when git_push_create_all_pipelines is disabled' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Todos::AllowedTargetFilterService do
include DesignManagementTestHelpers
let_it_be(:authorized_group) { create(:group, :private) }
let_it_be(:authorized_project) { create(:project, group: authorized_group) }
let_it_be(:unauthorized_group) { create(:group, :private) }
let_it_be(:unauthorized_project) { create(:project, group: unauthorized_group) }
let_it_be(:user) { create(:user) }
let_it_be(:authorized_issue) { create(:issue, project: authorized_project) }
let_it_be(:authorized_issue_todo) { create(:todo, project: authorized_project, target: authorized_issue, user: user) }
let_it_be(:unauthorized_issue) { create(:issue, project: unauthorized_project) }
let_it_be(:unauthorized_issue_todo) { create(:todo, project: unauthorized_project, target: unauthorized_issue, user: user) }
let_it_be(:authorized_design) { create(:design, issue: authorized_issue) }
let_it_be(:authorized_design_todo) { create(:todo, project: authorized_project, target: authorized_design, user: user) }
let_it_be(:unauthorized_design) { create(:design, issue: unauthorized_issue) }
let_it_be(:unauthorized_design_todo) { create(:todo, project: unauthorized_project, target: unauthorized_design, user: user) }
# Cannot use let_it_be with MRs
let(:authorized_mr) { create(:merge_request, source_project: authorized_project) }
let(:authorized_mr_todo) { create(:todo, project: authorized_project, user: user, target: authorized_mr) }
let(:unauthorized_mr) { create(:merge_request, source_project: unauthorized_project) }
let(:unauthorized_mr_todo) { create(:todo, project: unauthorized_project, user: user, target: unauthorized_mr) }
before_all do
authorized_group.add_developer(user)
end
describe '#execute' do
subject(:execute_service) { described_class.new(all_todos, user).execute }
let!(:all_todos) { authorized_todos + unauthorized_todos }
let(:authorized_todos) do
[
authorized_mr_todo,
authorized_issue_todo,
authorized_design_todo
]
end
let(:unauthorized_todos) do
[
unauthorized_mr_todo,
unauthorized_issue_todo,
unauthorized_design_todo
]
end
before do
enable_design_management
end
it { is_expected.to match_array(authorized_todos) }
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment