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

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

8
    resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
9 10 11 12
      before do
        authenticate_non_get!
      end

Robert Schilling's avatar
Robert Schilling committed
13
      helpers do
14
        def find_user(params)
15 16
          id = params[:user_id] || params[:id]
          User.find_by(id: id) || not_found!('User')
17 18
        end

Robert Schilling's avatar
Robert Schilling committed
19 20 21 22 23 24 25
        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'
26
          optional :extern_uid, type: String, desc: 'The external authentication provider UID'
Robert Schilling's avatar
Robert Schilling committed
27 28 29 30 31
          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'
32
          optional :skip_confirmation, type: Boolean, default: false, desc: 'Flag indicating the account is confirmed'
Robert Schilling's avatar
Robert Schilling committed
33
          optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
34
          optional :avatar, type: File, desc: 'Avatar image for user'
Robert Schilling's avatar
Robert Schilling committed
35 36 37 38 39 40 41 42
          all_or_none_of :extern_uid, :provider
        end
      end

      desc 'Get the list of users' do
        success Entities::UserBasic
      end
      params do
43
        # CE
Robert Schilling's avatar
Robert Schilling committed
44
        optional :username, type: String, desc: 'Get a single user with a specific username'
45 46
        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
47 48 49 50
        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'
51 52
        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'
53
        all_or_none_of :extern_uid, :provider
54

55
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
56
      end
57
      get do
58 59
        authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)

James Lopez's avatar
James Lopez committed
60
        unless current_user&.admin?
61 62 63
          params.except!(:created_after, :created_before)
        end

64
        users = UsersFinder.new(current_user, params).execute
65

66
        authorized = can?(current_user, :read_users_list)
67

68 69 70 71 72 73
        # 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?
74

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

        entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
Robert Schilling's avatar
Robert Schilling committed
78
        present paginate(users), with: entity
79 80
      end

Robert Schilling's avatar
Robert Schilling committed
81
      desc 'Get a single user' do
82
        success Entities::User
Robert Schilling's avatar
Robert Schilling committed
83 84 85 86
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
87
      get ":id" do
Robert Schilling's avatar
Robert Schilling committed
88
        user = User.find_by(id: params[:id])
89 90
        not_found!('User') unless user && can?(current_user, :read_user, user)

91
        opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
92
        present user, opts
93
      end
94

Robert Schilling's avatar
Robert Schilling committed
95
      desc 'Create a user. Available only for admins.' do
96
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
97 98 99
      end
      params do
        requires :email, type: String, desc: 'The email of the user'
100 101 102
        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'
        at_least_one_of :password, :reset_password
Robert Schilling's avatar
Robert Schilling committed
103 104 105 106
        requires :name, type: String, desc: 'The name of the user'
        requires :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
107 108
      post do
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
109

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

113
        if user.persisted?
114
          present user, with: Entities::UserPublic
115
        else
116 117 118
          conflict!('Email has already been taken') if User
              .where(email: user.email)
              .count > 0
119

120 121 122
          conflict!('Username has already been taken') if User
              .where(username: user.username)
              .count > 0
123 124

          render_validation_error!(user)
125 126
        end
      end
127

Robert Schilling's avatar
Robert Schilling committed
128
      desc 'Update a user. Available only for admins.' do
129
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
130 131 132 133 134 135 136 137 138
      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'
        optional :name, type: String, desc: 'The name of the user'
        optional :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
139 140
      put ":id" do
        authenticated_as_admin!
141

Robert Schilling's avatar
Robert Schilling committed
142
        user = User.find_by(id: params.delete(:id))
143
        not_found!('User') unless user
144

Robert Schilling's avatar
Robert Schilling committed
145
        conflict!('Email has already been taken') if params[:email] &&
146 147
            User.where(email: params[:email])
                .where.not(id: user.id).count > 0
148

Robert Schilling's avatar
Robert Schilling committed
149
        conflict!('Username has already been taken') if params[:username] &&
150 151
            User.where(username: params[:username])
                .where.not(id: user.id).count > 0
152

153 154
        user_params = declared_params(include_missing: false)
        identity_attrs = user_params.slice(:provider, :extern_uid)
Robert Schilling's avatar
Robert Schilling committed
155

156 157
        if identity_attrs.any?
          identity = user.identities.find_by(provider: identity_attrs[:provider])
Robert Schilling's avatar
Robert Schilling committed
158

