users.rb 25.1 KB
Newer Older
1
module API
2
  class Users < Grape::API
3
    include PaginationParams
4
    include APIGuard
5
    include Helpers::CustomAttributes
6

7
    allow_access_with_scope :read_user, if: -> (request) { request.get? }
8

9
    resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
10 11
      include CustomAttributesEndpoints

12 13 14 15
      before do
        authenticate_non_get!
      end

Robert Schilling's avatar
Robert Schilling committed
16
      helpers do
17
        def find_user_by_id(params)
18 19
          id = params[:user_id] || params[:id]
          User.find_by(id: id) || not_found!('User')
20 21
        end

22 23 24 25 26 27 28 29
        def reorder_users(users)
          if params[:order_by] && params[:sort]
            users.reorder(params[:order_by] => params[:sort])
          else
            users
          end
        end

Robert Schilling's avatar
Robert Schilling committed
30 31 32 33 34 35 36
        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'
37
          optional :extern_uid, type: String, desc: 'The external authentication provider UID'
Robert Schilling's avatar
Robert Schilling committed
38 39 40 41 42 43
          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'
          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'
44
          optional :avatar, type: File, desc: 'Avatar image for user'
Robert Schilling's avatar
Robert Schilling committed
45
          all_or_none_of :extern_uid, :provider
46 47

          # EE
48
          optional :shared_runners_minutes_limit, type: Integer, desc: 'Pipeline minutes quota for this user'
Robert Schilling's avatar
Robert Schilling committed
49
        end
50 51 52 53 54 55 56

        params :sort_params do
          optional :order_by, type: String, values: %w[id name username created_at updated_at],
                              default: 'id', desc: 'Return users ordered by a field'
          optional :sort, type: String, values: %w[asc desc], default: 'desc',
                          desc: 'Return users sorted in ascending and descending order'
        end
Robert Schilling's avatar
Robert Schilling committed
57 58 59 60 61 62
      end

      desc 'Get the list of users' do
        success Entities::UserBasic
      end
      params do
63
        # CE
Robert Schilling's avatar
Robert Schilling committed
64
        optional :username, type: String, desc: 'Get a single user with a specific username'
65 66
        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'
Robert Schilling's avatar
Robert Schilling committed
67 68 69 70
        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'
        optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
71 72
        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'
73
        all_or_none_of :extern_uid, :provider
74

75
        use :sort_params
76
        use :pagination
77
        use :with_custom_attributes
78 79 80

        # EE
        optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users'
Robert Schilling's avatar
Robert Schilling committed
81
      end
82
      get do
83 84
        authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)

James Lopez's avatar
James Lopez committed
85
        unless current_user&.admin?
86
          params.except!(:created_after, :created_before, :order_by, :sort)
87 88
        end

89
        users = UsersFinder.new(current_user, params).execute
90
        users = reorder_users(users)
91

92 93 94 95 96 97 98 99 100 101 102 103
        authorized = can?(current_user, :read_users_list)

        # 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?

        forbidden!("Not authorized to access /api/v4/users") unless authorized

        entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
104
        users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
105
        users, options = with_custom_attributes(users, with: entity)
106

107
        present paginate(users), options
108 109
      end

Robert Schilling's avatar
Robert Schilling committed
110
      desc 'Get a single user' do
111
        success Entities::User
Robert Schilling's avatar
Robert Schilling committed
112 113 114
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
115 116

        use :with_custom_attributes
Robert Schilling's avatar
Robert Schilling committed
117
      end
118
      get ":id" do
Robert Schilling's avatar
Robert Schilling committed
119
        user = User.find_by(id: params[:id])
120 121
        not_found!('User') unless user && can?(current_user, :read_user, user)

122
        opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
123 124
        user, opts = with_custom_attributes(user, opts)

125
        present user, opts
126
      end
127

Robert Schilling's avatar
Robert Schilling committed
128
      desc 'Create a user. Available only for admins.' do
129
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
130 131 132
      end
      params do
        requires :email, type: String, desc: 'The email of the user'
133 134
        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'
Daniel Juarez's avatar
Daniel Juarez committed
135
        optional :skip_confirmation, type: Boolean, desc: 'Flag indicating the account is confirmed'
136
        at_least_one_of :password, :reset_password
Robert Schilling's avatar
Robert Schilling committed
137 138 139 140
        requires :name, type: String, desc: 'The name of the user'
        requires :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
141 142
      post do
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
143

144
        params = declared_params(include_missing: false)
James Lopez's avatar
James Lopez committed
145
        user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true)
146

147
        if user.persisted?
148
          present user, with: Entities::UserPublic
149
        else
150 151 152
          conflict!('Email has already been taken') if User
              .where(email: user.email)
              .count > 0
153

154 155 156
          conflict!('Username has already been taken') if User
              .where(username: user.username)
              .count > 0
