From f2a9ee258e0ee3a6fe0cb614e4b73c56dcd7339d Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 1 Mar 2016 12:22:29 -0300 Subject: [PATCH 01/22] Add permission level to groups --- app/controllers/explore/groups_controller.rb | 2 +- app/controllers/groups_controller.rb | 2 +- app/finders/groups_finder.rb | 31 +++++++++++++++++++ app/helpers/groups_helper.rb | 4 +++ app/helpers/visibility_level_helper.rb | 13 ++++++++ app/models/ability.rb | 5 +-- app/models/group.rb | 25 +++++++++------ app/views/groups/edit.html.haml | 2 ++ app/views/groups/new.html.haml | 2 ++ app/views/shared/_group_tips.html.haml | 1 - ...01124843_add_visibility_level_to_groups.rb | 9 ++++++ db/schema.rb | 10 +++--- spec/finders/groups_finder_spec.rb | 25 +++++++++++++++ spec/helpers/groups_helper.rb | 15 +++++++++ spec/helpers/visibility_level_helper_spec.rb | 8 +++++ spec/models/group_spec.rb | 18 +++++++++++ 16 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 app/finders/groups_finder.rb create mode 100644 db/migrate/20160301124843_add_visibility_level_to_groups.rb create mode 100644 spec/finders/groups_finder_spec.rb diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index a9bf4321f73..9575a87ee41 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,6 +1,6 @@ class Explore::GroupsController < Explore::ApplicationController def index - @groups = Group.order_id_desc + @groups = GroupsFinder.new.execute(current_user) @groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index f05c29e9974..13de19bc141 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -131,7 +131,7 @@ class GroupsController < Groups::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar, :public) + params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level) end def load_events diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb new file mode 100644 index 00000000000..a3a8cd541de --- /dev/null +++ b/app/finders/groups_finder.rb @@ -0,0 +1,31 @@ +class GroupsFinder + def execute(current_user = nil) + + segments = all_groups(current_user) + + if segments.length > 1 + union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) + Group.where("namespaces.id IN (#{union.to_sql})").order_id_desc + else + segments.first + end + end + + private + + def all_groups(current_user) + if current_user + [current_user.authorized_groups, public_and_internal_groups] + else + [Group.public_only] + end + end + + def public_groups + Group.unscoped.public_only + end + + def public_and_internal_groups + Group.unscoped.public_and_internal_only + end +end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 1d36969cd62..b1f0a765bb9 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -19,6 +19,10 @@ module GroupsHelper end end + def can_change_group_visibility_level?(group) + can?(current_user, :change_visibility_level, group) + end + def group_icon(group) if group.is_a?(String) group = Group.find_by(path: group) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 71d33b445c2..c47342534a8 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -19,6 +19,8 @@ module VisibilityLevelHelper case form_model when Project project_visibility_level_description(level) + when Group + group_visibility_level_description(level) when Snippet snippet_visibility_level_description(level, form_model) end @@ -35,6 +37,17 @@ module VisibilityLevelHelper end end + def group_visibility_level_description(level) + case level + when Gitlab::VisibilityLevel::PRIVATE + "The group can be accessed only by members." + when Gitlab::VisibilityLevel::INTERNAL + "The group can be accessed by any logged user." + when Gitlab::VisibilityLevel::PUBLIC + "The group can be accessed without any authentication." + end + end + def snippet_visibility_level_description(level, snippet = nil) case level when Gitlab::VisibilityLevel::PRIVATE diff --git a/app/models/ability.rb b/app/models/ability.rb index fe9e0aab717..c84ded61606 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -275,11 +275,12 @@ class Ability rules << :read_group end - # Only group masters and group owners can create new projects in group + # Only group masters and group owners can create new projects and change permission level if group.has_master?(user) || group.has_owner?(user) || user.admin? rules += [ :create_projects, - :admin_milestones + :admin_milestones, + :change_visibility_level ] end diff --git a/app/models/group.rb b/app/models/group.rb index 76042b3e3fd..26914f55541 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -2,15 +2,16 @@ # # Table name: namespaces # -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) +# id :integer not null, primary key +# name :string(255) not null +# path :string(255) not null +# owner_id :integer +# visibility_level :integer default(20), not null +# created_at :key => "value", datetime +# updated_at :datetime +# type :string(255) +# description :string(255) default(""), not null +# avatar :string(255) # require 'carrierwave/orm/activerecord' @@ -18,8 +19,10 @@ require 'file_size_validator' class Group < Namespace include Gitlab::ConfigHelper + include Gitlab::VisibilityLevel include Referable + has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' alias_method :members, :group_members has_many :users, through: :group_members @@ -32,6 +35,10 @@ class Group < Namespace after_create :post_create_hook after_destroy :post_destroy_hook + scope :public_only, -> { where(visibility_level: Group::PUBLIC) } + scope :public_and_internal_only, -> { where(visibility_level: [Group::PUBLIC, Group::INTERNAL] ) } + + class << self def search(query) where("LOWER(namespaces.name) LIKE :query or LOWER(namespaces.path) LIKE :query", query: "%#{query.downcase}%") diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 3430f56a9c9..ea223d2209f 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -23,6 +23,8 @@ %hr = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group + .form-actions = f.submit 'Save group', class: "btn btn-save" diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 4bc31cabea6..1526ca42634 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -17,6 +17,8 @@ .col-sm-10 = render 'shared/choose_group_avatar_button', f: f + = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: true, form_model: @group + .form-group .col-sm-offset-2.col-sm-10 = render 'shared/group_tips' diff --git a/app/views/shared/_group_tips.html.haml b/app/views/shared/_group_tips.html.haml index e5cf783beb7..46e4340511a 100644 --- a/app/views/shared/_group_tips.html.haml +++ b/app/views/shared/_group_tips.html.haml @@ -1,6 +1,5 @@ %ul %li A group is a collection of several projects - %li Groups are private by default %li Members of a group may only view projects they have permission to access %li Group project URLs are prefixed with the group namespace %li Existing projects may be moved into a group diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb new file mode 100644 index 00000000000..b0afe795f42 --- /dev/null +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -0,0 +1,9 @@ +class AddVisibilityLevelToGroups < ActiveRecord::Migration + def change + #All groups will be private when created + add_column :namespaces, :visibility_level, :integer, null: false, default: 0 + + #Set all existing groups to public + Group.update_all(visibility_level: 20) + end +end diff --git a/db/schema.rb b/db/schema.rb index a74b86d8e2f..d0e3bb769d2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,8 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160309140734) do - +ActiveRecord::Schema.define(version: 20160305220806) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -568,14 +567,15 @@ ActiveRecord::Schema.define(version: 20160309140734) do add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: :cascade do |t| - t.string "name", null: false - t.string "path", null: false + t.string "name", null: false + t.string "path", null: false t.integer "owner_id" t.datetime "created_at" t.datetime "updated_at" t.string "type" - t.string "description", default: "", null: false + t.string "description", default: "", null: false t.string "avatar" + t.integer "visibility_level", default: 0, null: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb new file mode 100644 index 00000000000..ed24954af7a --- /dev/null +++ b/spec/finders/groups_finder_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe GroupsFinder do + describe '#execute' do + let(:user) { create(:user) } + let!(:private_group) { create(:group, visibility_level: 0) } + let!(:internal_group) { create(:group, visibility_level: 10) } + let!(:public_group) { create(:group, visibility_level: 20) } + let(:finder) { described_class.new } + + describe 'execute' do + describe 'without a user' do + subject { finder.execute } + + it { is_expected.to eq([public_group]) } + end + + describe 'with a user' do + subject { finder.execute(user) } + + it { is_expected.to eq([public_group, internal_group]) } + end + end + end +end diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper.rb index 4ea90a80a92..01ec9e5a07f 100644 --- a/spec/helpers/groups_helper.rb +++ b/spec/helpers/groups_helper.rb @@ -18,4 +18,19 @@ describe GroupsHelper do expect(group_icon(group.path)).to match('group_avatar.png') end end + + describe 'permissions' do + let(:group) { create(:group) } + let!(:user) { create(:user) } + + before do + allow(self).to receive(:current_user).and_return(user) + allow(self).to receive(:can?) { true } + end + + it 'checks user ability to change permissions' do + expect(self).to receive(:can?).with(user, :change_visibility_level, group) + can_change_group_visibility_level?(group) + end + end end diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index cd7596a763d..ef60ef75fbe 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -8,6 +8,7 @@ describe VisibilityLevelHelper do end let(:project) { build(:project) } + let(:group) { build(:group) } let(:personal_snippet) { build(:personal_snippet) } let(:project_snippet) { build(:project_snippet) } @@ -19,6 +20,13 @@ describe VisibilityLevelHelper do end end + context 'used with a Group' do + it 'delegates groups to #group_visibility_level_description' do + expect(visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, group)) + .to match /group/i + end + end + context 'called with a Snippet' do it 'delegates snippets to #snippet_visibility_level_description' do expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet)) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3c995053eec..25aa77dc4e8 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -56,6 +56,24 @@ describe Group, models: true do end end + describe 'scopes' do + let!(:private_group) { create(:group, visibility_level: 0) } + let!(:internal_group) { create(:group, visibility_level: 10) } + let!(:public_group) { create(:group, visibility_level: 20) } + + describe 'public_only' do + subject { described_class.public_only } + + it{ is_expected.to eq([public_group]) } + end + + describe 'public_and_internal_only' do + subject { described_class.public_and_internal_only } + + it{ is_expected.to eq([public_group, internal_group]) } + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(group.to_reference).to eq "@#{group.name}" -- 2.18.1 From 5551ccd7201ea6b45a2e2721502ba55e8f525d8f Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 2 Mar 2016 19:13:50 -0300 Subject: [PATCH 02/22] Code improvements --- app/controllers/groups_controller.rb | 4 ++-- app/controllers/users_controller.rb | 10 ++++++++++ app/finders/groups_finder.rb | 13 ++----------- app/helpers/groups_helper.rb | 2 +- app/models/ability.rb | 18 +++++++++++------- app/models/concerns/shared_scopes.rb | 8 ++++++++ app/models/group.rb | 10 +++++----- app/models/project.rb | 3 +-- ...301124843_add_visibility_level_to_groups.rb | 5 +---- db/schema.rb | 2 +- 10 files changed, 42 insertions(+), 33 deletions(-) create mode 100644 app/models/concerns/shared_scopes.rb diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 13de19bc141..6532eee1602 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -9,7 +9,7 @@ class GroupsController < Groups::ApplicationController before_action :group, except: [:index, :new, :create] # Authorize - before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:index, :new, :create] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] @@ -105,7 +105,7 @@ class GroupsController < Groups::ApplicationController # Dont allow unauthorized access to group def authorize_read_group! - unless @group and (@projects.present? or can?(current_user, :read_group, @group)) + unless can?(current_user, :read_group, @group) if current_user.nil? return authenticate_user! else diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e10c633690f..d26a1ce6737 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,6 +3,16 @@ class UsersController < ApplicationController before_action :set_user def show +<<<<<<< HEAD +======= + @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) + + @projects = PersonalProjectsFinder.new(@user).execute(current_user) + @projects = @projects.page(params[:page]).per(PER_PAGE) + + @groups = @user.groups.order_id_desc + +>>>>>>> Code improvements respond_to do |format| format.html diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index a3a8cd541de..ce62f5e762f 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -1,6 +1,5 @@ class GroupsFinder def execute(current_user = nil) - segments = all_groups(current_user) if segments.length > 1 @@ -15,17 +14,9 @@ class GroupsFinder def all_groups(current_user) if current_user - [current_user.authorized_groups, public_and_internal_groups] + [current_user.authorized_groups, Group.unscoped.public_and_internal_only] else - [Group.public_only] + [Group.unscoped.public_only] end end - - def public_groups - Group.unscoped.public_only - end - - def public_and_internal_groups - Group.unscoped.public_and_internal_only - end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index b1f0a765bb9..d918d8acd22 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -28,7 +28,7 @@ module GroupsHelper group = Group.find_by(path: group) end - if group && group.avatar.present? + if group && can?(current_user, :read_group, group) && group.avatar.present? group.avatar.url else 'no_group_avatar.png' diff --git a/app/models/ability.rb b/app/models/ability.rb index c84ded61606..ec5587d8fa5 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -83,7 +83,7 @@ class Ability subject.group end - if group && group.projects.public_only.any? + if group && group.public? [:read_group] else [] @@ -271,16 +271,13 @@ class Ability def group_abilities(user, group) rules = [] - if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? - rules << :read_group - end + rules << :read_group if can_read_group?(user, group) # Only group masters and group owners can create new projects and change permission level if group.has_master?(user) || group.has_owner?(user) || user.admin? rules += [ :create_projects, - :admin_milestones, - :change_visibility_level + :admin_milestones ] end @@ -289,13 +286,20 @@ class Ability rules += [ :admin_group, :admin_namespace, - :admin_group_member + :admin_group_member, + :change_visibility_level ] end rules.flatten end + def can_read_group?(user, group) + is_project_member = ProjectsFinder.new.execute(user, group: group).any? + internal_group_allowed = group.internal? && user.present? + user.admin? || group.users.include?(user) || is_project_member || group.public? || internal_group_allowed + end + def namespace_abilities(user, namespace) rules = [] diff --git a/app/models/concerns/shared_scopes.rb b/app/models/concerns/shared_scopes.rb new file mode 100644 index 00000000000..f576d2c0821 --- /dev/null +++ b/app/models/concerns/shared_scopes.rb @@ -0,0 +1,8 @@ +module SharedScopes + extend ActiveSupport::Concern + + included do + scope :public_only, -> { where(visibility_level: Group::PUBLIC) } + scope :public_and_internal_only, -> { where(visibility_level: [Group::PUBLIC, Group::INTERNAL] ) } + end +end diff --git a/app/models/group.rb b/app/models/group.rb index 26914f55541..d1a1817f0fa 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -21,7 +21,7 @@ class Group < Namespace include Gitlab::ConfigHelper include Gitlab::VisibilityLevel include Referable - + include SharedScopes has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' alias_method :members, :group_members @@ -35,10 +35,6 @@ class Group < Namespace after_create :post_create_hook after_destroy :post_destroy_hook - scope :public_only, -> { where(visibility_level: Group::PUBLIC) } - scope :public_and_internal_only, -> { where(visibility_level: [Group::PUBLIC, Group::INTERNAL] ) } - - class << self def search(query) where("LOWER(namespaces.name) LIKE :query or LOWER(namespaces.path) LIKE :query", query: "%#{query.downcase}%") @@ -69,6 +65,10 @@ class Group < Namespace name end + def visibility_level_field + visibility_level + end + def avatar_url(size = nil) if avatar.present? [gitlab_config.url, avatar.url].join diff --git a/app/models/project.rb b/app/models/project.rb index 426464dee81..6e86c7cc883 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -52,6 +52,7 @@ class Project < ActiveRecord::Base include AfterCommitQueue include CaseSensitivity include TokenAuthenticatable + include SharedScopes extend Gitlab::ConfigHelper @@ -213,8 +214,6 @@ class Project < ActiveRecord::Base scope :in_group_namespace, -> { joins(:group) } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } - scope :public_only, -> { where(visibility_level: Project::PUBLIC) } - scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb index b0afe795f42..86dc07f4333 100644 --- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -1,9 +1,6 @@ class AddVisibilityLevelToGroups < ActiveRecord::Migration def change #All groups will be private when created - add_column :namespaces, :visibility_level, :integer, null: false, default: 0 - - #Set all existing groups to public - Group.update_all(visibility_level: 20) + add_column :namespaces, :visibility_level, :integer, null: false, default: 20 end end diff --git a/db/schema.rb b/db/schema.rb index d0e3bb769d2..d2f311e42da 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160305220806) do +ActiveRecord::Schema.define(version: 20160301124843) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- 2.18.1 From bd59e59d01c5e845c7f7d451feaa1488670f20de Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 3 Mar 2016 16:52:38 -0300 Subject: [PATCH 03/22] Add visibility level icon and a couple of specs --- app/helpers/groups_helper.rb | 6 ++- app/views/shared/groups/_group.html.haml | 3 ++ ...01124843_add_visibility_level_to_groups.rb | 2 +- spec/controllers/groups_controller_spec.rb | 38 +++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index d918d8acd22..42e09149bd7 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -28,7 +28,7 @@ module GroupsHelper group = Group.find_by(path: group) end - if group && can?(current_user, :read_group, group) && group.avatar.present? + if group && group.avatar.present? group.avatar.url else 'no_group_avatar.png' @@ -43,4 +43,8 @@ module GroupsHelper full_title end end + + def group_visibility_description(group) + "#{visibility_level_label(group.visibility_level)} - #{group_visibility_level_description(group.visibility_level)}" + end end diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index fb9a8db0889..82eeb2088c8 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -21,6 +21,9 @@ = icon('users') = number_with_delimiter(group.users.count) + %span{title: group_visibility_description(group)} + = visibility_level_icon(group.visibility_level, fw: false) + = image_tag group_icon(group), class: "avatar s40 hidden-xs" = link_to group, class: 'group-name title' do = group.name diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb index 86dc07f4333..cef553981e7 100644 --- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -1,6 +1,6 @@ class AddVisibilityLevelToGroups < ActiveRecord::Migration def change - #All groups will be private when created + #All groups public by default add_column :namespaces, :visibility_level, :integer, null: false, default: 20 end end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 938e97298b6..e7ead824d20 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -20,4 +20,42 @@ describe GroupsController do end end end + + describe 'GET show' do + let(:group) { create(:group, visibility_level: 20) } + + it 'checks if group can be read' do + expect(controller).to receive(:authorize_read_group!) + get :show, id: group.path + end + end + + describe 'POST create' do + before { sign_in(create(:user)) } + + it 'checks if group can be created' do + expect(controller).to receive(:authorize_create_group!) + post :create, { group: { name: "any params" } } + end + end + + describe 'DELETE destroy' do + before { sign_in(create(:user)) } + let(:group) { create(:group, visibility_level: 20) } + + it 'checks if group can be deleted' do + expect(controller).to receive(:authorize_admin_group!) + delete :destroy, id: group.path + end + end + + describe 'PUT update' do + before { sign_in(create(:user)) } + let(:group) { create(:group, visibility_level: 20) } + + it 'checks if group can be updated' do + expect(controller).to receive(:authorize_admin_group!) + put :update, id: group.path, group: { name: 'test' } + end + end end -- 2.18.1 From c3e70280dffe7ee0859ebd73b902d424ca5f809a Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 8 Mar 2016 21:01:33 -0300 Subject: [PATCH 04/22] Prevent projects to have higher visibility than groups Prevent Groups to have smaller visibility than projects Add default_group_visibility_level to configuration Code improvements --- app/assets/stylesheets/framework/common.scss | 32 ++++++++++++ .../admin/application_settings_controller.rb | 1 + app/controllers/groups_controller.rb | 2 +- app/controllers/namespaces_controller.rb | 2 +- app/controllers/users_controller.rb | 5 +- app/finders/joined_groups_finder.rb | 45 ++++++++++++++++ app/helpers/visibility_level_helper.rb | 4 ++ app/models/ability.rb | 3 +- app/models/application_setting.rb | 1 + app/models/concerns/shared_scopes.rb | 8 --- app/models/group.rb | 1 - app/models/project.rb | 7 +-- app/services/groups/base_service.rb | 9 ++++ app/services/groups/update_service.rb | 44 ++++++++++++++++ app/services/projects/create_service.rb | 4 +- .../application_settings/_form.html.haml | 4 ++ app/views/groups/new.html.haml | 2 +- app/views/groups/show.html.haml | 12 ++--- app/views/layouts/header/_default.html.haml | 5 +- app/views/projects/_home_panel.html.haml | 2 +- config/gitlab.yml.example | 4 ++ ...roup_visibility_to_application_settings.rb | 11 ++++ db/schema.rb | 3 +- lib/api/entities.rb | 1 + lib/gitlab/current_settings.rb | 1 + lib/gitlab/visibility_level.rb | 7 +++ spec/controllers/groups_controller_spec.rb | 1 + spec/finders/joined_groups_finder_spec.rb | 51 +++++++++++++++++++ spec/models/project_spec.rb | 15 ++++++ spec/services/groups/update_service_spec.rb | 51 +++++++++++++++++++ 30 files changed, 304 insertions(+), 34 deletions(-) create mode 100644 app/finders/joined_groups_finder.rb delete mode 100644 app/models/concerns/shared_scopes.rb create mode 100644 app/services/groups/base_service.rb create mode 100644 app/services/groups/update_service.rb create mode 100644 db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb create mode 100644 spec/finders/joined_groups_finder_spec.rb create mode 100644 spec/services/groups/update_service_spec.rb diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index c98e43ad09f..18044b25b87 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -383,3 +383,35 @@ table { margin-right: -$gl-padding; border-top: 1px solid $border-color; } +.message { + border: 1px solid #ccc; + padding: 10px; + color: #333; +} +.message { + border: 1px solid #ccc; + padding: 10px; + color: #333; +} + +.group-projects-show-title{ + h1 { + color: #313236; + margin: 0; + margin-bottom: 6px; + font-size: 23px; + font-weight: normal; + } + + .visibility-icon { + display: inline-block; + margin-left: 5px; + font-size: 18px; + color: $gray; + } + + p { + padding: 0 $gl-padding; + color: #5c5d5e; + } + } diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 04a99d8c84a..ed9f6031389 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -61,6 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :session_expire_delay, :default_project_visibility, :default_snippet_visibility, + :default_group_visibility, :restricted_signup_domains_raw, :version_check_enabled, :admin_notification_email, diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 6532eee1602..54f14e62ead 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -79,7 +79,7 @@ class GroupsController < Groups::ApplicationController end def update - if @group.update_attributes(group_params) + if Groups::UpdateService.new(@group, current_user, group_params).execute redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated." else render action: "edit" diff --git a/app/controllers/namespaces_controller.rb b/app/controllers/namespaces_controller.rb index 282012c60a1..5a94dcb0dbd 100644 --- a/app/controllers/namespaces_controller.rb +++ b/app/controllers/namespaces_controller.rb @@ -14,7 +14,7 @@ class NamespacesController < ApplicationController if user redirect_to user_path(user) - elsif group + elsif group && can?(current_user, :read_group, namespace) redirect_to group_path(group) elsif current_user.nil? authenticate_user! diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d26a1ce6737..7b32572f822 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,16 +3,13 @@ class UsersController < ApplicationController before_action :set_user def show -<<<<<<< HEAD -======= @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) @projects = PersonalProjectsFinder.new(@user).execute(current_user) @projects = @projects.page(params[:page]).per(PER_PAGE) - @groups = @user.groups.order_id_desc + @groups = JoinedGroupsFinder.new(@user).execute(current_user) ->>>>>>> Code improvements respond_to do |format| format.html diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb new file mode 100644 index 00000000000..131b518563e --- /dev/null +++ b/app/finders/joined_groups_finder.rb @@ -0,0 +1,45 @@ +#Shows only authorized groups of a user +class JoinedGroupsFinder + def initialize(user = nil) + @user = user + end + + # Finds the groups of the source user, optionally limited to those visible to + # the current user. + # + # current_user - If given the groups of "@user" will only include the groups + # "current_user" can also see. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = groups_visible_to_user(current_user) + else + relation = public_groups + end + + relation.order_id_desc + end + + private + + # Returns the groups the user in "current_user" can see. + # + # This list includes all public/internal projects as well as the projects of + # "@user" that "current_user" also has access to. + def groups_visible_to_user(current_user) + base = @user.authorized_groups.visible_to_user(current_user) + extra = public_and_internal_groups + union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + + Group.where("namespaces.id IN (#{union.to_sql})") + end + + def public_groups + @user.authorized_groups.public_only + end + + def public_and_internal_groups + @user.authorized_groups.public_and_internal_only + end +end diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index c47342534a8..930cc883634 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -80,6 +80,10 @@ module VisibilityLevelHelper current_application_settings.default_snippet_visibility end + def default_group_visibility + current_application_settings.default_group_visibility + end + def skip_level?(form_model, level) form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level) diff --git a/app/models/ability.rb b/app/models/ability.rb index ec5587d8fa5..1c9b15069aa 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -296,8 +296,7 @@ class Ability def can_read_group?(user, group) is_project_member = ProjectsFinder.new.execute(user, group: group).any? - internal_group_allowed = group.internal? && user.present? - user.admin? || group.users.include?(user) || is_project_member || group.public? || internal_group_allowed + user.admin? || group.public? || group.internal? || group.users.include?(user) end def namespace_abilities(user, namespace) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 269056e0e77..c4879598c4e 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -18,6 +18,7 @@ # max_attachment_size :integer default(10), not null # default_project_visibility :integer # default_snippet_visibility :integer +# default_group_visibility :integer # restricted_signup_domains :text # user_oauth_applications :boolean default(TRUE) # after_sign_out_path :string(255) diff --git a/app/models/concerns/shared_scopes.rb b/app/models/concerns/shared_scopes.rb deleted file mode 100644 index f576d2c0821..00000000000 --- a/app/models/concerns/shared_scopes.rb +++ /dev/null @@ -1,8 +0,0 @@ -module SharedScopes - extend ActiveSupport::Concern - - included do - scope :public_only, -> { where(visibility_level: Group::PUBLIC) } - scope :public_and_internal_only, -> { where(visibility_level: [Group::PUBLIC, Group::INTERNAL] ) } - end -end diff --git a/app/models/group.rb b/app/models/group.rb index d1a1817f0fa..02b9a968dcd 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -21,7 +21,6 @@ class Group < Namespace include Gitlab::ConfigHelper include Gitlab::VisibilityLevel include Referable - include SharedScopes has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' alias_method :members, :group_members diff --git a/app/models/project.rb b/app/models/project.rb index 6e86c7cc883..ae5f6e2417d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -52,7 +52,6 @@ class Project < ActiveRecord::Base include AfterCommitQueue include CaseSensitivity include TokenAuthenticatable - include SharedScopes extend Gitlab::ConfigHelper @@ -934,8 +933,10 @@ class Project < ActiveRecord::Base end def visibility_level_allowed?(level) - return true unless forked? - Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i) + allowed_by_forks = forked? ? Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i) : true + allowed_by_groups = group.present? ? level.to_i <= group.visibility_level : true + + allowed_by_forks && allowed_by_groups end def runners_token diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb new file mode 100644 index 00000000000..5becd475d3a --- /dev/null +++ b/app/services/groups/base_service.rb @@ -0,0 +1,9 @@ +module Groups + class BaseService + attr_accessor :group, :current_user, :params + + def initialize(group, user, params = {}) + @group, @current_user, @params = group, user, params.dup + end + end +end diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb new file mode 100644 index 00000000000..acb6c529c17 --- /dev/null +++ b/app/services/groups/update_service.rb @@ -0,0 +1,44 @@ +#Checks visibility level permission check before updating a group +#Do not allow to put Group visibility level smaller than its projects +#Do not allow unauthorized permission levels + +module Groups + class UpdateService < Groups::BaseService + def execute + visibility_level_allowed?(params[:visibility_level]) ? group.update_attributes(params) : false + end + + private + + def visibility_level_allowed?(level) + return true unless level.present? + + allowed_by_projects = visibility_by_project(level) + allowed_by_user = visibility_by_user(level) + + allowed_by_projects && allowed_by_user + end + + def visibility_by_project(level) + projects_visibility = group.projects.pluck(:visibility_level) + + allowed_by_projects = !projects_visibility.any?{|project_visibility| level.to_i < project_visibility } + add_error_message("Cannot be changed. There are projects with higher visibility permissions.") unless allowed_by_projects + allowed_by_projects + end + + def visibility_by_user(level) + allowed_by_user = Gitlab::VisibilityLevel.allowed_for?(current_user, level) + add_error_message("You are not authorized to set this permission level.") unless allowed_by_user + allowed_by_user + end + + def add_error_message(message) + level_name = Gitlab::VisibilityLevel.level_name(params[:visibility_level]) + group.errors.add(:visibility_level, message) + end + end +end + + + diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index a6820183bee..522fae79503 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -11,8 +11,8 @@ module Projects # Make sure that the user is allowed to use the specified visibility # level - unless Gitlab::VisibilityLevel.allowed_for?(current_user, - params[:visibility_level]) + + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) && @project.visibility_level_allowed?(@project.visibility_level) deny_visibility_level(@project) return @project end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index b30dfd109ea..0350995d03d 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -19,6 +19,10 @@ = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) + .form-group.group-visibility-level-holder + = f.label :default_group_visibility, class: 'control-label col-sm-2' + .col-sm-10 + = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) .form-group = f.label :restricted_visibility_levels, class: 'control-label col-sm-2' .col-sm-10 diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 1526ca42634..30ab8aeba13 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -17,7 +17,7 @@ .col-sm-10 = render 'shared/choose_group_avatar_button', f: f - = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: true, form_model: @group + = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 6148d8cb3d2..0a8c2da7207 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,8 +1,5 @@ - @no_container = true -- unless can?(current_user, :read_group, @group) - - @disable_search_panel = true - = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") @@ -17,8 +14,12 @@ .avatar-holder = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" - .cover-title - = @group.name + .group-projects-show-title + %h1 + = @group.name + + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{visibility_level_label(@group.visibility_level)} - #{project_visibility_level_description(@group.visibility_level)}"} + = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username @#{@group.path} @@ -27,7 +28,6 @@ .cover-desc.description = markdown(@group.description, pipeline: :description) - %ul.nav-links %li.active = link_to "#activity", 'data-toggle' => 'tab' do diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 77d01a7736c..714da410f56 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -7,9 +7,8 @@ .navbar-collapse.collapse %ul.nav.navbar-nav.pull-right - - unless @disable_search_panel - %li.hidden-sm.hidden-xs - = render 'layouts/search' + %li.hidden-sm.hidden-xs + = render 'layouts/search' %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('search') diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b45df44f270..dd16f504c92 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -2,7 +2,7 @@ .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') - .project-home-desc + .group-projects-show-title %h1 = @project.name %span.visibility-icon.has_tooltip{data: { container: 'body' }, diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 05f127d622a..fc808e228de 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -90,6 +90,10 @@ production: &base snippets: false builds: true + ## Default group features settings + default_groups_features: + visibility_level: 20 + ## Webhook settings # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10) # webhook_timeout: 10 diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb new file mode 100644 index 00000000000..c2bdd7b31fa --- /dev/null +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -0,0 +1,11 @@ +class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration + def up + add_column :application_settings, :default_group_visibility, :integer + visibility = Settings.gitlab.default_groups_features['visibility_level'] + execute("update application_settings set default_group_visibility = #{visibility}") + end + + def down + remove_column :application_settings, :default_group_visibility + end +end diff --git a/db/schema.rb b/db/schema.rb index d2f311e42da..1d4e18c5f95 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160301124843) do +ActiveRecord::Schema.define(version: 20160308212903) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -75,6 +75,7 @@ ActiveRecord::Schema.define(version: 20160301124843) do t.boolean "akismet_enabled", default: false t.string "akismet_api_key" t.boolean "email_author_in_body", default: false + t.integer "default_group_visibility" end create_table "audit_events", force: :cascade do |t| diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 5b5b8bd044b..efad2390127 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -332,6 +332,7 @@ module API expose :session_expire_delay expose :default_project_visibility expose :default_snippet_visibility + expose :default_group_visibility expose :restricted_signup_domains expose :user_oauth_applications expose :after_sign_out_path diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 761b63e98f6..a50d8e3d5a8 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -29,6 +29,7 @@ module Gitlab session_expire_delay: Settings.gitlab['session_expire_delay'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], + default_group_visibility: Settings.gitlab.default_groups_features['visibility_level'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index 3160a3c7582..f6e0dd6afc0 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -12,6 +12,13 @@ module Gitlab PUBLIC = 20 unless const_defined?(:PUBLIC) class << self + def included(base) + base.class_eval do + scope :public_only, -> { where(visibility_level: PUBLIC) } + scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } + end + end + def values options.values end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index e7ead824d20..91db3fd1ee2 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -54,6 +54,7 @@ describe GroupsController do let(:group) { create(:group, visibility_level: 20) } it 'checks if group can be updated' do + expect_any_instance_of(Groups::UpdateService).to receive(:execute) expect(controller).to receive(:authorize_admin_group!) put :update, id: group.path, group: { name: 'test' } end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb new file mode 100644 index 00000000000..e2f6c593638 --- /dev/null +++ b/spec/finders/joined_groups_finder_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe JoinedGroupsFinder do + describe '#execute' do + let!(:profile_owner) { create(:user) } + let!(:profile_visitor) { create(:user) } + + let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + let!(:private_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let!(:internal_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:public_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:finder) { described_class.new(profile_owner) } + + describe 'execute' do + context 'without a user only shows public groups from profile owner' do + before { public_group.add_user(profile_owner, Gitlab::Access::MASTER)} + subject { finder.execute } + + it { is_expected.to eq([public_group]) } + end + + context 'only shows groups where both users are authorized to see' do + subject { finder.execute(profile_visitor) } + + before do + private_group.add_user(profile_owner, Gitlab::Access::MASTER) + private_group.add_user(profile_visitor, Gitlab::Access::DEVELOPER) + internal_group.add_user(profile_owner, Gitlab::Access::MASTER) + public_group.add_user(profile_owner, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([public_group, internal_group, private_group]) } + end + + context 'shows group if profile visitor is in one of its projects' do + before do + public_group.add_user(profile_owner, Gitlab::Access::MASTER) + private_group.add_user(profile_owner, Gitlab::Access::MASTER) + project = create(:project, :private, group: private_group, name: 'B', path: 'B') + project.team.add_user(profile_visitor, Gitlab::Access::DEVELOPER) + end + + subject { finder.execute(profile_visitor) } + + it { is_expected.to eq([public_group, private_group]) } + end + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 2fa38a5d3d3..9efaffbb577 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -583,6 +583,21 @@ describe Project, models: true do it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey } end + context 'when checking projects from groups' do + let(:private_group) { create(:group, visibility_level: 0) } + let(:internal_group) { create(:group, visibility_level: 10) } + + let(:private_project) { create :project, group: private_group, visibility_level: Gitlab::VisibilityLevel::PRIVATE } + let(:internal_project) { create :project, group: internal_group, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + + context 'when group is private project can not be internal' do + it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey } + end + + context 'when group is internal project can not be public' do + it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey } + end + end end describe '#rename_repo' do diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb new file mode 100644 index 00000000000..c759e32342d --- /dev/null +++ b/spec/services/groups/update_service_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Groups::UpdateService, services: true do + let!(:user) { create(:user) } + let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + + describe "execute" do + context "project visibility_level validation" do + + context "public group with public projects" do + let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL ) } + + before do + public_group.add_user(user, Gitlab::Access::MASTER) + create(:project, :public, group: public_group, name: 'B', path: 'B') + end + + it "cant downgrade permission level" do + expect(service.execute).to be_falsy + expect(public_group.errors.count).to eq(1) + end + end + + context "internal group with internal project" do + let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) } + + before do + internal_group.add_user(user, Gitlab::Access::MASTER) + create(:project, :internal, group: internal_group, name: 'B', path: 'B') + end + + it "cant downgrade permission level" do + expect(service.execute).to be_falsy + expect(internal_group.errors.count).to eq(1) + end + end + end + end + + context "unauthorized visibility_level validation" do + let!(:service) { described_class.new(internal_group, user, visibility_level: 99 ) } + before { internal_group.add_user(user, Gitlab::Access::MASTER) } + + it "does not change permission level" do + expect(service.execute).to be_falsy + expect(internal_group.errors.count).to eq(1) + end + end +end -- 2.18.1 From 96fc1d90927624345c7426b28fb3fd135e901e60 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 9 Mar 2016 13:57:57 -0300 Subject: [PATCH 05/22] Add security specs --- app/models/ability.rb | 2 +- .../security/group/internal_access_spec.rb | 104 ++++++++++++++++++ .../security/group/private_access_spec.rb | 104 ++++++++++++++++++ .../security/group/public_access_spec.rb | 104 ++++++++++++++++++ spec/features/security/group_access_spec.rb | 40 ------- spec/support/group_access_helper.rb | 17 +++ 6 files changed, 330 insertions(+), 41 deletions(-) create mode 100644 spec/features/security/group/internal_access_spec.rb create mode 100644 spec/features/security/group/private_access_spec.rb create mode 100644 spec/features/security/group/public_access_spec.rb create mode 100644 spec/support/group_access_helper.rb diff --git a/app/models/ability.rb b/app/models/ability.rb index 1c9b15069aa..fe460ccdaca 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -296,7 +296,7 @@ class Ability def can_read_group?(user, group) is_project_member = ProjectsFinder.new.execute(user, group: group).any? - user.admin? || group.public? || group.internal? || group.users.include?(user) + user.admin? || group.public? || group.internal? || is_project_member || group.users.include?(user) end def namespace_abilities(user, namespace) diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb new file mode 100644 index 00000000000..69a0fbb4468 --- /dev/null +++ b/spec/features/security/group/internal_access_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +describe 'Internal group access', feature: true do + include AccessMatchers + include GroupAccessHelper + + + + describe 'GET /groups/:path' do + subject { group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/merge_requests' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + + describe 'GET /groups/:path/group_members' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/edit' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end +end diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb new file mode 100644 index 00000000000..0d01310b449 --- /dev/null +++ b/spec/features/security/group/private_access_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +describe 'Private group access', feature: true do + include AccessMatchers + include GroupAccessHelper + + + + describe 'GET /groups/:path' do + subject { group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to_not be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to_not be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/merge_requests' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to_not be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + + describe 'GET /groups/:path/group_members' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to_not be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/edit' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to_not be_allowed_for :user } + it { is_expected.to_not be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to_not be_allowed_for :visitor } + end + end +end diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb new file mode 100644 index 00000000000..75d208f2949 --- /dev/null +++ b/spec/features/security/group/public_access_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +describe 'Public group access', feature: true do + include AccessMatchers + include GroupAccessHelper + + + + describe 'GET /groups/:path' do + subject { group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/merge_requests' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to be_allowed_for :visitor } + end + end + + + describe 'GET /groups/:path/group_members' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to be_allowed_for :visitor } + end + end + + describe 'GET /groups/:path/edit' do + subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } + + context "when user not in group project" do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context "when user in group project" do + it { is_expected.to be_allowed_for project_group_member(:user) } + it { is_expected.to be_allowed_for :visitor } + end + end +end diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb index 65f8073c693..0194581dfd1 100644 --- a/spec/features/security/group_access_spec.rb +++ b/spec/features/security/group_access_spec.rb @@ -43,8 +43,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with mixed projects' do @@ -55,8 +53,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with internal projects' do @@ -67,8 +63,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with no projects' do @@ -77,8 +71,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end end @@ -93,8 +85,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with mixed projects' do @@ -105,8 +95,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with internal projects' do @@ -117,8 +105,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -127,8 +113,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end end @@ -143,8 +127,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with mixed projects' do @@ -155,8 +137,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with internal projects' do @@ -167,8 +147,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -177,8 +155,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end end @@ -193,8 +169,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with mixed projects' do @@ -205,8 +179,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } end context 'with internal projects' do @@ -217,8 +189,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -227,8 +197,6 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end end @@ -243,8 +211,6 @@ describe 'Group access', feature: true do it { is_expected.to be_denied_for group_member(:reporter) } it { is_expected.to be_denied_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with mixed projects' do @@ -255,8 +221,6 @@ describe 'Group access', feature: true do it { is_expected.to be_denied_for group_member(:reporter) } it { is_expected.to be_denied_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with internal projects' do @@ -267,8 +231,6 @@ describe 'Group access', feature: true do it { is_expected.to be_denied_for group_member(:reporter) } it { is_expected.to be_denied_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -277,8 +239,6 @@ describe 'Group access', feature: true do it { is_expected.to be_denied_for group_member(:reporter) } it { is_expected.to be_denied_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } end end end diff --git a/spec/support/group_access_helper.rb b/spec/support/group_access_helper.rb new file mode 100644 index 00000000000..a1a8fb2bd72 --- /dev/null +++ b/spec/support/group_access_helper.rb @@ -0,0 +1,17 @@ +module GroupAccessHelper + def group(visibility_level=0) + @group ||= create(:group, visibility_level: visibility_level) + end + + def project_group_member(access_level) + project = create(:project, visibility_level: group.visibility_level, group: group, name: 'B', path: 'B') + + create(:user).tap { |user| project.team.add_user(user, Gitlab::Access::DEVELOPER) } + end + + def group_member(access_level, grp=group()) + level = Object.const_get("Gitlab::Access::#{access_level.upcase}") + + create(:user).tap { |user| grp.add_user(user, level) } + end +end -- 2.18.1 From de251bcf6d0f1db8858fa38ac14e108c1b9ea00f Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 10 Mar 2016 10:33:09 -0300 Subject: [PATCH 06/22] Fix conflicts --- db/schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/schema.rb b/db/schema.rb index 1d4e18c5f95..082d681a176 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,6 +12,7 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20160308212903) do + # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- 2.18.1 From ec20fdf366843e60ed30abb5322c3c1b8f471b4a Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 16 Mar 2016 19:44:33 -0300 Subject: [PATCH 07/22] Code improvements and add Create group service --- app/assets/stylesheets/framework/common.scss | 46 ++++++++----------- app/assets/stylesheets/framework/mobile.scss | 2 +- app/assets/stylesheets/pages/projects.scss | 22 --------- app/controllers/groups_controller.rb | 4 +- app/controllers/users_controller.rb | 9 +--- app/finders/joined_groups_finder.rb | 2 +- app/models/ability.rb | 4 +- app/services/groups/base_service.rb | 4 ++ app/services/groups/create_service.rb | 17 +++++++ app/services/groups/update_service.rb | 13 ++---- app/services/projects/create_service.rb | 6 ++- app/views/groups/show.html.haml | 4 +- app/views/projects/_home_panel.html.haml | 2 +- ...roup_visibility_to_application_settings.rb | 20 +++++++- db/schema.rb | 2 +- .../security/group/internal_access_spec.rb | 2 - spec/services/groups/create_service_spec.rb | 22 +++++++++ 17 files changed, 97 insertions(+), 84 deletions(-) create mode 100644 app/services/groups/create_service.rb create mode 100644 spec/services/groups/create_service_spec.rb diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 18044b25b87..0931090b840 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -383,35 +383,25 @@ table { margin-right: -$gl-padding; border-top: 1px solid $border-color; } -.message { - border: 1px solid #ccc; - padding: 10px; - color: #333; -} -.message { - border: 1px solid #ccc; - padding: 10px; - color: #333; -} -.group-projects-show-title{ - h1 { - color: #313236; - margin: 0; - margin-bottom: 6px; - font-size: 23px; - font-weight: normal; - } +.cover-title{ + h1 { + color: #313236; + margin: 0; + margin-bottom: 6px; + font-size: 23px; + font-weight: normal; + } - .visibility-icon { - display: inline-block; - margin-left: 5px; - font-size: 18px; - color: $gray; - } + .visibility-icon { + display: inline-block; + margin-left: 5px; + font-size: 18px; + color: $gray; + } - p { - padding: 0 $gl-padding; - color: #5c5d5e; - } + p { + padding: 0 $gl-padding; + color: #5c5d5e; } +} diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 3bfac2ad9b5..d088228fe4c 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -48,7 +48,7 @@ display: block; } - .project-home-desc { + #project-home-desc { font-size: 21px; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index b1b76edfb32..1a7b0c1e278 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -61,28 +61,6 @@ } } - .project-home-desc { - h1 { - color: #313236; - margin: 0; - margin-bottom: 6px; - font-size: 23px; - font-weight: normal; - } - - .visibility-icon { - display: inline-block; - margin-left: 5px; - font-size: 18px; - color: $gray; - } - - p { - padding: 0 $gl-padding; - color: #5c5d5e; - } - } - .project-repo-buttons { margin-top: 20px; margin-bottom: 0px; diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 54f14e62ead..5baeb3def08 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -29,10 +29,8 @@ class GroupsController < Groups::ApplicationController def create @group = Group.new(group_params) - @group.name = @group.path.dup unless @group.name - if @group.save - @group.add_owner(current_user) + if Groups::CreateService.new(@group, current_user, group_params).execute redirect_to @group, notice: "Group '#{@group.name}' was successfully created." else render action: "new" diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 7b32572f822..481d00d6aae 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,13 +3,6 @@ class UsersController < ApplicationController before_action :set_user def show - @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) - - @projects = PersonalProjectsFinder.new(@user).execute(current_user) - @projects = @projects.page(params[:page]).per(PER_PAGE) - - @groups = JoinedGroupsFinder.new(@user).execute(current_user) - respond_to do |format| format.html @@ -115,7 +108,7 @@ class UsersController < ApplicationController end def load_groups - @groups = @user.groups.order_id_desc + @groups = JoinedGroupsFinder.new(@user).execute(current_user) end def projects_for_current_user diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb index 131b518563e..fbdf492c965 100644 --- a/app/finders/joined_groups_finder.rb +++ b/app/finders/joined_groups_finder.rb @@ -1,6 +1,6 @@ #Shows only authorized groups of a user class JoinedGroupsFinder - def initialize(user = nil) + def initialize(user) @user = user end diff --git a/app/models/ability.rb b/app/models/ability.rb index fe460ccdaca..bd001ef1545 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -295,8 +295,8 @@ class Ability end def can_read_group?(user, group) - is_project_member = ProjectsFinder.new.execute(user, group: group).any? - user.admin? || group.public? || group.internal? || is_project_member || group.users.include?(user) + user.admin? || group.public? || group.internal? || group.users.include?(user) || + ProjectsFinder.new.execute(user, group: group).any? end def namespace_abilities(user, namespace) diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 5becd475d3a..644ec7c013e 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -5,5 +5,9 @@ module Groups def initialize(group, user, params = {}) @group, @current_user, @params = group, user, params.dup end + + def add_error_message(message) + group.errors.add(:visibility_level, message) + end end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb new file mode 100644 index 00000000000..e2875aafb94 --- /dev/null +++ b/app/services/groups/create_service.rb @@ -0,0 +1,17 @@ +module Groups + class CreateService < Groups::BaseService + def execute + return false unless visibility_level_allowed?(params[:visibility_level]) + @group.name = @group.path.dup unless @group.name + @group.save(params) && @group.add_owner(current_user) + end + + private + + def visibility_level_allowed?(level) + allowed = Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) + add_error_message("Visibility level restricted by admin.") unless allowed + allowed + end + end +end diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index acb6c529c17..a7382c1e07c 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -5,7 +5,8 @@ module Groups class UpdateService < Groups::BaseService def execute - visibility_level_allowed?(params[:visibility_level]) ? group.update_attributes(params) : false + return false unless visibility_level_allowed?(params[:visibility_level]) + group.update_attributes(params) end private @@ -22,7 +23,7 @@ module Groups def visibility_by_project(level) projects_visibility = group.projects.pluck(:visibility_level) - allowed_by_projects = !projects_visibility.any?{|project_visibility| level.to_i < project_visibility } + allowed_by_projects = !projects_visibility.any?{ |project_visibility| level.to_i < project_visibility } add_error_message("Cannot be changed. There are projects with higher visibility permissions.") unless allowed_by_projects allowed_by_projects end @@ -32,13 +33,5 @@ module Groups add_error_message("You are not authorized to set this permission level.") unless allowed_by_user allowed_by_user end - - def add_error_message(message) - level_name = Gitlab::VisibilityLevel.level_name(params[:visibility_level]) - group.errors.add(:visibility_level, message) - end end end - - - diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 522fae79503..4c121106bda 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -12,7 +12,7 @@ module Projects # Make sure that the user is allowed to use the specified visibility # level - unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) && @project.visibility_level_allowed?(@project.visibility_level) + unless visibility_level_allowed? deny_visibility_level(@project) return @project end @@ -100,5 +100,9 @@ module Projects @project.import_start if @project.import? end + + def visibility_level_allowed? + Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) && @project.visibility_level_allowed?(@project.visibility_level) + end end end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0a8c2da7207..641191edf05 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -14,11 +14,11 @@ .avatar-holder = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" - .group-projects-show-title + .cover-title %h1 = @group.name - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{visibility_level_label(@group.visibility_level)} - #{project_visibility_level_description(@group.visibility_level)}"} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{visibility_level_label(@group.visibility_level)} - #{group_visibility_description(@group)}"} = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index dd16f504c92..e8434b5292c 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -2,7 +2,7 @@ .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') - .group-projects-show-title + .cover-title#project-home-desc %h1 = @project.name %span.visibility-icon.has_tooltip{data: { container: 'body' }, diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb index c2bdd7b31fa..b71322376fa 100644 --- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -1,11 +1,27 @@ +#Create visibility level field on DB +#Sets default_visibility_level to value on settings if not restricted +#If value is restricted takes higher visibility level allowed + class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration def up add_column :application_settings, :default_group_visibility, :integer - visibility = Settings.gitlab.default_groups_features['visibility_level'] - execute("update application_settings set default_group_visibility = #{visibility}") + execute("update application_settings set default_group_visibility = #{allowed_visibility_level}") end def down remove_column :application_settings, :default_group_visibility end + + private + def allowed_visibility_level + default_visibility = Settings.gitlab.default_groups_features['visibility_level'] + restricted_levels = current_application_settings.restricted_visibility_levels + return default_visibility unless restricted_levels.present? + + if restricted_levels.include?(default_visibility) + Gitlab::VisibilityLevel.values.select{ |vis_level| vis_level unless restricted_levels.include?(vis_level) }.last + else + default_visibility + end + end end diff --git a/db/schema.rb b/db/schema.rb index 082d681a176..292a9100d9c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160308212903) do +ActiveRecord::Schema.define(version: 20160309140734) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index 69a0fbb4468..4e781c23ee0 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -4,8 +4,6 @@ describe 'Internal group access', feature: true do include AccessMatchers include GroupAccessHelper - - describe 'GET /groups/:path' do subject { group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb new file mode 100644 index 00000000000..7dbc5297978 --- /dev/null +++ b/spec/services/groups/create_service_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Groups::CreateService, services: true do + let!(:user) { create(:user) } + let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + + describe "execute" do + let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC ) } + subject { service.execute } + + context "create groups without restricted visibility level" do + it { is_expected.to be_truthy } + end + + context "cannot create group with restricted visibility level" do + before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } + it { is_expected.to be_falsy } + end + end +end -- 2.18.1 From a18ac62756573a2da2c42ca50b6f30033be6fa63 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 16 Mar 2016 21:23:59 -0300 Subject: [PATCH 08/22] Block internal groups/projects visibility to external users --- app/finders/contributed_projects_finder.rb | 2 +- app/finders/joined_groups_finder.rb | 2 +- app/finders/personal_projects_finder.rb | 2 +- app/models/ability.rb | 7 +++++-- db/schema.rb | 6 +++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb index 0209649b017..4f7fe1c748b 100644 --- a/app/finders/contributed_projects_finder.rb +++ b/app/finders/contributed_projects_finder.rb @@ -11,7 +11,7 @@ class ContributedProjectsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user + if current_user && !current_user.external? relation = projects_visible_to_user(current_user) else relation = public_projects diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb index fbdf492c965..ff744689e3d 100644 --- a/app/finders/joined_groups_finder.rb +++ b/app/finders/joined_groups_finder.rb @@ -12,7 +12,7 @@ class JoinedGroupsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user + if current_user && !current_user.external? relation = groups_visible_to_user(current_user) else relation = public_groups diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index a61ffa22990..0e2d915da54 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -11,7 +11,7 @@ class PersonalProjectsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user + if current_user && !current_user.external? relation = projects_visible_to_user(current_user) else relation = public_projects diff --git a/app/models/ability.rb b/app/models/ability.rb index 455ea7bcc69..134ae440c9c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -295,8 +295,11 @@ class Ability end def can_read_group?(user, group) - user.admin? || group.public? || group.internal? || group.users.include?(user) || - ProjectsFinder.new.execute(user, group: group).any? + if user.external? + group.public? || ProjectsFinder.new.execute(user, group: group).any? + else + user.admin? || group.public? || group.internal? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? + end end def namespace_abilities(user, namespace) diff --git a/db/schema.rb b/db/schema.rb index f5e3e5bc861..f1bccd62745 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -777,9 +777,9 @@ ActiveRecord::Schema.define(version: 20160314143402) do t.string "type" t.string "title" t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "active", null: false t.text "properties" t.boolean "template", default: false t.boolean "push_events", default: true -- 2.18.1 From 0a7f7161198feaa9a4cae7c16669a0e6187aed33 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 17 Mar 2016 19:42:46 -0300 Subject: [PATCH 09/22] Code fixes --- app/assets/stylesheets/framework/blocks.scss | 18 +++++++++++ app/assets/stylesheets/framework/common.scss | 22 -------------- app/controllers/groups_controller.rb | 4 +-- app/finders/contributed_projects_finder.rb | 7 ++--- app/finders/groups_finder.rb | 10 ++++++- app/finders/joined_groups_finder.rb | 4 +-- app/finders/personal_projects_finder.rb | 17 +++++++---- app/models/ability.rb | 7 ++--- app/services/groups/base_service.rb | 16 ++++++++-- app/services/groups/create_service.rb | 19 ++++++------ app/services/groups/update_service.rb | 19 +----------- app/views/groups/show.html.haml | 10 +------ config/gitlab.yml.example | 4 --- ...roup_visibility_to_application_settings.rb | 12 ++------ lib/gitlab/current_settings.rb | 1 - lib/gitlab/visibility_level.rb | 13 ++++---- .../security/group/internal_access_spec.rb | 21 +++++++++---- .../security/group/private_access_spec.rb | 30 ++++++++++++------- .../security/group/public_access_spec.rb | 10 +++++++ spec/finders/groups_finder_spec.rb | 9 +++++- spec/finders/joined_groups_finder_spec.rb | 19 ++++++++++++ spec/finders/personal_projects_finder_spec.rb | 15 +++++++++- spec/services/groups/create_service_spec.rb | 12 ++++---- spec/support/group_access_helper.rb | 4 +++ 24 files changed, 178 insertions(+), 125 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index d20b77ffae9..31084872367 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -115,6 +115,24 @@ color: #4c4e54; font-size: 23px; line-height: 1.1; + + h1 { + color: #313236; + margin-bottom: 6px; + font-size: 23px; + } + + .visibility-icon { + display: inline-block; + margin-left: 5px; + font-size: 18px; + color: $gray; + } + + p { + padding: 0 $gl-padding; + color: #5c5d5e; + } } .cover-desc { diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index f4608cd80bb..ff551f151f1 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -385,25 +385,3 @@ table { margin-right: -$gl-padding; border-top: 1px solid $border-color; } - -.cover-title{ - h1 { - color: #313236; - margin: 0; - margin-bottom: 6px; - font-size: 23px; - font-weight: normal; - } - - .visibility-icon { - display: inline-block; - margin-left: 5px; - font-size: 18px; - color: $gray; - } - - p { - padding: 0 $gl-padding; - color: #5c5d5e; - } -} diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8243946c852..ba2057eb2c8 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -28,9 +28,9 @@ class GroupsController < Groups::ApplicationController end def create - @group = Group.new(group_params) + @group = Groups::CreateService.new(current_user, group_params).execute - if Groups::CreateService.new(@group, current_user, group_params).execute + if @group.persisted? redirect_to @group, notice: "Group '#{@group.name}' was successfully created." else render action: "new" diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb index 4f7fe1c748b..f8b04dfa2aa 100644 --- a/app/finders/contributed_projects_finder.rb +++ b/app/finders/contributed_projects_finder.rb @@ -10,8 +10,9 @@ class ContributedProjectsFinder # visible by this user. # # Returns an ActiveRecord::Relation. + def execute(current_user = nil) - if current_user && !current_user.external? + if current_user relation = projects_visible_to_user(current_user) else relation = public_projects @@ -24,9 +25,7 @@ class ContributedProjectsFinder def projects_visible_to_user(current_user) authorized = @user.contributed_projects.visible_to_user(current_user) - - union = Gitlab::SQL::Union. - new([authorized.select(:id), public_projects.select(:id)]) + union = Gitlab::SQL::Union.new([authorized.select(:id), public_projects.select(:id)]) Project.where("projects.id IN (#{union.to_sql})") end diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index ce62f5e762f..30698f80231 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -14,9 +14,17 @@ class GroupsFinder def all_groups(current_user) if current_user - [current_user.authorized_groups, Group.unscoped.public_and_internal_only] + user_groups(current_user) else [Group.unscoped.public_only] end end + + def user_groups(current_user) + if current_user.external? + [current_user.authorized_groups, Group.unscoped.public_only] + else + [current_user.authorized_groups, Group.unscoped.public_and_internal_only] + end + end end diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb index ff744689e3d..867eb661682 100644 --- a/app/finders/joined_groups_finder.rb +++ b/app/finders/joined_groups_finder.rb @@ -12,7 +12,7 @@ class JoinedGroupsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user && !current_user.external? + if current_user relation = groups_visible_to_user(current_user) else relation = public_groups @@ -29,7 +29,7 @@ class JoinedGroupsFinder # "@user" that "current_user" also has access to. def groups_visible_to_user(current_user) base = @user.authorized_groups.visible_to_user(current_user) - extra = public_and_internal_groups + extra = current_user.external? ? public_groups : public_and_internal_groups union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) Group.where("namespaces.id IN (#{union.to_sql})") diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 0e2d915da54..34f33e2353b 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -11,7 +11,7 @@ class PersonalProjectsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user && !current_user.external? + if current_user relation = projects_visible_to_user(current_user) else relation = public_projects @@ -23,10 +23,7 @@ class PersonalProjectsFinder private def projects_visible_to_user(current_user) - authorized = @user.personal_projects.visible_to_user(current_user) - - union = Gitlab::SQL::Union. - new([authorized.select(:id), public_and_internal_projects.select(:id)]) + union = Gitlab::SQL::Union.new(projects_for_user_ids(current_user)) Project.where("projects.id IN (#{union.to_sql})") end @@ -38,4 +35,14 @@ class PersonalProjectsFinder def public_and_internal_projects @user.personal_projects.public_and_internal_only end + + def projects_for_user_ids(current_user) + authorized = @user.personal_projects.visible_to_user(current_user) + + if current_user.external? + [authorized.select(:id), public_projects.select(:id)] + else + [authorized.select(:id), public_and_internal_projects.select(:id)] + end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 134ae440c9c..ffcf05dcd33 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -295,11 +295,8 @@ class Ability end def can_read_group?(user, group) - if user.external? - group.public? || ProjectsFinder.new.execute(user, group: group).any? - else - user.admin? || group.public? || group.internal? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? - end + user.admin? || group.public? || (group.internal? && !user.external?) || group.users.include?(user) || + ProjectsFinder.new.execute(user, group: group).any? end def namespace_abilities(user, namespace) diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 644ec7c013e..053b6a05281 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -6,8 +6,20 @@ module Groups @group, @current_user, @params = group, user, params.dup end - def add_error_message(message) - group.errors.add(:visibility_level, message) + private + + def visibility_allowed_for_user?(level) + allowed_by_user = Gitlab::VisibilityLevel.allowed_for?(current_user, level) + @group.errors.add(:visibility_level, "You are not authorized to set this permission level.") unless allowed_by_user + allowed_by_user + end + + def visibility_allowed_for_project?(level) + projects_visibility = group.projects.pluck(:visibility_level) + + allowed_by_projects = !projects_visibility.any? { |project_visibility| level.to_i < project_visibility } + @group.errors.add(:visibility_level, "Cannot be changed. There are projects with higher visibility permissions.") unless allowed_by_projects + allowed_by_projects end end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index e2875aafb94..38742369d82 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -1,17 +1,16 @@ module Groups class CreateService < Groups::BaseService - def execute - return false unless visibility_level_allowed?(params[:visibility_level]) - @group.name = @group.path.dup unless @group.name - @group.save(params) && @group.add_owner(current_user) + def initialize(user, params = {}) + @current_user, @params = user, params.dup + @group = Group.new(@params) end - private - - def visibility_level_allowed?(level) - allowed = Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) - add_error_message("Visibility level restricted by admin.") unless allowed - allowed + def execute + return @group unless visibility_allowed_for_user?(@params[:visibility_level]) + @group.name = @group.path.dup unless @group.name + @group.save + @group.add_owner(@current_user) + @group end end end diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index a7382c1e07c..b910e0fde98 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -14,24 +14,7 @@ module Groups def visibility_level_allowed?(level) return true unless level.present? - allowed_by_projects = visibility_by_project(level) - allowed_by_user = visibility_by_user(level) - - allowed_by_projects && allowed_by_user - end - - def visibility_by_project(level) - projects_visibility = group.projects.pluck(:visibility_level) - - allowed_by_projects = !projects_visibility.any?{ |project_visibility| level.to_i < project_visibility } - add_error_message("Cannot be changed. There are projects with higher visibility permissions.") unless allowed_by_projects - allowed_by_projects - end - - def visibility_by_user(level) - allowed_by_user = Gitlab::VisibilityLevel.allowed_for?(current_user, level) - add_error_message("You are not authorized to set this permission level.") unless allowed_by_user - allowed_by_user + visibility_allowed_for_project?(level) && visibility_allowed_for_user?(level) end end end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 2653a03059f..4be117667db 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -18,7 +18,7 @@ %h1 = @group.name - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{visibility_level_label(@group.visibility_level)} - #{group_visibility_description(@group)}"} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{group_visibility_description(@group)}"} = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username @@ -28,14 +28,6 @@ .cover-desc.description = markdown(@group.description, pipeline: :description) - %ul.nav-links - %li.active - = link_to "#activity", 'data-toggle' => 'tab' do - Activity - %li - = link_to "#projects", 'data-toggle' => 'tab' do - Projects - - if can?(current_user, :read_group, @group) %div{ class: container_class } .top-area diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index fc808e228de..05f127d622a 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -90,10 +90,6 @@ production: &base snippets: false builds: true - ## Default group features settings - default_groups_features: - visibility_level: 20 - ## Webhook settings # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10) # webhook_timeout: 10 diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb index b71322376fa..d5c78db0aa3 100644 --- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -13,15 +13,9 @@ class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration end private - def allowed_visibility_level - default_visibility = Settings.gitlab.default_groups_features['visibility_level'] - restricted_levels = current_application_settings.restricted_visibility_levels - return default_visibility unless restricted_levels.present? - if restricted_levels.include?(default_visibility) - Gitlab::VisibilityLevel.values.select{ |vis_level| vis_level unless restricted_levels.include?(vis_level) }.last - else - default_visibility - end + def allowed_visibility_level + allowed_levels = Gitlab::VisibilityLevel.values - current_application_settings.restricted_visibility_levels + allowed_levels.max end end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index a50d8e3d5a8..761b63e98f6 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -29,7 +29,6 @@ module Gitlab session_expire_delay: Settings.gitlab['session_expire_delay'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], - default_group_visibility: Settings.gitlab.default_groups_features['visibility_level'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index f6e0dd6afc0..f193fb282ab 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -6,19 +6,18 @@ module Gitlab module VisibilityLevel extend CurrentSettings + extend ActiveSupport::Concern + + included do + scope :public_only, -> { where(visibility_level: PUBLIC) } + scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } + end PRIVATE = 0 unless const_defined?(:PRIVATE) INTERNAL = 10 unless const_defined?(:INTERNAL) PUBLIC = 20 unless const_defined?(:PUBLIC) class << self - def included(base) - base.class_eval do - scope :public_only, -> { where(visibility_level: PUBLIC) } - scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } - end - end - def values options.values end diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index 4e781c23ee0..e44d4c32921 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -12,9 +12,12 @@ describe 'Internal group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } + end context "when user in group project" do @@ -31,9 +34,11 @@ describe 'Internal group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -50,9 +55,11 @@ describe 'Internal group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -70,9 +77,11 @@ describe 'Internal group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -89,9 +98,11 @@ describe 'Internal group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index 0d01310b449..8d8c61a618f 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -14,9 +14,11 @@ describe 'Private group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } - it { is_expected.to_not be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -33,9 +35,11 @@ describe 'Private group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } - it { is_expected.to_not be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -52,9 +56,11 @@ describe 'Private group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } - it { is_expected.to_not be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -72,9 +78,11 @@ describe 'Private group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } - it { is_expected.to_not be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do @@ -91,9 +99,11 @@ describe 'Private group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } - it { is_expected.to_not be_allowed_for :user } - it { is_expected.to_not be_allowed_for :visitor } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end context "when user in group project" do diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index 75d208f2949..5ff982504c5 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -14,9 +14,11 @@ describe 'Public group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end context "when user in group project" do @@ -33,9 +35,11 @@ describe 'Public group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end context "when user in group project" do @@ -52,9 +56,11 @@ describe 'Public group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end context "when user in group project" do @@ -72,9 +78,11 @@ describe 'Public group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end context "when user in group project" do @@ -91,9 +99,11 @@ describe 'Public group access', feature: true do it { is_expected.to be_allowed_for group_member(:master) } it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for external_guest } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end context "when user in group project" do diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index ed24954af7a..d0fd7af8cc3 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -18,7 +18,14 @@ describe GroupsFinder do describe 'with a user' do subject { finder.execute(user) } - it { is_expected.to eq([public_group, internal_group]) } + context 'normal user' do + it { is_expected.to eq([public_group, internal_group]) } + end + + context 'external user' do + before { user.update_attribute(external: true) } + it { is_expected.to eq([public_group]) } + end end end end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb index e2f6c593638..7b6fc837e5f 100644 --- a/spec/finders/joined_groups_finder_spec.rb +++ b/spec/finders/joined_groups_finder_spec.rb @@ -46,6 +46,25 @@ describe JoinedGroupsFinder do it { is_expected.to eq([public_group, private_group]) } end + + context 'external users' do + before do + profile_visitor.update_attributes(external: true) + public_group.add_user(profile_owner, Gitlab::Access::MASTER) + internal_group.add_user(profile_owner, Gitlab::Access::MASTER) + end + + subject { finder.execute(profile_visitor) } + + it "doest not show internal groups if not member" do + expect(subject).to eq([public_group]) + end + + it "shows internal groups if authorized" do + internal_group.add_user(profile_visitor, Gitlab::Access::MASTER) + expect(subject).to eq([public_group, internal_group]) + end + end end end end diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index 38817add456..8758f61903c 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -16,6 +16,11 @@ describe PersonalProjectsFinder do path: 'B') end + let!(:internal_project) do + create(:project, :internal, namespace: source_user.namespace, name: 'c', + path: 'C') + end + before do private_project.team << [current_user, Gitlab::Access::DEVELOPER] end @@ -29,6 +34,14 @@ describe PersonalProjectsFinder do describe 'with a current user' do subject { finder.execute(current_user) } - it { is_expected.to eq([private_project, public_project]) } + context 'normal user' do + it { is_expected.to eq([internal_project, private_project, public_project]) } + end + + context 'external' do + before { current_user.update_attributes(external: true) } + + it { is_expected.to eq([private_project, public_project]) } + end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 7dbc5297978..b938a2f0c05 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,22 +1,20 @@ require 'spec_helper' describe Groups::CreateService, services: true do - let!(:user) { create(:user) } - let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } - let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:user) { create(:user) } + let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } describe "execute" do - let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC ) } + let!(:service) { described_class.new(user, group_params ) } subject { service.execute } context "create groups without restricted visibility level" do - it { is_expected.to be_truthy } + it { is_expected.to be_persisted } end context "cannot create group with restricted visibility level" do before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } - it { is_expected.to be_falsy } + it { is_expected.to_not be_persisted } end end end diff --git a/spec/support/group_access_helper.rb b/spec/support/group_access_helper.rb index a1a8fb2bd72..c8ed0e406a1 100644 --- a/spec/support/group_access_helper.rb +++ b/spec/support/group_access_helper.rb @@ -14,4 +14,8 @@ module GroupAccessHelper create(:user).tap { |user| grp.add_user(user, level) } end + + def external_guest(grp=group()) + create(:user, external: true).tap { |user| grp.add_user(user, Gitlab::Access::GUEST) } + end end -- 2.18.1 From b959ae553b1243e081d557b1e545d30830931e5b Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 18 Mar 2016 13:28:16 +0100 Subject: [PATCH 10/22] Improve group visibility level feature --- .../groups/application_controller.rb | 2 +- app/controllers/groups_controller.rb | 11 ------ app/helpers/visibility_level_helper.rb | 3 +- app/models/ability.rb | 2 +- app/models/group.rb | 22 ++++++++++++ app/models/project.rb | 20 ++++++++--- app/services/groups/base_service.rb | 15 +++----- app/services/groups/create_service.rb | 8 +++-- app/services/groups/update_service.rb | 17 ++++----- app/services/projects/create_service.rb | 13 ++----- app/views/groups/show.html.haml | 3 +- lib/api/entities.rb | 2 +- lib/gitlab/visibility_level.rb | 4 +-- spec/factories/groups.rb | 12 +++++++ spec/finders/groups_finder_spec.rb | 13 +++---- spec/finders/personal_projects_finder_spec.rb | 19 ++++------ spec/helpers/visibility_level_helper_spec.rb | 9 ++--- spec/models/group_spec.rb | 10 +++--- spec/models/project_spec.rb | 8 ++--- spec/services/groups/create_service_spec.rb | 4 +-- spec/services/groups/update_service_spec.rb | 36 +++++++++---------- 21 files changed, 118 insertions(+), 115 deletions(-) diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index be801858eaf..795ce50fe92 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -9,7 +9,7 @@ class Groups::ApplicationController < ApplicationController end def authorize_read_group! - unless @group and can?(current_user, :read_group, @group) + unless @group && can?(current_user, :read_group, @group) if current_user.nil? return authenticate_user! else diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index ba2057eb2c8..b635fb150ae 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -105,17 +105,6 @@ class GroupsController < Groups::ApplicationController @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity end - # Dont allow unauthorized access to group - def authorize_read_group! - unless can?(current_user, :read_group, @group) - if current_user.nil? - return authenticate_user! - else - return render_404 - end - end - end - def authorize_create_group! unless can?(current_user, :create_group, nil) return render_404 diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 930cc883634..7fa18ba9079 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -85,7 +85,6 @@ module VisibilityLevelHelper end def skip_level?(form_model, level) - form_model.is_a?(Project) && - !form_model.visibility_level_allowed?(level) + form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level) end end diff --git a/app/models/ability.rb b/app/models/ability.rb index ffcf05dcd33..61d5e7dc859 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -295,7 +295,7 @@ class Ability end def can_read_group?(user, group) - user.admin? || group.public? || (group.internal? && !user.external?) || group.users.include?(user) || + user.admin? || group.public? || (group.internal? && !user.external?) || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? end diff --git a/app/models/group.rb b/app/models/group.rb index b094a65e3d6..17c69af4d1b 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -29,6 +29,8 @@ class Group < Namespace has_many :shared_projects, through: :project_group_links, source: :project validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } + validate :visibility_level_allowed_by_projects + validates :avatar, file_size: { maximum: 200.kilobytes.to_i } mount_uploader :avatar, AvatarUploader @@ -80,6 +82,26 @@ class Group < Namespace visibility_level end + def visibility_level_allowed_by_projects + unless visibility_level_allowed? + level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase + self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.") + end + end + + def visibility_level_allowed? + projects_visibility = self.projects.pluck(:visibility_level) + + allowed_by_projects = projects_visibility.none? { |project_visibility| self.visibility_level < project_visibility } + + unless allowed_by_projects + level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase + self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.") + end + + allowed_by_projects + end + def avatar_url(size = nil) if avatar.present? [gitlab_config.url, avatar.url].join diff --git a/app/models/project.rb b/app/models/project.rb index 2828385a5f6..7c10ab35431 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -73,7 +73,7 @@ class Project < ActiveRecord::Base update_column(:last_activity_at, self.created_at) end - # update visibility_levet of forks + # update visibility_level of forks after_update :update_forks_visibility_level def update_forks_visibility_level return unless visibility_level < visibility_level_was @@ -197,6 +197,7 @@ class Project < ActiveRecord::Base validate :avatar_type, if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } + validate :visibility_level_allowed_in_group add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -446,6 +447,12 @@ class Project < ActiveRecord::Base errors[:base] << ("Can't check your ability to create project") end + def visibility_level_allowed_in_group + unless visibility_level_allowed? + self.errors.add(:visibility_level, "#{self.visibility_level} is not allowed in a #{self.group.visibility_level} group.") + end + end + def to_param path end @@ -961,9 +968,14 @@ class Project < ActiveRecord::Base issues.opened.count end - def visibility_level_allowed?(level) - allowed_by_forks = forked? ? Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i) : true - allowed_by_groups = group.present? ? level.to_i <= group.visibility_level : true + def visibility_level_allowed?(level = self.visibility_level) + allowed_by_forks = if forked? + Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) + else + true + end + + allowed_by_groups = group.present? ? level <= group.visibility_level : true allowed_by_forks && allowed_by_groups end diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 053b6a05281..1db81216084 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -8,18 +8,13 @@ module Groups private - def visibility_allowed_for_user?(level) + def visibility_allowed_for_user? + level = group.visibility_level allowed_by_user = Gitlab::VisibilityLevel.allowed_for?(current_user, level) - @group.errors.add(:visibility_level, "You are not authorized to set this permission level.") unless allowed_by_user - allowed_by_user - end - def visibility_allowed_for_project?(level) - projects_visibility = group.projects.pluck(:visibility_level) - - allowed_by_projects = !projects_visibility.any? { |project_visibility| level.to_i < project_visibility } - @group.errors.add(:visibility_level, "Cannot be changed. There are projects with higher visibility permissions.") unless allowed_by_projects - allowed_by_projects + group.errors.add(:visibility_level, "#{level} has been restricted by your GitLab administrator.") unless allowed_by_user + + allowed_by_user end end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 38742369d82..f605ccca81b 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -2,14 +2,16 @@ module Groups class CreateService < Groups::BaseService def initialize(user, params = {}) @current_user, @params = user, params.dup - @group = Group.new(@params) end def execute - return @group unless visibility_allowed_for_user?(@params[:visibility_level]) + @group = Group.new(params) + + return @group unless visibility_allowed_for_user? + @group.name = @group.path.dup unless @group.name @group.save - @group.add_owner(@current_user) + @group.add_owner(current_user) @group end end diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index b910e0fde98..0b0c5a35d37 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -1,20 +1,15 @@ -#Checks visibility level permission check before updating a group -#Do not allow to put Group visibility level smaller than its projects -#Do not allow unauthorized permission levels +# Checks visibility level permission check before updating a group +# Do not allow to put Group visibility level smaller than its projects +# Do not allow unauthorized permission levels module Groups class UpdateService < Groups::BaseService def execute - return false unless visibility_level_allowed?(params[:visibility_level]) - group.update_attributes(params) - end - - private + group.assign_attributes(params) - def visibility_level_allowed?(level) - return true unless level.present? + return false unless visibility_allowed_for_user? - visibility_allowed_for_project?(level) && visibility_allowed_for_user?(level) + group.save end end end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 4c121106bda..cebfc432002 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -9,13 +9,8 @@ module Projects @project = Project.new(params) - # Make sure that the user is allowed to use the specified visibility - # level - - unless visibility_level_allowed? - deny_visibility_level(@project) - return @project - end + # Make sure that the user is allowed to use the specified visibility level + return @project unless visibility_level_allowed? # Set project name from path if @project.name.present? && @project.path.present? @@ -55,9 +50,7 @@ module Projects @project.save if @project.persisted? && !@project.import? - unless @project.create_repository - raise 'Failed to create repository' - end + raise 'Failed to create repository' unless @project.create_repository end end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 4be117667db..222c3e4a40e 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -17,8 +17,7 @@ .cover-title %h1 = @group.name - - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{group_visibility_description(@group)}"} + %span.visibility-icon.has_tooltip{ data: { container: 'body', placement: 'left' }, title: group_visibility_description(@group) } = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 20565e368dd..197e826e5bc 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -85,7 +85,7 @@ module API end class Group < Grape::Entity - expose :id, :name, :path, :description + expose :id, :name, :path, :description, :visibility_level expose :avatar_url expose :web_url do |group, options| diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index f193fb282ab..58cd8df5219 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -9,8 +9,8 @@ module Gitlab extend ActiveSupport::Concern included do - scope :public_only, -> { where(visibility_level: PUBLIC) } - scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } + scope :public_only, -> { where(visibility_level: PUBLIC) } + scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } end PRIVATE = 0 unless const_defined?(:PRIVATE) diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 4a3a155d7ff..2d47a6f6c4c 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -3,5 +3,17 @@ FactoryGirl.define do sequence(:name) { |n| "group#{n}" } path { name.downcase.gsub(/\s/, '_') } type 'Group' + + trait :public do + visibility_level Gitlab::VisibilityLevel::PUBLIC + end + + trait :internal do + visibility_level Gitlab::VisibilityLevel::INTERNAL + end + + trait :private do + visibility_level Gitlab::VisibilityLevel::PRIVATE + end end end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index d0fd7af8cc3..d5d111e8d15 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' describe GroupsFinder do describe '#execute' do - let(:user) { create(:user) } - let!(:private_group) { create(:group, visibility_level: 0) } - let!(:internal_group) { create(:group, visibility_level: 10) } - let!(:public_group) { create(:group, visibility_level: 20) } - let(:finder) { described_class.new } + let(:user) { create(:user) } + let!(:private_group) { create(:group, :private) } + let!(:internal_group) { create(:group, :internal) } + let!(:public_group) { create(:group, :public) } + let(:finder) { described_class.new } describe 'execute' do describe 'without a user' do @@ -23,7 +23,8 @@ describe GroupsFinder do end context 'external user' do - before { user.update_attribute(external: true) } + let(:user) { create(:user, external: true) } + it { is_expected.to eq([public_group]) } end end diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index 8758f61903c..a4681fe59d8 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -1,24 +1,17 @@ require 'spec_helper' describe PersonalProjectsFinder do - let(:source_user) { create(:user) } - let(:current_user) { create(:user) } - - let(:finder) { described_class.new(source_user) } - - let!(:public_project) do - create(:project, :public, namespace: source_user.namespace, name: 'A', - path: 'A') - end + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + let(:finder) { described_class.new(source_user) } + let!(:public_project) { create(:project, :public, namespace: source_user.namespace) } let!(:private_project) do - create(:project, :private, namespace: source_user.namespace, name: 'B', - path: 'B') + create(:project, :private, namespace: source_user.namespace, path: 'mepmep') end let!(:internal_project) do - create(:project, :internal, namespace: source_user.namespace, name: 'c', - path: 'C') + create(:project, :internal, namespace: source_user.namespace, path: 'C') end before do diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index ef60ef75fbe..ff98249570d 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -66,13 +66,8 @@ describe VisibilityLevelHelper do describe "skip_level?" do describe "forks" do - let(:project) { create(:project, :internal) } - let(:fork_project) { create(:forked_project_with_submodules) } - - before do - fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id) - fork_project.save - end + let(:project) { create(:project, :internal) } + let(:fork_project) { create(:project, forked_from_project: project) } it "skips levels" do expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 135d298e10f..0591aa089d8 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -57,18 +57,18 @@ describe Group, models: true do end describe 'scopes' do - let!(:private_group) { create(:group, visibility_level: 0) } - let!(:internal_group) { create(:group, visibility_level: 10) } - let!(:public_group) { create(:group, visibility_level: 20) } + let!(:private_group) { create(:group, :private) } + let!(:internal_group) { create(:group, :internal) } + let!(:public_group) { create(:group, :public) } describe 'public_only' do - subject { described_class.public_only } + subject { described_class.public_only.to_a } it{ is_expected.to eq([public_group]) } end describe 'public_and_internal_only' do - subject { described_class.public_and_internal_only } + subject { described_class.public_and_internal_only.to_a } it{ is_expected.to eq([public_group, internal_group]) } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 7fd3726c6ad..74383204250 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -571,12 +571,8 @@ describe Project, models: true do 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 + let(:project) { create(:project, :internal) } + let(:forked_project) { create(:project, forked_from_project: project) } 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 } diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b938a2f0c05..6aefb48a4e8 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe Groups::CreateService, services: true do - let!(:user) { create(:user) } - let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } + let!(:user) { create(:user) } + let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } describe "execute" do let!(:service) { described_class.new(user, group_params ) } diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index c759e32342d..9d427ff2d90 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' describe Groups::UpdateService, services: true do - let!(:user) { create(:user) } - let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } - let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:user) { create(:user) } + let!(:private_group) { create(:group, :private) } + let!(:internal_group) { create(:group, :internal) } + let!(:public_group) { create(:group, :public) } describe "execute" do context "project visibility_level validation" do @@ -14,28 +14,28 @@ describe Groups::UpdateService, services: true do before do public_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :public, group: public_group, name: 'B', path: 'B') + create(:project, :public, group: public_group) end it "cant downgrade permission level" do expect(service.execute).to be_falsy - expect(public_group.errors.count).to eq(1) + expect(public_group.errors.count).to eq(2) end end - context "internal group with internal project" do - let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) } - - before do - internal_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :internal, group: internal_group, name: 'B', path: 'B') + context "internal group with internal project" do + let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) } + + before do + internal_group.add_user(user, Gitlab::Access::MASTER) + create(:project, :internal, group: internal_group) + end + + it "cant downgrade permission level" do + expect(service.execute).to be_falsy + expect(internal_group.errors.count).to eq(2) + end end - - it "cant downgrade permission level" do - expect(service.execute).to be_falsy - expect(internal_group.errors.count).to eq(1) - end - end end end -- 2.18.1 From 8b830b8c3b32774e8ccf562b8bc9dbce3ecf3073 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 18 Mar 2016 21:04:53 -0300 Subject: [PATCH 11/22] Fix specs --- app/models/project.rb | 10 ++++++++-- app/services/projects/create_service.rb | 5 ++++- features/steps/shared/group.rb | 4 ++-- spec/controllers/namespaces_controller_spec.rb | 2 +- spec/controllers/uploads_controller_spec.rb | 5 +++-- spec/features/projects_spec.rb | 8 ++++---- spec/features/security/group_access_spec.rb | 2 +- spec/finders/projects_finder_spec.rb | 2 +- spec/finders/snippets_finder_spec.rb | 2 +- spec/models/project_spec.rb | 2 +- spec/requests/api/projects_spec.rb | 1 + 11 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 7c10ab35431..3352959a53d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -969,8 +969,9 @@ class Project < ActiveRecord::Base end def visibility_level_allowed?(level = self.visibility_level) - allowed_by_forks = if forked? - Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) + allowed_by_forks = if forked? && forked_project_link.forked_from_project_id.present? + from_project = eager_load_forked_from_project + Gitlab::VisibilityLevel.allowed_fork_levels(from_project.visibility_level).include?(level) else true end @@ -980,6 +981,11 @@ class Project < ActiveRecord::Base allowed_by_forks && allowed_by_groups end + #Necessary to retrieve many-to-many associations on new forks before validating visibility level + def eager_load_forked_from_project + Project.find(forked_project_link.forked_from_project_id) + end + def runners_token ensure_runners_token! end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index cebfc432002..c4b8420f9f2 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -10,7 +10,10 @@ module Projects @project = Project.new(params) # Make sure that the user is allowed to use the specified visibility level - return @project unless visibility_level_allowed? + unless visibility_level_allowed? + deny_visibility_level(@project) + return @project + end # Set project name from path if @project.name.present? && @project.path.present? diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index fe6736dacd4..ca32faa3159 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -38,7 +38,7 @@ module SharedGroup def is_member_of(username, groupname, role) @project_count ||= 0 user = User.find_by(name: username) || create(:user, name: username) - group = Group.find_by(name: groupname) || create(:group, name: groupname) + group = Group.find_by(name: groupname) || create(:group, name: groupname, visibility_level: Gitlab::VisibilityLevel::PUBLIC) group.add_user(user, role) project ||= create(:project, namespace: group, path: "project#{@project_count}") create(:closed_issue_event, project: project) @@ -47,6 +47,6 @@ module SharedGroup end def owned_group - @owned_group ||= Group.find_by(name: "Owned") + @owned_group ||= Group.find_by(name: "Owned", visibility_level: Gitlab::VisibilityLevel::PUBLIC) end end diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 77436958711..6350c9c6e48 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -15,7 +15,7 @@ describe NamespacesController do end context "when the namespace belongs to a group" do - let!(:group) { create(:group) } + let!(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let!(:project) { create(:project, namespace: group) } context "when the group has public projects" do diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index af5d043cf02..bf5b13f2645 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -30,7 +30,7 @@ describe UploadsController do end end end - + context "when not signed in" do it "responds with status 200" do get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" @@ -126,11 +126,12 @@ describe UploadsController do end context "when viewing a group avatar" do - let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let!(:project) { create(:project, namespace: group) } context "when the group has public projects" do before do + group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) project.update_attribute(:visibility_level, Project::PUBLIC) end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index ed97b6cb577..e54a5a0b72a 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -12,25 +12,25 @@ feature 'Project', feature: true do it 'parses Markdown' do project.update_attribute(:description, 'This is **my** project') visit path - expect(page).to have_css('.project-home-desc > p > strong') + expect(page).to have_css('.cover-title > p > strong') end it 'passes through html-pipeline' do project.update_attribute(:description, 'This project is the :poop:') visit path - expect(page).to have_css('.project-home-desc > p > img') + expect(page).to have_css('.cover-title > p > img') end it 'sanitizes unwanted tags' do project.update_attribute(:description, "```\ncode\n```") visit path - expect(page).not_to have_css('.project-home-desc code') + expect(page).not_to have_css('.cover-title code') end it 'permits `rel` attribute on links' do project.update_attribute(:description, 'https://google.com/') visit path - expect(page).to have_css('.project-home-desc a[rel]') + expect(page).to have_css('.cover-title a[rel]') end end diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb index 0194581dfd1..55bbeafba33 100644 --- a/spec/features/security/group_access_spec.rb +++ b/spec/features/security/group_access_spec.rb @@ -4,7 +4,7 @@ describe 'Group access', feature: true do include AccessMatchers def group - @group ||= create(:group) + @group ||= create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) end def create_project(access_level) diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index fae0da9d898..194c9543772 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ProjectsFinder do describe '#execute' do let(:user) { create(:user) } - let(:group) { create(:group) } + let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let!(:private_project) do create(:project, :private, name: 'A', path: 'A') diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index 7fdc5e5d7aa..b8940483dfb 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe SnippetsFinder do let(:user) { create :user } let(:user1) { create :user } - let(:group) { create :group } + let(:group) { create :group, visibility_level: Gitlab::VisibilityLevel::PUBLIC } let(:project1) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :private, group: group) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 74383204250..dba7ffc8565 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -442,7 +442,7 @@ describe Project, models: true do end describe '.trending' do - let(:group) { create(:group) } + let(:group) { create(:group, :public) } let(:project1) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :public, group: group) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index a6699cdc81c..a5d4985dc78 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -275,6 +275,7 @@ describe API::API, api: true do it 'should not allow a non-admin to use a restricted visibility level' do post api('/projects', user), @project + expect(response.status).to eq(400) expect(json_response['message']['visibility_level'].first).to( match('restricted by your GitLab administrator') -- 2.18.1 From 8db1292139cfdac4c29c03b876b68b9e752cf75a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 21:03:53 +0100 Subject: [PATCH 12/22] Tweaks, refactoring, and specs --- app/assets/stylesheets/framework/mobile.scss | 2 +- app/controllers/application_controller.rb | 61 ----- .../groups/application_controller.rb | 27 +- app/controllers/groups/avatars_controller.rb | 2 + .../groups/group_members_controller.rb | 3 - .../groups/milestones_controller.rb | 12 +- app/controllers/groups_controller.rb | 19 +- .../projects/application_controller.rb | 75 +++++- .../projects/avatars_controller.rb | 2 +- .../projects/uploads_controller.rb | 6 +- app/controllers/projects_controller.rb | 4 +- app/finders/contributed_projects_finder.rb | 23 +- app/finders/group_projects_finder.rb | 43 +++ app/finders/groups_finder.rb | 26 +- app/finders/issuable_finder.rb | 4 +- app/finders/joined_groups_finder.rb | 32 +-- app/finders/personal_projects_finder.rb | 35 +-- app/finders/projects_finder.rb | 77 +----- app/finders/union_finder.rb | 11 + app/helpers/groups_helper.rb | 4 - app/helpers/visibility_level_helper.rb | 8 + app/models/ability.rb | 23 +- app/models/group.rb | 9 +- app/models/project.rb | 48 ++-- app/services/base_service.rb | 7 +- app/services/create_snippet_service.rb | 3 +- app/services/groups/base_service.rb | 13 +- app/services/groups/create_service.rb | 5 +- app/services/groups/update_service.rb | 13 +- app/services/projects/create_service.rb | 6 +- app/services/projects/update_service.rb | 29 +-- app/services/update_snippet_service.rb | 1 - app/views/groups/show.html.haml | 57 ++-- app/views/layouts/nav/_group.html.haml | 70 +++-- app/views/projects/_home_panel.html.haml | 18 +- app/views/shared/groups/_group.html.haml | 2 +- app/views/shared/projects/_project.html.haml | 3 +- ...roup_visibility_to_application_settings.rb | 3 +- db/schema.rb | 2 +- doc/api/groups.md | 1 + lib/api/groups.rb | 2 +- lib/gitlab/visibility_level.rb | 2 + spec/controllers/groups_controller_spec.rb | 39 --- .../controllers/namespaces_controller_spec.rb | 21 +- spec/controllers/uploads_controller_spec.rb | 9 +- spec/features/projects_spec.rb | 8 +- .../security/group/internal_access_spec.rb | 178 +++++++------ .../security/group/private_access_spec.rb | 177 ++++++------- .../security/group/public_access_spec.rb | 177 ++++++------- spec/features/security/group_access_spec.rb | 244 ------------------ .../security/project/internal_access_spec.rb | 109 ++++---- .../security/project/private_access_spec.rb | 110 ++++---- .../security/project/public_access_spec.rb | 111 +++++--- ...groups_helper.rb => groups_helper_spec.rb} | 15 -- spec/support/group_access_helper.rb | 21 -- spec/support/matchers/access_matchers.rb | 2 +- 56 files changed, 825 insertions(+), 1189 deletions(-) create mode 100644 app/finders/group_projects_finder.rb create mode 100644 app/finders/union_finder.rb delete mode 100644 spec/features/security/group_access_spec.rb rename spec/helpers/{groups_helper.rb => groups_helper_spec.rb} (59%) delete mode 100644 spec/support/group_access_helper.rb diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index bf874b9b822..5ea4f9a49db 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -48,7 +48,7 @@ display: block; } - #project-home-desc { + .project-home-desc { font-size: 21px; } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1f55b18e0b1..3a0eb96a460 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,6 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? - helper_method :repository, :can_collaborate_with_project? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -118,47 +117,6 @@ class ApplicationController < ActionController::Base abilities.allowed?(object, action, subject) end - def project - unless @project - namespace = params[:namespace_id] - id = params[:project_id] || params[:id] - - # Redirect from - # localhost/group/project.git - # to - # localhost/group/project - # - if id =~ /\.git\Z/ - redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return - end - - project_path = "#{namespace}/#{id}" - @project = Project.find_with_namespace(project_path) - - if @project and can?(current_user, :read_project, @project) - if @project.path_with_namespace != project_path - redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return - end - @project - elsif current_user.nil? - @project = nil - authenticate_user! - else - @project = nil - render_404 and return - end - end - @project - end - - def repository - @repository ||= project.repository - end - - def authorize_project!(action) - return access_denied! unless can?(current_user, action, project) - end - def access_denied! render "errors/access_denied", layout: "errors", status: 404 end @@ -167,14 +125,6 @@ class ApplicationController < ActionController::Base render "errors/git_not_found.html", layout: "errors", status: 404 end - def method_missing(method_sym, *arguments, &block) - if method_sym.to_s =~ /\Aauthorize_(.*)!\z/ - authorize_project!($1.to_sym) - else - super - end - end - def render_403 head :forbidden end @@ -183,10 +133,6 @@ class ApplicationController < ActionController::Base render file: Rails.root.join("public", "404"), layout: false, status: "404" end - def require_non_empty_project - redirect_to @project if @project.empty_repo? - end - def no_cache_headers response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" response.headers["Pragma"] = "no-cache" @@ -412,13 +358,6 @@ class ApplicationController < ActionController::Base current_user.nil? && root_path == request.path end - def can_collaborate_with_project?(project = nil) - project ||= @project - - can?(current_user, :push_code, project) || - (current_user && current_user.already_forked?(project)) - end - private def set_default_sort diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 795ce50fe92..949b4a6c25a 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -1,21 +1,32 @@ class Groups::ApplicationController < ApplicationController layout 'group' + + skip_before_action :authenticate_user! before_action :group private def group - @group ||= Group.find_by(path: params[:group_id]) - end + unless @group + id = params[:group_id] || params[:id] + @group = Group.find_by(path: id) + + unless @group && can?(current_user, :read_group, @group) + @group = nil - def authorize_read_group! - unless @group && can?(current_user, :read_group, @group) - if current_user.nil? - return authenticate_user! - else - return render_404 + if current_user.nil? + authenticate_user! + else + render_404 + end end end + + @group + end + + def group_projects + @projects ||= GroupProjectsFinder.new(group).execute(current_user) end def authorize_admin_group! diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index 76c87366baa..ad2c20b42db 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -1,4 +1,6 @@ class Groups::AvatarsController < Groups::ApplicationController + before_action :authorize_admin_group! + def destroy @group.remove_avatar! @group.save diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 0e902c4bb43..d5ef33888c6 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,8 +1,5 @@ class Groups::GroupMembersController < Groups::ApplicationController - skip_before_action :authenticate_user!, only: [:index] - # Authorize - before_action :authorize_read_group! before_action :authorize_admin_group_member!, except: [:index, :leave] def index diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 0c2a350bc39..0028f072d5b 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -1,10 +1,10 @@ class Groups::MilestonesController < Groups::ApplicationController include GlobalMilestones - before_action :projects + before_action :group_projects before_action :milestones, only: [:index] before_action :milestone, only: [:show, :update] - before_action :authorize_group_milestone!, only: [:create, :update] + before_action :authorize_admin_milestones!, only: [:new, :create, :update] def index end @@ -17,7 +17,7 @@ class Groups::MilestonesController < Groups::ApplicationController project_ids = params[:milestone][:project_ids] title = milestone_params[:title] - @group.projects.where(id: project_ids).each do |project| + @projects.where(id: project_ids).each do |project| Milestones::CreateService.new(project, current_user, milestone_params).execute end @@ -37,7 +37,7 @@ class Groups::MilestonesController < Groups::ApplicationController private - def authorize_group_milestone! + def authorize_admin_milestones! return render_404 unless can?(current_user, :admin_milestones, group) end @@ -48,8 +48,4 @@ class Groups::MilestonesController < Groups::ApplicationController def milestone_path(title) group_milestone_path(@group, title.to_slug.to_s, title: title) end - - def projects - @projects ||= @group.projects - end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index b635fb150ae..48565f44ffb 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -5,16 +5,15 @@ class GroupsController < Groups::ApplicationController respond_to :html - skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests] + before_action :authenticate_user!, only: [:new, :create] before_action :group, except: [:index, :new, :create] # Authorize - before_action :authorize_read_group!, except: [:index, :new, :create] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete] + before_action :group_projects, only: [:show, :projects, :activity, :issues, :merge_requests] before_action :event_filter, only: [:activity] layout :determine_layout @@ -39,12 +38,13 @@ class GroupsController < Groups::ApplicationController def show @last_push = current_user.recent_push if current_user + @projects = @projects.includes(:namespace) @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? - @shared_projects = @group.shared_projects + @shared_projects = GroupProjectsFinder.new(group, shared: true).execute(current_user) respond_to do |format| format.html @@ -77,7 +77,7 @@ class GroupsController < Groups::ApplicationController end def projects - @projects = @group.projects.page(params[:page]) + @projects = @projects.sorted_by_activity.page(params[:page]) end def update @@ -96,15 +96,6 @@ class GroupsController < Groups::ApplicationController protected - def group - @group ||= Group.find_by(path: params[:id]) - @group || render_404 - end - - def load_projects - @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity - end - def authorize_create_group! unless can?(current_user, :create_group, nil) return render_404 diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index a326bc58215..276f80f800a 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -1,20 +1,73 @@ class Projects::ApplicationController < ApplicationController - before_action :project - before_action :repository + skip_before_action :authenticate_user! + before_action :project, :repository layout 'project' - def authenticate_user! - # Restrict access to Projects area only - # for non-signed users - if !current_user + helper_method :repository, :can_collaborate_with_project? + + private + + def project + unless @project + namespace = params[:namespace_id] id = params[:project_id] || params[:id] - project_with_namespace = "#{params[:namespace_id]}/#{id}" - @project = Project.find_with_namespace(project_with_namespace) - return if @project && @project.public? + # Redirect from + # localhost/group/project.git + # to + # localhost/group/project + # + if id =~ /\.git\Z/ + redirect_to request.original_url.gsub(/\.git\/?\Z/, '') + return + end + + project_path = "#{namespace}/#{id}" + @project = Project.find_with_namespace(project_path) + + if @project && can?(current_user, :read_project, @project) + if @project.path_with_namespace != project_path + redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) + end + else + @project = nil + + if current_user.nil? + authenticate_user! + else + render_404 + end + end + end + + @project + end + + def repository + @repository ||= project.repository + end + + def can_collaborate_with_project?(project = nil) + project ||= @project + + can?(current_user, :push_code, project) || + (current_user && current_user.already_forked?(project)) + end + + def authorize_project!(action) + return access_denied! unless can?(current_user, action, project) + end + + def method_missing(method_sym, *arguments, &block) + if method_sym.to_s =~ /\Aauthorize_(.*)!\z/ + authorize_project!($1.to_sym) + else + super end + end - super + def require_non_empty_project + redirect_to @project if @project.empty_repo? end def require_branch_head @@ -26,8 +79,6 @@ class Projects::ApplicationController < ApplicationController end end - private - def apply_diff_view_cookie! view = params[:view] || cookies[:diff_view] cookies.permanent[:diff_view] = params[:view] = view if view diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index a6bebc46b06..72921b3aa14 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -1,7 +1,7 @@ class Projects::AvatarsController < Projects::ApplicationController include BlobHelper - before_action :project + before_action :authorize_admin_project!, only: [:destroy] def show @blob = @repository.blob_at_branch('master', @project.avatar_in_git) diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index e1fe7ea2114..94c51eeb94d 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -1,7 +1,9 @@ class Projects::UploadsController < Projects::ApplicationController - skip_before_action :authenticate_user!, :reject_blocked!, :project, + skip_before_action :reject_blocked!, :project, :repository, if: -> { action_name == 'show' && image? } + before_action :authenticate_user!, only: [:create] + def create link_to_file = ::Projects::UploadService.new(project, params[:file]). execute @@ -26,6 +28,8 @@ class Projects::UploadsController < Projects::ApplicationController send_file uploader.file.path, disposition: disposition end + private + def uploader return @uploader if defined?(@uploader) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c9930480770..928817ba811 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,7 +1,7 @@ -class ProjectsController < ApplicationController +class ProjectsController < Projects::ApplicationController include ExtractsPath - skip_before_action :authenticate_user!, only: [:show, :activity] + before_action :authenticate_user!, except: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists? diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb index f8b04dfa2aa..a685719555c 100644 --- a/app/finders/contributed_projects_finder.rb +++ b/app/finders/contributed_projects_finder.rb @@ -1,4 +1,4 @@ -class ContributedProjectsFinder +class ContributedProjectsFinder < UnionFinder def initialize(user) @user = user end @@ -10,27 +10,20 @@ class ContributedProjectsFinder # visible by this user. # # Returns an ActiveRecord::Relation. - def execute(current_user = nil) - if current_user - relation = projects_visible_to_user(current_user) - else - relation = public_projects - end + segments = all_projects(current_user) - relation.includes(:namespace).order_id_desc + find_union(segments, Project).includes(:namespace).order_id_desc end private - def projects_visible_to_user(current_user) - authorized = @user.contributed_projects.visible_to_user(current_user) - union = Gitlab::SQL::Union.new([authorized.select(:id), public_projects.select(:id)]) + def all_projects(current_user) + projects = [] - Project.where("projects.id IN (#{union.to_sql})") - end + projects << @user.contributed_projects.visible_to_user(current_user) if current_user + projects << @user.contributed_projects.public_to_user(current_user) - def public_projects - @user.contributed_projects.public_only + projects end end diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb new file mode 100644 index 00000000000..84fe468ae5d --- /dev/null +++ b/app/finders/group_projects_finder.rb @@ -0,0 +1,43 @@ +class GroupProjectsFinder < UnionFinder + def initialize(group, options = {}) + @group = group + @options = options + end + + def execute(current_user = nil) + segments = group_projects(current_user) + + find_union(segments, Project) + end + + private + + def group_projects(current_user) + include_owned = @options.fetch(:owned, true) + include_shared = @options.fetch(:shared, true) + + projects = [] + + if current_user + if @group.users.include?(current_user) + projects << @group.projects if include_owned + projects << @group.shared_projects if include_shared + else + if include_owned + projects << @group.projects.visible_to_user(current_user) + projects << @group.projects.public_to_user(current_user) + end + + if include_shared + projects << @group.shared_projects.visible_to_user(current_user) + projects << @group.shared_projects.public_to_user(current_user) + end + end + else + projects << @group.projects.public_only if include_owned + projects << @group.shared_projects.public_only if include_shared + end + + projects + end +end diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index 30698f80231..4e43f42e9e1 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -1,30 +1,18 @@ -class GroupsFinder +class GroupsFinder < UnionFinder def execute(current_user = nil) segments = all_groups(current_user) - if segments.length > 1 - union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) - Group.where("namespaces.id IN (#{union.to_sql})").order_id_desc - else - segments.first - end + find_union(segments, Group).order_id_desc end private def all_groups(current_user) - if current_user - user_groups(current_user) - else - [Group.unscoped.public_only] - end - end + groups = [] + + groups << current_user.authorized_groups if current_user + groups << Group.unscoped.public_to_user(current_user) - def user_groups(current_user) - if current_user.external? - [current_user.authorized_groups, Group.unscoped.public_only] - else - [current_user.authorized_groups, Group.unscoped.public_and_internal_only] - end + groups end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 19e8c7a92be..7510f6e9e29 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -81,7 +81,7 @@ class IssuableFinder elsif current_user && params[:authorized_only].presence && !current_user_related? @projects = current_user.authorized_projects.reorder(nil) else - @projects = ProjectsFinder.new.execute(current_user, group: group). + @projects = GroupProjectsFinder.new(group).execute(current_user). reorder(nil) end end @@ -170,7 +170,7 @@ class IssuableFinder end def by_scope(items) - case params[:scope] + case params[:scope] || 'all' when 'created-by-me', 'authored' then items.where(author_id: current_user.id) when 'all' then diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb index 867eb661682..2a3f0296d37 100644 --- a/app/finders/joined_groups_finder.rb +++ b/app/finders/joined_groups_finder.rb @@ -1,5 +1,4 @@ -#Shows only authorized groups of a user -class JoinedGroupsFinder +class JoinedGroupsFinder < UnionFinder def initialize(user) @user = user end @@ -12,34 +11,19 @@ class JoinedGroupsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user - relation = groups_visible_to_user(current_user) - else - relation = public_groups - end + segments = all_groups(current_user) - relation.order_id_desc + find_union(segments, Group).order_id_desc end private - # Returns the groups the user in "current_user" can see. - # - # This list includes all public/internal projects as well as the projects of - # "@user" that "current_user" also has access to. - def groups_visible_to_user(current_user) - base = @user.authorized_groups.visible_to_user(current_user) - extra = current_user.external? ? public_groups : public_and_internal_groups - union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) - - Group.where("namespaces.id IN (#{union.to_sql})") - end + def all_groups(current_user) + groups = [] - def public_groups - @user.authorized_groups.public_only - end + groups << @user.authorized_groups.visible_to_user(current_user) if current_user + groups << @user.authorized_groups.public_to_user(current_user) - def public_and_internal_groups - @user.authorized_groups.public_and_internal_only + groups end end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 34f33e2353b..3ad4bd5f066 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -1,4 +1,4 @@ -class PersonalProjectsFinder +class PersonalProjectsFinder < UnionFinder def initialize(user) @user = user end @@ -11,38 +11,19 @@ class PersonalProjectsFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) - if current_user - relation = projects_visible_to_user(current_user) - else - relation = public_projects - end + segments = all_projects(current_user) - relation.includes(:namespace).order_id_desc + find_union(segments, Project).includes(:namespace).order_id_desc end private - def projects_visible_to_user(current_user) - union = Gitlab::SQL::Union.new(projects_for_user_ids(current_user)) + def all_projects(current_user) + projects = [] - Project.where("projects.id IN (#{union.to_sql})") - end - - def public_projects - @user.personal_projects.public_only - end - - def public_and_internal_projects - @user.personal_projects.public_and_internal_only - end - - def projects_for_user_ids(current_user) - authorized = @user.personal_projects.visible_to_user(current_user) + projects << @user.personal_projects.visible_to_user(current_user) if current_user + projects << @user.personal_projects.public_to_user(current_user) - if current_user.external? - [authorized.select(:id), public_projects.select(:id)] - else - [authorized.select(:id), public_and_internal_projects.select(:id)] - end + projects end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 3a5fc5b5907..2f0a9659d15 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -1,81 +1,18 @@ -class ProjectsFinder - # Returns all projects, optionally including group projects a user has access - # to. - # - # ## Examples - # - # Retrieving all public projects: - # - # ProjectsFinder.new.execute - # - # Retrieving all public/internal projects and those the given user has access - # to: - # - # ProjectsFinder.new.execute(some_user) - # - # Retrieving all public/internal projects as well as the group's projects the - # user has access to: - # - # ProjectsFinder.new.execute(some_user, group: some_group) - # - # Returns an ActiveRecord::Relation. +class ProjectsFinder < UnionFinder def execute(current_user = nil, options = {}) - group = options[:group] + segments = all_projects(current_user) - if group - segments = group_projects(current_user, group) - else - segments = all_projects(current_user) - end - - if segments.length > 1 - union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) - - Project.where("projects.id IN (#{union.to_sql})") - else - segments.first - end + find_union(segments, Project) end private - def group_projects(current_user, group) - return [group.projects.public_only] unless current_user - - user_group_projects = [ - group_projects_for_user(current_user, group), - group.shared_projects.visible_to_user(current_user) - ] - if current_user.external? - user_group_projects << group.projects.public_only - else - user_group_projects << group.projects.public_and_internal_only - end - end - def all_projects(current_user) - return [public_projects] unless current_user + projects = [] - if current_user.external? - [current_user.authorized_projects, public_projects] - else - [current_user.authorized_projects, public_and_internal_projects] - end - end - - def group_projects_for_user(current_user, group) - if group.users.include?(current_user) - group.projects - else - group.projects.visible_to_user(current_user) - end - end - - def public_projects - Project.unscoped.public_only - end + projects << current_user.authorized_projects if current_user + projects << Project.unscoped.public_to_user(current_user) - def public_and_internal_projects - Project.unscoped.public_and_internal_only + projects end end diff --git a/app/finders/union_finder.rb b/app/finders/union_finder.rb new file mode 100644 index 00000000000..33cd1a491f3 --- /dev/null +++ b/app/finders/union_finder.rb @@ -0,0 +1,11 @@ +class UnionFinder + def find_union(segments, klass) + if segments.length > 1 + union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) + + klass.where("#{klass.table_name}.id IN (#{union.to_sql})") + else + segments.first + end + end +end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 42e09149bd7..b1f0a765bb9 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -43,8 +43,4 @@ module GroupsHelper full_title end end - - def group_visibility_description(group) - "#{visibility_level_label(group.visibility_level)} - #{group_visibility_level_description(group.visibility_level)}" - end end diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 7fa18ba9079..5b1bfb261a5 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -63,6 +63,14 @@ module VisibilityLevelHelper end end + def group_visibility_icon_description(group) + "#{visibility_level_label(group.visibility_level)} - #{group_visibility_level_description(group.visibility_level)}" + end + + def project_visibility_icon_description(project) + "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}" + end + def visibility_level_label(level) Project.visibility_levels.key(level) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 88d7ecf3a16..de9253fcdd8 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -114,6 +114,13 @@ class Ability # Push abilities on the users team role rules.push(*project_team_rules(project.team, user)) + if project.owner == user || + (project.group && project.group.has_owner?(user)) || + user.admin? + + rules.push(*project_owner_rules) + end + if project.public? || (project.internal? && !user.external?) rules.push(*public_project_rules) @@ -121,14 +128,6 @@ class Ability rules << :read_build if project.public_builds? end - if project.owner == user || user.admin? - rules.push(*project_admin_rules) - end - - if project.group && project.group.has_owner?(user) - rules.push(*project_admin_rules) - end - if project.archived? rules -= project_archived_rules end @@ -228,8 +227,8 @@ class Ability ] end - def project_admin_rules - @project_admin_rules ||= project_master_rules + [ + def project_owner_rules + @project_owner_rules ||= project_master_rules + [ :change_namespace, :change_visibility_level, :rename_project, @@ -275,7 +274,7 @@ class Ability rules << :read_group if can_read_group?(user, group) - # Only group masters and group owners can create new projects and change permission level + # Only group masters and group owners can create new projects if group.has_master?(user) || group.has_owner?(user) || user.admin? rules += [ :create_projects, @@ -298,7 +297,7 @@ class Ability def can_read_group?(user, group) user.admin? || group.public? || (group.internal? && !user.external?) || group.users.include?(user) || - ProjectsFinder.new.execute(user, group: group).any? + GroupProjectsFinder.new(group).execute(user).any? end def namespace_abilities(user, namespace) diff --git a/app/models/group.rb b/app/models/group.rb index 17c69af4d1b..900fcd71ff3 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -83,16 +83,9 @@ class Group < Namespace end def visibility_level_allowed_by_projects - unless visibility_level_allowed? - level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase - self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.") - end - end - - def visibility_level_allowed? projects_visibility = self.projects.pluck(:visibility_level) - allowed_by_projects = projects_visibility.none? { |project_visibility| self.visibility_level < project_visibility } + allowed_by_projects = projects_visibility.all? { |project_visibility| self.visibility_level >= project_visibility } unless allowed_by_projects level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase diff --git a/app/models/project.rb b/app/models/project.rb index c1374fc9b3c..ab31a635a82 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -197,7 +197,8 @@ class Project < ActiveRecord::Base validate :avatar_type, if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } - validate :visibility_level_allowed_in_group + validate :visibility_level_allowed_by_group + validate :visibility_level_allowed_as_fork add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -441,16 +442,25 @@ class Project < ActiveRecord::Base def check_limit unless creator.can_create_project? or namespace.kind == 'group' - errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") + self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") end rescue - errors[:base] << ("Can't check your ability to create project") + self.errors.add(:base, "Can't check your ability to create project") end - def visibility_level_allowed_in_group - unless visibility_level_allowed? - self.errors.add(:visibility_level, "#{self.visibility_level} is not allowed in a #{self.group.visibility_level} group.") - end + def visibility_level_allowed_by_group + return if visibility_level_allowed_by_group? + + level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase + group_level_name = Gitlab::VisibilityLevel.level_name(self.group.visibility_level).downcase + self.errors.add(:visibility_level, "#{level_name} is not allowed in a #{group_level_name} group.") + end + + def visibility_level_allowed_as_fork + return if visibility_level_allowed_as_fork? + + level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase + self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") end def to_param @@ -965,22 +975,22 @@ class Project < ActiveRecord::Base issues.opened.count end - def visibility_level_allowed?(level = self.visibility_level) - allowed_by_forks = if forked? && forked_project_link.forked_from_project_id.present? - from_project = eager_load_forked_from_project - Gitlab::VisibilityLevel.allowed_fork_levels(from_project.visibility_level).include?(level) - else - true - end + def visibility_level_allowed_as_fork?(level = self.visibility_level) + return true unless forked? && forked_project_link.forked_from_project_id.present? + + from_project = self.forked_from_project + from_project ||= Project.find(forked_project_link.forked_from_project_id) + Gitlab::VisibilityLevel.allowed_fork_levels(from_project.visibility_level).include?(level) + end - allowed_by_groups = group.present? ? level <= group.visibility_level : true + def visibility_level_allowed_by_group?(level = self.visibility_level) + return true unless group - allowed_by_forks && allowed_by_groups + level <= group.visibility_level end - #Necessary to retrieve many-to-many associations on new forks before validating visibility level - def eager_load_forked_from_project - Project.find(forked_project_link.forked_from_project_id) + def visibility_level_allowed?(level = self.visibility_level) + visibility_level_allowed_as_fork?(level) && visibility_level_allowed_by_group?(level) end def runners_token diff --git a/app/services/base_service.rb b/app/services/base_service.rb index 8563633816c..0d55ba5a981 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -43,12 +43,9 @@ class BaseService def deny_visibility_level(model, denied_visibility_level = nil) denied_visibility_level ||= model.visibility_level - level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level) + level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level).downcase - model.errors.add( - :visibility_level, - "#{level_name} visibility has been restricted by your GitLab administrator" - ) + model.errors.add(:visibility_level, "#{level_name} has been restricted by your GitLab administrator") end private diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb index 101a3df5eee..9884cb96661 100644 --- a/app/services/create_snippet_service.rb +++ b/app/services/create_snippet_service.rb @@ -6,8 +6,7 @@ class CreateSnippetService < BaseService snippet = project.snippets.build(params) end - unless Gitlab::VisibilityLevel.allowed_for?(current_user, - params[:visibility_level]) + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) deny_visibility_level(snippet) return snippet end diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 1db81216084..1642115583d 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -1,20 +1,9 @@ module Groups - class BaseService + class BaseService < BaseService attr_accessor :group, :current_user, :params def initialize(group, user, params = {}) @group, @current_user, @params = group, user, params.dup end - - private - - def visibility_allowed_for_user? - level = group.visibility_level - allowed_by_user = Gitlab::VisibilityLevel.allowed_for?(current_user, level) - - group.errors.add(:visibility_level, "#{level} has been restricted by your GitLab administrator.") unless allowed_by_user - - allowed_by_user - end end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index f605ccca81b..46c2a53e1f6 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -7,7 +7,10 @@ module Groups def execute @group = Group.new(params) - return @group unless visibility_allowed_for_user? + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) + deny_visibility_level(@group) + return @group + end @group.name = @group.path.dup unless @group.name @group.save diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 0b0c5a35d37..b70e2e4aaa9 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -5,9 +5,18 @@ module Groups class UpdateService < Groups::BaseService def execute - group.assign_attributes(params) + # check that user is allowed to set specified visibility_level + new_visibility = params[:visibility_level] + if new_visibility && new_visibility.to_i != group.visibility_level + unless can?(current_user, :change_visibility_level, group) && + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + + deny_visibility_level(group, new_visibility) + return group + end + end - return false unless visibility_allowed_for_user? + group.assign_attributes(params) group.save end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index c4b8420f9f2..501e58c1407 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -10,7 +10,7 @@ module Projects @project = Project.new(params) # Make sure that the user is allowed to use the specified visibility level - unless visibility_level_allowed? + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) deny_visibility_level(@project) return @project end @@ -96,9 +96,5 @@ module Projects @project.import_start if @project.import? end - - def visibility_level_allowed? - Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) && @project.visibility_level_allowed?(@project.visibility_level) - end end end diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 895e089bea3..941df08995c 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -3,16 +3,13 @@ module Projects def execute # check that user is allowed to set specified visibility_level new_visibility = params[:visibility_level] - if new_visibility - if new_visibility.to_i != project.visibility_level - unless can?(current_user, :change_visibility_level, project) && - Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) - deny_visibility_level(project, new_visibility) - return project - end + if new_visibility && new_visibility.to_i != project.visibility_level + unless can?(current_user, :change_visibility_level, project) && + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + + deny_visibility_level(project, new_visibility) + return project end - - return false unless visibility_level_allowed?(new_visibility) end new_branch = params[:default_branch] @@ -27,19 +24,5 @@ module Projects 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 diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb index e9328bb7323..93af8f21972 100644 --- a/app/services/update_snippet_service.rb +++ b/app/services/update_snippet_service.rb @@ -9,7 +9,6 @@ class UpdateSnippetService < BaseService def execute # check that user is allowed to set specified visibility_level new_visibility = params[:visibility_level] - if new_visibility && new_visibility.to_i != snippet.visibility_level unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) deny_visibility_level(snippet, new_visibility) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 222c3e4a40e..5a9fa5d9a4d 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -17,7 +17,7 @@ .cover-title %h1 = @group.name - %span.visibility-icon.has_tooltip{ data: { container: 'body', placement: 'left' }, title: group_visibility_description(@group) } + %span.visibility-icon.has_tooltip{ data: { container: 'body' }, title: group_visibility_icon_description(@group) } = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username @@ -27,34 +27,29 @@ .cover-desc.description = markdown(@group.description, pipeline: :description) -- if can?(current_user, :read_group, @group) - %div{ class: container_class } - .top-area - %ul.nav-links - %li.active - = link_to "#projects", 'data-toggle' => 'tab' do - All Projects - - if @shared_projects.present? - %li - = link_to "#shared", 'data-toggle' => 'tab' do - Shared Projects - .nav-controls - = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false - = render 'shared/projects/dropdown' - - if can? current_user, :create_projects, @group - = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do - = icon('plus') - New Project - - .tab-content - .tab-pane.active#projects - = render "projects", projects: @projects - +%div{ class: container_class } + .top-area + %ul.nav-links + %li.active + = link_to "#projects", 'data-toggle' => 'tab' do + All Projects - if @shared_projects.present? - .tab-pane#shared - = render "shared_projects", projects: @shared_projects - -- else - %p.nav-links.no-top - No projects to show + %li + = link_to "#shared", 'data-toggle' => 'tab' do + Shared Projects + .nav-controls + = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false + = render 'shared/projects/dropdown' + - if can? current_user, :create_projects, @group + = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do + = icon('plus') + New Project + + .tab-content + .tab-pane.active#projects + = render "projects", projects: @projects + + - if @shared_projects.present? + .tab-pane#shared + = render "shared_projects", projects: @shared_projects diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 59411ae1da1..55940741dc0 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -12,40 +12,38 @@ = icon('group fw') %span Group - - if can?(current_user, :read_group, @group) - = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do - = icon('dashboard fw') - %span - Activity - - if current_user - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones' do - = icon('clock-o fw') - %span - Milestones - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues' do - = icon('exclamation-circle fw') - %span - Issues - - if current_user - %span.count= number_with_delimiter(Issue.opened.of_group(@group).count) - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests' do - = icon('tasks fw') - %span - Merge Requests - - if current_user - %span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - = icon('users fw') + = nav_link(path: 'groups#activity') do + = link_to activity_group_path(@group), title: 'Activity' do + = icon('dashboard fw') + %span + Activity + = nav_link(controller: [:group, :milestones]) do + = link_to group_milestones_path(@group), title: 'Milestones' do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group), title: 'Issues' do + = icon('exclamation-circle fw') + %span + Issues + - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.count= number_with_delimiter(issues.count) + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group), title: 'Merge Requests' do + = icon('tasks fw') + %span + Merge Requests + - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.count= number_with_delimiter(merge_requests.count) + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do + = icon('users fw') + %span + Members + - if can?(current_user, :admin_group, @group) + = nav_link(html_options: { class: "separate-item" }) do + = link_to edit_group_path(@group), title: 'Settings' do + = icon ('cogs fw') %span - Members - - if can?(current_user, :admin_group, @group) - = nav_link(html_options: { class: "separate-item" }) do - = link_to edit_group_path(@group), title: 'Settings' do - = icon ('cogs fw') - %span - Settings + Settings diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index e8434b5292c..d4bbafbd40f 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -2,21 +2,21 @@ .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') - .cover-title#project-home-desc + .cover-title.project-home-desc %h1 = @project.name - %span.visibility-icon.has_tooltip{data: { container: 'body' }, - title: "#{visibility_level_label(@project.visibility_level)} - #{project_visibility_level_description(@project.visibility_level)}"} + %span.visibility-icon.has_tooltip{data: { container: 'body' }, title: project_visibility_icon_description(@project)} = visibility_level_icon(@project.visibility_level, fw: false) - - if @project.description.present? + - if @project.description.present? + .cover-desc.project-home-desc = markdown(@project.description, pipeline: :description) - - if forked_from_project = @project.forked_from_project - %p - Forked from - = link_to project_path(forked_from_project) do - = forked_from_project.namespace.try(:name) + - if forked_from_project = @project.forked_from_project + .cover-desc + Forked from + = link_to project_path(forked_from_project) do + = forked_from_project.namespace.try(:name) .cover-controls - if current_user diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 67a59f420f3..03103d46bb9 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -21,7 +21,7 @@ = icon('users') = number_with_delimiter(group.users.count) - %span{title: group_visibility_description(group)} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: group_visibility_icon_description(group)} = visibility_level_icon(group.visibility_level, fw: false) = image_tag group_icon(group), class: "avatar s40 hidden-xs" diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 872d2bdf46d..3b987987676 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -27,8 +27,7 @@ %span = icon('star') = project.star_count - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, - title: "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}"} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: project_visibility_icon_description(project)} = visibility_level_icon(project.visibility_level, fw: false) .title diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb index d5c78db0aa3..62d96907c8f 100644 --- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -5,7 +5,7 @@ class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration def up add_column :application_settings, :default_group_visibility, :integer - execute("update application_settings set default_group_visibility = #{allowed_visibility_level}") + execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}") end def down @@ -15,6 +15,7 @@ class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration private def allowed_visibility_level + # TODO: Don't use `current_application_settings` allowed_levels = Gitlab::VisibilityLevel.values - current_application_settings.restricted_visibility_levels allowed_levels.max end diff --git a/db/schema.rb b/db/schema.rb index 719fedcdbfb..2aa37cc590c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -590,8 +590,8 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.string "type" t.string "description", default: "", null: false t.string "avatar" - t.integer "visibility_level", default: 0, null: false t.boolean "share_with_group_lock", default: false + t.integer "visibility_level", default: 20, null: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree diff --git a/doc/api/groups.md b/doc/api/groups.md index d47e79ba47f..d1b5c9f5f04 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -111,6 +111,7 @@ Parameters: - `name` (required) - The name of the group - `path` (required) - The path of the group - `description` (optional) - The group's description +- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public. ## Transfer project to group diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 1a14d870a4a..c165de21a75 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -31,7 +31,7 @@ module API authorize! :create_group, current_user required_attributes! [:name, :path] - attrs = attributes_for_keys [:name, :path, :description] + attrs = attributes_for_keys [:name, :path, :description, :visibility_level] @group = Group.new(attrs) if @group.save diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index 58cd8df5219..0a1f66b0726 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -11,6 +11,8 @@ module Gitlab included do scope :public_only, -> { where(visibility_level: PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } + + scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only } end PRIVATE = 0 unless const_defined?(:PRIVATE) diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 91db3fd1ee2..938e97298b6 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -20,43 +20,4 @@ describe GroupsController do end end end - - describe 'GET show' do - let(:group) { create(:group, visibility_level: 20) } - - it 'checks if group can be read' do - expect(controller).to receive(:authorize_read_group!) - get :show, id: group.path - end - end - - describe 'POST create' do - before { sign_in(create(:user)) } - - it 'checks if group can be created' do - expect(controller).to receive(:authorize_create_group!) - post :create, { group: { name: "any params" } } - end - end - - describe 'DELETE destroy' do - before { sign_in(create(:user)) } - let(:group) { create(:group, visibility_level: 20) } - - it 'checks if group can be deleted' do - expect(controller).to receive(:authorize_admin_group!) - delete :destroy, id: group.path - end - end - - describe 'PUT update' do - before { sign_in(create(:user)) } - let(:group) { create(:group, visibility_level: 20) } - - it 'checks if group can be updated' do - expect_any_instance_of(Groups::UpdateService).to receive(:execute) - expect(controller).to receive(:authorize_admin_group!) - put :update, id: group.path, group: { name: 'test' } - end - end end diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 6350c9c6e48..41ae6063ae0 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -15,12 +15,11 @@ describe NamespacesController do end context "when the namespace belongs to a group" do - let!(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let!(:project) { create(:project, namespace: group) } + let!(:group) { create(:group) } - context "when the group has public projects" do + context "when the group is public" do before do - project.update_attribute(:visibility_level, Project::PUBLIC) + group.update_attribute(:visibility_level, Group::PUBLIC) end context "when not signed in" do @@ -44,27 +43,27 @@ describe NamespacesController do end end - context "when the project doesn't have public projects" do + context "when the group is private" do context "when not signed in" do it "does not redirect to the sign in page" do get :show, id: group.path expect(response).not_to redirect_to(new_user_session_path) end end + context "when signed in" do before do sign_in(user) end - context "when the user has access to the project" do + context "when the user has access to the group" do before do - project.team << [user, :master] + group.add_developer(user) end context "when the user is blocked" do before do user.block - project.team << [user, :master] end it "redirects to the sign in page" do @@ -83,11 +82,11 @@ describe NamespacesController do end end - context "when the user doesn't have access to the project" do - it "redirects to the group's page" do + context "when the user doesn't have access to the group" do + it "responds with status 404" do get :show, id: group.path - expect(response).to redirect_to(group_path(group)) + expect(response.status).to eq(404) end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index bf5b13f2645..0947744fc47 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -127,12 +127,10 @@ describe UploadsController do context "when viewing a group avatar" do let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } - let!(:project) { create(:project, namespace: group) } - context "when the group has public projects" do + context "when the group is public" do before do group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) - project.update_attribute(:visibility_level, Project::PUBLIC) end context "when not signed in" do @@ -156,7 +154,7 @@ describe UploadsController do end end - context "when the project doesn't have public projects" do + context "when the group is private" do context "when signed in" do before do sign_in(user) @@ -164,13 +162,12 @@ describe UploadsController do context "when the user has access to the project" do before do - project.team << [user, :master] + project.add_developer(user) end context "when the user is blocked" do before do user.block - project.team << [user, :master] end it "redirects to the sign in page" do diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index e54a5a0b72a..ed97b6cb577 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -12,25 +12,25 @@ feature 'Project', feature: true do it 'parses Markdown' do project.update_attribute(:description, 'This is **my** project') visit path - expect(page).to have_css('.cover-title > p > strong') + expect(page).to have_css('.project-home-desc > p > strong') end it 'passes through html-pipeline' do project.update_attribute(:description, 'This project is the :poop:') visit path - expect(page).to have_css('.cover-title > p > img') + expect(page).to have_css('.project-home-desc > p > img') end it 'sanitizes unwanted tags' do project.update_attribute(:description, "```\ncode\n```") visit path - expect(page).not_to have_css('.cover-title code') + expect(page).not_to have_css('.project-home-desc code') end it 'permits `rel` attribute on links' do project.update_attribute(:description, 'https://google.com/') visit path - expect(page).to have_css('.cover-title a[rel]') + expect(page).to have_css('.project-home-desc a[rel]') end end diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index e44d4c32921..d76eb454fe5 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -1,113 +1,109 @@ require 'rails_helper' -describe 'Internal group access', feature: true do +describe 'Internal Group access', feature: true do include AccessMatchers - include GroupAccessHelper - describe 'GET /groups/:path' do - subject { group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } + let(:group) { create(:group, :internal) } + let(:project) { create(:project, :internal, group: group) } - end + let(:owner) { create(:user) } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + let(:project_guest) { create(:user) } + + before do + group.add_user(owner, Gitlab::Access::OWNER) + group.add_user(master, Gitlab::Access::MASTER) + group.add_user(developer, Gitlab::Access::DEVELOPER) + group.add_user(reporter, Gitlab::Access::REPORTER) + group.add_user(guest, Gitlab::Access::GUEST) + + project.team << [project_guest, :guest] end - describe 'GET /groups/:path/issues' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } + describe "Group should be internal" do + describe '#internal?' do + subject { group.internal? } + it { is_expected.to be_truthy } end + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + describe 'GET /groups/:path' do + subject { group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end - describe 'GET /groups/:path/merge_requests' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + describe 'GET /groups/:path/merge_requests' do + subject { merge_requests_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end describe 'GET /groups/:path/group_members' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + subject { group_group_members_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end describe 'GET /groups/:path/edit' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::INTERNAL)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + subject { edit_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end end diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index 8d8c61a618f..8ca4a0ac83b 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -1,114 +1,109 @@ require 'rails_helper' -describe 'Private group access', feature: true do +describe 'Private Group access', feature: true do include AccessMatchers - include GroupAccessHelper + let(:group) { create(:group, :private) } + let(:project) { create(:project, :private, group: group) } + let(:owner) { create(:user) } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } - describe 'GET /groups/:path' do - subject { group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end + let(:project_guest) { create(:user) } - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + before do + group.add_user(owner, Gitlab::Access::OWNER) + group.add_user(master, Gitlab::Access::MASTER) + group.add_user(developer, Gitlab::Access::DEVELOPER) + group.add_user(reporter, Gitlab::Access::REPORTER) + group.add_user(guest, Gitlab::Access::GUEST) + + project.team << [project_guest, :guest] end - describe 'GET /groups/:path/issues' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } + describe "Group should be private" do + describe '#private?' do + subject { group.private? } + it { is_expected.to be_truthy } end + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + describe 'GET /groups/:path' do + subject { group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end - describe 'GET /groups/:path/merge_requests' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + describe 'GET /groups/:path/merge_requests' do + subject { merge_requests_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end describe 'GET /groups/:path/group_members' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + subject { group_group_members_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } end describe 'GET /groups/:path/edit' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PRIVATE)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - it { is_expected.to be_denied_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to_not be_allowed_for :visitor } - end + subject { edit_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end end diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index 5ff982504c5..f556fabb51e 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -1,114 +1,109 @@ require 'rails_helper' -describe 'Public group access', feature: true do +describe 'Public Group access', feature: true do include AccessMatchers - include GroupAccessHelper + let(:group) { create(:group, :public) } + let(:project) { create(:project, :public, group: group) } + let(:owner) { create(:user) } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } - describe 'GET /groups/:path' do - subject { group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - it { is_expected.to be_allowed_for :external } - end + let(:project_guest) { create(:user) } - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to be_allowed_for :visitor } - end + before do + group.add_user(owner, Gitlab::Access::OWNER) + group.add_user(master, Gitlab::Access::MASTER) + group.add_user(developer, Gitlab::Access::DEVELOPER) + group.add_user(reporter, Gitlab::Access::REPORTER) + group.add_user(guest, Gitlab::Access::GUEST) + + project.team << [project_guest, :guest] end - describe 'GET /groups/:path/issues' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - it { is_expected.to be_allowed_for :external } + describe "Group should be public" do + describe '#public?' do + subject { group.public? } + it { is_expected.to be_truthy } end + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to be_allowed_for :visitor } - end + describe 'GET /groups/:path' do + subject { group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } end - describe 'GET /groups/:path/merge_requests' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - it { is_expected.to be_allowed_for :external } - end + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to be_allowed_for :visitor } - end + describe 'GET /groups/:path/merge_requests' do + subject { merge_requests_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } end describe 'GET /groups/:path/group_members' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - it { is_expected.to be_allowed_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to be_allowed_for :visitor } - end + subject { group_group_members_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for project_guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } end describe 'GET /groups/:path/edit' do - subject { issues_group_path(group(Gitlab::VisibilityLevel::PUBLIC)) } - - context "when user not in group project" do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for external_guest } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - it { is_expected.to be_allowed_for :external } - end - - context "when user in group project" do - it { is_expected.to be_allowed_for project_group_member(:user) } - it { is_expected.to be_allowed_for :visitor } - end + subject { edit_group_path(group) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for project_guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end end diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb deleted file mode 100644 index 55bbeafba33..00000000000 --- a/spec/features/security/group_access_spec.rb +++ /dev/null @@ -1,244 +0,0 @@ -require 'rails_helper' - -describe 'Group access', feature: true do - include AccessMatchers - - def group - @group ||= create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) - end - - def create_project(access_level) - if access_level == :mixed - create(:empty_project, :public, group: group) - create(:empty_project, :internal, group: group) - else - create(:empty_project, access_level, group: group) - end - end - - def group_member(access_level, grp = group()) - level = Object.const_get("Gitlab::Access::#{access_level.upcase}") - - create(:user).tap do |user| - grp.add_user(user, level) - end - end - - describe 'GET /groups/new' do - subject { new_group_path } - - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe 'GET /groups/:path' do - subject { group_path(group) } - - context 'with public projects' do - let!(:project) { create_project(:public) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with mixed projects' do - let!(:project) { create_project(:mixed) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with internal projects' do - let!(:project) { create_project(:internal) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with no projects' do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - end - - describe 'GET /groups/:path/issues' do - subject { issues_group_path(group) } - - context 'with public projects' do - let!(:project) { create_project(:public) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with mixed projects' do - let!(:project) { create_project(:mixed) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with internal projects' do - let!(:project) { create_project(:internal) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with no projects' do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - end - - describe 'GET /groups/:path/merge_requests' do - subject { merge_requests_group_path(group) } - - context 'with public projects' do - let!(:project) { create_project(:public) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with mixed projects' do - let!(:project) { create_project(:mixed) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with internal projects' do - let!(:project) { create_project(:internal) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with no projects' do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - end - - describe 'GET /groups/:path/group_members' do - subject { group_group_members_path(group) } - - context 'with public projects' do - let!(:project) { create_project(:public) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with mixed projects' do - let!(:project) { create_project(:mixed) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with internal projects' do - let!(:project) { create_project(:internal) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with no projects' do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_allowed_for group_member(:master) } - it { is_expected.to be_allowed_for group_member(:reporter) } - it { is_expected.to be_allowed_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - end - - describe 'GET /groups/:path/edit' do - subject { edit_group_path(group) } - - context 'with public projects' do - let!(:project) { create_project(:public) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_denied_for group_member(:master) } - it { is_expected.to be_denied_for group_member(:reporter) } - it { is_expected.to be_denied_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with mixed projects' do - let!(:project) { create_project(:mixed) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_denied_for group_member(:master) } - it { is_expected.to be_denied_for group_member(:reporter) } - it { is_expected.to be_denied_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with internal projects' do - let!(:project) { create_project(:internal) } - - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_denied_for group_member(:master) } - it { is_expected.to be_denied_for group_member(:reporter) } - it { is_expected.to be_denied_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - - context 'with no projects' do - it { is_expected.to be_allowed_for group_member(:owner) } - it { is_expected.to be_denied_for group_member(:master) } - it { is_expected.to be_denied_for group_member(:reporter) } - it { is_expected.to be_denied_for group_member(:guest) } - it { is_expected.to be_allowed_for :admin } - end - end -end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index f88c591d897..79d5bf4cf06 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -5,25 +5,22 @@ describe "Internal Project Access", feature: true do let(:project) { create(:project, :internal) } - let(:master) { create(:user) } - let(:guest) { create(:user) } - let(:reporter) { create(:user) } - let(:external_team_member) { create(:user, external: true) } + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } before do - # full access project.team << [master, :master] - project.team << [external_team_member, :master] - - # readonly + project.team << [developer, :developer] project.team << [reporter, :reporter] + project.team << [guest, :guest] end describe "Project should be internal" do - subject { project } - describe '#internal?' do - subject { super().internal? } + subject { project.internal? } it { is_expected.to be_truthy } end end @@ -31,78 +28,84 @@ describe "Internal Project Access", feature: true do describe "GET /:project_path" do subject { namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tree/master" do subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commits/master" do subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commit/:sha" do subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/compare" do subject { namespace_project_compare_index_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/project_members" do subject { namespace_project_project_members_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -110,52 +113,56 @@ describe "Internal Project Access", feature: true do let(:commit) { project.repository.commit } subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/edit" do subject { edit_namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/deploy_keys" do subject { namespace_project_deploy_keys_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/issues" do subject { namespace_project_issues_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -163,65 +170,70 @@ describe "Internal Project Access", feature: true do let(:issue) { create(:issue, project: project) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets" do subject { namespace_project_snippets_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets/new" do subject { new_namespace_project_snippet_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests" do subject { namespace_project_merge_requests_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests/new" do subject { new_namespace_project_merge_request_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -233,13 +245,14 @@ describe "Internal Project Access", feature: true do allow_any_instance_of(Project).to receive(:branches).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -251,26 +264,28 @@ describe "Internal Project Access", feature: true do allow_any_instance_of(Project).to receive(:tags).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/hooks" do subject { namespace_project_hooks_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 19f287ce7a4..0a89193eb67 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -3,27 +3,24 @@ require 'spec_helper' describe "Private Project Access", feature: true do include AccessMatchers - let(:project) { create(:project) } + let(:project) { create(:project, :private) } - let(:master) { create(:user) } - let(:guest) { create(:user) } - let(:reporter) { create(:user) } - let(:external_team_member) { create(:user, external: true) } + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } before do - # full access project.team << [master, :master] - project.team << [external_team_member, :master] - - # readonly + project.team << [developer, :developer] project.team << [reporter, :reporter] + project.team << [guest, :guest] end describe "Project should be private" do - subject { project } - describe '#private?' do - subject { super().private? } + subject { project.private? } it { is_expected.to be_truthy } end end @@ -31,77 +28,84 @@ describe "Private Project Access", feature: true do describe "GET /:project_path" do subject { namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } + it { is_expected.to be_allowed_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tree/master" do subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commits/master" do subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commit/:sha" do subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } - it { is_expected.to be_allowed_for external_team_member } + it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/compare" do subject { namespace_project_compare_index_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/project_members" do subject { namespace_project_project_members_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -109,52 +113,56 @@ describe "Private Project Access", feature: true do let(:commit) { project.repository.commit } subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore'))} + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/edit" do subject { edit_namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/deploy_keys" do subject { namespace_project_deploy_keys_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/issues" do subject { namespace_project_issues_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } + it { is_expected.to be_allowed_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -162,39 +170,42 @@ describe "Private Project Access", feature: true do let(:issue) { create(:issue, project: project) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets" do subject { namespace_project_snippets_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } + it { is_expected.to be_allowed_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests" do subject { namespace_project_merge_requests_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } + it { is_expected.to be_allowed_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -206,13 +217,14 @@ describe "Private Project Access", feature: true do allow_any_instance_of(Project).to receive(:branches).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end @@ -224,26 +236,28 @@ describe "Private Project Access", feature: true do allow_any_instance_of(Project).to receive(:tags).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/hooks" do subject { namespace_project_hooks_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } - it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 4e135076367..40daac89d40 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -3,29 +3,24 @@ require 'spec_helper' describe "Public Project Access", feature: true do include AccessMatchers - let(:project) { create(:project) } + let(:project) { create(:project, :public) } - let(:master) { create(:user) } - let(:guest) { create(:user) } - let(:reporter) { create(:user) } + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } before do - # public project - project.visibility_level = Gitlab::VisibilityLevel::PUBLIC - project.save! - - # full access project.team << [master, :master] - - # readonly + project.team << [developer, :developer] project.team << [reporter, :reporter] + project.team << [guest, :guest] end describe "Project should be public" do - subject { project } - describe '#public?' do - subject { super().public? } + subject { project.public? } it { is_expected.to be_truthy } end end @@ -33,9 +28,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path" do subject { namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -45,9 +42,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/tree/master" do subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -57,9 +56,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/commits/master" do subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -69,9 +70,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/commit/:sha" do subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -81,9 +84,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/compare" do subject { namespace_project_compare_index_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -93,9 +98,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/project_members" do subject { namespace_project_project_members_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -108,9 +115,11 @@ describe "Public Project Access", feature: true do context "when allowed for public" do before { project.update(public_builds: true) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -120,9 +129,11 @@ describe "Public Project Access", feature: true do context "when disallowed for public" do before { project.update(public_builds: false) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -138,9 +149,11 @@ describe "Public Project Access", feature: true do context "when allowed for public" do before { project.update(public_builds: true) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -150,9 +163,11 @@ describe "Public Project Access", feature: true do context "when disallowed for public" do before { project.update(public_builds: false) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -165,9 +180,11 @@ describe "Public Project Access", feature: true do subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :visitor } @@ -176,9 +193,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/edit" do subject { edit_namespace_project_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -188,9 +207,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/deploy_keys" do subject { namespace_project_deploy_keys_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -200,9 +221,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/issues" do subject { namespace_project_issues_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -213,9 +236,11 @@ describe "Public Project Access", feature: true do let(:issue) { create(:issue, project: project) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -225,9 +250,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/snippets" do subject { namespace_project_snippets_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -237,9 +264,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/snippets/new" do subject { new_namespace_project_snippet_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -249,9 +278,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/merge_requests" do subject { namespace_project_merge_requests_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -261,9 +292,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/merge_requests/new" do subject { new_namespace_project_merge_request_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } @@ -278,9 +311,11 @@ describe "Public Project Access", feature: true do allow_any_instance_of(Project).to receive(:branches).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -295,9 +330,11 @@ describe "Public Project Access", feature: true do allow_any_instance_of(Project).to receive(:tags).and_return([]) end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :external } @@ -307,9 +344,11 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/hooks" do subject { namespace_project_hooks_path(project.namespace, project) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for developer } it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper_spec.rb similarity index 59% rename from spec/helpers/groups_helper.rb rename to spec/helpers/groups_helper_spec.rb index 01ec9e5a07f..4ea90a80a92 100644 --- a/spec/helpers/groups_helper.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -18,19 +18,4 @@ describe GroupsHelper do expect(group_icon(group.path)).to match('group_avatar.png') end end - - describe 'permissions' do - let(:group) { create(:group) } - let!(:user) { create(:user) } - - before do - allow(self).to receive(:current_user).and_return(user) - allow(self).to receive(:can?) { true } - end - - it 'checks user ability to change permissions' do - expect(self).to receive(:can?).with(user, :change_visibility_level, group) - can_change_group_visibility_level?(group) - end - end end diff --git a/spec/support/group_access_helper.rb b/spec/support/group_access_helper.rb deleted file mode 100644 index c8ed0e406a1..00000000000 --- a/spec/support/group_access_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module GroupAccessHelper - def group(visibility_level=0) - @group ||= create(:group, visibility_level: visibility_level) - end - - def project_group_member(access_level) - project = create(:project, visibility_level: group.visibility_level, group: group, name: 'B', path: 'B') - - create(:user).tap { |user| project.team.add_user(user, Gitlab::Access::DEVELOPER) } - end - - def group_member(access_level, grp=group()) - level = Object.const_get("Gitlab::Access::#{access_level.upcase}") - - create(:user).tap { |user| grp.add_user(user, level) } - end - - def external_guest(grp=group()) - create(:user, external: true).tap { |user| grp.add_user(user, Gitlab::Access::GUEST) } - end -end diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index 4e007c777e3..0497e391860 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -28,7 +28,7 @@ module AccessMatchers if user.kind_of?(User) # User#inspect displays too much information for RSpec's description # messages - "be #{type} for supplied User" + "be #{type} for the specified user" else "be #{type} for #{user}" end -- 2.18.1 From fd8d44ca6188b2ad9d6931ce385e61217724a712 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 21:14:39 +0100 Subject: [PATCH 13/22] Fix group project selection in IssuableFinder --- app/finders/issuable_finder.rb | 8 ++++---- app/models/issue.rb | 5 +---- app/models/merge_request.rb | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 7510f6e9e29..dd4208880b6 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -80,9 +80,10 @@ class IssuableFinder @projects = project elsif current_user && params[:authorized_only].presence && !current_user_related? @projects = current_user.authorized_projects.reorder(nil) + elsif group + @projects = GroupProjectsFinder.new(group).execute(current_user).reorder(nil) else - @projects = GroupProjectsFinder.new(group).execute(current_user). - reorder(nil) + @projects = ProjectsFinder.new.execute(current_user).reorder(nil) end end @@ -198,8 +199,7 @@ class IssuableFinder end def by_group(items) - items = items.of_group(group) if group - + # Selection by group is already covered by `by_project` and `projects` items end diff --git a/app/models/issue.rb b/app/models/issue.rb index 5347d4fa1be..60250322b04 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -33,9 +33,6 @@ class Issue < ActiveRecord::Base belongs_to :project validates :project, presence: true - scope :of_group, - ->(group) { where(project_id: group.projects.select(:id).reorder(nil)) } - scope :cared, ->(user) { where(assignee_id: user) } scope :open_for, ->(user) { opened.assigned_to(user) } scope :in_projects, ->(project_ids) { where(project_id: project_ids) } @@ -106,7 +103,7 @@ class Issue < ActiveRecord::Base def related_branches return [] if self.project.empty_repo? - + self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") } end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index a015a9ef394..ef48207f956 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -131,7 +131,6 @@ class MergeRequest < ActiveRecord::Base validate :validate_branches validate :validate_fork - scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } -- 2.18.1 From 3058a8fa4c1dfdf50a6f274bfbf280f8d2137168 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 21:30:08 +0100 Subject: [PATCH 14/22] Fix "Shared projects" tab --- app/controllers/groups_controller.rb | 4 ++-- app/finders/group_projects_finder.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 48565f44ffb..87efb0a8970 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -44,7 +44,7 @@ class GroupsController < Groups::ApplicationController @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? - @shared_projects = GroupProjectsFinder.new(group, shared: true).execute(current_user) + @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) respond_to do |format| format.html @@ -77,7 +77,7 @@ class GroupsController < Groups::ApplicationController end def projects - @projects = @projects.sorted_by_activity.page(params[:page]) + @projects = @group.projects.page(params[:page]) end def update diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb index 84fe468ae5d..2470af7c685 100644 --- a/app/finders/group_projects_finder.rb +++ b/app/finders/group_projects_finder.rb @@ -13,29 +13,29 @@ class GroupProjectsFinder < UnionFinder private def group_projects(current_user) - include_owned = @options.fetch(:owned, true) - include_shared = @options.fetch(:shared, true) + only_owned = @options.fetch(:only_owned, false) + only_shared = @options.fetch(:only_shared, false) projects = [] if current_user if @group.users.include?(current_user) - projects << @group.projects if include_owned - projects << @group.shared_projects if include_shared + projects << @group.projects unless only_shared + projects << @group.shared_projects unless only_owned else - if include_owned + unless only_shared projects << @group.projects.visible_to_user(current_user) projects << @group.projects.public_to_user(current_user) end - if include_shared + unless only_owned projects << @group.shared_projects.visible_to_user(current_user) projects << @group.shared_projects.public_to_user(current_user) end end else - projects << @group.projects.public_only if include_owned - projects << @group.shared_projects.public_only if include_shared + projects << @group.projects.public_only unless only_shared + projects << @group.shared_projects.public_only unless only_owned end projects -- 2.18.1 From 7c51d5efecdad1a7f52ffecdf57c86b7b90ca166 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 22:55:08 +0100 Subject: [PATCH 15/22] Fix some specs --- app/controllers/admin/projects_controller.rb | 1 - .../projects/application_controller.rb | 2 +- app/models/project.rb | 10 +---- app/views/events/event/_common.html.haml | 2 +- ...12_index_namespaces_on_visibility_level.rb | 5 +++ db/schema.rb | 1 + features/steps/shared/group.rb | 4 +- .../application_controller_spec.rb | 40 ------------------- .../lib/banzai/filter/redactor_filter_spec.rb | 4 +- spec/models/project_security_spec.rb | 10 ++--- spec/requests/api/groups_spec.rb | 2 +- 11 files changed, 20 insertions(+), 61 deletions(-) create mode 100644 db/migrate/20160320204112_index_namespaces_on_visibility_level.rb diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index ae1de06b983..8b212225b2d 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,7 +1,6 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :project, only: [:show, :transfer] before_action :group, only: [:show, :transfer] - before_action :repository, only: [:show, :transfer] def index @projects = Project.all diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 276f80f800a..9c8433c260b 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -67,7 +67,7 @@ class Projects::ApplicationController < ApplicationController end def require_non_empty_project - redirect_to @project if @project.empty_repo? + redirect_to namespace_project_path(@project.namespace, @project) if @project.empty_repo? end def require_branch_head diff --git a/app/models/project.rb b/app/models/project.rb index ab31a635a82..76dcff80bff 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -246,10 +246,6 @@ class Project < ActiveRecord::Base end class << self - def public_and_internal_levels - [Project::PUBLIC, Project::INTERNAL] - end - def abandoned where('projects.last_activity_at < ?', 6.months.ago) end @@ -976,11 +972,9 @@ class Project < ActiveRecord::Base end def visibility_level_allowed_as_fork?(level = self.visibility_level) - return true unless forked? && forked_project_link.forked_from_project_id.present? + return true unless forked? - from_project = self.forked_from_project - from_project ||= Project.find(forked_project_link.forked_from_project_id) - Gitlab::VisibilityLevel.allowed_fork_levels(from_project.visibility_level).include?(level) + Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) end def visibility_level_allowed_by_group?(level = self.visibility_level) diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index e9e16a7646f..c994e3b997d 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -4,7 +4,7 @@ = event_action_name(event) - if event.target - %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target] + %strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target] = event_preposition(event) diff --git a/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb new file mode 100644 index 00000000000..b3145443497 --- /dev/null +++ b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb @@ -0,0 +1,5 @@ +class IndexNamespacesOnVisibilityLevel < ActiveRecord::Migration + def change + add_index :namespaces, :visibility_level + end +end diff --git a/db/schema.rb b/db/schema.rb index 2aa37cc590c..9d1d7d9b823 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -601,6 +601,7 @@ ActiveRecord::Schema.define(version: 20160316204731) do add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree + add_index "namespaces", ["visibility_level"], name: "index_namespaces_on_visibility_level", using: :btree create_table "notes", force: :cascade do |t| t.text "note" diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index ca32faa3159..fe6736dacd4 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -38,7 +38,7 @@ module SharedGroup def is_member_of(username, groupname, role) @project_count ||= 0 user = User.find_by(name: username) || create(:user, name: username) - group = Group.find_by(name: groupname) || create(:group, name: groupname, visibility_level: Gitlab::VisibilityLevel::PUBLIC) + group = Group.find_by(name: groupname) || create(:group, name: groupname) group.add_user(user, role) project ||= create(:project, namespace: group, path: "project#{@project_count}") create(:closed_issue_event, project: project) @@ -47,6 +47,6 @@ module SharedGroup end def owned_group - @owned_group ||= Group.find_by(name: "Owned", visibility_level: Gitlab::VisibilityLevel::PUBLIC) + @owned_group ||= Group.find_by(name: "Owned") end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 55851befc8c..186239d3096 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -30,44 +30,4 @@ describe ApplicationController do controller.send(:check_password_expiration) end end - - describe 'check labels authorization' do - let(:project) { create(:project) } - let(:user) { create(:user) } - let(:controller) { ApplicationController.new } - - before do - project.team << [user, :guest] - allow(controller).to receive(:current_user).and_return(user) - allow(controller).to receive(:project).and_return(project) - end - - it 'should succeed if issues and MRs are enabled' do - project.issues_enabled = true - project.merge_requests_enabled = true - controller.send(:authorize_read_label!) - expect(response.status).to eq(200) - end - - it 'should succeed if issues are enabled, MRs are disabled' do - project.issues_enabled = true - project.merge_requests_enabled = false - controller.send(:authorize_read_label!) - expect(response.status).to eq(200) - end - - it 'should succeed if issues are disabled, MRs are enabled' do - project.issues_enabled = false - project.merge_requests_enabled = true - controller.send(:authorize_read_label!) - expect(response.status).to eq(200) - end - - it 'should fail if issues and MRs are disabled' do - project.issues_enabled = false - project.merge_requests_enabled = false - expect(controller).to receive(:access_denied!) - controller.send(:authorize_read_label!) - end - end end diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb index 9acf6304bcb..c2c2fd0eb6a 100644 --- a/spec/lib/banzai/filter/redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/redactor_filter_spec.rb @@ -119,7 +119,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do context 'with data-group' do it 'removes unpermitted Group references' do user = create(:user) - group = create(:group) + group = create(:group, :private) link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') doc = filter(link, current_user: user) @@ -129,7 +129,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do it 'allows permitted Group references' do user = create(:user) - group = create(:group) + group = create(:group, :private) group.add_developer(user) link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb index 3643ad1b052..e12258c0874 100644 --- a/spec/models/project_security_spec.rb +++ b/spec/models/project_security_spec.rb @@ -18,11 +18,11 @@ describe Project, models: true do let(:report_actions) { Ability.project_report_rules } let(:dev_actions) { Ability.project_dev_rules } let(:master_actions) { Ability.project_master_rules } - let(:admin_actions) { Ability.project_admin_rules } + let(:owner_actions) { Ability.project_owner_rules } describe "Non member rules" do it "should deny for non-project users any actions" do - admin_actions.each do |action| + owner_actions.each do |action| expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey end end @@ -90,20 +90,20 @@ describe Project, models: true do end end - describe "Admin Rules" do + describe "Owner Rules" do before do @p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER) @p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER) end it "should deny for masters admin-specific actions" do - [admin_actions - master_actions].each do |action| + [owner_actions - master_actions].each do |action| expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey end end it "should allow for project owner any admin actions" do - admin_actions.each do |action| + owner_actions.each do |action| expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 4cfa49d1566..41c9cacd455 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -9,7 +9,7 @@ describe API::API, api: true do let(:admin) { create(:admin) } let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') } let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) } - let!(:group2) { create(:group) } + let!(:group2) { create(:group, :private) } let!(:project1) { create(:project, namespace: group1) } let!(:project2) { create(:project, namespace: group2) } -- 2.18.1 From 19aa20d528aca670fd22954e08bf05f2f7a8fe32 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 23:09:33 +0100 Subject: [PATCH 16/22] Fix more specs --- app/views/admin/projects/show.html.haml | 2 +- db/schema.rb | 2 +- .../groups/avatars_controller_spec.rb | 3 +- .../controllers/namespaces_controller_spec.rb | 12 ++++---- spec/controllers/uploads_controller_spec.rb | 10 +++---- spec/models/group_spec.rb | 5 ++-- spec/services/create_snippet_service_spec.rb | 2 +- spec/services/groups/update_service_spec.rb | 30 +++++++++---------- spec/services/update_snippet_service_spec.rb | 2 +- 9 files changed, 33 insertions(+), 35 deletions(-) diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index d734e60682a..c638c32a654 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -52,7 +52,7 @@ %li %span.light fs: %strong - = @repository.path_to_repo + = @project.repository.path_to_repo %li %span.light Size diff --git a/db/schema.rb b/db/schema.rb index 9d1d7d9b823..11ae4815801 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160316204731) do +ActiveRecord::Schema.define(version: 20160320204112) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/controllers/groups/avatars_controller_spec.rb b/spec/controllers/groups/avatars_controller_spec.rb index 3dac134a731..91d639218e5 100644 --- a/spec/controllers/groups/avatars_controller_spec.rb +++ b/spec/controllers/groups/avatars_controller_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' describe Groups::AvatarsController do let(:user) { create(:user) } - let(:group) { create(:group, owner: user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } before do + group.add_owner(user) sign_in(user) end diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 41ae6063ae0..27e9afe582e 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -18,10 +18,6 @@ describe NamespacesController do let!(:group) { create(:group) } context "when the group is public" do - before do - group.update_attribute(:visibility_level, Group::PUBLIC) - end - context "when not signed in" do it "redirects to the group's page" do get :show, id: group.path @@ -44,10 +40,14 @@ describe NamespacesController do end context "when the group is private" do + before do + group.update_attribute(:visibility_level, Group::PRIVATE) + end + context "when not signed in" do - it "does not redirect to the sign in page" do + it "redirects to the sign in page" do get :show, id: group.path - expect(response).not_to redirect_to(new_user_session_path) + expect(response).to redirect_to(new_user_session_path) end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 0947744fc47..73858e6f063 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -129,10 +129,6 @@ describe UploadsController do let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } context "when the group is public" do - before do - group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) - end - context "when not signed in" do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" @@ -155,6 +151,10 @@ describe UploadsController do end context "when the group is private" do + before do + group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + end + context "when signed in" do before do sign_in(user) @@ -162,7 +162,7 @@ describe UploadsController do context "when the user has access to the project" do before do - project.add_developer(user) + group.add_developer(user) end context "when the user is blocked" do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 0591aa089d8..208922b5a8e 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -59,18 +59,17 @@ describe Group, models: true do describe 'scopes' do let!(:private_group) { create(:group, :private) } let!(:internal_group) { create(:group, :internal) } - let!(:public_group) { create(:group, :public) } describe 'public_only' do subject { described_class.public_only.to_a } - it{ is_expected.to eq([public_group]) } + it{ is_expected.to eq([group]) } end describe 'public_and_internal_only' do subject { described_class.public_and_internal_only.to_a } - it{ is_expected.to eq([public_group, internal_group]) } + it{ is_expected.to eq([group, internal_group]) } end end diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb index c800dea04fa..7a850066bf8 100644 --- a/spec/services/create_snippet_service_spec.rb +++ b/spec/services/create_snippet_service_spec.rb @@ -23,7 +23,7 @@ describe CreateSnippetService, services: true do snippet = create_snippet(nil, @user, @opts) expect(snippet.errors.messages).to have_key(:visibility_level) expect(snippet.errors.messages[:visibility_level].first).to( - match('Public visibility has been restricted') + match('has been restricted') ) end diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 9d427ff2d90..7732482cdaa 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -6,9 +6,8 @@ describe Groups::UpdateService, services: true do let!(:internal_group) { create(:group, :internal) } let!(:public_group) { create(:group, :public) } - describe "execute" do + describe "#execute" do context "project visibility_level validation" do - context "public group with public projects" do let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL ) } @@ -18,33 +17,32 @@ describe Groups::UpdateService, services: true do end it "cant downgrade permission level" do - expect(service.execute).to be_falsy expect(public_group.errors.count).to eq(2) end end context "internal group with internal project" do - let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) } - - before do - internal_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :internal, group: internal_group) - end - - it "cant downgrade permission level" do - expect(service.execute).to be_falsy - expect(internal_group.errors.count).to eq(2) - end + let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) } + + before do + internal_group.add_user(user, Gitlab::Access::MASTER) + create(:project, :internal, group: internal_group) + end + + it "cant downgrade permission level" do + expect(internal_group.errors.count).to eq(2) end + end end end context "unauthorized visibility_level validation" do let!(:service) { described_class.new(internal_group, user, visibility_level: 99 ) } - before { internal_group.add_user(user, Gitlab::Access::MASTER) } + before do + internal_group.add_user(user, Gitlab::Access::MASTER) + end it "does not change permission level" do - expect(service.execute).to be_falsy expect(internal_group.errors.count).to eq(1) end end diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index 48d114896d0..37c2e861362 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -25,7 +25,7 @@ describe UpdateSnippetService, services: true do update_snippet(@project, @user, @snippet, @opts) expect(@snippet.errors.messages).to have_key(:visibility_level) expect(@snippet.errors.messages[:visibility_level].first).to( - match('Public visibility has been restricted') + match('has been restricted') ) expect(@snippet.visibility_level).to eq(old_visibility) end -- 2.18.1 From 45e8650c4f987f0a25d829bf8ac189b023f1eaa3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Mar 2016 23:26:58 +0100 Subject: [PATCH 17/22] Fix specs --- app/models/project.rb | 4 ++-- app/services/groups/base_service.rb | 2 +- lib/gitlab/visibility_level.rb | 4 ---- spec/helpers/projects_helper_spec.rb | 10 +--------- spec/models/group_spec.rb | 4 ++-- spec/services/groups/update_service_spec.rb | 11 +++++++---- 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 76dcff80bff..acea89c1526 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -198,7 +198,7 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validate :visibility_level_allowed_by_group - validate :visibility_level_allowed_as_fork + validate :visibility_level_allowed_as_fork, on: :update add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -974,7 +974,7 @@ class Project < ActiveRecord::Base def visibility_level_allowed_as_fork?(level = self.visibility_level) return true unless forked? - Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) + level <= forked_from_project.visibility_level end def visibility_level_allowed_by_group?(level = self.visibility_level) diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 1642115583d..a8fa098246a 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -1,5 +1,5 @@ module Groups - class BaseService < BaseService + class BaseService < ::BaseService attr_accessor :group, :current_user, :params def initialize(group, user, params = {}) diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index 0a1f66b0726..a1ee1cba216 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -56,10 +56,6 @@ module Gitlab options.has_value?(level) end - def allowed_fork_levels(origin_level) - [PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level } - end - def level_name(level) level_name = 'Unknown' options.each do |name, lvl| diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 53207767581..dc324f1e60e 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -11,16 +11,8 @@ describe ProjectsHelper do describe "can_change_visibility_level?" do let(:project) { create(:project) } - - let(:fork_project) do - fork_project = create(:forked_project_with_submodules) - fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id) - fork_project.save - - fork_project - end - let(:user) { create(:user) } + let(:fork_project) { Projects::ForkService.new(project, user).execute } it "returns false if there are no appropriate permissions" do allow(helper).to receive(:can?) { false } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 208922b5a8e..68e213f4816 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -67,9 +67,9 @@ describe Group, models: true do end describe 'public_and_internal_only' do - subject { described_class.public_and_internal_only.to_a } + subject { described_class.public_and_internal_only.to_a.sort } - it{ is_expected.to eq([group, internal_group]) } + it{ is_expected.to eq([group, internal_group].sort) } end end diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 7732482cdaa..9c2331144a0 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -16,8 +16,9 @@ describe Groups::UpdateService, services: true do create(:project, :public, group: public_group) end - it "cant downgrade permission level" do - expect(public_group.errors.count).to eq(2) + it "does not change permission level" do + service.execute + expect(public_group.errors.count).to eq(1) end end @@ -29,8 +30,9 @@ describe Groups::UpdateService, services: true do create(:project, :internal, group: internal_group) end - it "cant downgrade permission level" do - expect(internal_group.errors.count).to eq(2) + it "does not change permission level" do + service.execute + expect(internal_group.errors.count).to eq(1) end end end @@ -43,6 +45,7 @@ describe Groups::UpdateService, services: true do end it "does not change permission level" do + service.execute expect(internal_group.errors.count).to eq(1) end end -- 2.18.1 From b689e2c0e0d0ad4303d99150c843ef914f1fd516 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Mar 2016 00:42:30 +0100 Subject: [PATCH 18/22] Fix spec --- app/models/project.rb | 9 +++++++-- spec/requests/api/group_members_spec.rb | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index acea89c1526..4d23cc8de40 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -198,7 +198,7 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validate :visibility_level_allowed_by_group - validate :visibility_level_allowed_as_fork, on: :update + validate :visibility_level_allowed_as_fork add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -974,7 +974,12 @@ class Project < ActiveRecord::Base def visibility_level_allowed_as_fork?(level = self.visibility_level) return true unless forked? - level <= forked_from_project.visibility_level + # self.forked_from_project will be nil before the project is saved, so + # we need to go through the relation + original_project = forked_project_link.forked_from_project + return true unless original_project + + level <= original_project.visibility_level end def visibility_level_allowed_by_group?(level = self.visibility_level) diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb index dd5baa44cb2..3e8b4aa1f88 100644 --- a/spec/requests/api/group_members_spec.rb +++ b/spec/requests/api/group_members_spec.rb @@ -11,7 +11,7 @@ describe API::API, api: true do let(:stranger) { create(:user) } let!(:group_with_members) do - group = create(:group) + group = create(:group, :private) group.add_users([reporter.id], GroupMember::REPORTER) group.add_users([developer.id], GroupMember::DEVELOPER) group.add_users([master.id], GroupMember::MASTER) -- 2.18.1 From 261569b2466e455ff308cc54fb1db51bc8dc2880 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Mar 2016 09:09:59 +0100 Subject: [PATCH 19/22] Fix specs --- spec/controllers/projects/avatars_controller_spec.rb | 2 +- spec/factories/broadcast_messages.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb index e79b46a3504..4d724ca9ed0 100644 --- a/spec/controllers/projects/avatars_controller_spec.rb +++ b/spec/controllers/projects/avatars_controller_spec.rb @@ -6,7 +6,7 @@ describe Projects::AvatarsController do before do sign_in(user) - project.team << [user, :developer] + project.team << [user, :master] controller.instance_variable_set(:@project, project) end diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb index 373ca75467e..c80e7366551 100644 --- a/spec/factories/broadcast_messages.rb +++ b/spec/factories/broadcast_messages.rb @@ -15,7 +15,7 @@ FactoryGirl.define do factory :broadcast_message do message "MyText" - starts_at Date.today + starts_at Date.yesterday ends_at Date.tomorrow trait :expired do -- 2.18.1 From 8d544645f0ef114586212835cf011a3e268c9ec1 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Mon, 21 Mar 2016 19:11:24 -0300 Subject: [PATCH 20/22] Add specs and add visibility level to admin groups --- app/controllers/admin/groups_controller.rb | 2 +- app/finders/group_projects_finder.rb | 5 +- app/views/admin/groups/_form.html.haml | 2 + app/views/admin/groups/index.html.haml | 3 + app/views/admin/groups/show.html.haml | 5 ++ ...01124843_add_visibility_level_to_groups.rb | 8 +- ...roup_visibility_to_application_settings.rb | 22 ----- db/schema.rb | 3 +- spec/finders/group_projects_finder_spec.rb | 89 +++++++++++++++++++ 9 files changed, 110 insertions(+), 29 deletions(-) delete mode 100644 db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb create mode 100644 spec/finders/group_projects_finder_spec.rb diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 668396a0f20..8e2a981be7c 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -59,6 +59,6 @@ class Admin::GroupsController < Admin::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar) + params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level) end end diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb index 2470af7c685..3b9a421b118 100644 --- a/app/finders/group_projects_finder.rb +++ b/app/finders/group_projects_finder.rb @@ -1,19 +1,18 @@ class GroupProjectsFinder < UnionFinder def initialize(group, options = {}) - @group = group + @group = group @options = options end def execute(current_user = nil) segments = group_projects(current_user) - find_union(segments, Project) end private def group_projects(current_user) - only_owned = @options.fetch(:only_owned, false) + only_owned = @options.fetch(:only_owned, false) only_shared = @options.fetch(:only_shared, false) projects = [] diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 198026a1f75..7f2b1cd235d 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -10,6 +10,8 @@ .col-sm-10 = render 'shared/choose_group_avatar_button', f: f + = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group + - if @group.new_record? .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 118d3cfea07..6bdc885a312 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -46,6 +46,9 @@ %h4 = link_to [:admin, group] do + %span{ class: visibility_level_color(group.visibility_level) } + = visibility_level_icon(group.visibility_level) + %i.fa.fa-folder = group.name diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 264fa1bf0cd..f309e80a39a 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -27,6 +27,11 @@ %strong = @group.description + %li + %span.light Visibility level: + %strong + = visibility_level_label(@group.visibility_level) + %li %span.light Created on: %strong diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb index cef553981e7..89b5ac19983 100644 --- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -1,6 +1,12 @@ class AddVisibilityLevelToGroups < ActiveRecord::Migration def change #All groups public by default - add_column :namespaces, :visibility_level, :integer, null: false, default: 20 + add_column :namespaces, :visibility_level, :integer, null: false, default: allowed_visibility_level + end + + def allowed_visibility_level + # TODO: Don't use `current_application_settings` + allowed_levels = Gitlab::VisibilityLevel.values - current_application_settings.restricted_visibility_levels + allowed_levels.max end end diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb deleted file mode 100644 index 62d96907c8f..00000000000 --- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb +++ /dev/null @@ -1,22 +0,0 @@ -#Create visibility level field on DB -#Sets default_visibility_level to value on settings if not restricted -#If value is restricted takes higher visibility level allowed - -class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration - def up - add_column :application_settings, :default_group_visibility, :integer - execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}") - end - - def down - remove_column :application_settings, :default_group_visibility - end - - private - - def allowed_visibility_level - # TODO: Don't use `current_application_settings` - allowed_levels = Gitlab::VisibilityLevel.values - current_application_settings.restricted_visibility_levels - allowed_levels.max - end -end diff --git a/db/schema.rb b/db/schema.rb index 11ae4815801..8537e5729a7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -77,7 +77,6 @@ ActiveRecord::Schema.define(version: 20160320204112) do t.boolean "akismet_enabled", default: false t.string "akismet_api_key" t.boolean "email_author_in_body", default: false - t.integer "default_group_visibility" end create_table "audit_events", force: :cascade do |t| @@ -417,7 +416,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do t.string "state" t.integer "iid" t.integer "updated_by_id" - t.boolean "confidential", default: false + t.boolean "confidential", default: false end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb new file mode 100644 index 00000000000..fdd3849816f --- /dev/null +++ b/spec/finders/group_projects_finder_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe GroupProjectsFinder do + let(:group) { create(:group) } + let(:current_user) { create(:user) } + + let(:finder) { described_class.new(source_user) } + + let!(:public_project) { create(:project, :public, group: group, path: '1') } + let!(:private_project) { create(:project, :private, group: group, path: '2') } + let!(:shared_project_1) { create(:project, :public, path: '3') } + let!(:shared_project_2) { create(:project, :private, path: '4') } + let!(:shared_project_3) { create(:project, :internal, path: '5') } + + + before do + shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group) + shared_project_2.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group) + shared_project_3.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group) + end + + + describe 'with a group member current user' do + before { group.add_user(current_user, Gitlab::Access::MASTER) } + + context "only shared" do + subject { described_class.new(group, only_shared: true).execute(current_user) } + it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) } + end + + context "only owned" do + subject { described_class.new(group, only_owned: true).execute(current_user) } + it { is_expected.to eq([private_project, public_project]) } + end + + context "all" do + subject { described_class.new(group).execute(current_user) } + it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) } + end + end + + describe 'without group member current_user' do + before { shared_project_2.team << [current_user, Gitlab::Access::MASTER] } + + context "only shared" do + context "without external user" do + subject { described_class.new(group, only_shared: true).execute(current_user) } + it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) } + end + + context "with external user" do + before { current_user.update_attributes(external: true) } + subject { described_class.new(group, only_shared: true).execute(current_user) } + it { is_expected.to eq([shared_project_2, shared_project_1]) } + end + end + + context "only owned" do + context "without external user" do + before { private_project.team << [current_user, Gitlab::Access::MASTER] } + subject { described_class.new(group, only_owned: true).execute(current_user) } + it { is_expected.to eq([private_project, public_project]) } + end + + context "with external user" do + before { current_user.update_attributes(external: true) } + subject { described_class.new(group, only_owned: true).execute(current_user) } + it { is_expected.to eq([public_project]) } + end + + context "all" do + subject { described_class.new(group).execute(current_user) } + it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, public_project]) } + end + end + end + + describe "no user" do + context "only shared" do + subject { described_class.new(group, only_shared: true).execute(current_user) } + it { is_expected.to eq([shared_project_3, shared_project_1]) } + end + + context "only owned" do + subject { described_class.new(group, only_owned: true).execute(current_user) } + it { is_expected.to eq([public_project]) } + end + end +end -- 2.18.1 From 31266c5be4748f57a7d56bbcc6f06d570cbf5356 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 22 Mar 2016 00:09:20 +0100 Subject: [PATCH 21/22] Address feedback --- .../projects/application_controller.rb | 3 +- .../projects/uploads_controller.rb | 2 +- app/finders/issuable_finder.rb | 10 +-- app/finders/joined_groups_finder.rb | 5 -- app/helpers/visibility_level_helper.rb | 23 ++++-- app/models/ability.rb | 11 ++- app/models/group.rb | 6 +- app/services/groups/create_service.rb | 2 +- app/services/groups/update_service.rb | 4 - app/views/groups/show.html.haml | 2 +- app/views/projects/_home_panel.html.haml | 2 +- app/views/shared/groups/_group.html.haml | 2 +- app/views/shared/projects/_project.html.haml | 2 +- ...01124843_add_visibility_level_to_groups.rb | 28 +++++-- ...roup_visibility_to_application_settings.rb | 27 +++++++ db/schema.rb | 1 + .../security/group/internal_access_spec.rb | 10 +-- .../security/group/private_access_spec.rb | 10 +-- .../security/group/public_access_spec.rb | 12 +-- spec/finders/joined_groups_finder_spec.rb | 80 ++++++++++--------- spec/finders/projects_finder_spec.rb | 2 +- spec/finders/snippets_finder_spec.rb | 2 +- spec/models/group_spec.rb | 4 +- spec/models/project_spec.rb | 4 +- 24 files changed, 154 insertions(+), 100 deletions(-) create mode 100644 db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 9c8433c260b..657ee94cfd7 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -1,6 +1,7 @@ class Projects::ApplicationController < ApplicationController skip_before_action :authenticate_user! - before_action :project, :repository + before_action :project + before_action :repository layout 'project' helper_method :repository, :can_collaborate_with_project? diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 94c51eeb94d..caed064dfbc 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -2,7 +2,7 @@ class Projects::UploadsController < Projects::ApplicationController skip_before_action :reject_blocked!, :project, :repository, if: -> { action_name == 'show' && image? } - before_action :authenticate_user!, only: [:create] + before_action :authorize_upload_file!, only: [:create] def create link_to_file = ::Projects::UploadService.new(project, params[:file]). diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index dd4208880b6..046286dd9e1 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -171,15 +171,13 @@ class IssuableFinder end def by_scope(items) - case params[:scope] || 'all' - when 'created-by-me', 'authored' then + case params[:scope] + when 'created-by-me', 'authored' items.where(author_id: current_user.id) - when 'all' then - items - when 'assigned-to-me' then + when 'assigned-to-me' items.where(assignee_id: current_user.id) else - raise 'You must specify default scope' + items end end diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb index 2a3f0296d37..47174980258 100644 --- a/app/finders/joined_groups_finder.rb +++ b/app/finders/joined_groups_finder.rb @@ -5,11 +5,6 @@ class JoinedGroupsFinder < UnionFinder # Finds the groups of the source user, optionally limited to those visible to # the current user. - # - # current_user - If given the groups of "@user" will only include the groups - # "current_user" can also see. - # - # Returns an ActiveRecord::Relation. def execute(current_user = nil) segments = all_groups(current_user) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 5b1bfb261a5..3a83ae15dd8 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -40,11 +40,11 @@ module VisibilityLevelHelper def group_visibility_level_description(level) case level when Gitlab::VisibilityLevel::PRIVATE - "The group can be accessed only by members." + "The group and its projects can only be viewed by members." when Gitlab::VisibilityLevel::INTERNAL - "The group can be accessed by any logged user." + "The group and any internal projects can be viewed by any logged in user." when Gitlab::VisibilityLevel::PUBLIC - "The group can be accessed without any authentication." + "The group and any public projects can be viewed without any authentication." end end @@ -63,12 +63,21 @@ module VisibilityLevelHelper end end - def group_visibility_icon_description(group) - "#{visibility_level_label(group.visibility_level)} - #{group_visibility_level_description(group.visibility_level)}" + def visibility_icon_description(form_model) + case form_model + when Project + project_visibility_icon_description(form_model.visibility_level) + when Group + group_visibility_icon_description(form_model.visibility_level) + end + end + + def group_visibility_icon_description(level) + "#{visibility_level_label(level)} - #{group_visibility_level_description(level)}" end - def project_visibility_icon_description(project) - "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}" + def project_visibility_icon_description(level) + "#{visibility_level_label(level)} - #{project_visibility_level_description(level)}" end def visibility_level_label(level) diff --git a/app/models/ability.rb b/app/models/ability.rb index 42b978e04d5..fa2345f6faa 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -170,7 +170,8 @@ class Ability :read_note, :create_project, :create_issue, - :create_note + :create_note, + :upload_file ] end @@ -298,8 +299,12 @@ class Ability end def can_read_group?(user, group) - user.admin? || group.public? || (group.internal? && !user.external?) || group.users.include?(user) || - GroupProjectsFinder.new(group).execute(user).any? + return true if user.admin? + return true if group.public? + return true if group.internal? && !user.external? + return true if group.users.include?(user) + + GroupProjectsFinder.new(group).execute(user).any? end def namespace_abilities(user, namespace) diff --git a/app/models/group.rb b/app/models/group.rb index 900fcd71ff3..b332601c59b 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -7,7 +7,7 @@ # path :string(255) not null # owner_id :integer # visibility_level :integer default(20), not null -# created_at :key => "value", datetime +# created_at :datetime # updated_at :datetime # type :string(255) # description :string(255) default(""), not null @@ -83,9 +83,7 @@ class Group < Namespace end def visibility_level_allowed_by_projects - projects_visibility = self.projects.pluck(:visibility_level) - - allowed_by_projects = projects_visibility.all? { |project_visibility| self.visibility_level >= project_visibility } + allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none? unless allowed_by_projects level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 46c2a53e1f6..2bccd584dde 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -12,7 +12,7 @@ module Groups return @group end - @group.name = @group.path.dup unless @group.name + @group.name ||= @group.path.dup @group.save @group.add_owner(current_user) @group diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index b70e2e4aaa9..99ad12b1003 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -1,7 +1,3 @@ -# Checks visibility level permission check before updating a group -# Do not allow to put Group visibility level smaller than its projects -# Do not allow unauthorized permission levels - module Groups class UpdateService < Groups::BaseService def execute diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5a9fa5d9a4d..820743dc8dd 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -17,7 +17,7 @@ .cover-title %h1 = @group.name - %span.visibility-icon.has_tooltip{ data: { container: 'body' }, title: group_visibility_icon_description(@group) } + %span.visibility-icon.has_tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } = visibility_level_icon(@group.visibility_level, fw: false) .cover-desc.username diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index d4bbafbd40f..514cbfa339d 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -5,7 +5,7 @@ .cover-title.project-home-desc %h1 = @project.name - %span.visibility-icon.has_tooltip{data: { container: 'body' }, title: project_visibility_icon_description(@project)} + %span.visibility-icon.has_tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)} = visibility_level_icon(@project.visibility_level, fw: false) - if @project.description.present? diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index db416b9d91a..66b7ef99650 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -21,7 +21,7 @@ = icon('users') = number_with_delimiter(group.users.count) - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: group_visibility_icon_description(group)} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)} = visibility_level_icon(group.visibility_level, fw: false) = image_tag group_icon(group), class: "avatar s40 hidden-xs" diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 3b987987676..803dd95bc65 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -27,7 +27,7 @@ %span = icon('star') = project.star_count - %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: project_visibility_icon_description(project)} + %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)} = visibility_level_icon(project.visibility_level, fw: false) .title diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb index 89b5ac19983..8121a0c6f90 100644 --- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -1,12 +1,30 @@ class AddVisibilityLevelToGroups < ActiveRecord::Migration - def change - #All groups public by default - add_column :namespaces, :visibility_level, :integer, null: false, default: allowed_visibility_level + def up + add_column :namespaces, :visibility_level, :integer, null: false, default: Gitlab::VisibilityLevel::PUBLIC + add_index :namespaces, :visibility_level + + # Unfortunately, this is needed on top of the `default`, since we don't want the configuration specific + # `allowed_visibility_level` to end up in schema.rb + if allowed_visibility_level < Gitlab::VisibilityLevel::PUBLIC + execute("UPDATE namespaces SET visibility_level = #{allowed_visibility_level}") + end + end + + def down + remove_column :namespaces, :visibility_level end + private + def allowed_visibility_level - # TODO: Don't use `current_application_settings` - allowed_levels = Gitlab::VisibilityLevel.values - current_application_settings.restricted_visibility_levels + return 20 + application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1") + if application_settings + restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil + end + restricted_visibility_levels ||= [] + + allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels allowed_levels.max end end diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb new file mode 100644 index 00000000000..37179926d42 --- /dev/null +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -0,0 +1,27 @@ +# Create visibility level field on DB +# Sets default_visibility_level to value on settings if not restricted +# If value is restricted takes higher visibility level allowed + +class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration + def up + add_column :application_settings, :default_group_visibility, :integer + execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}") + end + + def down + remove_column :application_settings, :default_group_visibility + end + + private + + def allowed_visibility_level + application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1") + if application_settings + restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil + end + restricted_visibility_levels ||= [] + + allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels + allowed_levels.max + end +end diff --git a/db/schema.rb b/db/schema.rb index bb3f0497539..dce2bfe62ca 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -77,6 +77,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do t.boolean "akismet_enabled", default: false t.string "akismet_api_key" t.boolean "email_author_in_body", default: false + t.integer "default_group_visibility" end create_table "audit_events", force: :cascade do |t| diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index d76eb454fe5..71b783b7276 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -15,11 +15,11 @@ describe 'Internal Group access', feature: true do let(:project_guest) { create(:user) } before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(developer, Gitlab::Access::DEVELOPER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) + group.add_owner(owner) + group.add_master(master) + group.add_developer(developer) + group.add_reporter(reporter) + group.add_guest(guest) project.team << [project_guest, :guest] end diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index 8ca4a0ac83b..cc9aee802f9 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -15,11 +15,11 @@ describe 'Private Group access', feature: true do let(:project_guest) { create(:user) } before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(developer, Gitlab::Access::DEVELOPER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) + group.add_owner(owner) + group.add_master(master) + group.add_developer(developer) + group.add_reporter(reporter) + group.add_guest(guest) project.team << [project_guest, :guest] end diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index f556fabb51e..db986683dbe 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -15,12 +15,12 @@ describe 'Public Group access', feature: true do let(:project_guest) { create(:user) } before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(developer, Gitlab::Access::DEVELOPER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) - + group.add_owner(owner) + group.add_master(master) + group.add_developer(developer) + group.add_reporter(reporter) + group.add_guest(guest) + project.team << [project_guest, :guest] end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb index 7b6fc837e5f..66a250f9dd1 100644 --- a/spec/finders/joined_groups_finder_spec.rb +++ b/spec/finders/joined_groups_finder_spec.rb @@ -5,64 +5,70 @@ describe JoinedGroupsFinder do let!(:profile_owner) { create(:user) } let!(:profile_visitor) { create(:user) } - let!(:private_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } - let!(:private_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } - let!(:internal_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - let!(:internal_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - let!(:public_group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let!(:public_group_2) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:private_group) { create(:group, :private) } + let!(:private_group_2) { create(:group, :private) } + let!(:internal_group) { create(:group, :internal) } + let!(:internal_group_2) { create(:group, :internal) } + let!(:public_group) { create(:group, :public) } + let!(:public_group_2) { create(:group, :public) } let!(:finder) { described_class.new(profile_owner) } - describe 'execute' do - context 'without a user only shows public groups from profile owner' do - before { public_group.add_user(profile_owner, Gitlab::Access::MASTER)} - subject { finder.execute } - - it { is_expected.to eq([public_group]) } + context 'without a user' do + before do + public_group.add_master(profile_owner) end - context 'only shows groups where both users are authorized to see' do - subject { finder.execute(profile_visitor) } + it 'only shows public groups from profile owner' do + expect(finder.execute).to eq([public_group]) + end + end - before do - private_group.add_user(profile_owner, Gitlab::Access::MASTER) - private_group.add_user(profile_visitor, Gitlab::Access::DEVELOPER) - internal_group.add_user(profile_owner, Gitlab::Access::MASTER) - public_group.add_user(profile_owner, Gitlab::Access::MASTER) - end + context "with a user" do + before do + private_group.add_master(profile_owner) + private_group.add_developer(profile_visitor) + internal_group.add_master(profile_owner) + public_group.add_master(profile_owner) + end - it { is_expected.to eq([public_group, internal_group, private_group]) } + it 'only shows groups where both users are authorized to see' do + expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group]) end - context 'shows group if profile visitor is in one of its projects' do + context 'if profile visitor is in one of its projects' do before do - public_group.add_user(profile_owner, Gitlab::Access::MASTER) - private_group.add_user(profile_owner, Gitlab::Access::MASTER) + public_group.add_master(profile_owner) + private_group.add_master(profile_owner) project = create(:project, :private, group: private_group, name: 'B', path: 'B') - project.team.add_user(profile_visitor, Gitlab::Access::DEVELOPER) + project.team.add_developer(profile_visitor) end - subject { finder.execute(profile_visitor) } - - it { is_expected.to eq([public_group, private_group]) } + it 'shows group' do + expect(finder.execute(profile_visitor)).to eq([public_group, private_group]) + end end context 'external users' do before do profile_visitor.update_attributes(external: true) - public_group.add_user(profile_owner, Gitlab::Access::MASTER) - internal_group.add_user(profile_owner, Gitlab::Access::MASTER) + public_group.add_master(profile_owner) + internal_group.add_master(profile_owner) end - subject { finder.execute(profile_visitor) } - - it "doest not show internal groups if not member" do - expect(subject).to eq([public_group]) + context 'if not a member' do + it "does not show internal groups" do + expect(finder.execute(profile_visitor)).to eq([public_group]) + end end - it "shows internal groups if authorized" do - internal_group.add_user(profile_visitor, Gitlab::Access::MASTER) - expect(subject).to eq([public_group, internal_group]) + context "if authorized" do + before do + internal_group.add_master(profile_visitor) + end + + it "shows internal groups if authorized" do + expect(finder.execute(profile_visitor)).to eq([public_group, internal_group]) + end end end end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 194c9543772..0a1cc3b3df7 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ProjectsFinder do describe '#execute' do let(:user) { create(:user) } - let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let(:group) { create(:group, :public) } let!(:private_project) do create(:project, :private, name: 'A', path: 'A') diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index b8940483dfb..810016c9658 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe SnippetsFinder do let(:user) { create :user } let(:user1) { create :user } - let(:group) { create :group, visibility_level: Gitlab::VisibilityLevel::PUBLIC } + let(:group) { create :group, :public } let(:project1) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :private, group: group) } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 68e213f4816..7bfca1e72c3 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -67,9 +67,9 @@ describe Group, models: true do end describe 'public_and_internal_only' do - subject { described_class.public_and_internal_only.to_a.sort } + subject { described_class.public_and_internal_only.to_a } - it{ is_expected.to eq([group, internal_group].sort) } + it{ is_expected.to match_array([group, internal_group]) } end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 757324184bd..20f06f4b7e1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -721,8 +721,8 @@ describe Project, models: true do let(:private_group) { create(:group, visibility_level: 0) } let(:internal_group) { create(:group, visibility_level: 10) } - let(:private_project) { create :project, group: private_group, visibility_level: Gitlab::VisibilityLevel::PRIVATE } - let(:internal_project) { create :project, group: internal_group, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + let(:private_project) { create :project, :private, group: private_group } + let(:internal_project) { create :project, :internal, group: internal_group } context 'when group is private project can not be internal' do it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey } -- 2.18.1 From 503244eb9638bb141e3883d40281d7188fe8c02e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 22 Mar 2016 00:23:58 +0100 Subject: [PATCH 22/22] Fix specs --- ...01124843_add_visibility_level_to_groups.rb | 1 - ...roup_visibility_to_application_settings.rb | 2 ++ spec/finders/joined_groups_finder_spec.rb | 21 ++++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb index 8121a0c6f90..d1b921bb208 100644 --- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb +++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb @@ -17,7 +17,6 @@ class AddVisibilityLevelToGroups < ActiveRecord::Migration private def allowed_visibility_level - return 20 application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1") if application_settings restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb index 37179926d42..75de5f70fa2 100644 --- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb +++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb @@ -5,6 +5,8 @@ class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration def up add_column :application_settings, :default_group_visibility, :integer + # Unfortunately, this can't be a `default`, since we don't want the configuration specific + # `allowed_visibility_level` to end up in schema.rb execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}") end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb index 66a250f9dd1..f90a8e007c8 100644 --- a/spec/finders/joined_groups_finder_spec.rb +++ b/spec/finders/joined_groups_finder_spec.rb @@ -26,33 +26,34 @@ describe JoinedGroupsFinder do context "with a user" do before do private_group.add_master(profile_owner) - private_group.add_developer(profile_visitor) internal_group.add_master(profile_owner) public_group.add_master(profile_owner) end - it 'only shows groups where both users are authorized to see' do - expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group]) + context "when the profile visitor is in the private group" do + before do + private_group.add_developer(profile_visitor) + end + + it 'only shows groups where both users are authorized to see' do + expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group]) + end end - context 'if profile visitor is in one of its projects' do + context 'if profile visitor is in one of the private group projects' do before do - public_group.add_master(profile_owner) - private_group.add_master(profile_owner) project = create(:project, :private, group: private_group, name: 'B', path: 'B') - project.team.add_developer(profile_visitor) + project.team.add_user(profile_visitor, Gitlab::Access::DEVELOPER) end it 'shows group' do - expect(finder.execute(profile_visitor)).to eq([public_group, private_group]) + expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group]) end end context 'external users' do before do profile_visitor.update_attributes(external: true) - public_group.add_master(profile_owner) - internal_group.add_master(profile_owner) end context 'if not a member' do -- 2.18.1