Commit 84e75ebd authored by Douwe Maan's avatar Douwe Maan

Merge branch 'fix/visibility-level-setting-in-forked-projects' into 'master'

Fix/visibility level setting in forked projects

Fixes #3136

/cc @DouweM 

See merge request !1744
parents 6b968a8f c301ab04
...@@ -69,7 +69,6 @@ module VisibilityLevelHelper ...@@ -69,7 +69,6 @@ module VisibilityLevelHelper
def skip_level?(form_model, level) def skip_level?(form_model, level)
form_model.is_a?(Project) && form_model.is_a?(Project) &&
form_model.forked? && !form_model.visibility_level_allowed?(level)
!Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
end end
end end
...@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base ...@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at) update_column(:last_activity_at, self.created_at)
end end
# update visibility_levet of forks
after_update :update_forks_visibility_level
def update_forks_visibility_level
return unless visibility_level < visibility_level_was
forks.each do |forked_project|
if forked_project.visibility_level > visibility_level
forked_project.visibility_level = visibility_level
forked_project.save!
end
end
end
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :tags acts_as_taggable_on :tags
...@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base ...@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy has_one :gitlab_issue_tracker_service, dependent: :destroy
has_one :external_wiki_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
has_many :forked_project_links, foreign_key: "forked_from_project_id"
has_many :forks, through: :forked_project_links, source: :forked_to_project
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed # Merge requests from source project should be kept when source project was removed
...@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base ...@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base
end end
def forks_count def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count forks.count
end end
def find_label(name) def find_label(name)
...@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base ...@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base
def open_issues_count def open_issues_count
issues.opened.count issues.opened.count
end end
def visibility_level_allowed?(level)
return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
end
end end
...@@ -39,10 +39,7 @@ class BaseService ...@@ -39,10 +39,7 @@ class BaseService
def deny_visibility_level(model, denied_visibility_level = nil) def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level denied_visibility_level ||= model.visibility_level
level_name = 'Unknown' level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
Gitlab::VisibilityLevel.options.each do |name, level|
level_name = name if level == denied_visibility_level
end
model.errors.add( model.errors.add(
:visibility_level, :visibility_level,
......
...@@ -3,12 +3,16 @@ module Projects ...@@ -3,12 +3,16 @@ module Projects
def execute def execute
# check that user is allowed to set specified visibility_level # check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level] new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != project.visibility_level if new_visibility
unless can?(current_user, :change_visibility_level, project) && if new_visibility.to_i != project.visibility_level
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) unless can?(current_user, :change_visibility_level, project) &&
deny_visibility_level(project, new_visibility) Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
return project deny_visibility_level(project, new_visibility)
return project
end
end end
return false unless visibility_level_allowed?(new_visibility)
end end
new_branch = params[:default_branch] new_branch = params[:default_branch]
...@@ -23,5 +27,19 @@ module Projects ...@@ -23,5 +27,19 @@ module Projects
end end
end end
end end
private
def visibility_level_allowed?(level)
return true if project.visibility_level_allowed?(level)
level_name = Gitlab::VisibilityLevel.level_name(level)
project.errors.add(
:visibility_level,
"#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
)
false
end
end end
end end
...@@ -51,6 +51,15 @@ module Gitlab ...@@ -51,6 +51,15 @@ module Gitlab
def allowed_fork_levels(origin_level) def allowed_fork_levels(origin_level)
[PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level } [PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
end end
def level_name(level)
level_name = 'Unknown'
options.each do |name, lvl|
level_name = name if lvl == level.to_i
end
level_name
end
end end
def private? def private?
......
...@@ -552,4 +552,28 @@ describe Project, models: true do ...@@ -552,4 +552,28 @@ describe Project, models: true do
end end
end end
end end
describe '#visibility_level_allowed?' do
let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
context 'when checking on non-forked project' do
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_truthy }
end
context 'when checking on forked project' do
let(:forked_project) { create :forked_project_with_submodules }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
end
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end
end
end end
...@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do ...@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do
end end
end end
describe :visibility_level do
let(:user) { create :user, admin: true }
let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
let(:opts) { {} }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
@created_internal = project.internal?
@fork_created_internal = forked_project.internal?
end
context 'should update forks visibility level when parent set to more restrictive' do
before do
opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
update_project(project, user, opts).inspect
end
it { expect(@created_internal).to be_truthy }
it { expect(@fork_created_internal).to be_truthy }
it { expect(project.private?).to be_truthy }
it { expect(project.forks.first.private?).to be_truthy }
end
context 'should not update forks visibility level when parent set to less restrictive' do
before do
opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_project(project, user, opts).inspect
end
it { expect(@created_internal).to be_truthy }
it { expect(@fork_created_internal).to be_truthy }
it { expect(project.public?).to be_truthy }
it { expect(project.forks.first.internal?).to be_truthy }
end
end
def update_project(project, user, opts) def update_project(project, user, opts)
Projects::UpdateService.new(project, user, opts).execute Projects::UpdateService.new(project, user, opts).execute
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