157 158

          render_validation_error!(user)
159 160
        end
      end
161

Robert Schilling's avatar
Robert Schilling committed
162
      desc 'Update a user. Available only for admins.' do
163
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
164 165 166 167 168
      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'
Daniel Juarez's avatar
Daniel Juarez committed
169
        optional :skip_reconfirmation, type: Boolean, desc: 'Flag indicating the account skips the confirmation by email'
Robert Schilling's avatar
Robert Schilling committed
170 171 172 173
        optional :name, type: String, desc: 'The name of the user'
        optional :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
174 175
      put ":id" do
        authenticated_as_admin!
176

Robert Schilling's avatar
Robert Schilling committed
177
        user = User.find_by(id: params.delete(:id))
178
        not_found!('User') unless user
179

Robert Schilling's avatar
Robert Schilling committed
180
        conflict!('Email has already been taken') if params[:email] &&
181 182
            User.where(email: params[:email])
                .where.not(id: user.id).count > 0
183

Robert Schilling's avatar
Robert Schilling committed
184
        conflict!('Username has already been taken') if params[:username] &&
185 186
            User.where(username: params[:username])
                .where.not(id: user.id).count > 0
187

188 189
        user_params = declared_params(include_missing: false)
        identity_attrs = user_params.slice(:provider, :extern_uid)
Robert Schilling's avatar
Robert Schilling committed
190

191 192
        if identity_attrs.any?
          identity = user.identities.find_by(provider: identity_attrs[:provider])
Robert Schilling's avatar
Robert Schilling committed
193

194 195 196 197 198 199 200 201
          if identity
            identity.update_attributes(identity_attrs)
          else
            identity = user.identities.build(identity_attrs)
            identity.save
          end
        end

202
        user_params[:password_expires_at] = Time.now if user_params[:password].present?
203

204
        result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute
James Lopez's avatar
James Lopez committed
205 206

        if result[:status] == :success
207
          present user, with: Entities::UserPublic
208
        else
209
          render_validation_error!(user)
210 211 212
        end
      end

Robert Schilling's avatar
Robert Schilling committed
213 214 215 216 217 218 219 220
      desc 'Add an SSH key to 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, type: String, desc: 'The new SSH key'
        requires :title, type: String, desc: 'The title of the new SSH key'
      end
Angus MacArthur's avatar
Angus MacArthur committed
221 222
      post ":id/keys" do
        authenticated_as_admin!
223

Robert Schilling's avatar
Robert Schilling committed
224 225 226 227 228
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

        key = user.keys.new(declared_params(include_missing: false))

Angus MacArthur's avatar
Angus MacArthur committed
229 230 231
        if key.save
          present key, with: Entities::SSHKey
        else
232
          render_validation_error!(key)
Angus MacArthur's avatar
Angus MacArthur committed
233 234 235
        end
      end

Robert Schilling's avatar
Robert Schilling committed
236 237 238 239 240
      desc 'Get the SSH keys of a specified user. Available only for admins.' do
        success Entities::SSHKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
241
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
242 243
      end
      get ':id/keys' do
244
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
245 246

        user = User.find_by(id: params[:id])
247 248
        not_found!('User') unless user

249
        present paginate(user.keys), with: Entities::SSHKey
250 251
      end

Robert Schilling's avatar
Robert Schilling committed
252 253 254 255 256 257 258 259
      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
      delete ':id/keys/:key_id' do
260
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
261 262

        user = User.find_by(id: params[:id])
263 264
        not_found!('User') unless user

Robert Schilling's avatar
Robert Schilling committed
265 266 267
        key = user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

268
        destroy_conditionally!(key)
269 270
      end

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
      desc 'Add a GPG key to a specified user. Available only for admins.' do
        detail 'This feature was added in GitLab 10.0'
        success Entities::GPGKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        requires :key, type: String, desc: 'The new GPG key'
      end
      post ':id/gpg_keys' do
        authenticated_as_admin!

        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

        key = user.gpg_keys.new(declared_params(include_missing: false))

        if key.save
          present key, with: Entities::GPGKey
        else
          render_validation_error!(key)
        end
      end

      desc 'Get the GPG keys of a specified user. Available only for admins.' do
        detail 'This feature was added in GitLab 10.0'
        success Entities::GPGKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        use :pagination
      end
      get ':id/gpg_keys' do
        authenticated_as_admin!

        user = User.find_by(id: params[:id])
        not_found!('User') unless user

        present paginate(user.gpg_keys), with: Entities::GPGKey
      end

      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
      delete ':id/gpg_keys/:key_id' do
        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

        status 204
        key.destroy
      end

      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
      post ':id/gpg_keys/:key_id/revoke' do
        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

Robert Schilling's avatar
Robert Schilling committed
351 352 353 354 355 356 357
      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'
      end