159 160 161 162 163 164 165 166
          if identity
            identity.update_attributes(identity_attrs)
          else
            identity = user.identities.build(identity_attrs)
            identity.save
          end
        end

167
        user_params[:password_expires_at] = Time.now if user_params[:password].present?
168

169
        result = ::Users::UpdateService.new(user, user_params.except(:extern_uid, :provider)).execute
170 171

        if result[:status] == :success
172
          present user, with: Entities::UserPublic
173
        else
174
          render_validation_error!(user)
175 176 177
        end
      end

Robert Schilling's avatar
Robert Schilling committed
178 179 180 181 182 183 184 185
      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
186 187
      post ":id/keys" do
        authenticated_as_admin!
188

Robert Schilling's avatar
Robert Schilling committed
189 190 191 192 193
        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
194 195 196
        if key.save
          present key, with: Entities::SSHKey
        else
197
          render_validation_error!(key)
Angus MacArthur's avatar
Angus MacArthur committed
198 199 200
        end
      end

Robert Schilling's avatar
Robert Schilling committed
201 202 203 204 205
      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'
206
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
207 208
      end
      get ':id/keys' do
209
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
210 211

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

214
        present paginate(user.keys), with: Entities::SSHKey
215 216
      end

Robert Schilling's avatar
Robert Schilling committed
217 218 219 220 221 222 223 224
      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
225
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
226 227

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

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

233
        destroy_conditionally!(key)
234 235
      end

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 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
      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
316 317 318 319 320 321 322
      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
323 324 325
      post ":id/emails" do
        authenticated_as_admin!

Robert Schilling's avatar
Robert Schilling committed
326 327 328
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

James Lopez's avatar
James Lopez committed
329
        email = Emails::CreateService.new(user, declared_params(include_missing: false)).execute
James Lopez's avatar
James Lopez committed
330 331

        if email.errors.blank?
332 333 334 335 336 337 338
          NotificationService.new.new_email(email)
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
339 340 341 342 343
      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'
344
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
345 346
      end
      get ':id/emails' do
347
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
348
        user = User.find_by(id: params[:id])
349 350
        not_found!('User') unless user

351
        present paginate(user.emails), with: Entities::Email
352 353
      end

Robert Schilling's avatar
Robert Schilling committed
354 355 356 357 358 359 360 361
      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
362
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
363
        user = User.find_by(id: params[:id])
364 365
        not_found!('User') unless user

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

369 370 371
        destroy_conditionally!(email) do |email|
          Emails::DestroyService.new(current_user, email: email.email).execute
        end
372 373

        user.update_secondary_emails!
374 375
      end

Robert Schilling's avatar
Robert Schilling committed
376 377 378 379 380
      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'
381
        optional :hard_delete, type: Boolean, desc: "Whether to remove a user's contributions"
Robert Schilling's avatar
Robert Schilling committed
382
      end
383 384
      delete ":id" do
        authenticated_as_admin!
385

skv's avatar
skv committed
386
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
387
        not_found!('User') unless user
388

389 390 391
        destroy_conditionally!(user) do
          user.delete_async(deleted_by: current_user, params: params)
        end
392
      end
393

Robert Schilling's avatar
Robert Schilling committed
394 395 396 397
      desc 'Block a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
398
      post ':id/block' do
399 400
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
401
        not_found!('User') unless user
402

Robert Schilling's avatar
Robert Schilling committed
403
        if !user.ldap_blocked?
404 405
          user.block
        else
406
          forbidden!('LDAP blocked users cannot be modified by the API')
407 408 409
        end
      end

Robert Schilling's avatar
Robert Schilling committed
410 411 412 413
      desc 'Unblock a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
414
      post ':id/unblock' do
415 416
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
417
        not_found!('User') unless user
418

Robert Schilling's avatar
Robert Schilling committed
419
        if user.ldap_blocked?
420
          forbidden!('LDAP blocked users cannot be unblocked by the API')
Gabriel Mazetto's avatar
Gabriel Mazetto committed
421 422
        else
          user.activate
423 424
        end
      end
425

426
      params do
427 428 429 430 431 432 433 434 435 436 437 438 439
        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 = {})
              user = find_user(params)
              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
440
          end
441

442 443 444
          before { authenticated_as_admin! }

          desc 'Retrieve impersonation tokens. Available only for admins.' do
445
            detail 'This feature was introduced in GitLab 9.0'
446
            success Entities::ImpersonationToken
