2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2014-09-02 18:07:02 +05:30
module API
2021-01-03 14:25:43 +05:30
class Users < :: API :: Base
2017-08-17 22:00:37 +05:30
include PaginationParams
2017-09-10 17:25:29 +05:30
include APIGuard
2018-03-27 19:54:05 +05:30
include Helpers :: CustomAttributes
2017-08-17 22:00:37 +05:30
2021-03-11 19:13:27 +05:30
allow_access_with_scope :read_user , if : - > ( request ) { request . get? || request . head? }
2014-09-02 18:07:02 +05:30
2023-01-13 00:05:48 +05:30
feature_category :users ,
%w[
/ users / :id / custom_attributes
/ users / :id / custom_attributes / :key
/ users / :id / associations_count
]
urgency :medium ,
%w[
/ users / :id / custom_attributes
/ users / :id / custom_attributes / :key
]
2022-07-16 23:28:13 +05:30
2015-09-25 12:07:36 +05:30
resource :users , requirements : { uid : / [0-9]* / , id : / [0-9]* / } do
2018-03-17 18:26:18 +05:30
include CustomAttributesEndpoints
2017-09-10 17:25:29 +05:30
before do
authenticate_non_get!
end
2019-09-04 21:01:54 +05:30
helpers Helpers :: UsersHelpers
2023-01-13 00:05:48 +05:30
helpers Gitlab :: Tracking :: Helpers :: WeakPasswordErrorEvent
2019-09-04 21:01:54 +05:30
2017-08-17 22:00:37 +05:30
helpers do
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
def reorder_users ( users )
if params [ :order_by ] && params [ :sort ]
2019-07-07 11:18:12 +05:30
users . reorder ( order_options_with_tie_breaker )
2018-03-17 18:26:18 +05:30
else
users
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2017-08-17 22:00:37 +05:30
params :optional_attributes do
optional :skype , type : String , desc : 'The Skype username'
optional :linkedin , type : String , desc : 'The LinkedIn username'
optional :twitter , type : String , desc : 'The Twitter username'
optional :website_url , type : String , desc : 'The website of the user'
optional :organization , type : String , desc : 'The organization of the user'
optional :projects_limit , type : Integer , desc : 'The number of projects a user can create'
optional :extern_uid , type : String , desc : 'The external authentication provider UID'
optional :provider , type : String , desc : 'The external provider'
optional :bio , type : String , desc : 'The biography of the user'
optional :location , type : String , desc : 'The location of the user'
2022-11-25 23:54:43 +05:30
optional :pronouns , type : String , desc : 'The pronouns of the user'
2018-12-05 23:21:45 +05:30
optional :public_email , type : String , desc : 'The public email of the user'
2022-11-25 23:54:43 +05:30
optional :commit_email , type : String , desc : 'The commit email, _private for private commit email'
2017-08-17 22:00:37 +05:30
optional :admin , type : Boolean , desc : 'Flag indicating the user is an administrator'
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'
2022-11-25 23:54:43 +05:30
optional :avatar , type : :: API :: Validations :: Types :: WorkhorseFile , desc : 'Avatar image for user' , documentation : { type : 'file' }
2020-04-08 14:13:33 +05:30
optional :theme_id , type : Integer , desc : 'The GitLab theme for the user'
optional :color_scheme_id , type : Integer , desc : 'The color scheme for the file viewer'
2023-03-17 16:20:25 +05:30
# TODO: Add `allow_blank: false` in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
2020-03-13 15:44:24 +05:30
optional :private_profile , type : Boolean , desc : 'Flag indicating the user has a private profile'
2020-06-23 00:09:42 +05:30
optional :note , type : String , desc : 'Admin note for this user'
2021-04-17 20:07:23 +05:30
optional :view_diffs_file_by_file , type : Boolean , desc : 'Flag indicating the user sees only one file diff per page'
2017-08-17 22:00:37 +05:30
all_or_none_of :extern_uid , :provider
2019-07-07 11:18:12 +05:30
2019-09-04 21:01:54 +05:30
use :optional_params_ee
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
params :sort_params do
optional :order_by , type : String , values : %w[ id name username created_at updated_at ] ,
2022-08-27 11:52:29 +05:30
default : 'id' , desc : 'Return users ordered by a field'
2018-03-17 18:26:18 +05:30
optional :sort , type : String , values : %w[ asc desc ] , default : 'desc' ,
2022-08-27 11:52:29 +05:30
desc : 'Return users sorted in ascending and descending order'
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
end
2023-01-13 00:05:48 +05:30
resources ':id/associations_count' do
helpers do
def present_entity ( result )
present result ,
with : :: API :: Entities :: UserAssociationsCount
end
end
desc " Returns a list of a specified user's count of projects, groups, issues and merge requests. "
params do
requires :id ,
type : Integer ,
desc : 'ID of the user to query.'
end
get do
authenticate!
user = find_user_by_id ( params )
forbidden! unless can? ( current_user , :get_user_associations_count , user )
not_found! ( 'User' ) unless user
present_entity ( user )
end
end
2017-08-17 22:00:37 +05:30
desc 'Get the list of users' do
success Entities :: UserBasic
end
params do
# CE
optional :username , type : String , desc : 'Get a single user with a specific username'
optional :extern_uid , type : String , desc : 'Get a single user with a specific external authentication provider UID'
optional :provider , type : String , desc : 'The external provider'
optional :search , type : String , desc : 'Search for a username'
optional :active , type : Boolean , default : false , desc : 'Filters only active users'
optional :external , type : Boolean , default : false , desc : 'Filters only external users'
2021-04-17 20:07:23 +05:30
optional :exclude_external , as : :non_external , type : Boolean , default : false , desc : 'Filters only non external users'
2017-08-17 22:00:37 +05:30
optional :blocked , type : Boolean , default : false , desc : 'Filters only blocked users'
2017-09-10 17:25:29 +05:30
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'
2020-04-22 19:07:51 +05:30
optional :without_projects , type : Boolean , default : false , desc : 'Filters only users without projects'
2020-11-24 15:15:51 +05:30
optional :exclude_internal , as : :non_internal , type : Boolean , default : false , desc : 'Filters only non internal users'
2022-06-21 17:19:12 +05:30
optional :without_project_bots , type : Boolean , default : false , desc : 'Filters users without project bots'
2021-03-08 18:12:59 +05:30
optional :admins , type : Boolean , default : false , desc : 'Filters only admin users'
2017-08-17 22:00:37 +05:30
all_or_none_of :extern_uid , :provider
2018-03-17 18:26:18 +05:30
use :sort_params
2017-08-17 22:00:37 +05:30
use :pagination
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2019-09-04 21:01:54 +05:30
use :optional_index_params_ee
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2022-07-16 23:28:13 +05:30
get feature_category : :users , urgency : :low do
2021-04-17 20:07:23 +05:30
authenticated_as_admin! if params [ :extern_uid ] . present? && params [ :provider ] . present?
2017-09-10 17:25:29 +05:30
2023-03-04 22:38:38 +05:30
unless current_user & . can_read_all_resources?
2020-04-22 19:07:51 +05:30
params . except! ( :created_after , :created_before , :order_by , :sort , :two_factor , :without_projects )
2016-06-02 11:05:42 +05:30
end
2017-09-10 17:25:29 +05:30
authorized = can? ( current_user , :read_users_list )
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
# When `current_user` is not present, require that the `username`
# parameter is passed, to prevent an unauthenticated user from accessing
# a list of all the users on the GitLab instance. `UsersFinder` performs
# an exact match on the `username` parameter, so we are guaranteed to
# get either 0 or 1 `users` here.
authorized && = params [ :username ] . present? if current_user . blank?
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
forbidden! ( " Not authorized to access /api/v4/users " ) unless authorized
2022-02-27 12:50:16 +05:30
users = UsersFinder . new ( current_user , params ) . execute
users = reorder_users ( users )
2023-03-04 22:38:38 +05:30
entity = current_user & . can_read_all_resources? ? Entities :: UserWithAdmin : Entities :: UserBasic
2022-06-21 17:19:12 +05:30
if entity == Entities :: UserWithAdmin
2022-08-13 15:12:31 +05:30
users = users . preload ( :identities , :u2f_registrations , :webauthn_registrations , :namespace , :followers , :followees , :user_preference )
2022-06-21 17:19:12 +05:30
end
2018-11-18 11:00:15 +05:30
users , options = with_custom_attributes ( users , { with : entity , current_user : current_user } )
2018-03-17 18:26:18 +05:30
2020-07-28 23:09:34 +05:30
users = users . preload ( :user_detail )
2018-03-27 19:54:05 +05:30
present paginate ( users ) , options
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2017-08-17 22:00:37 +05:30
desc 'Get a single user' do
2018-03-17 18:26:18 +05:30
success Entities :: User
2017-08-17 22:00:37 +05:30
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2022-07-23 23:45:48 +05:30
get " :id " , feature_category : :users , urgency : :low do
2021-09-30 23:02:18 +05:30
forbidden! ( 'Not authorized!' ) unless current_user
2023-03-04 22:38:38 +05:30
unless current_user . can_read_all_resources?
2022-05-07 20:08:51 +05:30
check_rate_limit! ( :users_get_by_id ,
scope : current_user ,
users_allowlist : Gitlab :: CurrentSettings . current_application_settings . users_get_by_id_limit_allowlist
)
2022-03-02 08:16:31 +05:30
end
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params [ :id ] )
2021-09-30 23:02:18 +05:30
2018-03-17 18:26:18 +05:30
not_found! ( 'User' ) unless user && can? ( current_user , :read_user , user )
2014-09-02 18:07:02 +05:30
2023-03-04 22:38:38 +05:30
opts = { with : current_user . can_read_all_resources? ? Entities :: UserDetailsWithAdmin : Entities :: User , current_user : current_user }
2018-03-27 19:54:05 +05:30
user , opts = with_custom_attributes ( user , opts )
2018-03-17 18:26:18 +05:30
present user , opts
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2018-11-18 11:00:15 +05:30
desc " Get the status of a user "
params do
2019-03-02 22:35:43 +05:30
requires :user_id , type : String , desc : 'The ID or username of the user'
2018-11-18 11:00:15 +05:30
end
2022-07-23 23:45:48 +05:30
get " :user_id/status " , requirements : API :: USER_REQUIREMENTS , feature_category : :users , urgency : :default do
2019-03-02 22:35:43 +05:30
user = find_user ( params [ :user_id ] )
2021-09-30 23:02:18 +05:30
2018-11-18 11:00:15 +05:30
not_found! ( 'User' ) unless user && can? ( current_user , :read_user , user )
present user . status || { } , with : Entities :: UserStatus
end
2021-03-11 19:13:27 +05:30
desc 'Follow a user' do
success Entities :: User
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/follow' , feature_category : :users do
user = find_user ( params [ :id ] )
not_found! ( 'User' ) unless user
2022-11-25 23:54:43 +05:30
followee = current_user . follow ( user )
if followee & . errors & . any?
render_api_error! ( followee . errors . full_messages . join ( ', ' ) , 400 )
elsif followee & . persisted?
2021-03-11 19:13:27 +05:30
present user , with : Entities :: UserBasic
else
not_modified!
end
end
desc 'Unfollow a user' do
success Entities :: User
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/unfollow' , feature_category : :users do
user = find_user ( params [ :id ] )
not_found! ( 'User' ) unless user
if current_user . unfollow ( user )
present user , with : Entities :: UserBasic
else
not_modified!
end
end
desc 'Get the users who follow a user' do
success Entities :: UserBasic
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
use :pagination
end
get ':id/following' , feature_category : :users do
2021-09-30 23:02:18 +05:30
forbidden! ( 'Not authorized!' ) unless current_user
2021-03-11 19:13:27 +05:30
user = find_user ( params [ :id ] )
not_found! ( 'User' ) unless user && can? ( current_user , :read_user_profile , user )
present paginate ( user . followees ) , with : Entities :: UserBasic
end
desc 'Get the followers of a user' do
success Entities :: UserBasic
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
use :pagination
end
get ':id/followers' , feature_category : :users do
2021-09-30 23:02:18 +05:30
forbidden! ( 'Not authorized!' ) unless current_user
2021-03-11 19:13:27 +05:30
user = find_user ( params [ :id ] )
not_found! ( 'User' ) unless user && can? ( current_user , :read_user_profile , user )
present paginate ( user . followers ) , with : Entities :: UserBasic
end
2017-08-17 22:00:37 +05:30
desc 'Create a user. Available only for admins.' do
2019-09-30 21:07:59 +05:30
success Entities :: UserWithAdmin
2017-08-17 22:00:37 +05:30
end
params do
requires :email , type : String , desc : 'The email of the user'
optional :password , type : String , desc : 'The password of the new user'
optional :reset_password , type : Boolean , desc : 'Flag indicating the user will be sent a password reset token'
2018-03-17 18:26:18 +05:30
optional :skip_confirmation , type : Boolean , desc : 'Flag indicating the account is confirmed'
2021-04-29 21:17:54 +05:30
at_least_one_of :password , :reset_password , :force_random_password
2017-08-17 22:00:37 +05:30
requires :name , type : String , desc : 'The name of the user'
requires :username , type : String , desc : 'The username of the user'
2019-09-30 21:07:59 +05:30
optional :force_random_password , type : Boolean , desc : 'Flag indicating a random password will be set'
2017-08-17 22:00:37 +05:30
use :optional_attributes
end
2021-01-29 00:20:46 +05:30
post feature_category : :users do
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2016-06-02 11:05:42 +05:30
2017-08-17 22:00:37 +05:30
params = declared_params ( include_missing : false )
2023-03-17 16:20:25 +05:30
# TODO: Remove in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
if params . key? ( :private_profile ) && params [ :private_profile ] . nil?
params [ :private_profile ] = Gitlab :: CurrentSettings . user_defaults_to_private_profile
end
2021-09-04 01:27:46 +05:30
user = :: Users :: AuthorizedCreateService . new ( current_user , params ) . execute
2015-04-26 12:48:37 +05:30
2017-08-17 22:00:37 +05:30
if user . persisted?
2019-09-30 21:07:59 +05:30
present user , with : Entities :: UserWithAdmin , current_user : current_user
2014-09-02 18:07:02 +05:30
else
2017-09-10 17:25:29 +05:30
conflict! ( 'Email has already been taken' ) if User
2018-12-13 13:39:08 +05:30
. by_any_email ( user . email . downcase )
. any?
2015-04-26 12:48:37 +05:30
2017-09-10 17:25:29 +05:30
conflict! ( 'Username has already been taken' ) if User
2018-12-13 13:39:08 +05:30
. by_username ( user . username )
. any?
2015-04-26 12:48:37 +05:30
2023-01-13 00:05:48 +05:30
track_weak_password_error ( user , 'API::Users' , 'create' )
2015-04-26 12:48:37 +05:30
render_validation_error! ( user )
2014-09-02 18:07:02 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Update a user. Available only for admins.' do
2019-09-30 21:07:59 +05:30
success Entities :: UserWithAdmin
2017-08-17 22:00:37 +05:30
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
optional :email , type : String , desc : 'The email of the user'
optional :password , type : String , desc : 'The password of the new user'
2018-03-17 18:26:18 +05:30
optional :skip_reconfirmation , type : Boolean , desc : 'Flag indicating the account skips the confirmation by email'
2017-08-17 22:00:37 +05:30
optional :name , type : String , desc : 'The name of the user'
optional :username , type : String , desc : 'The username of the user'
use :optional_attributes
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
put " :id " , feature_category : :users do
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params . delete ( :id ) )
2015-04-26 12:48:37 +05:30
not_found! ( 'User' ) unless user
2014-09-02 18:07:02 +05:30
2017-08-17 22:00:37 +05:30
conflict! ( 'Email has already been taken' ) if params [ :email ] &&
2023-03-04 22:38:38 +05:30
User . by_any_email ( params [ :email ] . downcase )
. where . not ( id : user . id ) . exists?
2015-04-26 12:48:37 +05:30
2017-08-17 22:00:37 +05:30
conflict! ( 'Username has already been taken' ) if params [ :username ] &&
2023-03-04 22:38:38 +05:30
User . by_username ( params [ :username ] )
. where . not ( id : user . id ) . exists?
2015-04-26 12:48:37 +05:30
2017-08-17 22:00:37 +05:30
user_params = declared_params ( include_missing : false )
2023-03-17 16:20:25 +05:30
# TODO: Remove in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
if user_params . key? ( :private_profile ) && user_params [ :private_profile ] . nil?
user_params [ :private_profile ] = Gitlab :: CurrentSettings . user_defaults_to_private_profile
end
2020-11-24 15:15:51 +05:30
admin_making_changes_for_another_user = ( current_user != user )
2015-10-24 18:46:33 +05:30
2020-11-24 15:15:51 +05:30
if user_params [ :password ] . present?
user_params [ :password_expires_at ] = Time . current if admin_making_changes_for_another_user
end
result = :: Users :: UpdateService . new ( current_user , user_params . merge ( user : user ) ) . execute do | user |
user . send_only_admin_changed_your_password_notification! if admin_making_changes_for_another_user
end
2017-09-10 17:25:29 +05:30
if result [ :status ] == :success
2019-09-30 21:07:59 +05:30
present user , with : Entities :: UserWithAdmin , current_user : current_user
2014-09-02 18:07:02 +05:30
else
2023-01-13 00:05:48 +05:30
track_weak_password_error ( user , 'API::Users' , 'update' )
2015-04-26 12:48:37 +05:30
render_validation_error! ( user )
2014-09-02 18:07:02 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2022-08-13 15:12:31 +05:30
desc " Disable two factor authentication for a user. Available only for admins " do
detail 'This feature was added in GitLab 15.2'
success Entities :: UserWithAdmin
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
patch " :id/disable_two_factor " , feature_category : :authentication_and_authorization do
authenticated_as_admin!
user = User . find_by_id ( params [ :id ] )
not_found! ( 'User' ) unless user
2023-03-04 22:38:38 +05:30
# We're disabling Cop/UserAdmin because it checks if the given user (not the current user) is an admin.
forbidden! ( 'Two-factor authentication for admins cannot be disabled via the API. Use the Rails console' ) if user . admin? # rubocop:disable Cop/UserAdmin
2022-08-13 15:12:31 +05:30
result = TwoFactor :: DestroyService . new ( current_user , user : user ) . execute
if result [ :status ] == :success
no_content!
else
bad_request! ( result [ :message ] )
end
end
2020-03-13 15:44:24 +05:30
desc " Delete a user's identity. Available only for admins " do
success Entities :: UserWithAdmin
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :provider , type : String , desc : 'The external provider'
end
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete " :id/identities/:provider " , feature_category : :authentication_and_authorization do
2020-03-13 15:44:24 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
identity = user . identities . find_by ( provider : params [ :provider ] )
not_found! ( 'Identity' ) unless identity
destroy_conditionally! ( identity )
end
# rubocop: enable CodeReuse/ActiveRecord
2022-07-23 23:45:48 +05:30
desc 'Get the project-level Deploy keys that a specified user can access to.' do
success Entities :: DeployKey
end
params do
requires :user_id , type : String , desc : 'The ID or username of the user'
use :pagination
end
get ':user_id/project_deploy_keys' , requirements : API :: USER_REQUIREMENTS , feature_category : :continuous_delivery do
user = find_user ( params [ :user_id ] )
not_found! ( 'User' ) unless user && can? ( current_user , :read_user , user )
project_ids = Project . visible_to_user_and_access_level ( current_user , Gitlab :: Access :: MAINTAINER )
unless current_user == user
# Restrict to only common projects of both current_user and user.
project_ids = project_ids . visible_to_user_and_access_level ( user , Gitlab :: Access :: MAINTAINER )
end
forbidden! ( 'No common authorized project found' ) unless project_ids . present?
keys = DeployKey . in_projects ( project_ids )
present paginate ( keys ) , with : Entities :: DeployKey
end
2017-08-17 22:00:37 +05:30
desc 'Add an SSH key to a specified user. Available only for admins.' do
success Entities :: SSHKey
end
params do
2023-01-13 00:05:48 +05:30
requires :user_id , type : Integer , desc : 'The ID of the user'
2017-08-17 22:00:37 +05:30
requires :key , type : String , desc : 'The new SSH key'
requires :title , type : String , desc : 'The title of the new SSH key'
2020-06-23 00:09:42 +05:30
optional :expires_at , type : DateTime , desc : 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
2023-03-04 22:38:38 +05:30
optional :usage_type , type : String , values : Key . usage_types . keys , default : 'auth_and_signing' ,
desc : 'Scope of usage for the SSH key'
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2023-01-13 00:05:48 +05:30
post " :user_id/keys " , feature_category : :authentication_and_authorization do
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2015-04-26 12:48:37 +05:30
2023-01-13 00:05:48 +05:30
user = User . find_by ( id : params . delete ( :user_id ) )
2017-08-17 22:00:37 +05:30
not_found! ( 'User' ) unless user
2020-06-23 00:09:42 +05:30
key = :: Keys :: CreateService . new ( current_user , declared_params ( include_missing : false ) . merge ( user : user ) ) . execute
2017-08-17 22:00:37 +05:30
2020-06-23 00:09:42 +05:30
if key . persisted?
2014-09-02 18:07:02 +05:30
present key , with : Entities :: SSHKey
else
2015-04-26 12:48:37 +05:30
render_validation_error! ( key )
2014-09-02 18:07:02 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2018-12-05 23:21:45 +05:30
desc 'Get the SSH keys of a specified user.' do
2017-08-17 22:00:37 +05:30
success Entities :: SSHKey
end
params do
2020-03-13 15:44:24 +05:30
requires :user_id , type : String , desc : 'The ID or username of the user'
2017-08-17 22:00:37 +05:30
use :pagination
end
2021-01-29 00:20:46 +05:30
get ':user_id/keys' , requirements : API :: USER_REQUIREMENTS , feature_category : :authentication_and_authorization do
2020-03-13 15:44:24 +05:30
user = find_user ( params [ :user_id ] )
2018-12-05 23:21:45 +05:30
not_found! ( 'User' ) unless user && can? ( current_user , :read_user , user )
2015-04-26 12:48:37 +05:30
2020-06-23 00:09:42 +05:30
keys = user . keys . preload_users
present paginate ( keys ) , with : Entities :: SSHKey
2014-09-02 18:07:02 +05:30
end
2022-05-07 20:08:51 +05:30
desc 'Get a SSH key of a specified user.' do
success Entities :: SSHKey
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key_id , type : Integer , desc : 'The ID of the SSH key'
end
get ':id/keys/:key_id' , requirements : API :: USER_REQUIREMENTS , feature_category : :authentication_and_authorization do
user = find_user ( params [ :id ] )
not_found! ( 'User' ) unless user && can? ( current_user , :read_user , user )
key = user . keys . find_by ( id : params [ :key_id ] ) # rubocop: disable CodeReuse/ActiveRecord
not_found! ( 'Key' ) unless key
present key , with : Entities :: SSHKey
end
2017-08-17 22:00:37 +05:30
desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
success Entities :: SSHKey
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key_id , type : Integer , desc : 'The ID of the SSH key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete ':id/keys/:key_id' , feature_category : :authentication_and_authorization do
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params [ :id ] )
2015-04-26 12:48:37 +05:30
not_found! ( 'User' ) unless user
2017-08-17 22:00:37 +05:30
key = user . keys . find_by ( id : params [ :key_id ] )
not_found! ( 'Key' ) unless key
2020-06-23 00:09:42 +05:30
destroy_conditionally! ( key ) do | key |
destroy_service = :: Keys :: DestroyService . new ( current_user )
destroy_service . execute ( key )
end
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
desc 'Add a GPG key to a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
2020-04-08 14:13:33 +05:30
success Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key , type : String , desc : 'The new GPG key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/gpg_keys' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
authenticated_as_admin!
user = User . find_by ( id : params . delete ( :id ) )
not_found! ( 'User' ) unless user
2020-07-28 23:09:34 +05:30
key = :: GpgKeys :: CreateService . new ( user , declared_params ( include_missing : false ) ) . execute
2018-03-17 18:26:18 +05:30
2020-07-28 23:09:34 +05:30
if key . persisted?
2020-04-08 14:13:33 +05:30
present key , with : Entities :: GpgKey
2018-03-17 18:26:18 +05:30
else
render_validation_error! ( key )
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2021-01-03 14:25:43 +05:30
desc 'Get the GPG keys of a specified user.' do
2018-03-17 18:26:18 +05:30
detail 'This feature was added in GitLab 10.0'
2020-04-08 14:13:33 +05:30
success Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
use :pagination
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get ':id/gpg_keys' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
2020-04-08 14:13:33 +05:30
present paginate ( user . gpg_keys ) , with : Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2021-01-03 14:25:43 +05:30
desc 'Get a specific GPG key for a given user.' do
detail 'This feature was added in GitLab 13.5'
success Entities :: GpgKey
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key_id , type : Integer , desc : 'The ID of the GPG key'
end
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get ':id/gpg_keys/:key_id' , feature_category : :authentication_and_authorization do
2021-01-03 14:25:43 +05:30
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
key = user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
present key , with : Entities :: GpgKey
end
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
desc 'Delete an existing GPG key from a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key_id , type : Integer , desc : 'The ID of the GPG key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete ':id/gpg_keys/:key_id' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
key = user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
2020-07-28 23:09:34 +05:30
destroy_conditionally! ( key ) do | key |
destroy_service = :: GpgKeys :: DestroyService . new ( current_user )
destroy_service . execute ( key )
end
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2018-03-17 18:26:18 +05:30
desc 'Revokes an existing GPG key from a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :key_id , type : Integer , desc : 'The ID of the GPG key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/gpg_keys/:key_id/revoke' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
key = user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
key . revoke
status :accepted
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2017-08-17 22:00:37 +05:30
desc 'Add an email address to a specified user. Available only for admins.' do
success Entities :: Email
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :email , type : String , desc : 'The email of the user'
2018-12-05 23:21:45 +05:30
optional :skip_confirmation , type : Boolean , desc : 'Skip confirmation of email and assume it is verified'
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post " :id/emails " , feature_category : :users do
2015-09-11 14:41:01 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params . delete ( :id ) )
not_found! ( 'User' ) unless user
2018-03-17 18:26:18 +05:30
email = Emails :: CreateService . new ( current_user , declared_params ( include_missing : false ) . merge ( user : user ) ) . execute
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
if email . errors . blank?
2015-09-11 14:41:01 +05:30
present email , with : Entities :: Email
else
render_validation_error! ( email )
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc 'Get the emails addresses of a specified user. Available only for admins.' do
success Entities :: Email
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
use :pagination
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get ':id/emails' , feature_category : :users do
2015-09-11 14:41:01 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params [ :id ] )
2015-09-11 14:41:01 +05:30
not_found! ( 'User' ) unless user
2017-08-17 22:00:37 +05:30
present paginate ( user . emails ) , with : Entities :: Email
2015-09-11 14:41:01 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc 'Delete an email address of a specified user. Available only for admins.' do
success Entities :: Email
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
requires :email_id , type : Integer , desc : 'The ID of the email'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete ':id/emails/:email_id' , feature_category : :users do
2015-09-11 14:41:01 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User . find_by ( id : params [ :id ] )
2015-09-11 14:41:01 +05:30
not_found! ( 'User' ) unless user
2017-08-17 22:00:37 +05:30
email = user . emails . find_by ( id : params [ :email_id ] )
not_found! ( 'Email' ) unless email
2015-09-11 14:41:01 +05:30
2018-03-17 18:26:18 +05:30
destroy_conditionally! ( email ) do | email |
Emails :: DestroyService . new ( current_user , user : user ) . execute ( email )
end
2015-09-11 14:41:01 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc 'Delete a user. Available only for admins.' do
success Entities :: Email
end
params do
requires :id , type : Integer , desc : 'The ID of the user'
2017-09-10 17:25:29 +05:30
optional :hard_delete , type : Boolean , desc : " Whether to remove a user's contributions "
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete " :id " , feature_category : :users do
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2018-03-17 18:26:18 +05:30
2014-09-02 18:07:02 +05:30
user = User . find_by ( id : params [ :id ] )
2017-08-17 22:00:37 +05:30
not_found! ( 'User' ) unless user
2020-01-01 13:55:28 +05:30
conflict! ( 'User cannot be removed while is the sole-owner of a group' ) unless user . can_be_removed? || params [ :hard_delete ]
2014-09-02 18:07:02 +05:30
2018-03-17 18:26:18 +05:30
destroy_conditionally! ( user ) do
user . delete_async ( deleted_by : current_user , params : params )
end
2015-09-11 14:41:01 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2019-12-21 20:55:43 +05:30
desc 'Activate a deactivated user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/activate' , feature_category : :authentication_and_authorization do
2019-12-21 20:55:43 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
forbidden! ( 'A blocked user must be unblocked to be activated' ) if user . blocked?
user . activate
end
2021-02-22 17:27:13 +05:30
desc 'Approve a pending user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/approve' , feature_category : :authentication_and_authorization do
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless can? ( current_user , :read_user , user )
result = :: Users :: ApproveService . new ( current_user ) . execute ( user )
if result [ :success ]
result
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
2021-11-11 11:23:49 +05:30
desc 'Reject a pending user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/reject' , feature_category : :authentication_and_authorization do
user = find_user_by_id ( params )
result = :: Users :: RejectService . new ( current_user ) . execute ( user )
if result [ :success ]
present user
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
2019-12-21 20:55:43 +05:30
# rubocop: enable CodeReuse/ActiveRecord
desc 'Deactivate an active user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/deactivate' , feature_category : :authentication_and_authorization do
2019-12-21 20:55:43 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
not_found! ( 'User' ) unless user
break if user . deactivated?
unless user . can_be_deactivated?
forbidden! ( 'A blocked user cannot be deactivated by the API' ) if user . blocked?
2021-01-03 14:25:43 +05:30
forbidden! ( 'An internal user cannot be deactivated by the API' ) if user . internal?
2022-10-11 01:57:18 +05:30
forbidden! ( " The user you are trying to deactivate has been active in the past #{ Gitlab :: CurrentSettings . deactivate_dormant_users_period } days and cannot be deactivated " )
2019-12-21 20:55:43 +05:30
end
2021-01-03 14:25:43 +05:30
if user . deactivate
true
else
render_api_error! ( user . errors . full_messages , 400 )
end
2019-12-21 20:55:43 +05:30
end
# rubocop: enable CodeReuse/ActiveRecord
2017-08-17 22:00:37 +05:30
desc 'Block a user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/block' , feature_category : :authentication_and_authorization do
2015-09-11 14:41:01 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
2017-08-17 22:00:37 +05:30
not_found! ( 'User' ) unless user
2015-09-11 14:41:01 +05:30
2020-04-08 14:13:33 +05:30
if user . ldap_blocked?
2016-01-19 16:12:03 +05:30
forbidden! ( 'LDAP blocked users cannot be modified by the API' )
2022-05-07 20:08:51 +05:30
elsif current_user == user
forbidden! ( 'The API initiating user cannot be blocked by the API' )
2015-09-11 14:41:01 +05:30
end
2020-04-08 14:13:33 +05:30
break if user . blocked?
result = :: Users :: BlockService . new ( current_user ) . execute ( user )
if result [ :status ] == :success
true
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
2015-09-11 14:41:01 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc 'Unblock a user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post ':id/unblock' , feature_category : :authentication_and_authorization do
2015-09-11 14:41:01 +05:30
authenticated_as_admin!
user = User . find_by ( id : params [ :id ] )
2017-08-17 22:00:37 +05:30
not_found! ( 'User' ) unless user
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
if user . ldap_blocked?
2016-01-19 16:12:03 +05:30
forbidden! ( 'LDAP blocked users cannot be unblocked by the API' )
2019-12-21 20:55:43 +05:30
elsif user . deactivated?
forbidden! ( 'Deactivated users cannot be unblocked by the API' )
2016-01-19 16:12:03 +05:30
else
2023-03-17 16:20:25 +05:30
result = :: Users :: UnblockService . new ( current_user ) . execute ( user )
result . success?
2014-09-02 18:07:02 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2016-11-03 12:29:30 +05:30
2021-11-11 11:23:49 +05:30
desc 'Ban a user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/ban' , feature_category : :authentication_and_authorization do
authenticated_as_admin!
user = find_user_by_id ( params )
result = :: Users :: BanService . new ( current_user ) . execute ( user )
if result [ :status ] == :success
true
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
desc 'Unban a user. Available only for admins.'
params do
requires :id , type : Integer , desc : 'The ID of the user'
end
post ':id/unban' , feature_category : :authentication_and_authorization do
authenticated_as_admin!
user = find_user_by_id ( params )
result = :: Users :: UnbanService . new ( current_user ) . execute ( user )
if result [ :status ] == :success
true
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
2020-03-13 15:44:24 +05:30
desc 'Get memberships' do
success Entities :: Membership
end
params do
requires :user_id , type : Integer , desc : 'The ID of the user'
optional :type , type : String , values : %w[ Project Namespace ]
use :pagination
end
2022-07-16 23:28:13 +05:30
get " :user_id/memberships " , feature_category : :users , urgency : :high do
2020-03-13 15:44:24 +05:30
authenticated_as_admin!
user = find_user_by_id ( params )
members = case params [ :type ]
when 'Project'
user . project_members
when 'Namespace'
user . group_members
else
user . members
end
members = members . including_source
present paginate ( members ) , with : Entities :: Membership
end
2017-08-17 22:00:37 +05:30
params do
requires :user_id , type : Integer , desc : 'The ID of the user'
end
segment ':user_id' do
resource :impersonation_tokens do
helpers do
def finder ( options = { } )
2018-03-17 18:26:18 +05:30
user = find_user_by_id ( params )
2017-08-17 22:00:37 +05:30
PersonalAccessTokensFinder . new ( { user : user , impersonation : true } . merge ( options ) )
end
def find_impersonation_token
2018-12-13 13:39:08 +05:30
finder . find_by_id ( declared_params [ :impersonation_token_id ] ) || not_found! ( 'Impersonation Token' )
2017-08-17 22:00:37 +05:30
end
end
before { authenticated_as_admin! }
desc 'Retrieve impersonation tokens. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
success Entities :: ImpersonationToken
end
params do
use :pagination
optional :state , type : String , default : 'all' , values : %w[ all active inactive ] , desc : 'Filters (all|active|inactive) impersonation_tokens'
end
2021-11-18 22:05:49 +05:30
get feature_category : :authentication_and_authorization do
2021-01-29 00:20:46 +05:30
present paginate ( finder ( declared_params ( include_missing : false ) ) . execute ) , with : Entities :: ImpersonationToken
end
2017-08-17 22:00:37 +05:30
desc 'Create a impersonation token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
2018-12-05 23:21:45 +05:30
success Entities :: ImpersonationTokenWithToken
2017-08-17 22:00:37 +05:30
end
params do
requires :name , type : String , desc : 'The name of the impersonation token'
optional :expires_at , type : Date , desc : 'The expiration date in the format YEAR-MONTH-DAY of the impersonation token'
2022-11-25 23:54:43 +05:30
optional :scopes , type : Array [ String ] , coerce_with : :: API :: Validations :: Types :: CommaSeparatedToArray . coerce , desc : 'The array of scopes of the impersonation token'
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post feature_category : :authentication_and_authorization do
2017-08-17 22:00:37 +05:30
impersonation_token = finder . build ( declared_params ( include_missing : false ) )
if impersonation_token . save
2018-12-05 23:21:45 +05:30
present impersonation_token , with : Entities :: ImpersonationTokenWithToken
2017-08-17 22:00:37 +05:30
else
render_validation_error! ( impersonation_token )
end
end
desc 'Retrieve impersonation token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
success Entities :: ImpersonationToken
end
params do
requires :impersonation_token_id , type : Integer , desc : 'The ID of the impersonation token'
end
2021-01-29 00:20:46 +05:30
get ':impersonation_token_id' , feature_category : :authentication_and_authorization do
2017-08-17 22:00:37 +05:30
present find_impersonation_token , with : Entities :: ImpersonationToken
end
desc 'Revoke a impersonation token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
end
params do
requires :impersonation_token_id , type : Integer , desc : 'The ID of the impersonation token'
end
2021-01-29 00:20:46 +05:30
delete ':impersonation_token_id' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
token = find_impersonation_token
destroy_conditionally! ( token ) do
token . revoke!
end
2017-08-17 22:00:37 +05:30
end
end
2021-01-29 00:20:46 +05:30
resource :personal_access_tokens do
helpers do
def target_user
find_user_by_id ( params )
end
end
before { authenticated_as_admin! }
desc 'Create a personal access token. Available only for admins.' do
detail 'This feature was introduced in GitLab 13.6'
success Entities :: PersonalAccessTokenWithToken
end
params do
requires :name , type : String , desc : 'The name of the personal access token'
requires :scopes , type : Array [ String ] , coerce_with : :: API :: Validations :: Types :: CommaSeparatedToArray . coerce , values : :: Gitlab :: Auth . all_available_scopes . map ( & :to_s ) ,
2022-08-27 11:52:29 +05:30
desc : 'The array of scopes of the personal access token'
2021-01-29 00:20:46 +05:30
optional :expires_at , type : Date , desc : 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
end
post feature_category : :authentication_and_authorization do
response = :: PersonalAccessTokens :: CreateService . new (
current_user : current_user , target_user : target_user , params : declared_params ( include_missing : false )
) . execute
if response . success?
present response . payload [ :personal_access_token ] , with : Entities :: PersonalAccessTokenWithToken
else
render_api_error! ( response . message , response . http_status || :unprocessable_entity )
end
end
end
2017-08-17 22:00:37 +05:30
end
2014-09-02 18:07:02 +05:30
end
resource :user do
2017-09-10 17:25:29 +05:30
before do
authenticate!
end
2018-11-08 19:23:39 +05:30
# Enabling /user endpoint for the v3 version to allow oauth
# authentication through this endpoint.
version %w( v3 v4 ) , using : :path do
desc 'Get the currently authenticated user' do
success Entities :: UserPublic
end
2022-07-23 23:45:48 +05:30
get feature_category : :users , urgency : :low do
2018-11-08 19:23:39 +05:30
entity =
2023-03-04 22:38:38 +05:30
# We're disabling Cop/UserAdmin because it checks if the given user is an admin.
if current_user . admin? # rubocop:disable Cop/UserAdmin
2018-11-08 19:23:39 +05:30
Entities :: UserWithAdmin
else
Entities :: UserPublic
end
2017-09-10 17:25:29 +05:30
2018-11-18 11:00:15 +05:30
present current_user , with : entity , current_user : current_user
2018-11-08 19:23:39 +05:30
end
2014-09-02 18:07:02 +05:30
end
2023-03-17 16:20:25 +05:30
helpers do
def set_user_status ( include_missing_params : )
forbidden! unless can? ( current_user , :update_user_status , current_user )
if :: Users :: SetStatusService . new ( current_user , declared_params ( include_missing : include_missing_params ) ) . execute
present current_user . status , with : Entities :: UserStatus
else
render_validation_error! ( current_user . status )
end
end
params :set_user_status_params do
optional :emoji , type : String , desc : " The emoji to set on the status "
optional :message , type : String , desc : " The status message to set "
optional :availability , type : String , desc : " The availability of user to set "
optional :clear_status_after , type : String , desc : " Automatically clear emoji, message and availability fields after a certain time " , values : UserStatus :: CLEAR_STATUS_QUICK_OPTIONS . keys
end
end
2017-08-17 22:00:37 +05:30
desc " Get the currently authenticated user's SSH keys " do
success Entities :: SSHKey
end
params do
use :pagination
end
2021-01-29 00:20:46 +05:30
get " keys " , feature_category : :authentication_and_authorization do
2020-06-23 00:09:42 +05:30
keys = current_user . keys . preload_users
present paginate ( keys ) , with : Entities :: SSHKey
2014-09-02 18:07:02 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Get a single key owned by currently authenticated user' do
success Entities :: SSHKey
end
params do
requires :key_id , type : Integer , desc : 'The ID of the SSH key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get " keys/:key_id " , feature_category : :authentication_and_authorization do
2017-08-17 22:00:37 +05:30
key = current_user . keys . find_by ( id : params [ :key_id ] )
not_found! ( 'Key' ) unless key
2014-09-02 18:07:02 +05:30
present key , with : Entities :: SSHKey
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
2017-08-17 22:00:37 +05:30
desc 'Add a new SSH key to the currently authenticated user' do
success Entities :: SSHKey
end
params do
requires :key , type : String , desc : 'The new SSH key'
requires :title , type : String , desc : 'The title of the new SSH key'
2020-06-23 00:09:42 +05:30
optional :expires_at , type : DateTime , desc : 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
2023-03-04 22:38:38 +05:30
optional :usage_type , type : String , values : Key . usage_types . keys , default : 'auth_and_signing' ,
desc : 'Scope of usage for the SSH key'
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post " keys " , feature_category : :authentication_and_authorization do
2020-07-28 23:09:34 +05:30
key = :: Keys :: CreateService . new ( current_user , declared_params ( include_missing : false ) ) . execute
2014-09-02 18:07:02 +05:30
2020-07-28 23:09:34 +05:30
if key . persisted?
2014-09-02 18:07:02 +05:30
present key , with : Entities :: SSHKey
else
2015-04-26 12:48:37 +05:30
render_validation_error! ( key )
2014-09-02 18:07:02 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Delete an SSH key from the currently authenticated user' do
success Entities :: SSHKey
end
params do
requires :key_id , type : Integer , desc : 'The ID of the SSH key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete " keys/:key_id " , feature_category : :authentication_and_authorization do
2017-08-17 22:00:37 +05:30
key = current_user . keys . find_by ( id : params [ :key_id ] )
not_found! ( 'Key' ) unless key
2020-07-28 23:09:34 +05:30
destroy_conditionally! ( key ) do | key |
destroy_service = :: Keys :: DestroyService . new ( current_user )
destroy_service . execute ( key )
end
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
desc " Get the currently authenticated user's GPG keys " do
detail 'This feature was added in GitLab 10.0'
2020-04-08 14:13:33 +05:30
success Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
params do
use :pagination
end
2021-01-29 00:20:46 +05:30
get 'gpg_keys' , feature_category : :authentication_and_authorization do
2020-04-08 14:13:33 +05:30
present paginate ( current_user . gpg_keys ) , with : Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
desc 'Get a single GPG key owned by currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
2020-04-08 14:13:33 +05:30
success Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
params do
requires :key_id , type : Integer , desc : 'The ID of the GPG key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get 'gpg_keys/:key_id' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
key = current_user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
2020-04-08 14:13:33 +05:30
present key , with : Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
desc 'Add a new GPG key to the currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
2020-04-08 14:13:33 +05:30
success Entities :: GpgKey
2018-03-17 18:26:18 +05:30
end
params do
requires :key , type : String , desc : 'The new GPG key'
end
2021-01-29 00:20:46 +05:30
post 'gpg_keys' , feature_category : :authentication_and_authorization do
2020-07-28 23:09:34 +05:30
key = :: GpgKeys :: CreateService . new ( current_user , declared_params ( include_missing : false ) ) . execute
2018-03-17 18:26:18 +05:30
2020-07-28 23:09:34 +05:30
if key . persisted?
2020-04-08 14:13:33 +05:30
present key , with : Entities :: GpgKey
2018-03-17 18:26:18 +05:30
else
render_validation_error! ( key )
end
end
desc 'Revoke a GPG key owned by currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
end
params do
requires :key_id , type : Integer , desc : 'The ID of the GPG key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post 'gpg_keys/:key_id/revoke' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
key = current_user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
key . revoke
status :accepted
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
desc 'Delete a GPG key from the currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
end
params do
requires :key_id , type : Integer , desc : 'The ID of the SSH key'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete 'gpg_keys/:key_id' , feature_category : :authentication_and_authorization do
2018-03-17 18:26:18 +05:30
key = current_user . gpg_keys . find_by ( id : params [ :key_id ] )
not_found! ( 'GPG Key' ) unless key
2020-07-28 23:09:34 +05:30
destroy_conditionally! ( key ) do | key |
destroy_service = :: GpgKeys :: DestroyService . new ( current_user )
destroy_service . execute ( key )
end
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc " Get the currently authenticated user's email addresses " do
success Entities :: Email
end
params do
use :pagination
end
2022-07-16 23:28:13 +05:30
get " emails " , feature_category : :users , urgency : :high do
2017-08-17 22:00:37 +05:30
present paginate ( current_user . emails ) , with : Entities :: Email
2015-09-11 14:41:01 +05:30
end
2021-06-08 01:23:25 +05:30
desc " Update a user's credit_card_validation " do
success Entities :: UserCreditCardValidations
end
params do
requires :user_id , type : String , desc : 'The ID or username of the user'
requires :credit_card_validated_at , type : DateTime , desc : 'The time when the user\'s credit card was validated'
2021-11-18 22:05:49 +05:30
requires :credit_card_expiration_month , type : Integer , desc : 'The month the credit card expires'
requires :credit_card_expiration_year , type : Integer , desc : 'The year the credit card expires'
requires :credit_card_holder_name , type : String , desc : 'The credit card holder name'
requires :credit_card_mask_number , type : String , desc : 'The last 4 digits of credit card number'
2021-12-11 22:18:48 +05:30
requires :credit_card_type , type : String , desc : 'The credit card network name'
2021-06-08 01:23:25 +05:30
end
2022-07-23 23:45:48 +05:30
put " :user_id/credit_card_validation " , urgency : :low , feature_category : :purchase do
2021-06-08 01:23:25 +05:30
authenticated_as_admin!
user = find_user ( params [ :user_id ] )
not_found! ( 'User' ) unless user
attrs = declared_params ( include_missing : false )
2022-03-02 08:16:31 +05:30
service = :: Users :: UpsertCreditCardValidationService . new ( attrs , user ) . execute
2021-06-08 01:23:25 +05:30
if service . success?
present user . credit_card_validation , with : Entities :: UserCreditCardValidations
else
render_api_error! ( '400 Bad Request' , 400 )
end
end
2021-04-29 21:17:54 +05:30
desc " Update the current user's preferences " do
success Entities :: UserPreferences
detail 'This feature was introduced in GitLab 13.10.'
end
params do
2021-09-04 01:27:46 +05:30
optional :view_diffs_file_by_file , type : Boolean , desc : 'Flag indicating the user sees only one file diff per page'
optional :show_whitespace_in_diffs , type : Boolean , desc : 'Flag indicating the user sees whitespace changes in diffs'
at_least_one_of :view_diffs_file_by_file , :show_whitespace_in_diffs
2021-04-29 21:17:54 +05:30
end
2022-07-16 23:28:13 +05:30
put " preferences " , feature_category : :users , urgency : :high do
2021-04-29 21:17:54 +05:30
authenticate!
preferences = current_user . user_preference
attrs = declared_params ( include_missing : false )
service = :: UserPreferences :: UpdateService . new ( current_user , attrs ) . execute
if service . success?
present preferences , with : Entities :: UserPreferences
else
render_api_error! ( '400 Bad Request' , 400 )
end
end
2021-09-04 01:27:46 +05:30
desc " Get the current user's preferences " do
success Entities :: UserPreferences
detail 'This feature was introduced in GitLab 14.0.'
end
get " preferences " , feature_category : :users do
present current_user . user_preference , with : Entities :: UserPreferences
end
2017-08-17 22:00:37 +05:30
desc 'Get a single email address owned by the currently authenticated user' do
success Entities :: Email
end
params do
requires :email_id , type : Integer , desc : 'The ID of the email'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get " emails/:email_id " , feature_category : :users do
2017-08-17 22:00:37 +05:30
email = current_user . emails . find_by ( id : params [ :email_id ] )
not_found! ( 'Email' ) unless email
2015-09-11 14:41:01 +05:30
present email , with : Entities :: Email
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
desc 'Add new email address to the currently authenticated user' do
success Entities :: Email
end
params do
requires :email , type : String , desc : 'The new email'
end
2021-01-29 00:20:46 +05:30
post " emails " , feature_category : :users do
2018-03-17 18:26:18 +05:30
email = Emails :: CreateService . new ( current_user , declared_params . merge ( user : current_user ) ) . execute
2015-09-11 14:41:01 +05:30
2017-09-10 17:25:29 +05:30
if email . errors . blank?
2015-09-11 14:41:01 +05:30
present email , with : Entities :: Email
else
render_validation_error! ( email )
end
end
2017-08-17 22:00:37 +05:30
desc 'Delete an email address from the currently authenticated user'
params do
requires :email_id , type : Integer , desc : 'The ID of the email'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete " emails/:email_id " , feature_category : :users do
2017-08-17 22:00:37 +05:30
email = current_user . emails . find_by ( id : params [ :email_id ] )
not_found! ( 'Email' ) unless email
2015-09-11 14:41:01 +05:30
2018-03-17 18:26:18 +05:30
destroy_conditionally! ( email ) do | email |
Emails :: DestroyService . new ( current_user , user : current_user ) . execute ( email )
end
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2017-08-17 22:00:37 +05:30
desc 'Get a list of user activities'
params do
optional :from , type : DateTime , default : 6 . months . ago , desc : 'Date string in the format YEAR-MONTH-DAY'
use :pagination
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
get " activities " , feature_category : :users do
2017-08-17 22:00:37 +05:30
authenticated_as_admin!
2017-09-10 17:25:29 +05:30
activities = User
. where ( User . arel_table [ :last_activity_on ] . gteq ( params [ :from ] ) )
. reorder ( last_activity_on : :asc )
2017-08-17 22:00:37 +05:30
present paginate ( activities ) , with : Entities :: UserActivity
2015-09-11 14:41:01 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-11-18 11:00:15 +05:30
desc 'Set the status of the current user' do
success Entities :: UserStatus
2023-03-17 16:20:25 +05:30
detail 'Any parameters that are not passed will be nullified.'
2018-11-18 11:00:15 +05:30
end
params do
2023-03-17 16:20:25 +05:30
use :set_user_status_params
2018-11-18 11:00:15 +05:30
end
2021-01-29 00:20:46 +05:30
put " status " , feature_category : :users do
2023-03-17 16:20:25 +05:30
set_user_status ( include_missing_params : true )
end
2018-11-18 11:00:15 +05:30
2023-03-17 16:20:25 +05:30
desc 'Set the status of the current user' do
success Entities :: UserStatus
detail 'Any parameters that are not passed will be ignored.'
end
params do
use :set_user_status_params
end
patch " status " , feature_category : :users do
if declared_params ( include_missing : false ) . empty?
status :ok
break
2018-11-18 11:00:15 +05:30
end
2023-03-17 16:20:25 +05:30
set_user_status ( include_missing_params : false )
2018-11-18 11:00:15 +05:30
end
desc 'get the status of the current user' do
success Entities :: UserStatus
end
2021-01-29 00:20:46 +05:30
get 'status' , feature_category : :users do
2018-11-18 11:00:15 +05:30
present current_user . status || { } , with : Entities :: UserStatus
end
2014-09-02 18:07:02 +05:30
end
end
end
2022-08-13 15:12:31 +05:30
API :: Users . prepend_mod