Commit 50f5960c authored by Douwe Maan's avatar Douwe Maan

Merge branch 'ee-1439-read-only-user' into 'master'

Backport changes from gitlab-org/gitlab-ee!998

See merge request !8984
parents 437b46b9 f5a798c7
*.erb *.erb
lib/gitlab/sanitizers/svg/whitelist.rb lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb lib/gitlab/diff/position_tracer.rb
app/policies/project_policy.rb
...@@ -175,7 +175,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -175,7 +175,7 @@ class Admin::UsersController < Admin::ApplicationController
def user_params_ce def user_params_ce
[ [
:admin, :access_level,
:avatar, :avatar,
:bio, :bio,
:can_create_group, :can_create_group,
......
...@@ -18,7 +18,7 @@ class GroupProjectsFinder < UnionFinder ...@@ -18,7 +18,7 @@ class GroupProjectsFinder < UnionFinder
projects = [] projects = []
if current_user if current_user
if @group.users.include?(current_user) || current_user.admin? if @group.users.include?(current_user)
projects << @group.projects unless only_shared projects << @group.projects unless only_shared
projects << @group.shared_projects unless only_owned projects << @group.shared_projects unless only_owned
else else
......
...@@ -904,6 +904,21 @@ class User < ActiveRecord::Base ...@@ -904,6 +904,21 @@ class User < ActiveRecord::Base
end end
end end
def access_level
if admin?
:admin
else
:regular
end
end
def access_level=(new_level)
new_level = new_level.to_s
return unless %w(admin regular).include?(new_level)
self.admin = (new_level == 'admin')
end
private private
def ci_projects_union def ci_projects_union
......
...@@ -218,25 +218,7 @@ class ProjectPolicy < BasePolicy ...@@ -218,25 +218,7 @@ class ProjectPolicy < BasePolicy
def anonymous_rules def anonymous_rules
return unless project.public? return unless project.public?
can! :read_project base_readonly_access!
can! :read_board
can! :read_list
can! :read_wiki
can! :read_label
can! :read_milestone
can! :read_project_snippet
can! :read_project_member
can! :read_merge_request
can! :read_note
can! :read_pipeline
can! :read_commit_status
can! :read_container_image
can! :download_code
can! :download_wiki_code
can! :read_cycle_analytics
# NOTE: may be overridden by IssuePolicy
can! :read_issue
# Allow to read builds by anonymous user if guests are allowed # Allow to read builds by anonymous user if guests are allowed
can! :read_build if project.public_builds? can! :read_build if project.public_builds?
...@@ -269,4 +251,31 @@ class ProjectPolicy < BasePolicy ...@@ -269,4 +251,31 @@ class ProjectPolicy < BasePolicy
:"admin_#{name}" :"admin_#{name}"
] ]
end end
private
# A base set of abilities for read-only users, which
# is then augmented as necessary for anonymous and other
# read-only users.
def base_readonly_access!
can! :read_project
can! :read_board
can! :read_list
can! :read_wiki
can! :read_label
can! :read_milestone
can! :read_project_snippet
can! :read_project_member
can! :read_merge_request
can! :read_note
can! :read_pipeline
can! :read_commit_status
can! :read_container_image
can! :download_code
can! :download_wiki_code
can! :read_cycle_analytics
# NOTE: may be overridden by IssuePolicy
can! :read_issue
end
end end
...@@ -3,7 +3,7 @@ class ProjectSnippetPolicy < BasePolicy ...@@ -3,7 +3,7 @@ class ProjectSnippetPolicy < BasePolicy
can! :read_project_snippet if @subject.public? can! :read_project_snippet if @subject.public?
return unless @user return unless @user
if @user && @subject.author == @user || @user.admin? if @user && (@subject.author == @user || @user.admin?)
can! :read_project_snippet can! :read_project_snippet
can! :update_project_snippet can! :update_project_snippet
can! :admin_project_snippet can! :admin_project_snippet
......
%fieldset
%legend Access
.form-group
= f.label :projects_limit, class: 'control-label'
.col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control'
.form-group
= f.label :can_create_group, class: 'control-label'
.col-sm-10= f.check_box :can_create_group
.form-group
= f.label :access_level, class: 'control-label'
.col-sm-10
- editing_current_user = (current_user == @user)
= f.radio_button :access_level, :regular, disabled: editing_current_user
= label_tag :regular do
Regular
%p.light
Regular users have access to their groups and projects
= f.radio_button :access_level, :admin, disabled: editing_current_user
= label_tag :admin do
Admin
%p.light
Administrators have access to all groups, projects and users and can manage all features in this installation
- if editing_current_user
%p.light
You cannot remove your own admin rights.
.form-group
= f.label :external, class: 'control-label'
.col-sm-10
= f.check_box :external do
External
%p.light
External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects or groups.
...@@ -40,28 +40,7 @@ ...@@ -40,28 +40,7 @@
= f.label :password_confirmation, class: 'control-label' = f.label :password_confirmation, class: 'control-label'
.col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control' .col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
%fieldset = render partial: 'access_levels', locals: { f: f }
%legend Access
.form-group
= f.label :projects_limit, class: 'control-label'
.col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control'
.form-group
= f.label :can_create_group, class: 'control-label'
.col-sm-10= f.check_box :can_create_group
.form-group
= f.label :admin, class: 'control-label'
- if current_user == @user
.col-sm-10= f.check_box :admin, disabled: true
.col-sm-10 You cannot remove your own admin rights.
- else
.col-sm-10= f.check_box :admin
.form-group
= f.label :external, class: 'control-label'
.col-sm-10= f.check_box :external
.col-sm-10 External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects or groups.
%fieldset %fieldset
%legend Profile %legend Profile
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40' = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
.timeline-content.timeline-content-form .timeline-content.timeline-content-form
= render "projects/notes/form", view: diff_view = render "projects/notes/form", view: diff_view
- else - elsif !current_user
.disabled-comment.text-center .disabled-comment.text-center
.disabled-comment-text.inline .disabled-comment-text.inline
Please Please
......
...@@ -13,7 +13,19 @@ module Gitlab ...@@ -13,7 +13,19 @@ module Gitlab
scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) } scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
scope :non_public_only, -> { where.not(visibility_level: PUBLIC) } scope :non_public_only, -> { where.not(visibility_level: PUBLIC) }
scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only } scope :public_to_user, -> (user) do
if user
if user.admin?
all
elsif !user.external?
public_and_internal_only
else
public_only
end
else
public_only
end
end
end end
PRIVATE = 0 unless const_defined?(:PRIVATE) PRIVATE = 0 unless const_defined?(:PRIVATE)
......
...@@ -211,7 +211,7 @@ describe "Admin::Users", feature: true do ...@@ -211,7 +211,7 @@ describe "Admin::Users", feature: true do
fill_in "user_email", with: "bigbang@mail.com" fill_in "user_email", with: "bigbang@mail.com"
fill_in "user_password", with: "AValidPassword1" fill_in "user_password", with: "AValidPassword1"
fill_in "user_password_confirmation", with: "AValidPassword1" fill_in "user_password_confirmation", with: "AValidPassword1"
check "user_admin" choose "user_access_level_admin"
click_button "Save changes" click_button "Save changes"
end end
......
...@@ -1422,4 +1422,37 @@ describe User, models: true do ...@@ -1422,4 +1422,37 @@ describe User, models: true do
expect(user.project_authorizations.where(access_level: Gitlab::Access::REPORTER).exists?).to eq(true) expect(user.project_authorizations.where(access_level: Gitlab::Access::REPORTER).exists?).to eq(true)
end end
end end
describe '#access_level=' do
let(:user) { build(:user) }
it 'does nothing for an invalid access level' do
user.access_level = :invalid_access_level
expect(user.access_level).to eq(:regular)
expect(user.admin).to be false
end
it "assigns the 'admin' access level" do
user.access_level = :admin
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
it "doesn't clear existing access levels when an invalid access level is passed in" do
user.access_level = :admin
user.access_level = :invalid_access_level
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
it "accepts string values in addition to symbols" do
user.access_level = 'admin'
expect(user.access_level).to eq(:admin)
expect(user.admin).to be true
end
end
end end
...@@ -10,61 +10,59 @@ describe ProjectPolicy, models: true do ...@@ -10,61 +10,59 @@ describe ProjectPolicy, models: true do
let(:project) { create(:empty_project, :public, namespace: owner.namespace) } let(:project) { create(:empty_project, :public, namespace: owner.namespace) }
let(:guest_permissions) do let(:guest_permissions) do
[ %i[
:read_project, :read_board, :read_list, :read_wiki, :read_issue, :read_label, read_project read_board read_list read_wiki read_issue read_label
:read_milestone, :read_project_snippet, :read_project_member, read_milestone read_project_snippet read_project_member
:read_note, :create_project, :create_issue, :create_note, read_note create_project create_issue create_note
:upload_file upload_file
] ]
end end
let(:reporter_permissions) do let(:reporter_permissions) do
[ %i[
:download_code, :fork_project, :create_project_snippet, :update_issue, download_code fork_project create_project_snippet update_issue
:admin_issue, :admin_label, :admin_list, :read_commit_status, :read_build, admin_issue admin_label admin_list read_commit_status read_build
:read_container_image, :read_pipeline, :read_environment, :read_deployment, read_container_image read_pipeline read_environment read_deployment
:read_merge_request, :download_wiki_code read_merge_request download_wiki_code
] ]
end end
let(:team_member_reporter_permissions) do let(:team_member_reporter_permissions) do
[ %i[build_download_code build_read_container_image]
:build_download_code, :build_read_container_image
]
end end
let(:developer_permissions) do let(:developer_permissions) do
[ %i[
:admin_merge_request, :update_merge_request, :create_commit_status, admin_merge_request update_merge_request create_commit_status
:update_commit_status, :create_build, :update_build, :create_pipeline, update_commit_status create_build update_build create_pipeline
:update_pipeline, :create_merge_request, :create_wiki, :push_code, update_pipeline create_merge_request create_wiki push_code
:resolve_note, :create_container_image, :update_container_image, resolve_note create_container_image update_container_image
:create_environment, :create_deployment create_environment create_deployment
] ]
end end
let(:master_permissions) do let(:master_permissions) do
[ %i[
:push_code_to_protected_branches, :update_project_snippet, :update_environment, push_code_to_protected_branches update_project_snippet update_environment
:update_deployment, :admin_milestone, :admin_project_snippet, update_deployment admin_milestone admin_project_snippet
:admin_project_member, :admin_note, :admin_wiki, :admin_project, admin_project_member admin_note admin_wiki admin_project
:admin_commit_status, :admin_build, :admin_container_image, admin_commit_status admin_build admin_container_image
:admin_pipeline, :admin_environment, :admin_deployment admin_pipeline admin_environment admin_deployment
] ]
end end
let(:public_permissions) do let(:public_permissions) do
[ %i[
:download_code, :fork_project, :read_commit_status, :read_pipeline, download_code fork_project read_commit_status read_pipeline
:read_container_image, :build_download_code, :build_read_container_image, read_container_image build_download_code build_read_container_image
:download_wiki_code download_wiki_code
] ]
end end
let(:owner_permissions) do let(:owner_permissions) do
[ %i[
:change_namespace, :change_visibility_level, :rename_project, :remove_project, change_namespace change_visibility_level rename_project remove_project
:archive_project, :remove_fork_project, :destroy_merge_request, :destroy_issue archive_project remove_fork_project destroy_merge_request destroy_issue
] ]
end end
......
require 'spec_helper'
describe ProjectSnippetPolicy, models: true do
let(:current_user) { create(:user) }
let(:author_permissions) do
[
:update_project_snippet,
:admin_project_snippet
]
end
subject { described_class.abilities(current_user, project_snippet).to_set }
context 'public snippet' do
let(:project_snippet) { create(:project_snippet, :public) }
context 'no user' do
let(:current_user) { nil }
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
context 'regular user' do
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
end
context 'internal snippet' do
let(:project_snippet) { create(:project_snippet, :internal) }
context 'no user' do
let(:current_user) { nil }
it do
is_expected.not_to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
context 'regular user' do
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
end
context 'private snippet' do
let(:project_snippet) { create(:project_snippet, :private) }
context 'no user' do
let(:current_user) { nil }
it do
is_expected.not_to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
context 'regular user' do
it do
is_expected.not_to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
context 'snippet author' do
let(:project_snippet) { create(:project_snippet, :private, author: current_user) }
it do
is_expected.to include(:read_project_snippet)
is_expected.to include(*author_permissions)
end
end
context 'project team member' do
before { project_snippet.project.team << [current_user, :developer] }
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
end
end
context 'admin user' do
let(:current_user) { create(:admin) }
it do
is_expected.to include(:read_project_snippet)
is_expected.to include(*author_permissions)
end
end
end
end
...@@ -51,7 +51,7 @@ describe Groups::UpdateService, services: true do ...@@ -51,7 +51,7 @@ describe Groups::UpdateService, services: true do
end end
context 'rename group' do context 'rename group' do
let!(:service) { described_class.new(internal_group, user, path: 'new_path') } let!(:service) { described_class.new(internal_group, user, path: SecureRandom.hex) }
before do before do
internal_group.add_user(user, Gitlab::Access::MASTER) internal_group.add_user(user, Gitlab::Access::MASTER)
......
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