358 359 360
      post ":id/emails" do
        authenticated_as_admin!

Robert Schilling's avatar
Robert Schilling committed
361 362 363
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

James Lopez's avatar
James Lopez committed
364
        email = Emails::CreateService.new(current_user, declared_params(include_missing: false).merge(user: user)).execute
Robert Schilling's avatar
Robert Schilling committed
365

James Lopez's avatar
James Lopez committed
366
        if email.errors.blank?
367 368 369 370 371 372
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
373 374 375 376 377
      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'
378
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
379 380
      end
      get ':id/emails' do
381
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
382
        user = User.find_by(id: params[:id])
383 384
        not_found!('User') unless user

385
        present paginate(user.emails), with: Entities::Email
386 387
      end

Robert Schilling's avatar
Robert Schilling committed
388 389 390 391 392 393 394 395
      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
      delete ':id/emails/:email_id' do
396
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
397
        user = User.find_by(id: params[:id])
398 399
        not_found!('User') unless user

Robert Schilling's avatar
Robert Schilling committed
400 401
        email = user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email
402

403
        destroy_conditionally!(email) do |email|
404
          Emails::DestroyService.new(current_user, user: user).execute(email)
405
        end
406 407
      end

Robert Schilling's avatar
Robert Schilling committed
408 409 410 411 412
      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'
413
        optional :hard_delete, type: Boolean, desc: "Whether to remove a user's contributions"
Robert Schilling's avatar
Robert Schilling committed
414
      end
415
      delete ":id" do
416 417
        Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42279')

418
        authenticated_as_admin!
419

skv's avatar
skv committed
420
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
421
        not_found!('User') unless user
422

423 424 425
        destroy_conditionally!(user) do
          user.delete_async(deleted_by: current_user, params: params)
        end
426
      end
427

Robert Schilling's avatar
Robert Schilling committed
428 429 430 431
      desc 'Block a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
432
      post ':id/block' do
433 434
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
435
        not_found!('User') unless user
436

Robert Schilling's avatar
Robert Schilling committed
437
        if !user.ldap_blocked?
438 439
          user.block
        else
440
          forbidden!('LDAP blocked users cannot be modified by the API')
441 442 443
        end
      end

Robert Schilling's avatar
Robert Schilling committed
444 445 446 447
      desc 'Unblock a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
448
      post ':id/unblock' do
449 450
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
451
        not_found!('User') unless user
452

Robert Schilling's avatar
Robert Schilling committed
453
        if user.ldap_blocked?
454 455 456
          forbidden!('LDAP blocked users cannot be unblocked by the API')
        else
          user.activate
457 458
        end
      end
459

460
      params do
461 462 463 464 465 466
        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 = {})
467
              user = find_user_by_id(params)
468 469 470 471 472 473
              PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options))
            end

            def find_impersonation_token
              finder.find_by(id: declared_params[:impersonation_token_id]) || not_found!('Impersonation Token')
            end
474
          end
475

476 477 478
          before { authenticated_as_admin! }

          desc 'Retrieve impersonation tokens. Available only for admins.' do
479
            detail 'This feature was introduced in GitLab 9.0'
480
            success Entities::ImpersonationToken
481 482
          end
          params do
483
            use :pagination
484
            optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) impersonation_tokens'
485
          end
486
          get { present paginate(finder(declared_params(include_missing: false)).execute), with: Entities::ImpersonationToken }
487

488
          desc 'Create a impersonation token. Available only for admins.' do
489
            detail 'This feature was introduced in GitLab 9.0'
490
            success Entities::ImpersonationToken
491 492
          end
          params do
493 494 495
            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'
            optional :scopes, type: Array, desc: 'The array of scopes of the impersonation token'
496 497
          end
          post do
498
            impersonation_token = finder.build(declared_params(include_missing: false))
499

500 501
            if impersonation_token.save
              present impersonation_token, with: Entities::ImpersonationToken
502
            else
503
              render_validation_error!(impersonation_token)
504 505
            end
          end
506

507
          desc 'Retrieve impersonation token. Available only for admins.' do
508
            detail 'This feature was introduced in GitLab 9.0'
509
            success Entities::ImpersonationToken
510 511
          end
          params do
512
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
513
          end
514 515
          get ':impersonation_token_id' do
            present find_impersonation_token, with: Entities::ImpersonationToken
516
          end
517

518
          desc 'Revoke a impersonation token. Available only for admins.' do
519 520 521
            detail 'This feature was introduced in GitLab 9.0'
          end
          params do
522
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
523
          end
524
          delete ':impersonation_token_id' do
525 526 527 528 529
            token = find_impersonation_token

            destroy_conditionally!(token) do
              token.revoke!
            end
530 531
          end
        end
532
      end
533 534
    end

535
    resource :user do
536 537 538 539
      before do
        authenticate!
      end

