Commit bf2fbcaa authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '241144-fj-add-non-internal-filter-to-users-rest-endpoint' into 'master'

Add filter to exclude non internal users in REST API

See merge request gitlab-org/gitlab!40372
parents 6d2b145b c782bf75
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# without_projects: boolean # without_projects: boolean
# sort: string # sort: string
# id: integer # id: integer
# non_internal: boolean
# #
class UsersFinder class UsersFinder
include CreatedAtFilter include CreatedAtFilter
...@@ -42,6 +43,7 @@ class UsersFinder ...@@ -42,6 +43,7 @@ class UsersFinder
users = by_created_at(users) users = by_created_at(users)
users = by_without_projects(users) users = by_without_projects(users)
users = by_custom_attributes(users) users = by_custom_attributes(users)
users = by_non_internal(users)
order(users) order(users)
end end
...@@ -112,6 +114,12 @@ class UsersFinder ...@@ -112,6 +114,12 @@ class UsersFinder
users.without_projects users.without_projects
end end
def by_non_internal(users)
return users unless params[:non_internal]
users.non_internal
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def order(users) def order(users)
return users unless params[:sort] return users unless params[:sort]
......
---
title: Add filter to exclude non internal users in REST API
merge_request: 40372
author:
type: changed
...@@ -61,6 +61,15 @@ GET /users?active=true ...@@ -61,6 +61,15 @@ GET /users?active=true
GET /users?blocked=true GET /users?blocked=true
``` ```
GitLab supports bot users such as the [alert bot](../operations/incident_management/generic_alerts.md)
or the [support bot](../user/project/service_desk.md#support-bot-user).
To exclude these users from the users' list, you can use the parameter `exclude_internal=true`
([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241144) in GitLab 13.4).
```plaintext
GET /users?exclude_internal=true
```
NOTE: **Note:** NOTE: **Note:**
Username search is case insensitive. Username search is case insensitive.
......
...@@ -13,13 +13,13 @@ RSpec.describe UsersFinder do ...@@ -13,13 +13,13 @@ RSpec.describe UsersFinder do
it 'returns ldap users by default' do it 'returns ldap users by default' do
users = described_class.new(normal_user).execute users = described_class.new(normal_user).execute
expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user, ldap_user) expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user, ldap_user, internal_user)
end end
it 'returns only non-ldap users with skip_ldap: true' do it 'returns only non-ldap users with skip_ldap: true' do
users = described_class.new(normal_user, skip_ldap: true).execute users = described_class.new(normal_user, skip_ldap: true).execute
expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user) expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user, internal_user)
end end
end end
end end
......
...@@ -84,6 +84,7 @@ module API ...@@ -84,6 +84,7 @@ module API
optional :created_after, type: DateTime, desc: 'Return users created after the specified time' optional :created_after, type: DateTime, desc: 'Return users created after the specified time'
optional :created_before, type: DateTime, desc: 'Return users created before the specified time' optional :created_before, type: DateTime, desc: 'Return users created before the specified time'
optional :without_projects, type: Boolean, default: false, desc: 'Filters only users without projects' optional :without_projects, type: Boolean, default: false, desc: 'Filters only users without projects'
optional :exclude_internal, as: :non_internal, type: Boolean, default: false, desc: 'Filters only non internal users'
all_or_none_of :extern_uid, :provider all_or_none_of :extern_uid, :provider
use :sort_params use :sort_params
......
...@@ -12,7 +12,7 @@ RSpec.describe UsersFinder do ...@@ -12,7 +12,7 @@ RSpec.describe UsersFinder do
it 'returns all users' do it 'returns all users' do
users = described_class.new(user).execute users = described_class.new(user).execute
expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user)
end end
it 'filters by username' do it 'filters by username' do
...@@ -54,7 +54,7 @@ RSpec.describe UsersFinder do ...@@ -54,7 +54,7 @@ RSpec.describe UsersFinder do
it 'returns no external users' do it 'returns no external users' do
users = described_class.new(user, external: true).execute users = described_class.new(user, external: true).execute
expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user)
end end
it 'filters by created_at' do it 'filters by created_at' do
...@@ -68,19 +68,25 @@ RSpec.describe UsersFinder do ...@@ -68,19 +68,25 @@ RSpec.describe UsersFinder do
expect(users.map(&:username)).not_to include([filtered_user_before.username, filtered_user_after.username]) expect(users.map(&:username)).not_to include([filtered_user_before.username, filtered_user_after.username])
end end
it 'filters by non internal users' do
users = described_class.new(user, non_internal: true).execute
expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user)
end
it 'does not filter by custom attributes' do it 'does not filter by custom attributes' do
users = described_class.new( users = described_class.new(
user, user,
custom_attributes: { foo: 'bar' } custom_attributes: { foo: 'bar' }
).execute ).execute
expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user)
end end
it 'orders returned results' do it 'orders returned results' do
users = described_class.new(user, sort: 'id_asc').execute users = described_class.new(user, sort: 'id_asc').execute
expect(users).to eq([normal_user, blocked_user, omniauth_user, user]) expect(users).to eq([normal_user, blocked_user, omniauth_user, internal_user, user])
end end
end end
...@@ -96,13 +102,14 @@ RSpec.describe UsersFinder do ...@@ -96,13 +102,14 @@ RSpec.describe UsersFinder do
it 'returns all users' do it 'returns all users' do
users = described_class.new(admin).execute users = described_class.new(admin).execute
expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user) expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user, internal_user)
end end
it 'filters by custom attributes' do it 'filters by custom attributes' do
create :user_custom_attribute, user: normal_user, key: 'foo', value: 'foo' create :user_custom_attribute, user: normal_user, key: 'foo', value: 'foo'
create :user_custom_attribute, user: normal_user, key: 'bar', value: 'bar' create :user_custom_attribute, user: normal_user, key: 'bar', value: 'bar'
create :user_custom_attribute, user: blocked_user, key: 'foo', value: 'foo' create :user_custom_attribute, user: blocked_user, key: 'foo', value: 'foo'
create :user_custom_attribute, user: internal_user, key: 'foo', value: 'foo'
users = described_class.new( users = described_class.new(
admin, admin,
......
...@@ -348,6 +348,26 @@ RSpec.describe API::Users, :do_not_mock_admin_mode do ...@@ -348,6 +348,26 @@ RSpec.describe API::Users, :do_not_mock_admin_mode do
expect(response).to match_response_schema('public_api/v4/user/basics') expect(response).to match_response_schema('public_api/v4/user/basics')
expect(json_response.first.keys).not_to include 'is_admin' expect(json_response.first.keys).not_to include 'is_admin'
end end
context 'exclude_internal param' do
let_it_be(:internal_user) { User.alert_bot }
it 'returns all users when it is not set' do
get api("/users?exclude_internal=false", user)
expect(response).to match_response_schema('public_api/v4/user/basics')
expect(response).to include_pagination_headers
expect(json_response.map { |u| u['id'] }).to include(internal_user.id)
end
it 'returns all non internal users when it is set' do
get api("/users?exclude_internal=true", user)
expect(response).to match_response_schema('public_api/v4/user/basics')
expect(response).to include_pagination_headers
expect(json_response.map { |u| u['id'] }).not_to include(internal_user.id)
end
end
end end
context "when admin" do context "when admin" do
......
...@@ -5,4 +5,5 @@ RSpec.shared_context 'UsersFinder#execute filter by project context' do ...@@ -5,4 +5,5 @@ RSpec.shared_context 'UsersFinder#execute filter by project context' do
let_it_be(:blocked_user) { create(:user, :blocked, username: 'notsorandom') } let_it_be(:blocked_user) { create(:user, :blocked, username: 'notsorandom') }
let_it_be(:external_user) { create(:user, :external) } let_it_be(:external_user) { create(:user, :external) }
let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:internal_user) { User.alert_bot }
end end
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