summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2018-07-24 12:46:19 +0000
committerDouwe Maan <douwe@gitlab.com>2018-07-24 12:46:19 +0000
commit4de8a3dab1d578a6f8176ec40e2b4bae2468adf2 (patch)
tree99486b31dc0df1b86db0bb11ec32b05c9bc1fb2d
parentadc327d3fa72b9f5b9c42c629c99f0a89ca15192 (diff)
parent99011a61cf4136c806e7de43fcd55475d2407fa1 (diff)
downloadgitlab-ce-4de8a3dab1d578a6f8176ec40e2b4bae2468adf2.tar.gz
Merge branch 'jxterry-private-profile' into 'master'
Add an option to have a private profile on GitLab See merge request gitlab-org/gitlab-ce!20387
-rw-r--r--app/controllers/profiles_controller.rb3
-rw-r--r--app/controllers/users_controller.rb6
-rw-r--r--app/finders/personal_projects_finder.rb4
-rw-r--r--app/finders/user_recent_events_finder.rb3
-rw-r--r--app/helpers/users_helper.rb8
-rw-r--r--app/policies/user_policy.rb6
-rw-r--r--app/services/users/build_service.rb3
-rw-r--r--app/views/profiles/show.html.haml6
-rw-r--r--app/views/users/show.html.haml74
-rw-r--r--changelogs/unreleased/38604-add-private-profile.yml5
-rw-r--r--db/migrate/20180722103201_add_private_profile_to_users.rb10
-rw-r--r--db/schema.rb3
-rw-r--r--doc/api/users.md17
-rw-r--r--doc/user/profile/index.md22
-rw-r--r--lib/api/entities.rb3
-rw-r--r--lib/api/keys.rb2
-rw-r--r--lib/api/users.rb11
-rw-r--r--spec/controllers/users_controller_spec.rb64
-rw-r--r--spec/features/users/show_spec.rb56
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb15
-rw-r--r--spec/helpers/users_helper_spec.rb16
-rw-r--r--spec/requests/api/users_spec.rb41
22 files changed, 312 insertions, 66 deletions
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 074db361949..56a7b766b77 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -99,7 +99,8 @@ class ProfilesController < Profiles::ApplicationController
:username,
:website_url,
:organization,
- :preferred_language
+ :preferred_language,
+ :private_profile
)
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 31f47a7aa7c..2f65f4a7403 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -13,6 +13,8 @@ class UsersController < ApplicationController
skip_before_action :authenticate_user!
before_action :user, except: [:exists]
+ before_action :authorize_read_user_profile!,
+ only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets]
def show
respond_to do |format|
@@ -148,4 +150,8 @@ class UsersController < ApplicationController
def build_canonical_path(user)
url_for(safe_params.merge(username: user.to_param))
end
+
+ def authorize_read_user_profile!
+ access_denied! unless can?(current_user, :read_user_profile, user)
+ end
end
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 18adfea747f..a56a3a1e1a9 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,4 +1,6 @@
class PersonalProjectsFinder < UnionFinder
+ include Gitlab::Allowable
+
def initialize(user, params = {})
@user = user
@params = params
@@ -14,6 +16,8 @@ class PersonalProjectsFinder < UnionFinder
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
+ return Project.none unless can?(current_user, :read_user_profile, @user)
+
segments = all_projects(current_user)
find_union(segments, Project).includes(:namespace).order_updated_desc
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
index 74776b2ed1f..876f086a3ef 100644
--- a/app/finders/user_recent_events_finder.rb
+++ b/app/finders/user_recent_events_finder.rb
@@ -7,6 +7,7 @@
class UserRecentEventsFinder
prepend FinderWithCrossProjectAccess
include FinderMethods
+ include Gitlab::Allowable
requires_cross_project_access
@@ -21,6 +22,8 @@ class UserRecentEventsFinder
end
def execute
+ return Event.none unless can?(current_user, :read_user_profile, target_user)
+
recent_events(params[:offset] || 0)
.joins(:project)
.with_associations
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 4d17b22a4a1..8ee4203b6f5 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -42,7 +42,13 @@ module UsersHelper
private
def get_profile_tabs
- [:activity, :groups, :contributed, :projects, :snippets]
+ tabs = []
+
+ if can?(current_user, :read_user_profile, @user)
+ tabs += [:activity, :groups, :contributed, :projects, :snippets]
+ end
+
+ tabs
end
def get_current_user_menu_items
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index ee219f0a0d0..8499e45e846 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -5,6 +5,9 @@ class UserPolicy < BasePolicy
desc "This is the ghost user"
condition(:subject_ghost, scope: :subject, score: 0) { @subject.ghost? }
+ desc "The profile is private"
+ condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? }
+
rule { ~restricted_public_level }.enable :read_user
rule { ~anonymous }.enable :read_user
@@ -12,4 +15,7 @@ class UserPolicy < BasePolicy
enable :destroy_user
enable :update_user
end
+
+ rule { default }.enable :read_user_profile
+ rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile
end
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index c69b46cab5a..acc2fa153ae 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -64,7 +64,8 @@ module Users
:theme_id,
:twitter,
:username,
- :website_url
+ :website_url,
+ :private_profile
]
end
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 507cd5dcc12..a4835584b50 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -69,6 +69,12 @@
= f.text_field :location
= f.text_field :organization
= f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.'
+ %hr
+ %h5 Private profile
+ - private_profile_label = capture do
+ Don't display activity-related personal information on your profile
+ = link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
+ = f.check_box :private_profile, label: private_profile_label
.prepend-top-default.append-bottom-default
= f.submit 'Update profile settings', class: 'btn btn-success'
= link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel'
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index b2ec7166832..8d9e86d02c4 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -23,8 +23,9 @@
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
- = icon('rss')
+ - if can?(current_user, :read_user_profile, @user)
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
+ = icon('rss')
- if current_user && current_user.admin?
= link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area',
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
@@ -40,10 +41,12 @@
= @user.name
.cover-desc.member-date
- %span.middle-dot-divider
- @#{@user.username}
- %span.middle-dot-divider
- Member since #{@user.created_at.to_date.to_s(:long)}
+ %p
+ %span.middle-dot-divider
+ @#{@user.username}
+ - if can?(current_user, :read_user_profile, @user)
+ %span.middle-dot-divider
+ Member since #{@user.created_at.to_date.to_s(:long)}
.cover-desc
- unless @user.public_email.blank?
@@ -78,30 +81,31 @@
%p.profile-user-bio
= @user.bio
- .scrolling-tabs-container
- .fade-left= icon('angle-left')
- .fade-right= icon('angle-right')
- %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
- - if profile_tab?(:activity)
- %li.js-activity-tab
- = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
- Activity
- - if profile_tab?(:groups)
- %li.js-groups-tab
- = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
- Groups
- - if profile_tab?(:contributed)
- %li.js-contributed-tab
- = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
- Contributed projects
- - if profile_tab?(:projects)
- %li.js-projects-tab
- = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
- Personal projects
- - if profile_tab?(:snippets)
- %li.js-snippets-tab
- = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
- Snippets
+ - unless profile_tabs.empty?
+ .scrolling-tabs-container
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
+ - if profile_tab?(:activity)
+ %li.js-activity-tab
+ = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+ Activity
+ - if profile_tab?(:groups)
+ %li.js-groups-tab
+ = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
+ Groups
+ - if profile_tab?(:contributed)
+ %li.js-contributed-tab
+ = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
+ Contributed projects
+ - if profile_tab?(:projects)
+ %li.js-projects-tab
+ = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
+ Personal projects
+ - if profile_tab?(:snippets)
+ %li.js-snippets-tab
+ = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
+ Snippets
%div{ class: container_class }
.tab-content
@@ -137,3 +141,13 @@
.loading-status
= spinner
+
+ - if profile_tabs.empty?
+ .row
+ .col-12
+ .svg-content
+ = image_tag 'illustrations/profile_private_mode.svg'
+ .col-12.text-center
+ .text-content
+ %h4
+ This user has a private profile
diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml
new file mode 100644
index 00000000000..e40e7d9321e
--- /dev/null
+++ b/changelogs/unreleased/38604-add-private-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Add an option to have a private profile on GitLab.
+merge_request: 20387
+author: jxterry
+type: added
diff --git a/db/migrate/20180722103201_add_private_profile_to_users.rb b/db/migrate/20180722103201_add_private_profile_to_users.rb
new file mode 100644
index 00000000000..4f7ef1322d8
--- /dev/null
+++ b/db/migrate/20180722103201_add_private_profile_to_users.rb
@@ -0,0 +1,10 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPrivateProfileToUsers < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :users, :private_profile, :boolean
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1a5555fb3a4..3db11d8447e 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: 20180704204006) do
+ActiveRecord::Schema.define(version: 20180722103201) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -2124,6 +2124,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do
t.integer "theme_id", limit: 2
t.integer "accepted_term_id"
t.string "feed_token"
+ t.boolean "private_profile"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/api/users.md b/doc/api/users.md
index 72fdaaa2c74..1bcc7b7f346 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -105,7 +105,8 @@ GET /users
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
},
{
"id": 2,
@@ -135,7 +136,8 @@ GET /users
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
]
```
@@ -248,7 +250,8 @@ Parameters:
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
```
@@ -288,6 +291,7 @@ Parameters:
- `skip_confirmation` (optional) - Skip confirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
+- `private_profile (optional) - User's profile is private - true or false
## User modification
@@ -318,6 +322,7 @@ Parameters:
- `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
+- `private_profile (optional) - User's profile is private - true or false
On password update, user will be forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
@@ -382,7 +387,8 @@ GET /user
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
```
@@ -429,7 +435,8 @@ GET /user
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
```
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 91cdef8d1dd..96a08c04905 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -68,6 +68,28 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand
which also covers the case where you have projects hosted with
[GitLab Pages](../project/pages/index.md).
+## Private profile
+
+The following information will be hidden from the user profile page (https://gitlab.example.com/username) if this feature is enabled:
+
+- Atom feed
+- Date when account is created
+- Activity tab
+- Groups tab
+- Contributed projects tab
+- Personal projects tab
+- Snippets tab
+
+To enable private profile:
+
+1. Navigate to your personal [profile settings](#profile-settings).
+1. Check the "Private profile" option.
+1. Hit **Update profile settings**.
+
+
+NOTE: **Note:**
+You and GitLab admins can see your the abovementioned information on your profile even if it is private.
+
## Troubleshooting
### Why do I keep getting signed out?
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 464a31ee819..e883687f2db 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -30,7 +30,7 @@ module API
end
class User < UserBasic
- expose :created_at
+ expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
end
@@ -55,6 +55,7 @@ module API
expose :can_create_project?, as: :can_create_project
expose :two_factor_enabled?, as: :two_factor_enabled
expose :external
+ expose :private_profile
end
class UserWithAdmin < UserPublic
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index 767f27ef334..fd93f797f72 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -12,7 +12,7 @@ module API
key = Key.find(params[:id])
- present key, with: Entities::SSHKeyWithUser
+ present key, with: Entities::SSHKeyWithUser, current_user: current_user
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 6da6c2b43de..e83887b3e9e 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -42,6 +42,7 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user'
+ optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider
end
@@ -97,7 +98,7 @@ module API
entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
- users, options = with_custom_attributes(users, with: entity)
+ users, options = with_custom_attributes(users, { with: entity, current_user: current_user })
present paginate(users), options
end
@@ -114,7 +115,7 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user && can?(current_user, :read_user, user)
- opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
+ opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user }
user, opts = with_custom_attributes(user, opts)
present user, opts
@@ -140,7 +141,7 @@ module API
user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true)
if user.persisted?
- present user, with: Entities::UserPublic
+ present user, with: Entities::UserPublic, current_user: current_user
else
conflict!('Email has already been taken') if User
.where(email: user.email)
@@ -199,7 +200,7 @@ module API
result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute
if result[:status] == :success
- present user, with: Entities::UserPublic
+ present user, with: Entities::UserPublic, current_user: current_user
else
render_validation_error!(user)
end
@@ -546,7 +547,7 @@ module API
Entities::UserPublic
end
- present current_user, with: entity
+ present current_user, with: entity, current_user: current_user
end
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index b0acf4a49ac..071f96a729e 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe UsersController do
let(:user) { create(:user) }
+ let(:private_user) { create(:user, private_profile: true) }
+ let(:public_user) { create(:user) }
describe 'GET #show' do
context 'with rendered views' do
@@ -98,16 +100,47 @@ describe UsersController do
expect(assigns(:events)).to be_empty
end
+
+ it 'hides events if the user has a private profile' do
+ Gitlab::DataBuilder::Push.build_sample(project, private_user)
+
+ get :show, username: private_user.username, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
end
end
describe 'GET #calendar' do
- it 'renders calendar' do
- sign_in(user)
+ context 'for user' do
+ let(:project) { create(:project) }
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ context 'with public profile' do
+ it 'renders calendar' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+ EventCreateService.new.push(project, public_user, push_data)
+
+ get :calendar, username: public_user.username, format: :json
- get :calendar, username: user.username, format: :json
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context 'with private profile' do
+ it 'does not render calendar' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+ EventCreateService.new.push(project, private_user, push_data)
- expect(response).to have_gitlab_http_status(200)
+ get :calendar, username: private_user.username, format: :json
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'forked project' do
@@ -150,9 +183,26 @@ describe UsersController do
expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31'))
end
- it 'renders calendar_activities' do
- get :calendar_activities, username: user.username
- expect(response).to render_template('calendar_activities')
+ context 'for user' do
+ context 'with public profile' do
+ it 'renders calendar_activities' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+ EventCreateService.new.push(project, public_user, push_data)
+
+ get :calendar_activities, username: public_user.username
+ expect(assigns[:events]).not_to be_empty
+ end
+ end
+
+ context 'with private profile' do
+ it 'does not render calendar_activities' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+ EventCreateService.new.push(project, private_user, push_data)
+
+ get :calendar_activities, username: private_user.username
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index 3e2fb704bc6..207c333c636 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -3,15 +3,53 @@ require 'spec_helper'
describe 'User page' do
let(:user) { create(:user) }
- it 'shows all the tabs' do
- visit(user_path(user))
-
- page.within '.nav-links' do
- expect(page).to have_link('Activity')
- expect(page).to have_link('Groups')
- expect(page).to have_link('Contributed projects')
- expect(page).to have_link('Personal projects')
- expect(page).to have_link('Snippets')
+ context 'with public profile' do
+ it 'shows all the tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+
+ it 'does not show private profile message' do
+ visit(user_path(user))
+
+ expect(page).not_to have_content("This user has a private profile")
+ end
+ end
+
+ context 'with private profile' do
+ let(:user) { create(:user, private_profile: true) }
+
+ it 'shows no tab' do
+ visit(user_path(user))
+
+ expect(page).to have_css("div.profile-header")
+ expect(page).not_to have_css("ul.nav-links")
+ end
+
+ it 'shows private profile message' do
+ visit(user_path(user))
+
+ expect(page).to have_content("This user has a private profile")
+ end
+
+ it 'shows own tabs' do
+ sign_in(user)
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
end
end
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index da043f94021..58470f4c84d 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -29,11 +29,22 @@ describe UserRecentEventsFinder do
public_project.add_developer(current_user)
end
- it 'returns all the events' do
- expect(finder.execute).to include(private_event, internal_event, public_event)
+ context 'when profile is public' do
+ it 'returns all the events' do
+ expect(finder.execute).to include(private_event, internal_event, public_event)
+ end
+ end
+
+ context 'when profile is private' do
+ it 'returns no event' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false)
+ expect(finder.execute).to be_empty
+ end
end
it 'does not include the events if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).and_call_original
expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
expect(finder.execute).to be_empty
end
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index b18c045848f..b079802cb81 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -25,8 +25,20 @@ describe UsersHelper do
allow(helper).to receive(:can?).and_return(true)
end
- it 'includes all the expected tabs' do
- expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ context 'with public profile' do
+ it 'includes all the expected tabs' do
+ expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ end
+ end
+
+ context 'with private profile' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false)
+ end
+
+ it 'is empty' do
+ expect(tabs).to be_empty
+ end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index b3079c0a77b..6a051f865aa 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,6 +11,7 @@ describe API::Users do
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
+ let(:private_user) { create(:user, private_profile: true) }
describe 'GET /users' do
context "when unauthenticated" do
@@ -254,6 +255,13 @@ describe API::Users do
expect(response).to match_response_schema('public_api/v4/user/admin')
expect(json_response['is_admin']).to be(false)
end
+
+ it "includes the `created_at` field for private users" do
+ get api("/users/#{private_user.id}", admin)
+
+ expect(response).to match_response_schema('public_api/v4/user/admin')
+ expect(json_response.keys).to include 'created_at'
+ end
end
context 'for an anonymous user' do
@@ -272,6 +280,20 @@ describe API::Users do
expect(response).to have_gitlab_http_status(404)
end
+
+ it "returns the `created_at` field for public users" do
+ get api("/users/#{user.id}")
+
+ expect(response).to match_response_schema('public_api/v4/user/basic')
+ expect(json_response.keys).to include 'created_at'
+ end
+
+ it "does not return the `created_at` field for private users" do
+ get api("/users/#{private_user.id}")
+
+ expect(response).to match_response_schema('public_api/v4/user/basic')
+ expect(json_response.keys).not_to include 'created_at'
+ end
end
it "returns a 404 error if user id not found" do
@@ -374,6 +396,18 @@ describe API::Users do
expect(new_user.recently_sent_password_reset?).to eq(true)
end
+ it "creates user with private profile" do
+ post api('/users', admin), attributes_for(:user, private_profile: true)
+
+ expect(response).to have_gitlab_http_status(201)
+
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+
+ expect(new_user).not_to eq(nil)
+ expect(new_user.private_profile?).to eq(true)
+ end
+
it "does not create user with invalid email" do
post api('/users', admin),
email: 'invalid email',
@@ -583,6 +617,13 @@ describe API::Users do
expect(user.reload.external?).to be_truthy
end
+ it "updates private profile" do
+ put api("/users/#{user.id}", admin), { private_profile: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(user.reload.private_profile).to eq(true)
+ end
+
it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), { can_create_group: false }