Commit f2a9ee25 authored by Felipe Artur's avatar Felipe Artur

Add permission level to groups

parent 60ddd5ef
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)
......
......@@ -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
......
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
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)
......
......@@ -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
......
......@@ -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
......
......@@ -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}%")
......
......@@ -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"
......
......@@ -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'
......
%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
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 @@
#
# 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
......
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
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
......@@ -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))
......
......@@ -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}"
......
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