Commit 1b62b86f authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into artifacts-expire-date

parents 60e0137c 0c0ef7df
...@@ -92,9 +92,7 @@ update-knapsack: ...@@ -92,9 +92,7 @@ update-knapsack:
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH} - cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun" - knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
# retry failed tests 3 times
- retry '[ ! -e tmp/spinach-rerun.txt ] || bin/spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts: artifacts:
paths: paths:
- knapsack/ - knapsack/
......
...@@ -349,7 +349,7 @@ Style/MultilineArrayBraceLayout: ...@@ -349,7 +349,7 @@ Style/MultilineArrayBraceLayout:
# Avoid multi-line chains of blocks. # Avoid multi-line chains of blocks.
Style/MultilineBlockChain: Style/MultilineBlockChain:
Enabled: false Enabled: true
# Ensures newlines after multiline block do statements. # Ensures newlines after multiline block do statements.
Style/MultilineBlockLayout: Style/MultilineBlockLayout:
......
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased) v 8.9.0 (unreleased)
- Fix Error 500 when using closes_issues API with an external issue tracker - Fix Error 500 when using closes_issues API with an external issue tracker
- Add more information into RSS feed for issues (Alexander Matyushentsev)
- Bulk assign/unassign labels to issues. - Bulk assign/unassign labels to issues.
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters) - Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
- Fix endless redirections when accessing user OAuth applications when they are disabled - Fix endless redirections when accessing user OAuth applications when they are disabled
...@@ -38,6 +39,8 @@ v 8.9.0 (unreleased) ...@@ -38,6 +39,8 @@ v 8.9.0 (unreleased)
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos) - Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Fix issues filter when ordering by milestone - Fix issues filter when ordering by milestone
- Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3 - Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
- Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
- TeamCity Service: Fix URL handling when base URL contains a path
- Todos will display target state if issuable target is 'Closed' or 'Merged' - Todos will display target state if issuable target is 'Closed' or 'Merged'
- Fix bug when sorting issues by milestone due date and filtering by two or more labels - Fix bug when sorting issues by milestone due date and filtering by two or more labels
- Add support for using Yubikeys (U2F) for two-factor authentication - Add support for using Yubikeys (U2F) for two-factor authentication
...@@ -72,6 +75,8 @@ v 8.9.0 (unreleased) ...@@ -72,6 +75,8 @@ v 8.9.0 (unreleased)
- Cache on the database if a project has an active external issue tracker. - Cache on the database if a project has an active external issue tracker.
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
- All classes in the Banzai::ReferenceParser namespace are now instrumented - All classes in the Banzai::ReferenceParser namespace are now instrumented
- Remove deprecated issues_tracker and issues_tracker_id from project model
- Allow users to create confidential issues in private projects
v 8.8.5 (unreleased) v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds - Ensure branch cleanup regardless of whether the GitHub import process succeeds
......
...@@ -248,7 +248,7 @@ end ...@@ -248,7 +248,7 @@ end
group :development do group :development do
gem "foreman" gem "foreman"
gem 'brakeman', '~> 3.2.0', require: false gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
......
...@@ -97,16 +97,7 @@ GEM ...@@ -97,16 +97,7 @@ GEM
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
brakeman (3.2.1) brakeman (3.3.2)
erubis (~> 2.6)
haml (>= 3.0, < 5.0)
highline (>= 1.6.20, < 2.0)
ruby2ruby (~> 2.3.0)
ruby_parser (~> 3.8.1)
safe_yaml (>= 1.0)
sass (~> 3.0)
slim (>= 1.3.6, < 4.0)
terminal-table (~> 1.4)
browser (2.0.3) browser (2.0.3)
builder (3.2.2) builder (3.2.2)
bullet (5.0.0) bullet (5.0.0)
...@@ -340,7 +331,6 @@ GEM ...@@ -340,7 +331,6 @@ GEM
hashie (3.4.3) hashie (3.4.3)
health_check (1.5.1) health_check (1.5.1)
rails (>= 2.3.0) rails (>= 2.3.0)
highline (1.7.8)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
...@@ -645,10 +635,7 @@ GEM ...@@ -645,10 +635,7 @@ GEM
ruby-saml (1.1.2) ruby-saml (1.1.2)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3) uuid (~> 2.3)
ruby2ruby (2.3.0) ruby_parser (3.8.2)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.8.1)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
...@@ -658,7 +645,7 @@ GEM ...@@ -658,7 +645,7 @@ GEM
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
sass (3.4.21) sass (3.4.22)
sass-rails (5.0.4) sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
sass (~> 3.1) sass (~> 3.1)
...@@ -707,9 +694,6 @@ GEM ...@@ -707,9 +694,6 @@ GEM
tilt (>= 1.3, < 3) tilt (>= 1.3, < 3)
six (0.2.0) six (0.2.0)
slack-notifier (1.2.1) slack-notifier (1.2.1)
slim (3.0.6)
temple (~> 0.7.3)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0) slop (3.6.0)
spinach (0.8.10) spinach (0.8.10)
colorize colorize
...@@ -750,10 +734,8 @@ GEM ...@@ -750,10 +734,8 @@ GEM
railties (>= 3.2.5, < 6) railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0) teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0) teaspoon (>= 1.0.0)
temple (0.7.6)
term-ansicolor (1.3.2) term-ansicolor (1.3.2)
tins (~> 1.0) tins (~> 1.0)
terminal-table (1.5.2)
test_after_commit (0.4.2) test_after_commit (0.4.2)
activerecord (>= 3.2) activerecord (>= 3.2)
thin (1.6.4) thin (1.6.4)
...@@ -762,7 +744,7 @@ GEM ...@@ -762,7 +744,7 @@ GEM
rack (~> 1.0) rack (~> 1.0)
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.5)
tilt (2.0.2) tilt (2.0.5)
timecop (0.8.1) timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
tinder (1.10.1) tinder (1.10.1)
...@@ -851,7 +833,7 @@ DEPENDENCIES ...@@ -851,7 +833,7 @@ DEPENDENCIES
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
brakeman (~> 3.2.0) brakeman (~> 3.3.0)
browser (~> 2.0.3) browser (~> 2.0.3)
bullet bullet
bundler-audit bundler-audit
......
...@@ -42,7 +42,7 @@ class JwtController < ApplicationController ...@@ -42,7 +42,7 @@ class JwtController < ApplicationController
end end
def authenticate_user(login, password) def authenticate_user(login, password)
user = Gitlab::Auth.find_in_gitlab_or_ldap(login, password) user = Gitlab::Auth.find_with_user_password(login, password)
Gitlab::Auth.rate_limit!(request.ip, success: user.present?, login: login) Gitlab::Auth.rate_limit!(request.ip, success: user.present?, login: login)
user user
end end
......
...@@ -43,7 +43,7 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -43,7 +43,7 @@ class Projects::GitHttpController < Projects::ApplicationController
return if project && project.public? && upload_pack? return if project && project.public? && upload_pack?
authenticate_or_request_with_http_basic do |login, password| authenticate_or_request_with_http_basic do |login, password|
auth_result = Gitlab::Auth.find(login, password, project: project, ip: request.ip) auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && upload_pack? if auth_result.type == :ci && upload_pack?
@ci = true @ci = true
......
...@@ -51,7 +51,7 @@ class SnippetsFinder ...@@ -51,7 +51,7 @@ class SnippetsFinder
snippets = project.snippets.fresh snippets = project.snippets.fresh
if current_user if current_user
if project.team.member?(current_user.id) || current_user.admin? if project.team.member?(current_user) || current_user.admin?
snippets snippets
else else
snippets.public_and_internal snippets.public_and_internal
......
...@@ -533,7 +533,7 @@ class Ability ...@@ -533,7 +533,7 @@ class Ability
def filter_confidential_issues_abilities(user, issue, rules) def filter_confidential_issues_abilities(user, issue, rules)
return rules if user.admin? || !issue.confidential? return rules if user.admin? || !issue.confidential?
unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id) unless issue.author == user || issue.assignee == user || issue.project.team.member?(user, Gitlab::Access::REPORTER)
rules.delete(:admin_issue) rules.delete(:admin_issue)
rules.delete(:read_issue) rules.delete(:read_issue)
rules.delete(:update_issue) rules.delete(:update_issue)
......
...@@ -51,10 +51,18 @@ class Issue < ActiveRecord::Base ...@@ -51,10 +51,18 @@ class Issue < ActiveRecord::Base
end end
def self.visible_to_user(user) def self.visible_to_user(user)
return where(confidential: false) if user.blank? return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
return all if user.admin? return all if user.admin?
where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id)) where('
issues.confidential IS NULL
OR issues.confidential IS FALSE
OR (issues.confidential = TRUE
AND (issues.author_id = :user_id
OR issues.assignee_id = :user_id
OR issues.project_id IN(:project_ids)))',
user_id: user.id,
project_ids: user.authorized_projects(Gitlab::Access::REPORTER).select(:id))
end end
def self.reference_prefix def self.reference_prefix
......
...@@ -88,22 +88,9 @@ class Note < ActiveRecord::Base ...@@ -88,22 +88,9 @@ class Note < ActiveRecord::Base
table = arel_table table = arel_table
pattern = "%#{query}%" pattern = "%#{query}%"
found_notes = joins('LEFT JOIN issues ON issues.id = noteable_id'). Note.joins('LEFT JOIN issues ON issues.id = noteable_id').
where(table[:note].matches(pattern)) where(table[:note].matches(pattern)).
merge(Issue.visible_to_user(as_user))
if as_user
found_notes.where('
issues.confidential IS NULL
OR issues.confidential IS FALSE
OR (issues.confidential IS TRUE
AND (issues.author_id = :user_id
OR issues.assignee_id = :user_id
OR issues.project_id IN(:project_ids)))',
user_id: as_user.id,
project_ids: as_user.authorized_projects.select(:id))
else
found_notes.where('issues.confidential IS NULL OR issues.confidential IS FALSE')
end
end end
end end
......
...@@ -146,7 +146,6 @@ class Project < ActiveRecord::Base ...@@ -146,7 +146,6 @@ class Project < ActiveRecord::Base
message: Gitlab::Regex.project_path_regex_message } message: Gitlab::Regex.project_path_regex_message }
validates :issues_enabled, :merge_requests_enabled, validates :issues_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] } :wiki_enabled, inclusion: { in: [true, false] }
validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
validates :namespace, presence: true validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id
...@@ -589,10 +588,6 @@ class Project < ActiveRecord::Base ...@@ -589,10 +588,6 @@ class Project < ActiveRecord::Base
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end end
def can_have_issues_tracker_id?
self.issues_enabled && !self.default_issues_tracker?
end
def build_missing_services def build_missing_services
services_templates = Service.where(template: true) services_templates = Service.where(template: true)
......
class BambooService < CiService class BambooService < CiService
include HTTParty
prop_accessor :bamboo_url, :build_key, :username, :password prop_accessor :bamboo_url, :build_key, :username, :password
validates :bamboo_url, presence: true, url: true, if: :activated? validates :bamboo_url, presence: true, url: true, if: :activated?
...@@ -61,18 +59,7 @@ class BambooService < CiService ...@@ -61,18 +59,7 @@ class BambooService < CiService
end end
def build_info(sha) def build_info(sha)
url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s @response = get_path("rest/api/latest/result?label=#{sha}")
if username.blank? && password.blank?
@response = HTTParty.get(url, verify: false)
else
url << '&os_authType=basic'
auth = {
username: username,
password: password
}
@response = HTTParty.get(url, verify: false, basic_auth: auth)
end
end end
def build_page(sha, ref) def build_page(sha, ref)
...@@ -80,11 +67,11 @@ class BambooService < CiService ...@@ -80,11 +67,11 @@ class BambooService < CiService
if @response.code != 200 || @response['results']['results']['size'] == '0' if @response.code != 200 || @response['results']['results']['size'] == '0'
# If actual build link can't be determined, send user to build summary page. # If actual build link can't be determined, send user to build summary page.
URI.join(bamboo_url, "/browse/#{build_key}").to_s URI.join("#{bamboo_url}/", "browse/#{build_key}").to_s
else else
# If actual build link is available, go to build result page. # If actual build link is available, go to build result page.
result_key = @response['results']['results']['result']['planResultKey']['key'] result_key = @response['results']['results']['result']['planResultKey']['key']
URI.join(bamboo_url, "/browse/#{result_key}").to_s URI.join("#{bamboo_url}/", "browse/#{result_key}").to_s
end end
end end
...@@ -112,8 +99,27 @@ class BambooService < CiService ...@@ -112,8 +99,27 @@ class BambooService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
# Bamboo requires a GET and does not take any data. get_path("updateAndBuild.action?buildKey=#{build_key}")
url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s end
self.class.get(url, verify: false)
private
def build_url(path)
URI.join("#{bamboo_url}/", path).to_s
end
def get_path(path)
url = build_url(path)
if username.blank? && password.blank?
HTTParty.get(url, verify: false)
else
url << '&os_authType=basic'
HTTParty.get(url, verify: false,
basic_auth: {
username: username,
password: password
})
end
end end
end end
...@@ -38,9 +38,9 @@ class IssueTrackerService < Service ...@@ -38,9 +38,9 @@ class IssueTrackerService < Service
if enabled_in_gitlab_config if enabled_in_gitlab_config
self.properties = { self.properties = {
title: issues_tracker['title'], title: issues_tracker['title'],
project_url: add_issues_tracker_id(issues_tracker['project_url']), project_url: issues_tracker['project_url'],
issues_url: add_issues_tracker_id(issues_tracker['issues_url']), issues_url: issues_tracker['issues_url'],
new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url']) new_issue_url: issues_tracker['new_issue_url']
} }
else else
self.properties = {} self.properties = {}
...@@ -83,16 +83,4 @@ class IssueTrackerService < Service ...@@ -83,16 +83,4 @@ class IssueTrackerService < Service
def issues_tracker def issues_tracker
Gitlab.config.issues_tracker[to_param] Gitlab.config.issues_tracker[to_param]
end end
def add_issues_tracker_id(url)
if self.project
id = self.project.issues_tracker_id
if id
url = url.gsub(":issues_tracker_id", id)
end
end
url
end
end end
class TeamcityService < CiService class TeamcityService < CiService
include HTTParty
prop_accessor :teamcity_url, :build_type, :username, :password prop_accessor :teamcity_url, :build_type, :username, :password
validates :teamcity_url, presence: true, url: true, if: :activated? validates :teamcity_url, presence: true, url: true, if: :activated?
...@@ -64,15 +62,7 @@ class TeamcityService < CiService ...@@ -64,15 +62,7 @@ class TeamcityService < CiService
end end
def build_info(sha) def build_info(sha)
url = URI.join( @response = get_path("httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}")
teamcity_url,
"/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
).to_s
auth = {
username: username,
password: password
}
@response = HTTParty.get(url, verify: false, basic_auth: auth)
end end
def build_page(sha, ref) def build_page(sha, ref)
...@@ -81,14 +71,11 @@ class TeamcityService < CiService ...@@ -81,14 +71,11 @@ class TeamcityService < CiService
if @response.code != 200 if @response.code != 200
# If actual build link can't be determined, # If actual build link can't be determined,
# send user to build summary page. # send user to build summary page.
URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s build_url("viewLog.html?buildTypeId=#{build_type}")
else else
# If actual build link is available, go to build result page. # If actual build link is available, go to build result page.
built_id = @response['build']['id'] built_id = @response['build']['id']
URI.join( build_url("viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}")
teamcity_url,
"/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
).to_s
end end
end end
...@@ -123,8 +110,8 @@ class TeamcityService < CiService ...@@ -123,8 +110,8 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref]) branch = Gitlab::Git.ref_name(data[:ref])
self.class.post( HTTParty.post(
URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s, build_url('httpAuth/app/rest/buildQueue'),
body: "<build branchName=\"#{branch}\">"\ body: "<build branchName=\"#{branch}\">"\
"<buildType id=\"#{build_type}\"/>"\ "<buildType id=\"#{build_type}\"/>"\
'</build>', '</build>',
...@@ -132,4 +119,18 @@ class TeamcityService < CiService ...@@ -132,4 +119,18 @@ class TeamcityService < CiService
basic_auth: auth basic_auth: auth
) )
end end
private
def build_url(path)
URI.join("#{teamcity_url}/", path).to_s
end
def get_path(path)
HTTParty.get(build_url(path), verify: false,
basic_auth: {
username: username,
password: password
})
end
end end
...@@ -131,8 +131,14 @@ class ProjectTeam ...@@ -131,8 +131,14 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER max_member_access(user.id) == Gitlab::Access::MASTER
end end
def member?(user_id) def member?(user, min_member_access = nil)
!!find_member(user_id) member = !!find_member(user.id)
if min_member_access
member && max_member_access(user.id) >= min_member_access
else
member
end
end end
def human_max_access(user_id) def human_max_access(user_id)
......
...@@ -405,8 +405,8 @@ class User < ActiveRecord::Base ...@@ -405,8 +405,8 @@ class User < ActiveRecord::Base
end end
# Returns projects user is authorized to access. # Returns projects user is authorized to access.
def authorized_projects def authorized_projects(min_access_level = nil)
Project.where("projects.id IN (#{projects_union.to_sql})") Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
end end
def viewable_starred_projects def viewable_starred_projects
...@@ -824,11 +824,19 @@ class User < ActiveRecord::Base ...@@ -824,11 +824,19 @@ class User < ActiveRecord::Base
private private
def projects_union def projects_union(min_access_level = nil)
Gitlab::SQL::Union.new([personal_projects.select(:id), relations = [personal_projects.select(:id),
groups_projects.select(:id), groups_projects.select(:id),
projects.select(:id), projects.select(:id),
groups.joins(:shared_projects).select(:project_id)]) groups.joins(:shared_projects).select(:project_id)]
if min_access_level
scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) }
end
Gitlab::SQL::Union.new(relations)
end end
def ci_projects_union def ci_projects_union
......
...@@ -5,10 +5,28 @@ xml.entry do ...@@ -5,10 +5,28 @@ xml.entry do
xml.updated issue.created_at.xmlschema xml.updated issue.created_at.xmlschema
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
xml.author do |author| xml.author do
xml.name issue.author_name xml.name issue.author_name
xml.email issue.author_email xml.email issue.author_email
end end
xml.summary issue.title xml.summary issue.title
xml.description issue.description if issue.description
xml.milestone issue.milestone.title if issue.milestone
xml.due_date issue.due_date if issue.due_date
unless issue.labels.empty?
xml.labels do
issue.labels.each do |label|
xml.label label.name
end
end
end
if issue.assignee
xml.assignee do
xml.name issue.assignee.name
xml.email issue.assignee.email
end
end
end end
...@@ -5,11 +5,9 @@ ...@@ -5,11 +5,9 @@
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
%span %span
Pipelines Pipelines
%span.badge.count.ci_counter= number_with_delimiter(@project.pipelines.running_or_pending.count)
- if project_nav_tab? :builds - if project_nav_tab? :builds
= nav_link(controller: %w(builds)) do = nav_link(controller: %w(builds)) do
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
%span %span
Builds Builds
%span.badge.count.builds_counter= number_with_delimiter(@project.running_or_pending_build_count)
...@@ -35,13 +35,13 @@ ...@@ -35,13 +35,13 @@
.clearfix .clearfix
.error-alert .error-alert
- if issuable.is_a?(Issue) && !issuable.project.private? - if issuable.is_a?(Issue)
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
= f.label :confidential do = f.label :confidential do
= f.check_box :confidential = f.check_box :confidential
This issue is confidential and should only be visible to team members This issue is confidential and should only be visible to team members with at least Reporter access.
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date) - has_due_date = issuable.has_attribute?(:due_date)
......
...@@ -12,7 +12,7 @@ Doorkeeper.configure do ...@@ -12,7 +12,7 @@ Doorkeeper.configure do
end end
resource_owner_from_credentials do |routes| resource_owner_from_credentials do |routes|
Gitlab::Auth.find_in_gitlab_or_ldap(params[:username], params[:password]) Gitlab::Auth.find_with_user_password(params[:username], params[:password])
end end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below. # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
......
class RemoveDeprecatedIssuesTrackerColumnsFromProjects < ActiveRecord::Migration
def change
remove_column :projects, :issues_tracker, :string, default: 'gitlab', null: false
remove_column :projects, :issues_tracker_id, :string
end
end
...@@ -752,8 +752,6 @@ ActiveRecord::Schema.define(version: 20160610301627) do ...@@ -752,8 +752,6 @@ ActiveRecord::Schema.define(version: 20160610301627) do
t.boolean "merge_requests_enabled", default: true, null: false t.boolean "merge_requests_enabled", default: true, null: false
t.boolean "wiki_enabled", default: true, null: false t.boolean "wiki_enabled", default: true, null: false
t.integer "namespace_id" t.integer "namespace_id"
t.string "issues_tracker", default: "gitlab", null: false
t.string "issues_tracker_id"
t.boolean "snippets_enabled", default: true, null: false t.boolean "snippets_enabled", default: true, null: false
t.datetime "last_activity_at" t.datetime "last_activity_at"
t.string "import_url" t.string "import_url"
......
...@@ -11,7 +11,7 @@ module API ...@@ -11,7 +11,7 @@ module API
# Example Request: # Example Request:
# POST /session # POST /session
post "/session" do post "/session" do
user = Gitlab::Auth.find_in_gitlab_or_ldap(params[:email] || params[:login], params[:password]) user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password])
return unauthorized! unless user return unauthorized! unless user
present user, with: Entities::UserLogin present user, with: Entities::UserLogin
......
...@@ -3,14 +3,14 @@ module Gitlab ...@@ -3,14 +3,14 @@ module Gitlab
Result = Struct.new(:user, :type) Result = Struct.new(:user, :type)
class << self class << self
def find(login, password, project:, ip:) def find_for_git_client(login, password, project:, ip:)
raise "Must provide an IP for rate limiting" if ip.nil? raise "Must provide an IP for rate limiting" if ip.nil?
result = Result.new result = Result.new
if valid_ci_request?(login, password, project) if valid_ci_request?(login, password, project)
result.type = :ci result.type = :ci
elsif result.user = find_in_gitlab_or_ldap(login, password) elsif result.user = find_with_user_password(login, password)
result.type = :gitlab_or_ldap result.type = :gitlab_or_ldap
elsif result.user = oauth_access_token_check(login, password) elsif result.user = oauth_access_token_check(login, password)
result.type = :oauth result.type = :oauth
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
result result
end end
def find_in_gitlab_or_ldap(login, password) def find_with_user_password(login, password)
user = User.by_login(login) user = User.by_login(login)
# If no user is found, or it's an LDAP server, try LDAP. # If no user is found, or it's an LDAP server, try LDAP.
......
...@@ -95,7 +95,7 @@ module Grack ...@@ -95,7 +95,7 @@ module Grack
end end
def authenticate_user(login, password) def authenticate_user(login, password)
user = Gitlab::Auth.find_in_gitlab_or_ldap(login, password) user = Gitlab::Auth.find_with_user_password(login, password)
unless user unless user
user = oauth_access_token_check(login, password) user = oauth_access_token_check(login, password)
......
...@@ -105,6 +105,15 @@ describe Projects::IssuesController do ...@@ -105,6 +105,15 @@ describe Projects::IssuesController do
expect(assigns(:issues)).to eq [issue] expect(assigns(:issues)).to eq [issue]
end end
it 'should not list confidential issues for project members with guest role' do
sign_in(member)
project.team << [member, :guest]
get_issues
expect(assigns(:issues)).to eq [issue]
end
it 'should list confidential issues for author' do it 'should list confidential issues for author' do
sign_in(author) sign_in(author)
get_issues get_issues
...@@ -148,7 +157,7 @@ describe Projects::IssuesController do ...@@ -148,7 +157,7 @@ describe Projects::IssuesController do
shared_examples_for 'restricted action' do |http_status| shared_examples_for 'restricted action' do |http_status|
it 'returns 404 for guests' do it 'returns 404 for guests' do
sign_out :user sign_out(:user)
go(id: unescaped_parameter_value.to_param) go(id: unescaped_parameter_value.to_param)
expect(response).to have_http_status :not_found expect(response).to have_http_status :not_found
...@@ -161,6 +170,14 @@ describe Projects::IssuesController do ...@@ -161,6 +170,14 @@ describe Projects::IssuesController do
expect(response).to have_http_status :not_found expect(response).to have_http_status :not_found
end end
it 'returns 404 for project members with guest role' do
sign_in(member)
project.team << [member, :guest]
go(id: unescaped_parameter_value.to_param)
expect(response).to have_http_status :not_found
end
it "returns #{http_status[:success]} for author" do it "returns #{http_status[:success]} for author" do
sign_in(author) sign_in(author)
go(id: unescaped_parameter_value.to_param) go(id: unescaped_parameter_value.to_param)
......
...@@ -67,9 +67,6 @@ FactoryGirl.define do ...@@ -67,9 +67,6 @@ FactoryGirl.define do
'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new' 'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
} }
) )
project.issues_tracker = 'redmine'
project.issues_tracker_id = 'project_name_in_redmine'
end end
end end
...@@ -84,9 +81,6 @@ FactoryGirl.define do ...@@ -84,9 +81,6 @@ FactoryGirl.define do
'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa' 'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
} }
) )
project.issues_tracker = 'jira'
project.issues_tracker_id = 'project_name_in_jira'
end end
end end
end end
...@@ -5,8 +5,6 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -5,8 +5,6 @@ describe "Dashboard Issues Feed", feature: true do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:project1) { create(:project) } let!(:project1) { create(:project) }
let!(:project2) { create(:project) } let!(:project2) { create(:project) }
let!(:issue1) { create(:issue, author: user, assignee: user, project: project1) }
let!(:issue2) { create(:issue, author: user, assignee: user, project: project2) }
before do before do
project1.team << [user, :master] project1.team << [user, :master]
...@@ -14,16 +12,51 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -14,16 +12,51 @@ describe "Dashboard Issues Feed", feature: true do
end end
describe "atom feed" do describe "atom feed" do
it "should render atom feed via private token" do it "renders atom feed via private token" do
visit issues_dashboard_path(:atom, private_token: user.private_token) visit issues_dashboard_path(:atom, private_token: user.private_token)
expect(response_headers['Content-Type']). expect(response_headers['Content-Type']).to have_content('application/atom+xml')
to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues") expect(body).to have_selector('title', text: "#{user.name} issues")
expect(body).to have_selector('author email', text: issue1.author_email) end
expect(body).to have_selector('entry summary', text: issue1.title)
expect(body).to have_selector('author email', text: issue2.author_email) context "issue with basic fields" do
expect(body).to have_selector('entry summary', text: issue2.title) let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') }
it "renders issue fields" do
visit issues_dashboard_path(:atom, private_token: user.private_token)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue2.author_email)
expect(entry).to have_selector('assignee email', text: issue2.author_email)
expect(entry).not_to have_selector('labels')
expect(entry).not_to have_selector('milestone')
expect(entry).to have_selector('description', text: issue2.description)
end
end
context "issue with label and milestone" do
let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
let!(:label1) { create(:label, project: project1, title: 'label1') }
let!(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone1) }
before do
issue1.labels << label1
end
it "renders issue label and milestone info" do
visit issues_dashboard_path(:atom, private_token: user.private_token)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue1.author_email)
expect(entry).to have_selector('assignee email', text: issue1.author_email)
expect(entry).to have_selector('labels label', text: label1.title)
expect(entry).to have_selector('milestone', text: milestone1.title)
expect(entry).not_to have_selector('description')
end
end end
end end
end end
......
...@@ -7,10 +7,7 @@ describe IssuesHelper do ...@@ -7,10 +7,7 @@ describe IssuesHelper do
describe "url_for_project_issues" do describe "url_for_project_issues" do
let(:project_url) { ext_project.external_issue_tracker.project_url } let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do let(:ext_expected) { project_url.gsub(':project_id', ext_project.id.to_s) }
project_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
end
let(:int_expected) { polymorphic_path([@project.namespace, project]) } let(:int_expected) { polymorphic_path([@project.namespace, project]) }
it "should return internal path if used internal tracker" do it "should return internal path if used internal tracker" do
...@@ -56,11 +53,7 @@ describe IssuesHelper do ...@@ -56,11 +53,7 @@ describe IssuesHelper do
describe "url_for_issue" do describe "url_for_issue" do
let(:issues_url) { ext_project.external_issue_tracker.issues_url} let(:issues_url) { ext_project.external_issue_tracker.issues_url}
let(:ext_expected) do let(:ext_expected) { issues_url.gsub(':id', issue.iid.to_s).gsub(':project_id', ext_project.id.to_s) }
issues_url.gsub(':id', issue.iid.to_s)
.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
end
let(:int_expected) { polymorphic_path([@project.namespace, project, issue]) } let(:int_expected) { polymorphic_path([@project.namespace, project, issue]) }
it "should return internal path if used internal tracker" do it "should return internal path if used internal tracker" do
...@@ -106,10 +99,7 @@ describe IssuesHelper do ...@@ -106,10 +99,7 @@ describe IssuesHelper do
describe 'url_for_new_issue' do describe 'url_for_new_issue' do
let(:issues_url) { ext_project.external_issue_tracker.new_issue_url } let(:issues_url) { ext_project.external_issue_tracker.new_issue_url }
let(:ext_expected) do let(:ext_expected) { issues_url.gsub(':project_id', ext_project.id.to_s) }
issues_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
end
let(:int_expected) { new_namespace_project_issue_path(project.namespace, project) } let(:int_expected) { new_namespace_project_issue_path(project.namespace, project) }
it "should return internal path if used internal tracker" do it "should return internal path if used internal tracker" do
......
...@@ -69,6 +69,18 @@ describe Banzai::Filter::RedactorFilter, lib: true do ...@@ -69,6 +69,18 @@ describe Banzai::Filter::RedactorFilter, lib: true do
expect(doc.css('a').length).to eq 0 expect(doc.css('a').length).to eq 0
end end
it 'removes references for project members with guest role' do
member = create(:user)
project = create(:empty_project, :public)
project.team << [member, :guest]
issue = create(:issue, :confidential, project: project)
link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
doc = filter(link, current_user: member)
expect(doc.css('a').length).to eq 0
end
it 'allows references for author' do it 'allows references for author' do
author = create(:user) author = create(:user)
project = create(:empty_project, :public) project = create(:empty_project, :public)
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Auth, lib: true do describe Gitlab::Auth, lib: true do
let(:gl_auth) { described_class } let(:gl_auth) { described_class }
describe 'find' do describe 'find_for_git_client' do
it 'recognizes CI' do it 'recognizes CI' do
token = '123' token = '123'
project = create(:empty_project) project = create(:empty_project)
...@@ -11,7 +11,7 @@ describe Gitlab::Auth, lib: true do ...@@ -11,7 +11,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip' ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token') expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token')
expect(gl_auth.find('gitlab-ci-token', token, project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, :ci)) expect(gl_auth.find_for_git_client('gitlab-ci-token', token, project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, :ci))
end end
it 'recognizes master passwords' do it 'recognizes master passwords' do
...@@ -19,7 +19,7 @@ describe Gitlab::Auth, lib: true do ...@@ -19,7 +19,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip' ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username) expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
expect(gl_auth.find(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :gitlab_or_ldap)) expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :gitlab_or_ldap))
end end
it 'recognizes OAuth tokens' do it 'recognizes OAuth tokens' do
...@@ -29,7 +29,7 @@ describe Gitlab::Auth, lib: true do ...@@ -29,7 +29,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip' ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2') expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2')
expect(gl_auth.find("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :oauth)) expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :oauth))
end end
it 'returns double nil for invalid credentials' do it 'returns double nil for invalid credentials' do
...@@ -37,11 +37,11 @@ describe Gitlab::Auth, lib: true do ...@@ -37,11 +37,11 @@ describe Gitlab::Auth, lib: true do
ip = 'ip' ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: login) expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: login)
expect(gl_auth.find(login, 'bar', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new) expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new)
end end
end end
describe 'find_in_gitlab_or_ldap' do describe 'find_with_user_password' do
let!(:user) do let!(:user) do
create(:user, create(:user,
username: username, username: username,
...@@ -52,25 +52,25 @@ describe Gitlab::Auth, lib: true do ...@@ -52,25 +52,25 @@ describe Gitlab::Auth, lib: true do
let(:password) { 'my-secret' } let(:password) { 'my-secret' }
it "should find user by valid login/password" do it "should find user by valid login/password" do
expect( gl_auth.find_in_gitlab_or_ldap(username, password) ).to eql user expect( gl_auth.find_with_user_password(username, password) ).to eql user
end end
it 'should find user by valid email/password with case-insensitive email' do it 'should find user by valid email/password with case-insensitive email' do
expect(gl_auth.find_in_gitlab_or_ldap(user.email.upcase, password)).to eql user expect(gl_auth.find_with_user_password(user.email.upcase, password)).to eql user
end end
it 'should find user by valid username/password with case-insensitive username' do it 'should find user by valid username/password with case-insensitive username' do
expect(gl_auth.find_in_gitlab_or_ldap(username.upcase, password)).to eql user expect(gl_auth.find_with_user_password(username.upcase, password)).to eql user
end end
it "should not find user with invalid password" do it "should not find user with invalid password" do
password = 'wrong' password = 'wrong'
expect( gl_auth.find_in_gitlab_or_ldap(username, password) ).not_to eql user expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
end end
it "should not find user with invalid login" do it "should not find user with invalid login" do
user = 'wrong' user = 'wrong'
expect( gl_auth.find_in_gitlab_or_ldap(username, password) ).not_to eql user expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
end end
context "with ldap enabled" do context "with ldap enabled" do
...@@ -81,13 +81,13 @@ describe Gitlab::Auth, lib: true do ...@@ -81,13 +81,13 @@ describe Gitlab::Auth, lib: true do
it "tries to autheticate with db before ldap" do it "tries to autheticate with db before ldap" do
expect(Gitlab::LDAP::Authentication).not_to receive(:login) expect(Gitlab::LDAP::Authentication).not_to receive(:login)
gl_auth.find_in_gitlab_or_ldap(username, password) gl_auth.find_with_user_password(username, password)
end end
it "uses ldap as fallback to for authentication" do it "uses ldap as fallback to for authentication" do
expect(Gitlab::LDAP::Authentication).to receive(:login) expect(Gitlab::LDAP::Authentication).to receive(:login)
gl_auth.find_in_gitlab_or_ldap('ldap_user', 'password') gl_auth.find_with_user_password('ldap_user', 'password')
end end
end end
end end
......
...@@ -43,6 +43,18 @@ describe Gitlab::ProjectSearchResults, lib: true do ...@@ -43,6 +43,18 @@ describe Gitlab::ProjectSearchResults, lib: true do
expect(results.issues_count).to eq 1 expect(results.issues_count).to eq 1
end end
it 'should not list project confidential issues for project members with guest role' do
project.team << [member, :guest]
results = described_class.new(member, project, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.issues_count).to eq 1
end
it 'should list project confidential issues for author' do it 'should list project confidential issues for author' do
results = described_class.new(author, project, query) results = described_class.new(author, project, query)
issues = results.objects('issues') issues = results.objects('issues')
......
...@@ -86,6 +86,22 @@ describe Gitlab::SearchResults do ...@@ -86,6 +86,22 @@ describe Gitlab::SearchResults do
expect(results.issues_count).to eq 1 expect(results.issues_count).to eq 1
end end
it 'should not list confidential issues for project members with guest role' do
project_1.team << [member, :guest]
project_2.team << [member, :guest]
results = described_class.new(member, limit_projects, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(issues).not_to include security_issue_3
expect(issues).not_to include security_issue_4
expect(issues).not_to include security_issue_5
expect(results.issues_count).to eq 1
end
it 'should list confidential issues for author' do it 'should list confidential issues for author' do
results = described_class.new(author, limit_projects, query) results = described_class.new(author, limit_projects, query)
issues = results.objects('issues') issues = results.objects('issues')
......
...@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do ...@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
...@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do ...@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do
before do before do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
end end
describe '#closed_items_count' do describe '#closed_items_count' do
...@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do ...@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.closed_items_count(non_member)).to eq 2 expect(milestone.closed_items_count(non_member)).to eq 2
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.closed_items_count(guest)).to eq 2
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.closed_items_count(author)).to eq 4 expect(milestone.closed_items_count(author)).to eq 4
end end
...@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do ...@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.total_items_count(non_member)).to eq 4 expect(milestone.total_items_count(non_member)).to eq 4
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.total_items_count(guest)).to eq 4
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.total_items_count(author)).to eq 7 expect(milestone.total_items_count(author)).to eq 7
end end
...@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do ...@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.percent_complete(non_member)).to eq 50 expect(milestone.percent_complete(non_member)).to eq 50
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.percent_complete(guest)).to eq 50
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.percent_complete(author)).to eq 57 expect(milestone.percent_complete(author)).to eq 57
end end
......
...@@ -50,6 +50,7 @@ describe Event, models: true do ...@@ -50,6 +50,7 @@ describe Event, models: true do
let(:project) { create(:empty_project, :public) } let(:project) { create(:empty_project, :public) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
...@@ -61,6 +62,7 @@ describe Event, models: true do ...@@ -61,6 +62,7 @@ describe Event, models: true do
before do before do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
end end
context 'issue event' do context 'issue event' do
...@@ -71,6 +73,7 @@ describe Event, models: true do ...@@ -71,6 +73,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
...@@ -81,6 +84,7 @@ describe Event, models: true do ...@@ -81,6 +84,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
end end
...@@ -93,6 +97,7 @@ describe Event, models: true do ...@@ -93,6 +97,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
...@@ -103,6 +108,7 @@ describe Event, models: true do ...@@ -103,6 +108,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
end end
......
...@@ -162,16 +162,23 @@ describe Note, models: true do ...@@ -162,16 +162,23 @@ describe Note, models: true do
end end
context "confidential issues" do context "confidential issues" do
let(:user) { create :user } let(:user) { create(:user) }
let(:confidential_issue) { create(:issue, :confidential, author: user) } let(:project) { create(:project) }
let(:confidential_note) { create :note, note: "Random", noteable: confidential_issue, project: confidential_issue.project } let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
it "returns notes with matching content if user can see the issue" do it "returns notes with matching content if user can see the issue" do
expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note]) expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note])
end end
it "does not return notes with matching content if user can not see the issue" do it "does not return notes with matching content if user can not see the issue" do
user = create :user user = create(:user)
expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
end
it "does not return notes with matching content for project members with guest role" do
user = create(:user)
project.team << [user, :guest]
expect(described_class.search(confidential_note.note, as_user: user)).to be_empty expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
end end
......
...@@ -126,25 +126,25 @@ describe BambooService, models: true do ...@@ -126,25 +126,25 @@ describe BambooService, models: true do
it 'returns a specific URL when status is 500' do it 'returns a specific URL when status is 500' do
stub_request(status: 500) stub_request(status: 500)
expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo') expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
end end
it 'returns a specific URL when response has no results' do it 'returns a specific URL when response has no results' do
stub_request(body: %Q({"results":{"results":{"size":"0"}}})) stub_request(body: %Q({"results":{"results":{"size":"0"}}}))
expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo') expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
end end
it 'returns a build URL when bamboo_url has no trailing slash' do it 'returns a build URL when bamboo_url has no trailing slash' do
stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}})) stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
expect(service(bamboo_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42') expect(service(bamboo_url: 'http://gitlab.com/bamboo').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
end end
it 'returns a build URL when bamboo_url has a trailing slash' do it 'returns a build URL when bamboo_url has a trailing slash' do
stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}})) stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
expect(service(bamboo_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42') expect(service(bamboo_url: 'http://gitlab.com/bamboo/').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
end end
end end
...@@ -192,7 +192,7 @@ describe BambooService, models: true do ...@@ -192,7 +192,7 @@ describe BambooService, models: true do
end end
end end
def service(bamboo_url: 'http://gitlab.com') def service(bamboo_url: 'http://gitlab.com/bamboo')
described_class.create( described_class.create(
project: create(:empty_project), project: create(:empty_project),
properties: { properties: {
...@@ -205,7 +205,7 @@ describe BambooService, models: true do ...@@ -205,7 +205,7 @@ describe BambooService, models: true do
end end
def stub_request(status: 200, body: nil, build_state: 'success') def stub_request(status: 200, body: nil, build_state: 'success')
bamboo_full_url = 'http://mic:password@gitlab.com/rest/api/latest/result?label=123&os_authType=basic' bamboo_full_url = 'http://mic:password@gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic'
body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}}) body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}})
WebMock.stub_request(:get, bamboo_full_url).to_return( WebMock.stub_request(:get, bamboo_full_url).to_return(
......
...@@ -126,19 +126,19 @@ describe TeamcityService, models: true do ...@@ -126,19 +126,19 @@ describe TeamcityService, models: true do
it 'returns a specific URL when status is 500' do it 'returns a specific URL when status is 500' do
stub_request(status: 500) stub_request(status: 500)
expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo') expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildTypeId=foo')
end end
it 'returns a build URL when teamcity_url has no trailing slash' do it 'returns a build URL when teamcity_url has no trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}})) stub_request(body: %Q({"build":{"id":"666"}}))
expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo') expect(service(teamcity_url: 'http://gitlab.com/teamcity').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
end end
it 'returns a build URL when teamcity_url has a trailing slash' do it 'returns a build URL when teamcity_url has a trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}})) stub_request(body: %Q({"build":{"id":"666"}}))
expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo') expect(service(teamcity_url: 'http://gitlab.com/teamcity/').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
end end
end end
...@@ -180,7 +180,7 @@ describe TeamcityService, models: true do ...@@ -180,7 +180,7 @@ describe TeamcityService, models: true do
end end
end end
def service(teamcity_url: 'http://gitlab.com') def service(teamcity_url: 'http://gitlab.com/teamcity')
described_class.create( described_class.create(
project: create(:empty_project), project: create(:empty_project),
properties: { properties: {
...@@ -193,7 +193,7 @@ describe TeamcityService, models: true do ...@@ -193,7 +193,7 @@ describe TeamcityService, models: true do
end end
def stub_request(status: 200, body: nil, build_status: 'success') def stub_request(status: 200, body: nil, build_status: 'success')
teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123' teamcity_full_url = 'http://mic:password@gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
body ||= %Q({"build":{"status":"#{build_status}","id":"666"}}) body ||= %Q({"build":{"status":"#{build_status}","id":"666"}})
WebMock.stub_request(:get, teamcity_full_url).to_return( WebMock.stub_request(:get, teamcity_full_url).to_return(
......
...@@ -53,7 +53,6 @@ describe Project, models: true do ...@@ -53,7 +53,6 @@ describe Project, models: true do
it { is_expected.to validate_length_of(:path).is_within(0..255) } it { is_expected.to validate_length_of(:path).is_within(0..255) }
it { is_expected.to validate_length_of(:description).is_within(0..2000) } it { is_expected.to validate_length_of(:description).is_within(0..2000) }
it { is_expected.to validate_presence_of(:creator) } it { is_expected.to validate_presence_of(:creator) }
it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
it { is_expected.to validate_presence_of(:namespace) } it { is_expected.to validate_presence_of(:namespace) }
it 'should not allow new projects beyond user limits' do it 'should not allow new projects beyond user limits' do
...@@ -321,27 +320,6 @@ describe Project, models: true do ...@@ -321,27 +320,6 @@ describe Project, models: true do
end end
end end
describe :can_have_issues_tracker_id? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
it 'should be true for projects with external issues tracker if issues enabled' do
expect(ext_project.can_have_issues_tracker_id?).to be_truthy
end
it 'should be false for projects with internal issue tracker if issues enabled' do
expect(project.can_have_issues_tracker_id?).to be_falsey
end
it 'should be always false if issues disabled' do
project.issues_enabled = false
ext_project.issues_enabled = false
expect(project.can_have_issues_tracker_id?).to be_falsey
expect(ext_project.can_have_issues_tracker_id?).to be_falsey
end
end
describe :open_branches do describe :open_branches do
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do ...@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey } it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey } it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy } it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
it { expect(project.team.member?(guest, Gitlab::Access::REPORTER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end end
end end
...@@ -64,6 +67,9 @@ describe ProjectTeam, models: true do ...@@ -64,6 +67,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey } it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey } it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy } it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end end
end end
......
...@@ -5,6 +5,7 @@ describe API::API, api: true do ...@@ -5,6 +5,7 @@ describe API::API, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:guest) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
...@@ -41,7 +42,10 @@ describe API::API, api: true do ...@@ -41,7 +42,10 @@ describe API::API, api: true do
end end
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) } let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
before { project.team << [user, :reporter] } before do
project.team << [user, :reporter]
project.team << [guest, :guest]
end
describe "GET /issues" do describe "GET /issues" do
context "when unauthenticated" do context "when unauthenticated" do
...@@ -144,6 +148,14 @@ describe API::API, api: true do ...@@ -144,6 +148,14 @@ describe API::API, api: true do
expect(json_response.first['title']).to eq(issue.title) expect(json_response.first['title']).to eq(issue.title)
end end
it 'should return project issues without confidential issues for project members with guest role' do
get api("#{base_url}/issues", guest)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response.first['title']).to eq(issue.title)
end
it 'should return project confidential issues for author' do it 'should return project confidential issues for author' do
get api("#{base_url}/issues", author) get api("#{base_url}/issues", author)
expect(response.status).to eq(200) expect(response.status).to eq(200)
...@@ -278,6 +290,11 @@ describe API::API, api: true do ...@@ -278,6 +290,11 @@ describe API::API, api: true do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
it "should return 404 for project members with guest role" do
get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest)
expect(response.status).to eq(404)
end
it "should return confidential issue for project members" do it "should return confidential issue for project members" do
get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user) get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
...@@ -413,6 +430,12 @@ describe API::API, api: true do ...@@ -413,6 +430,12 @@ describe API::API, api: true do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it "should return 403 for project members with guest role" do
put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest),
title: 'updated title'
expect(response.status).to eq(403)
end
it "should update a confidential issue for project members" do it "should update a confidential issue for project members" do
put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user), put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
title: 'updated title' title: 'updated title'
......
...@@ -146,6 +146,7 @@ describe API::API, api: true do ...@@ -146,6 +146,7 @@ describe API::API, api: true do
let(:milestone) { create(:milestone, project: public_project) } let(:milestone) { create(:milestone, project: public_project) }
let(:issue) { create(:issue, project: public_project) } let(:issue) { create(:issue, project: public_project) }
let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
before do before do
public_project.team << [user, :developer] public_project.team << [user, :developer]
milestone.issues << issue << confidential_issue milestone.issues << issue << confidential_issue
...@@ -160,6 +161,18 @@ describe API::API, api: true do ...@@ -160,6 +161,18 @@ describe API::API, api: true do
expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
end end
it 'does not return confidential issues to team members with guest role' do
member = create(:user)
project.team << [member, :guest]
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
end
it 'does not return confidential issues to regular users' do it 'does not return confidential issues to regular users' do
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
......
...@@ -44,7 +44,7 @@ describe JwtController do ...@@ -44,7 +44,7 @@ describe JwtController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:headers) { { authorization: credentials('user', 'password') } } let(:headers) { { authorization: credentials('user', 'password') } }
before { expect(Gitlab::Auth).to receive(:find_in_gitlab_or_ldap).with('user', 'password').and_return(user) } before { expect(Gitlab::Auth).to receive(:find_with_user_password).with('user', 'password').and_return(user) }
subject! { get '/jwt/auth', parameters, headers } subject! { get '/jwt/auth', parameters, headers }
......
...@@ -132,12 +132,14 @@ describe NotificationService, services: true do ...@@ -132,12 +132,14 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") } let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") }
it 'filters out users that can not read the issue' do it 'filters out users that can not read the issue' do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
...@@ -146,6 +148,7 @@ describe NotificationService, services: true do ...@@ -146,6 +148,7 @@ describe NotificationService, services: true do
notification.new_note(note) notification.new_note(note)
should_not_email(non_member) should_not_email(non_member)
should_not_email(guest)
should_email(author) should_email(author)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
...@@ -322,17 +325,20 @@ describe NotificationService, services: true do ...@@ -322,17 +325,20 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
it "emails subscribers of the issue's labels that can read the issue" do it "emails subscribers of the issue's labels that can read the issue" do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
label = create(:label, issues: [confidential_issue]) label = create(:label, issues: [confidential_issue])
label.toggle_subscription(non_member) label.toggle_subscription(non_member)
label.toggle_subscription(author) label.toggle_subscription(author)
label.toggle_subscription(assignee) label.toggle_subscription(assignee)
label.toggle_subscription(member) label.toggle_subscription(member)
label.toggle_subscription(guest)
label.toggle_subscription(admin) label.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
...@@ -341,6 +347,7 @@ describe NotificationService, services: true do ...@@ -341,6 +347,7 @@ describe NotificationService, services: true do
should_not_email(non_member) should_not_email(non_member)
should_not_email(author) should_not_email(author)
should_not_email(guest)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
should_email(admin) should_email(admin)
...@@ -490,6 +497,7 @@ describe NotificationService, services: true do ...@@ -490,6 +497,7 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
let!(:label_1) { create(:label, issues: [confidential_issue]) } let!(:label_1) { create(:label, issues: [confidential_issue]) }
...@@ -497,11 +505,13 @@ describe NotificationService, services: true do ...@@ -497,11 +505,13 @@ describe NotificationService, services: true do
it "emails subscribers of the issue's labels that can read the issue" do it "emails subscribers of the issue's labels that can read the issue" do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
label_2.toggle_subscription(non_member) label_2.toggle_subscription(non_member)
label_2.toggle_subscription(author) label_2.toggle_subscription(author)
label_2.toggle_subscription(assignee) label_2.toggle_subscription(assignee)
label_2.toggle_subscription(member) label_2.toggle_subscription(member)
label_2.toggle_subscription(guest)
label_2.toggle_subscription(admin) label_2.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
...@@ -509,6 +519,7 @@ describe NotificationService, services: true do ...@@ -509,6 +519,7 @@ describe NotificationService, services: true do
notification.relabeled_issue(confidential_issue, [label_2], @u_disabled) notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
should_not_email(non_member) should_not_email(non_member)
should_not_email(guest)
should_email(author) should_email(author)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
......
...@@ -33,6 +33,18 @@ describe Projects::AutocompleteService, services: true do ...@@ -33,6 +33,18 @@ describe Projects::AutocompleteService, services: true do
expect(issues.count).to eq 1 expect(issues.count).to eq 1
end end
it 'should not list project confidential issues for project members with guest role' do
project.team << [member, :guest]
autocomplete = described_class.new(project, non_member)
issues = autocomplete.issues.map(&:iid)
expect(issues).to include issue.iid
expect(issues).not_to include security_issue_1.iid
expect(issues).not_to include security_issue_2.iid
expect(issues.count).to eq 1
end
it 'should list project confidential issues for author' do it 'should list project confidential issues for author' do
autocomplete = described_class.new(project, author) autocomplete = described_class.new(project, author)
issues = autocomplete.issues.map(&:iid) issues = autocomplete.issues.map(&:iid)
......
...@@ -5,13 +5,15 @@ describe TodoService, services: true do ...@@ -5,13 +5,15 @@ describe TodoService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:john_doe) { create(:user) } let(:john_doe) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') } let(:mentions) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
let(:service) { described_class.new } let(:service) { described_class.new }
before do before do
project.team << [guest, :guest]
project.team << [author, :developer] project.team << [author, :developer]
project.team << [member, :developer] project.team << [member, :developer]
project.team << [john_doe, :developer] project.team << [john_doe, :developer]
...@@ -41,18 +43,20 @@ describe TodoService, services: true do ...@@ -41,18 +43,20 @@ describe TodoService, services: true do
service.new_issue(issue, author) service.new_issue(issue, author)
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
it 'does not create todo for non project members when issue is confidential' do it 'does not create todo if user can not see the issue when issue is confidential' do
service.new_issue(confidential_issue, john_doe) service.new_issue(confidential_issue, john_doe)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
...@@ -81,6 +85,7 @@ describe TodoService, services: true do ...@@ -81,6 +85,7 @@ describe TodoService, services: true do
service.update_issue(issue, author) service.update_issue(issue, author)
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
...@@ -92,13 +97,14 @@ describe TodoService, services: true do ...@@ -92,13 +97,14 @@ describe TodoService, services: true do
expect { service.update_issue(issue, author) }.not_to change(member.todos, :count) expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
end end
it 'does not create todo for non project members when issue is confidential' do it 'does not create todo if user can not see the issue when issue is confidential' do
service.update_issue(confidential_issue, john_doe) service.update_issue(confidential_issue, john_doe)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
...@@ -192,18 +198,20 @@ describe TodoService, services: true do ...@@ -192,18 +198,20 @@ describe TodoService, services: true do
service.new_note(note, john_doe) service.new_note(note, john_doe)
should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end end
it 'does not create todo for non project members when leaving a note on a confidential issue' do it 'does not create todo if user can not see the issue when leaving a note on a confidential issue' do
service.new_note(note_on_confidential_issue, john_doe) service.new_note(note_on_confidential_issue, john_doe)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end end
...@@ -245,6 +253,7 @@ describe TodoService, services: true do ...@@ -245,6 +253,7 @@ describe TodoService, services: true do
service.new_merge_request(mr_assigned, author) service.new_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
...@@ -256,6 +265,7 @@ describe TodoService, services: true do ...@@ -256,6 +265,7 @@ describe TodoService, services: true do
service.update_merge_request(mr_assigned, author) service.update_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
......
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