Robert Schilling's avatar
Robert Schilling committed
540
      desc 'Get the currently authenticated user' do
541
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
542
      end
543
      get do
544
        entity =
545
          if current_user.admin?
546 547 548 549 550 551
            Entities::UserWithAdmin
          else
            Entities::UserPublic
          end

        present current_user, with: entity
552 553
      end

Robert Schilling's avatar
Robert Schilling committed
554 555 556
      desc "Get the currently authenticated user's SSH keys" do
        success Entities::SSHKey
      end
557 558 559
      params do
        use :pagination
      end
560
      get "keys" do
561
        present paginate(current_user.keys), with: Entities::SSHKey
562 563
      end

Robert Schilling's avatar
Robert Schilling committed
564 565 566 567 568 569 570 571 572 573
      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
      get "keys/:key_id" do
        key = current_user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

574 575 576
        present key, with: Entities::SSHKey
      end

Robert Schilling's avatar
Robert Schilling committed
577 578 579 580 581 582 583
      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'
      end
584
      post "keys" do
Robert Schilling's avatar
Robert Schilling committed
585
        key = current_user.keys.new(declared_params)
586

587 588 589
        if key.save
          present key, with: Entities::SSHKey
        else
590
          render_validation_error!(key)
591 592 593
        end
      end

Robert Schilling's avatar
Robert Schilling committed
594 595 596 597 598 599 600 601 602 603
      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
      delete "keys/:key_id" do
        key = current_user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

604
        destroy_conditionally!(key)
605
      end
606

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
      desc "Get the currently authenticated user's GPG keys" do
        detail 'This feature was added in GitLab 10.0'
        success Entities::GPGKey
      end
      params do
        use :pagination
      end
      get 'gpg_keys' do
        present paginate(current_user.gpg_keys), with: Entities::GPGKey
      end

      desc 'Get a single GPG key owned by currently authenticated user' do
        detail 'This feature was added in GitLab 10.0'
        success Entities::GPGKey
      end
      params do
        requires :key_id, type: Integer, desc: 'The ID of the GPG key'
      end
      get 'gpg_keys/:key_id' do
        key = current_user.gpg_keys.find_by(id: params[:key_id])
        not_found!('GPG Key') unless key

        present key, with: Entities::GPGKey
      end

      desc 'Add a new GPG key to the currently authenticated user' do
        detail 'This feature was added in GitLab 10.0'
        success Entities::GPGKey
      end
      params do
        requires :key, type: String, desc: 'The new GPG key'
      end
      post 'gpg_keys' do
        key = current_user.gpg_keys.new(declared_params)

        if key.save
          present key, with: Entities::GPGKey
        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
      post 'gpg_keys/:key_id/revoke' do
        key = current_user.gpg_keys.find_by(id: params[:key_id])
        not_found!('GPG Key') unless key

        key.revoke
        status :accepted
      end

      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
      delete 'gpg_keys/:key_id' do
        key = current_user.gpg_keys.find_by(id: params[:key_id])
        not_found!('GPG Key') unless key

        status 204
        key.destroy
      end

Robert Schilling's avatar
Robert Schilling committed
677 678 679
      desc "Get the currently authenticated user's email addresses" do
        success Entities::Email
      end
680 681 682
      params do
        use :pagination
      end
683
      get "emails" do
684
        present paginate(current_user.emails), with: Entities::Email
685 686
      end

Robert Schilling's avatar
Robert Schilling committed
687 688 689 690 691 692 693 694 695 696
      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
      get "emails/:email_id" do
        email = current_user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email

697 698 699
        present email, with: Entities::Email
      end

Robert Schilling's avatar
Robert Schilling committed
700 701 702 703 704 705
      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
706
      post "emails" do
James Lopez's avatar
James Lopez committed
707
        email = Emails::CreateService.new(current_user, declared_params.merge(user: current_user)).execute
708

James Lopez's avatar
James Lopez committed
709
        if email.errors.blank?
710 711 712 713 714 715
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
716 717 718 719 720 721 722
      desc 'Delete an email address from the currently authenticated user'
      params do
        requires :email_id, type: Integer, desc: 'The ID of the email'
      end
      delete "emails/:email_id" do
        email = current_user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email
723

724
        destroy_conditionally!(email) do |email|
725
          Emails::DestroyService.new(current_user, user: current_user).execute(email)
726
        end
727
      end
728

James Lopez's avatar
James Lopez committed
729
      desc 'Get a list of user activities'
730
      params do
731
        optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
732 733
        use :pagination
      end
734
      get "activities" do
735 736
        authenticated_as_admin!

737 738 739
        activities = User
          .where(User.arel_table[:last_activity_on].gteq(params[:from]))
          .reorder(last_activity_on: :asc)
740

741
        present paginate(activities), with: Entities::UserActivity
742
      end
743 744 745
    end
  end
end