Commit 58665b64 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'issue_3508' into 'master'

Restrict public users for private instances

Implements #3508

See merge request !3440
parents 70e6fa31 2366768d
...@@ -13,6 +13,7 @@ v 8.7.0 (unreleased) ...@@ -13,6 +13,7 @@ v 8.7.0 (unreleased)
- Project switcher uses new dropdown styling - Project switcher uses new dropdown styling
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles) - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
- Restrict user profiles when public visibility level is restricted.
- All images in discussions and wikis now link to their source files !3464 (Connor Shea). - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu) - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Add setting for customizing the list of trusted proxies !3524 - Add setting for customizing the list of trusted proxies !3524
......
class UsersController < ApplicationController class UsersController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :set_user before_action :user
before_action :authorize_read_user!, only: [:show]
def show def show
respond_to do |format| respond_to do |format|
...@@ -75,22 +76,26 @@ class UsersController < ApplicationController ...@@ -75,22 +76,26 @@ class UsersController < ApplicationController
private private
def set_user def authorize_read_user!
@user = User.find_by_username!(params[:username]) render_404 unless can?(current_user, :read_user, user)
end
def user
@user ||= User.find_by_username!(params[:username])
end end
def contributed_projects def contributed_projects
ContributedProjectsFinder.new(@user).execute(current_user) ContributedProjectsFinder.new(user).execute(current_user)
end end
def contributions_calendar def contributions_calendar
@contributions_calendar ||= Gitlab::ContributionsCalendar. @contributions_calendar ||= Gitlab::ContributionsCalendar.
new(contributed_projects, @user) new(contributed_projects, user)
end end
def load_events def load_events
# Get user activity feed for projects common for both users # Get user activity feed for projects common for both users
@events = @user.recent_events. @events = user.recent_events.
merge(projects_for_current_user). merge(projects_for_current_user).
references(:project). references(:project).
with_associations. with_associations.
...@@ -99,16 +104,16 @@ class UsersController < ApplicationController ...@@ -99,16 +104,16 @@ class UsersController < ApplicationController
def load_projects def load_projects
@projects = @projects =
PersonalProjectsFinder.new(@user).execute(current_user) PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page]) .page(params[:page])
end end
def load_contributed_projects def load_contributed_projects
@contributed_projects = contributed_projects.joined(@user) @contributed_projects = contributed_projects.joined(user)
end end
def load_groups def load_groups
@groups = JoinedGroupsFinder.new(@user).execute(current_user) @groups = JoinedGroupsFinder.new(user).execute(current_user)
end end
def projects_for_current_user def projects_for_current_user
......
...@@ -18,6 +18,7 @@ class Ability ...@@ -18,6 +18,7 @@ class Ability
when Namespace then namespace_abilities(user, subject) when Namespace then namespace_abilities(user, subject)
when GroupMember then group_member_abilities(user, subject) when GroupMember then group_member_abilities(user, subject)
when ProjectMember then project_member_abilities(user, subject) when ProjectMember then project_member_abilities(user, subject)
when User then user_abilities
else [] else []
end.concat(global_abilities(user)) end.concat(global_abilities(user))
end end
...@@ -35,6 +36,8 @@ class Ability ...@@ -35,6 +36,8 @@ class Ability
anonymous_project_abilities(subject) anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group) when subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject) anonymous_group_abilities(subject)
when subject.is_a?(User)
anonymous_user_abilities
else else
[] []
end end
...@@ -81,17 +84,17 @@ class Ability ...@@ -81,17 +84,17 @@ class Ability
end end
def anonymous_group_abilities(subject) def anonymous_group_abilities(subject)
rules = []
group = if subject.is_a?(Group) group = if subject.is_a?(Group)
subject subject
else else
subject.group subject.group
end end
if group && group.public? rules << :read_group if group.public?
[:read_group]
else rules
[]
end
end end
def anonymous_personal_snippet_abilities(snippet) def anonymous_personal_snippet_abilities(snippet)
...@@ -110,9 +113,14 @@ class Ability ...@@ -110,9 +113,14 @@ class Ability
end end
end end
def anonymous_user_abilities
[:read_user] unless restricted_public_level?
end
def global_abilities(user) def global_abilities(user)
rules = [] rules = []
rules << :create_group if user.can_create_group rules << :create_group if user.can_create_group
rules << :read_users_list
rules rules
end end
...@@ -163,7 +171,7 @@ class Ability ...@@ -163,7 +171,7 @@ class Ability
@public_project_rules ||= project_guest_rules + [ @public_project_rules ||= project_guest_rules + [
:download_code, :download_code,
:fork_project, :fork_project,
:read_commit_status, :read_commit_status
] ]
end end
...@@ -284,7 +292,6 @@ class Ability ...@@ -284,7 +292,6 @@ class Ability
def group_abilities(user, group) def group_abilities(user, group)
rules = [] rules = []
rules << :read_group if can_read_group?(user, group) rules << :read_group if can_read_group?(user, group)
# Only group masters and group owners can create new projects # Only group masters and group owners can create new projects
...@@ -456,6 +463,10 @@ class Ability ...@@ -456,6 +463,10 @@ class Ability
rules rules
end end
def user_abilities
[:read_user]
end
def abilities def abilities
@abilities ||= begin @abilities ||= begin
abilities = Six.new abilities = Six.new
...@@ -470,6 +481,10 @@ class Ability ...@@ -470,6 +481,10 @@ class Ability
private private
def restricted_public_level?
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
def named_abilities(name) def named_abilities(name)
[ [
:"read_#{name}", :"read_#{name}",
......
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
.btn-group{ data: data_attrs } .btn-group{ data: data_attrs }
- restricted_level_checkboxes('restricted-visibility-help').each do |level| - restricted_level_checkboxes('restricted-visibility-help').each do |level|
= level = level
%span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets %span.help-block#restricted-visibility-help
Selected levels cannot be used by non-admin users for projects or snippets.
If the public level is restricted, user profiles are only visible to logged in users.
.form-group .form-group
= f.label :import_sources, class: 'control-label col-sm-2' = f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -58,6 +58,9 @@ you are logged in or not. ...@@ -58,6 +58,9 @@ you are logged in or not.
When visiting the public page of a user, you can only see the projects which When visiting the public page of a user, you can only see the projects which
you are privileged to. you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users.
## Restricting the use of public or internal projects ## Restricting the use of public or internal projects
In the Admin area under **Settings** (`/admin/application_settings`), you can In the Admin area under **Settings** (`/admin/application_settings`), you can
......
...@@ -11,6 +11,10 @@ module API ...@@ -11,6 +11,10 @@ module API
# GET /users?search=Admin # GET /users?search=Admin
# GET /users?username=root # GET /users?username=root
get do get do
unless can?(current_user, :read_users_list, nil)
render_api_error!("Not authorized.", 403)
end
if params[:username].present? if params[:username].present?
@users = User.where(username: params[:username]) @users = User.where(username: params[:username])
else else
...@@ -36,10 +40,12 @@ module API ...@@ -36,10 +40,12 @@ module API
get ":id" do get ":id" do
@user = User.find(params[:id]) @user = User.find(params[:id])
if current_user.is_admin? if current_user && current_user.is_admin?
present @user, with: Entities::UserFull present @user, with: Entities::UserFull
else elsif can?(current_user, :read_user, @user)
present @user, with: Entities::User present @user, with: Entities::User
else
render_api_error!("User not found.", 404)
end end
end end
......
require 'spec_helper'
describe Groups::GroupMembersController do
let(:user) { create(:user) }
let(:group) { create(:group) }
context "index" do
before do
group.add_owner(user)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'renders index with group members' do
get :index, group_id: group.path
expect(response.status).to eq(200)
expect(response).to render_template(:index)
end
end
end
...@@ -33,7 +33,30 @@ describe UsersController do ...@@ -33,7 +33,30 @@ describe UsersController do
it 'renders the show template' do it 'renders the show template' do
get :show, username: user.username get :show, username: user.username
expect(response).to be_success expect(response.status).to eq(200)
expect(response).to render_template('show')
end
end
end
context 'when public visibility level is restricted' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
context 'when logged out' do
it 'renders 404' do
get :show, username: user.username
expect(response.status).to eq(404)
end
end
context 'when logged in' do
before { sign_in(user) }
it 'renders show' do
get :show, username: user.username
expect(response.status).to eq(200)
expect(response).to render_template('show') expect(response).to render_template('show')
end end
end end
......
...@@ -20,6 +20,24 @@ describe API::API, api: true do ...@@ -20,6 +20,24 @@ describe API::API, api: true do
end end
context "when authenticated" do context "when authenticated" do
#These specs are written just in case API authentication is not required anymore
context "when public level is restricted" do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true)
end
it "renders 403" do
get api("/users")
expect(response.status).to eq(403)
end
it "renders 404" do
get api("/users/#{user.id}")
expect(response.status).to eq(404)
end
end
it "should return an array of users" do it "should return an array of users" do
get api("/users", user) get api("/users", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
......
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