diff options
author | JX Terry <jxterry@protonmail.com> | 2018-07-24 12:46:19 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2018-07-24 12:46:19 +0000 |
commit | 99011a61cf4136c806e7de43fcd55475d2407fa1 (patch) | |
tree | 99486b31dc0df1b86db0bb11ec32b05c9bc1fb2d | |
parent | adc327d3fa72b9f5b9c42c629c99f0a89ca15192 (diff) | |
download | gitlab-ce-99011a61cf4136c806e7de43fcd55475d2407fa1.tar.gz |
Add an option to have a private profile on GitLab
-rw-r--r-- | app/controllers/profiles_controller.rb | 3 | ||||
-rw-r--r-- | app/controllers/users_controller.rb | 6 | ||||
-rw-r--r-- | app/finders/personal_projects_finder.rb | 4 | ||||
-rw-r--r-- | app/finders/user_recent_events_finder.rb | 3 | ||||
-rw-r--r-- | app/helpers/users_helper.rb | 8 | ||||
-rw-r--r-- | app/policies/user_policy.rb | 6 | ||||
-rw-r--r-- | app/services/users/build_service.rb | 3 | ||||
-rw-r--r-- | app/views/profiles/show.html.haml | 6 | ||||
-rw-r--r-- | app/views/users/show.html.haml | 74 | ||||
-rw-r--r-- | changelogs/unreleased/38604-add-private-profile.yml | 5 | ||||
-rw-r--r-- | db/migrate/20180722103201_add_private_profile_to_users.rb | 10 | ||||
-rw-r--r-- | db/schema.rb | 3 | ||||
-rw-r--r-- | doc/api/users.md | 17 | ||||
-rw-r--r-- | doc/user/profile/index.md | 22 | ||||
-rw-r--r-- | lib/api/entities.rb | 3 | ||||
-rw-r--r-- | lib/api/keys.rb | 2 | ||||
-rw-r--r-- | lib/api/users.rb | 11 | ||||
-rw-r--r-- | spec/controllers/users_controller_spec.rb | 64 | ||||
-rw-r--r-- | spec/features/users/show_spec.rb | 56 | ||||
-rw-r--r-- | spec/finders/user_recent_events_finder_spec.rb | 15 | ||||
-rw-r--r-- | spec/helpers/users_helper_spec.rb | 16 | ||||
-rw-r--r-- | spec/requests/api/users_spec.rb | 41 |
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 } |