Commit f2a9ee25 authored by Felipe Artur's avatar Felipe Artur

Add permission level to groups

parent 60ddd5ef
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
def index def index
@groups = Group.order_id_desc @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page]).per(PER_PAGE)
......
...@@ -131,7 +131,7 @@ class GroupsController < Groups::ApplicationController ...@@ -131,7 +131,7 @@ class GroupsController < Groups::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :public) params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level)
end end
def load_events def load_events
......
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
...@@ -19,6 +19,10 @@ module GroupsHelper ...@@ -19,6 +19,10 @@ module GroupsHelper
end end
end end
def can_change_group_visibility_level?(group)
can?(current_user, :change_visibility_level, group)
end
def group_icon(group) def group_icon(group)
if group.is_a?(String) if group.is_a?(String)
group = Group.find_by(path: group) group = Group.find_by(path: group)
......
...@@ -19,6 +19,8 @@ module VisibilityLevelHelper ...@@ -19,6 +19,8 @@ module VisibilityLevelHelper
case form_model case form_model
when Project when Project
project_visibility_level_description(level) project_visibility_level_description(level)
when Group
group_visibility_level_description(level)
when Snippet when Snippet
snippet_visibility_level_description(level, form_model) snippet_visibility_level_description(level, form_model)
end end
...@@ -35,6 +37,17 @@ module VisibilityLevelHelper ...@@ -35,6 +37,17 @@ module VisibilityLevelHelper
end end
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) def snippet_visibility_level_description(level, snippet = nil)
case level case level
when Gitlab::VisibilityLevel::PRIVATE when Gitlab::VisibilityLevel::PRIVATE
......
...@@ -275,11 +275,12 @@ class Ability ...@@ -275,11 +275,12 @@ class Ability
rules << :read_group rules << :read_group
end 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? if group.has_master?(user) || group.has_owner?(user) || user.admin?
rules += [ rules += [
:create_projects, :create_projects,
:admin_milestones :admin_milestones,
:change_visibility_level
] ]
end end
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
# name :string(255) not null # name :string(255) not null
# path :string(255) not null # path :string(255) not null
# owner_id :integer # owner_id :integer
# created_at :datetime # visibility_level :integer default(20), not null
# created_at :key => "value", datetime
# updated_at :datetime # updated_at :datetime
# type :string(255) # type :string(255)
# description :string(255) default(""), not null # description :string(255) default(""), not null
...@@ -18,8 +19,10 @@ require 'file_size_validator' ...@@ -18,8 +19,10 @@ require 'file_size_validator'
class Group < Namespace class Group < Namespace
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include Gitlab::VisibilityLevel
include Referable include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
alias_method :members, :group_members alias_method :members, :group_members
has_many :users, through: :group_members has_many :users, through: :group_members
...@@ -32,6 +35,10 @@ class Group < Namespace ...@@ -32,6 +35,10 @@ class Group < Namespace
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_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 class << self
def search(query) def search(query)
where("LOWER(namespaces.name) LIKE :query or LOWER(namespaces.path) LIKE :query", query: "%#{query.downcase}%") where("LOWER(namespaces.name) LIKE :query or LOWER(namespaces.path) LIKE :query", query: "%#{query.downcase}%")
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
%hr %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" = 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 .form-actions
= f.submit 'Save group', class: "btn btn-save" = f.submit 'Save group', class: "btn btn-save"
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
.col-sm-10 .col-sm-10
= render 'shared/choose_group_avatar_button', f: f = 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 .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
= render 'shared/group_tips' = render 'shared/group_tips'
......
%ul %ul
%li A group is a collection of several projects %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 Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace %li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group %li Existing projects may be moved into a group
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
...@@ -11,8 +11,7 @@ ...@@ -11,8 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -576,6 +575,7 @@ ActiveRecord::Schema.define(version: 20160309140734) do ...@@ -576,6 +575,7 @@ ActiveRecord::Schema.define(version: 20160309140734) do
t.string "type" t.string "type"
t.string "description", default: "", null: false t.string "description", default: "", null: false
t.string "avatar" t.string "avatar"
t.integer "visibility_level", default: 0, null: false
end end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
......
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
...@@ -18,4 +18,19 @@ describe GroupsHelper do ...@@ -18,4 +18,19 @@ describe GroupsHelper do
expect(group_icon(group.path)).to match('group_avatar.png') expect(group_icon(group.path)).to match('group_avatar.png')
end end
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 end
...@@ -8,6 +8,7 @@ describe VisibilityLevelHelper do ...@@ -8,6 +8,7 @@ describe VisibilityLevelHelper do
end end
let(:project) { build(:project) } let(:project) { build(:project) }
let(:group) { build(:group) }
let(:personal_snippet) { build(:personal_snippet) } let(:personal_snippet) { build(:personal_snippet) }
let(:project_snippet) { build(:project_snippet) } let(:project_snippet) { build(:project_snippet) }
...@@ -19,6 +20,13 @@ describe VisibilityLevelHelper do ...@@ -19,6 +20,13 @@ describe VisibilityLevelHelper do
end end
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 context 'called with a Snippet' do
it 'delegates snippets to #snippet_visibility_level_description' do it 'delegates snippets to #snippet_visibility_level_description' do
expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet)) expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet))
......
...@@ -56,6 +56,24 @@ describe Group, models: true do ...@@ -56,6 +56,24 @@ describe Group, models: true do
end end
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 describe '#to_reference' do
it 'returns a String reference to the object' do it 'returns a String reference to the object' do
expect(group.to_reference).to eq "@#{group.name}" expect(group.to_reference).to eq "@#{group.name}"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment