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 46
          all_or_none_of :extern_uid, :provider
        end
47 48 49 50 51 52 53

        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
54 55 56 57 58 59
      end

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

72
        use :sort_params
73
        use :pagination
74
        use :with_custom_attributes
Robert Schilling's avatar
Robert Schilling committed
75
      end
76
      get do
77 78
        authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)

James Lopez's avatar
James Lopez committed
79
        unless current_user&.admin?
80
          params.except!(:created_after, :created_before, :order_by, :sort, :two_factor)
81 82
        end

George Andrinopoulos's avatar
George Andrinopoulos committed
83
        users = UsersFinder.new(current_user, params).execute
84
        users = reorder_users(users)
85

86
        authorized = can?(current_user, :read_users_list)
87

88 89 90 91 92 93
        # 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?
94

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

        entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
98
        users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
99
        users, options = with_custom_attributes(users, with: entity)
100

101
        present paginate(users), options
102 103
      end

Robert Schilling's avatar
Robert Schilling committed
104
      desc 'Get a single user' do
105
        success Entities::User
Robert Schilling's avatar
Robert Schilling committed
106 107 108
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
109 110

        use :with_custom_attributes
Robert Schilling's avatar
Robert Schilling committed
111
      end
112
      get ":id" do
Robert Schilling's avatar
Robert Schilling committed
113
        user = User.find_by(id: params[:id])
114 115
        not_found!('User') unless user && can?(current_user, :read_user, user)

116
        opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
117 118
        user, opts = with_custom_attributes(user, opts)

119
        present user, opts
120
      end
121

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

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

141
        if user.persisted?
142
          present user, with: Entities::UserPublic
143
        else
144 145 146
          conflict!('Email has already been taken') if User
              .where(email: user.email)
              .count > 0
147

148 149 150
          conflict!('Username has already been taken') if User
              .where(username: user.username)
              .count > 0
151 152

          render_validation_error!(user)
153 154
        end
      end
155

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

Robert Schilling's avatar
Robert Schilling committed
171
        user = User.find_by(id: params.delete(:id))
172
        not_found!('User') unless user
173

Robert Schilling's avatar
Robert Schilling committed
174
        conflict!('Email has already been taken') if params[:email] &&
175 176
            User.where(email: params[:email])
                .where.not(id: user.id).count > 0
177

Robert Schilling's avatar
Robert Schilling committed
178
        conflict!('Username has already been taken') if params[:username] &&
179 180
            User.where(username: params[:username])
                .where.not(id: user.id).count > 0
181

182 183
        user_params = declared_params(include_missing: false)
        identity_attrs = user_params.slice(:provider, :extern_uid)
Robert Schilling's avatar
Robert Schilling committed
184

185 186
        if identity_attrs.any?
          identity = user.identities.find_by(provider: identity_attrs[:provider])
Robert Schilling's avatar
Robert Schilling committed
187

188 189 190 191 192 193 194 195
          if identity
            identity.update_attributes(identity_attrs)
          else
            identity = user.identities.build(identity_attrs)
            identity.save
          end
        end

196
        user_params[:password_expires_at] = Time.now if user_params[:password].present?
197

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

        if result[:status] == :success
201
          present user, with: Entities::UserPublic
202
        else
203
          render_validation_error!(user)
204 205 206
        end
      end

Robert Schilling's avatar
Robert Schilling committed
207 208 209 210 211 212 213 214
      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
215 216
      post ":id/keys" do
        authenticated_as_admin!
217

Robert Schilling's avatar
Robert Schilling committed
218 219 220 221 222
        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
223 224 225
        if key.save
          present key, with: Entities::SSHKey
        else
226
          render_validation_error!(key)
Angus MacArthur's avatar
Angus MacArthur committed
227 228 229
        end
      end

Robert Schilling's avatar
Robert Schilling committed
230 231 232 233 234
      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'
235
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
236 237
      end
      get ':id/keys' do
238
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
239 240

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

243
        present paginate(user.keys), with: Entities::SSHKey
244 245
      end

Robert Schilling's avatar
Robert Schilling committed
246 247 248 249 250 251 252 253
      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
254
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
255 256

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

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

262
        destroy_conditionally!(key)
263 264
      end

265 266 267 268 269 270 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
      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
345 346 347 348 349 350 351
      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
352 353 354
      post ":id/emails" do
        authenticated_as_admin!

Robert Schilling's avatar
Robert Schilling committed
355 356 357
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

James Lopez's avatar
James Lopez committed
358
        email = Emails::CreateService.new(current_user, declared_params(include_missing: false).merge(user: user)).execute
James Lopez's avatar
James Lopez committed
359 360

        if email.errors.blank?
361 362 363 364 365 366
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
367 368 369 370 371
      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'
372
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
373 374
      end
      get ':id/emails' do
375
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
376
        user = User.find_by(id: params[:id])
377 378
        not_found!('User') unless user

379
        present paginate(user.emails), with: Entities::Email
380 381
      end

Robert Schilling's avatar
Robert Schilling committed
382 383 384 385 386 387 388 389
      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
390
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
391
        user = User.find_by(id: params[:id])
392 393
        not_found!('User') unless user

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

397
        destroy_conditionally!(email) do |email|
398
          Emails::DestroyService.new(current_user, user: user).execute(email)
399
        end
400 401
      end

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

412
        authenticated_as_admin!
413

skv's avatar
skv committed
414
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
415
        not_found!('User') unless user
416

417 418 419
        destroy_conditionally!(user) do
          user.delete_async(deleted_by: current_user, params: params)
        end
420
      end
421

Robert Schilling's avatar
Robert Schilling committed
422 423 424 425
      desc 'Block a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
426
      post ':id/block' do
427 428
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
429
        not_found!('User') unless user
430

Robert Schilling's avatar
Robert Schilling committed
431
        if !user.ldap_blocked?
432 433
          user.block
        else
434
          forbidden!('LDAP blocked users cannot be modified by the API')
435 436 437
        end
      end

Robert Schilling's avatar
Robert Schilling committed
438 439 440 441
      desc 'Unblock a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
442
      post ':id/unblock' do
443 444
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
445
        not_found!('User') unless user
446

Robert Schilling's avatar
Robert Schilling committed
447
        if user.ldap_blocked?
448
          forbidden!('LDAP blocked users cannot be unblocked by the API')
Gabriel Mazetto's avatar
Gabriel Mazetto committed
449 450
        else
          user.activate
451 452
        end
      end
453

454
      params do
455 456 457 458 459 460
        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 = {})
461
              user = find_user_by_id(params)
462 463 464 465 466 467
              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
468
          end
469

470 471 472
          before { authenticated_as_admin! }

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

482
          desc 'Create a impersonation token. Available only for admins.' do
483
            detail 'This feature was introduced in GitLab 9.0'
484
            success Entities::ImpersonationToken
485 486
          end
          params do
487 488 489
            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'
490 491
          end
          post do
492
            impersonation_token = finder.build(declared_params(include_missing: false))
493

494 495
            if impersonation_token.save
              present impersonation_token, with: Entities::ImpersonationToken
496
            else
497
              render_validation_error!(impersonation_token)
498 499
            end
          end
500

501
          desc 'Retrieve impersonation token. Available only for admins.' do
502
            detail 'This feature was introduced in GitLab 9.0'
503
            success Entities::ImpersonationToken
504 505
          end
          params do
506
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
507
          end
508 509
          get ':impersonation_token_id' do
            present find_impersonation_token, with: Entities::ImpersonationToken
510
          end
511

512
          desc 'Revoke a impersonation token. Available only for admins.' do
513 514 515
            detail 'This feature was introduced in GitLab 9.0'
          end
          params do
516
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
517
          end
518
          delete ':impersonation_token_id' do
519 520 521 522 523
            token = find_impersonation_token

            destroy_conditionally!(token) do
              token.revoke!
            end
524 525
          end
        end
526
      end
527 528
    end

529
    resource :user do
530 531 532 533
      before do
        authenticate!
      end

534 535 536 537 538 539 540 541 542 543 544 545 546
      # 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
        get do
          entity =
            if current_user.admin?
              Entities::UserWithAdmin
            else
              Entities::UserPublic
            end
547

548 549
          present current_user, with: entity
        end
550 551
      end

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

Robert Schilling's avatar
Robert Schilling committed
562 563 564 565 566 567 568 569 570 571
      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

572 573 574
        present key, with: Entities::SSHKey
      end

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

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

Robert Schilling's avatar
Robert Schilling committed
592 593 594 595 596 597 598 599 600 601
      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

602
        destroy_conditionally!(key)
603
      end
604

605 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
      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
675 676 677
      desc "Get the currently authenticated user's email addresses" do
        success Entities::Email
      end
678 679 680
      params do
        use :pagination
      end
681
      get "emails" do
682
        present paginate(current_user.emails), with: Entities::Email
683 684
      end

Robert Schilling's avatar
Robert Schilling committed
685 686 687 688 689 690 691 692 693 694
      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

695 696 697
        present email, with: Entities::Email
      end

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

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

Robert Schilling's avatar
Robert Schilling committed
714 715 716 717 718 719 720
      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
721

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

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

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

739
        present paginate(activities), with: Entities::UserActivity
740
      end
741 742 743
    end
  end
end