447 448
          end
          params do
449
            use :pagination
450
            optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) impersonation_tokens'
451
          end
452
          get { present paginate(finder(declared_params(include_missing: false)).execute), with: Entities::ImpersonationToken }
453

454
          desc 'Create a impersonation token. Available only for admins.' do
455
            detail 'This feature was introduced in GitLab 9.0'
456
            success Entities::ImpersonationToken
457 458
          end
          params do
459 460 461
            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'
462 463
          end
          post do
464
            impersonation_token = finder.build(declared_params(include_missing: false))
465

466 467
            if impersonation_token.save
              present impersonation_token, with: Entities::ImpersonationToken
468
            else
469
              render_validation_error!(impersonation_token)
470 471
            end
          end
472

473
          desc 'Retrieve impersonation token. Available only for admins.' do
474
            detail 'This feature was introduced in GitLab 9.0'
475
            success Entities::ImpersonationToken
476 477
          end
          params do
478
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
479
          end
480 481
          get ':impersonation_token_id' do
            present find_impersonation_token, with: Entities::ImpersonationToken
482
          end
483

484
          desc 'Revoke a impersonation token. Available only for admins.' do
485 486 487
            detail 'This feature was introduced in GitLab 9.0'
          end
          params do
488
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
489
          end
490
          delete ':impersonation_token_id' do
491 492 493 494 495
            token = find_impersonation_token

            destroy_conditionally!(token) do
              token.revoke!
            end
496 497
          end
        end
498
      end
499 500
    end

501
    resource :user do
502 503 504 505
      before do
        authenticate!
      end

Robert Schilling's avatar
Robert Schilling committed
506
      desc 'Get the currently authenticated user' do
507
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
508
      end
509
      get do
510 511 512 513 514 515 516 517 518 519
        entity =
          if sudo?
            Entities::UserWithPrivateDetails
          elsif current_user.admin?
            Entities::UserWithAdmin
          else
            Entities::UserPublic
          end

        present current_user, with: entity
520 521
      end

Robert Schilling's avatar
Robert Schilling committed
522 523 524
      desc "Get the currently authenticated user's SSH keys" do
        success Entities::SSHKey
      end
525 526 527
      params do
        use :pagination
      end
528
      get "keys" do
529
        present paginate(current_user.keys), with: Entities::SSHKey
530 531
      end

Robert Schilling's avatar
Robert Schilling committed
532 533 534 535 536 537 538 539 540 541
      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

542 543 544
        present key, with: Entities::SSHKey
      end

Robert Schilling's avatar
Robert Schilling committed
545 546 547 548 549 550 551
      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
552
      post "keys" do
Robert Schilling's avatar
Robert Schilling committed
553
        key = current_user.keys.new(declared_params)
554

555 556 557
        if key.save
          present key, with: Entities::SSHKey
        else
558
          render_validation_error!(key)
559 560 561
        end
      end

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

572
        destroy_conditionally!(key)
573
      end
574

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 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
      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
645 646 647
      desc "Get the currently authenticated user's email addresses" do
        success Entities::Email
      end
648 649 650
      params do
        use :pagination
      end
651
      get "emails" do
652
        present paginate(current_user.emails), with: Entities::Email
653 654
      end

Robert Schilling's avatar
Robert Schilling committed
655 656 657 658 659 660 661 662 663 664
      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

665 666 667
        present email, with: Entities::Email
      end

Robert Schilling's avatar
Robert Schilling committed
668 669 670 671 672 673
      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
674
      post "emails" do
James Lopez's avatar
James Lopez committed
675
        email = Emails::CreateService.new(current_user, declared_params).execute
676

James Lopez's avatar
James Lopez committed
677
        if email.errors.blank?
678 679 680 681 682 683 684
          NotificationService.new.new_email(email)
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

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

693 694 695 696 697
        destroy_conditionally!(email) do |email|
          Emails::DestroyService.new(current_user, email: email.email).execute
        end

        current_user.update_secondary_emails!
698
      end
699 700 701

      desc 'Get a list of user activities'
      params do
702
        optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
703 704
        use :pagination
      end
705
      get "activities" do
706 707
        authenticated_as_admin!

708 709 710
        activities = User
          .where(User.arel_table[:last_activity_on].gteq(params[:from]))
          .reorder(last_activity_on: :asc)
711

712
        present paginate(activities), with: Entities::UserActivity
713
      end
714 715 716
    end
  end
end