users_spec.rb 114 KB
Newer Older
1 2
# frozen_string_literal: true

Nihad Abbasov's avatar
Nihad Abbasov committed
3 4
require 'spec_helper'

5
RSpec.describe API::Users do
6 7 8 9 10
  let_it_be(:admin) { create(:admin) }
  let_it_be(:user, reload: true) { create(:user, username: 'user.with.dot') }
  let_it_be(:key) { create(:key, user: user) }
  let_it_be(:gpg_key) { create(:gpg_key, user: user) }
  let_it_be(:email) { create(:email, user: user) }
11

12
  let(:omniauth_user) { create(:omniauth_user) }
13
  let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
14
  let(:private_user) { create(:user, private_profile: true) }
Nihad Abbasov's avatar
Nihad Abbasov committed
15

16
  context 'admin notes' do
17 18
    let_it_be(:admin) { create(:admin, note: '2019-10-06 | 2FA added | user requested | www.gitlab.com') }
    let_it_be(:user, reload: true) { create(:user, note: '2018-11-05 | 2FA removed | user requested | www.gitlab.com') }
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

    describe 'POST /users' do
      context 'when unauthenticated' do
        it 'return authentication error' do
          post api('/users')

          expect(response).to have_gitlab_http_status(:unauthorized)
        end
      end

      context 'when authenticated' do
        context 'as an admin' do
          it 'contains the note of the user' do
            optional_attributes = { note: 'Awesome Note' }
            attributes = attributes_for(:user).merge(optional_attributes)

            post api('/users', admin), params: attributes

            expect(response).to have_gitlab_http_status(:created)
            expect(json_response['note']).to eq(optional_attributes[:note])
          end
        end

        context 'as a regular user' do
          it 'does not allow creating new user' do
            post api('/users', user), params: attributes_for(:user)

            expect(response).to have_gitlab_http_status(:forbidden)
          end
        end
      end
    end

    describe 'GET /users/:id' do
      context 'when unauthenticated' do
        it 'does not contain the note of the user' do
          get api("/users/#{user.id}")

          expect(json_response).not_to have_key('note')
        end
      end

      context 'when authenticated' do
        context 'as an admin' do
          it 'contains the note of the user' do
            get api("/users/#{user.id}", admin)

            expect(json_response).to have_key('note')
            expect(json_response['note']).to eq(user.note)
68
            expect(json_response).to have_key('sign_in_count')
69 70 71 72 73 74 75 76
          end
        end

        context 'as a regular user' do
          it 'does not contain the note of the user' do
            get api("/users/#{user.id}", user)

            expect(json_response).not_to have_key('note')
77
            expect(json_response).not_to have_key('sign_in_count')
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
          end
        end
      end
    end

    describe "PUT /users/:id" do
      context 'when user is an admin' do
        it "updates note of the user" do
          new_note = '2019-07-07 | Email changed | user requested | www.gitlab.com'

          expect do
            put api("/users/#{user.id}", admin), params: { note: new_note }
          end.to change { user.reload.note }
                   .from('2018-11-05 | 2FA removed | user requested | www.gitlab.com')
                   .to(new_note)

          expect(response).to have_gitlab_http_status(:success)
          expect(json_response['note']).to eq(new_note)
        end
      end

      context 'when user is not an admin' do
        it "cannot update their own note" do
          expect do
            put api("/users/#{user.id}", user), params: { note: 'new note' }
          end.not_to change { user.reload.note }

          expect(response).to have_gitlab_http_status(:forbidden)
        end
      end
    end

    describe 'GET /users/' do
      context 'when unauthenticated' do
        it "does not contain the note of users" do
          get api("/users"), params: { username: user.username }

          expect(json_response.first).not_to have_key('note')
        end
      end

      context 'when authenticated' do
        context 'as a regular user' do
          it 'does not contain the note of users' do
            get api("/users", user), params: { username: user.username }

            expect(json_response.first).not_to have_key('note')
          end
        end

        context 'as an admin' do
          it 'contains the note of users' do
            get api("/users", admin), params: { username: user.username }

            expect(response).to have_gitlab_http_status(:success)
            expect(json_response.first).to have_key('note')
            expect(json_response.first['note']).to eq '2018-11-05 | 2FA removed | user requested | www.gitlab.com'
          end
        end
      end
    end

    describe 'GET /user' do
      context 'when authenticated' do
        context 'as an admin' do
          context 'accesses their own profile' do
            it 'contains the note of the user' do
              get api("/user", admin)

              expect(json_response).to have_key('note')
              expect(json_response['note']).to eq(admin.note)
            end
          end

          context 'sudo' do
            let(:admin_personal_access_token) { create(:personal_access_token, user: admin, scopes: %w[api sudo]).token }

            context 'accesses the profile of another regular user' do
              it 'does not contain the note of the user' do
                get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}")

                expect(json_response['id']).to eq(user.id)
                expect(json_response).not_to have_key('note')
              end
            end

            context 'accesses the profile of another admin' do
165
              let(:admin_2) { create(:admin, note: '2010-10-10 | 2FA added | admin requested | www.gitlab.com') }
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

              it 'contains the note of the user' do
                get api("/user?private_token=#{admin_personal_access_token}&sudo=#{admin_2.id}")

                expect(json_response['id']).to eq(admin_2.id)
                expect(json_response).to have_key('note')
                expect(json_response['note']).to eq(admin_2.note)
              end
            end
          end
        end

        context 'as a regular user' do
          it 'does not contain the note of the user' do
            get api("/user", user)

            expect(json_response).not_to have_key('note')
          end
        end
      end
    end
  end

189 190 191 192 193 194 195 196
  shared_examples 'rendering user status' do
    it 'returns the status if there was one' do
      create(:user_status, user: user)

      get api(path, user)

      expect(response).to have_gitlab_http_status(:success)
      expect(json_response['message']).to be_present
197
      expect(json_response['message_html']).to be_present
198 199 200 201 202 203 204 205 206 207 208 209
      expect(json_response['emoji']).to be_present
    end

    it 'returns an empty response if there was no status' do
      get api(path, user)

      expect(response).to have_gitlab_http_status(:success)
      expect(json_response['message']).to be_nil
      expect(json_response['emoji']).to be_nil
    end
  end

210
  describe 'GET /users' do
211
    context "when unauthenticated" do
212
      it "returns authorization error when the `username` parameter is not passed" do
213
        get api("/users")
214

215
        expect(response).to have_gitlab_http_status(:forbidden)
216 217 218
      end

      it "returns the user when a valid `username` parameter is passed" do
blackst0ne's avatar
blackst0ne committed
219
        get api("/users"), params: { username: user.username }
220

221
        expect(response).to match_response_schema('public_api/v4/user/basics')
222 223 224 225 226
        expect(json_response.size).to eq(1)
        expect(json_response[0]['id']).to eq(user.id)
        expect(json_response[0]['username']).to eq(user.username)
      end

227
      it "returns the user when a valid `username` parameter is passed (case insensitive)" do
blackst0ne's avatar
blackst0ne committed
228
        get api("/users"), params: { username: user.username.upcase }
229 230 231 232 233 234 235

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(json_response.size).to eq(1)
        expect(json_response[0]['id']).to eq(user.id)
        expect(json_response[0]['username']).to eq(user.username)
      end

236
      it "returns an empty response when an invalid `username` parameter is passed" do
blackst0ne's avatar
blackst0ne committed
237
        get api("/users"), params: { username: 'invalid' }
238

239
        expect(response).to have_gitlab_http_status(:ok)
240 241
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(0)
242
      end
243

244 245 246 247 248 249 250
      it "does not return the highest role" do
        get api("/users"), params: { username: user.username }

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(json_response.first.keys).not_to include 'highest_role'
      end

251 252 253 254 255 256 257 258
      it "does not return the current or last sign-in ip addresses" do
        get api("/users"), params: { username: user.username }

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(json_response.first.keys).not_to include 'current_sign_in_ip'
        expect(json_response.first.keys).not_to include 'last_sign_in_ip'
      end

259 260 261 262 263 264
      context "when public level is restricted" do
        before do
          stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
        end

        it "returns authorization error when the `username` parameter refers to an inaccessible user" do
blackst0ne's avatar
blackst0ne committed
265
          get api("/users"), params: { username: user.username }
266

267
          expect(response).to have_gitlab_http_status(:forbidden)
268 269 270 271 272
        end

        it "returns authorization error when the `username` parameter is not passed" do
          get api("/users")

273
          expect(response).to have_gitlab_http_status(:forbidden)
274 275
        end
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
276 277
    end

278
    context "when authenticated" do
279
      # These specs are written just in case API authentication is not required anymore
Felipe Artur's avatar
Felipe Artur committed
280 281 282 283 284
      context "when public level is restricted" do
        before do
          stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
        end

285
        context 'when authenticate as a regular user' do
286
          it "renders 200" do
287 288
            get api("/users", user)

289
            expect(response).to match_response_schema('public_api/v4/user/basics')
290
          end
Felipe Artur's avatar
Felipe Artur committed
291 292
        end

293 294 295 296
        context 'when authenticate as an admin' do
          it "renders 200" do
            get api("/users", admin)

297
            expect(response).to match_response_schema('public_api/v4/user/basics')
298
          end
Felipe Artur's avatar
Felipe Artur committed
299 300 301
        end
      end

302
      it "returns an array of users" do
Robert Speicher's avatar
Robert Speicher committed
303
        get api("/users", user)
304

305
        expect(response).to match_response_schema('public_api/v4/user/basics')
306
        expect(response).to include_pagination_headers
Marin Jankovski's avatar
Marin Jankovski committed
307
        username = user.username
308 309 310
        expect(json_response.detect do |user|
          user['username'] == username
        end['username']).to eq(username)
Nihad Abbasov's avatar
Nihad Abbasov committed
311
      end
312

313 314 315 316 317 318
      it "returns an array of blocked users" do
        ldap_blocked_user
        create(:user, state: 'blocked')

        get api("/users?blocked=true", user)

319
        expect(response).to match_response_schema('public_api/v4/user/basics')
320
        expect(response).to include_pagination_headers
321 322 323
        expect(json_response).to all(include('state' => /(blocked|ldap_blocked)/))
      end

324 325 326 327 328 329 330 331 332 333 334 335
      it "returns an array of external users" do
        create(:user)
        external_user = create(:user, external: true)

        get api("/users?external=true", user)

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(response).to include_pagination_headers
        expect(json_response.size).to eq(1)
        expect(json_response[0]['id']).to eq(external_user.id)
      end

336
      it "returns one user" do
337
        get api("/users?username=#{omniauth_user.username}", user)
338

339
        expect(response).to match_response_schema('public_api/v4/user/basics')
340
        expect(response).to include_pagination_headers
341 342
        expect(json_response.first['username']).to eq(omniauth_user.username)
      end
343

344 345 346 347 348 349 350 351
      it "returns one user (case insensitive)" do
        get api("/users?username=#{omniauth_user.username.upcase}", user)

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(response).to include_pagination_headers
        expect(json_response.first['username']).to eq(omniauth_user.username)
      end

352 353 354
      it "returns a 403 when non-admin user searches by external UID" do
        get api("/users?extern_uid=#{omniauth_user.identities.first.extern_uid}&provider=#{omniauth_user.identities.first.provider}", user)

355
        expect(response).to have_gitlab_http_status(:forbidden)
356
      end
357 358 359 360

      it 'does not reveal the `is_admin` flag of the user' do
        get api('/users', user)

361
        expect(response).to match_response_schema('public_api/v4/user/basics')
362 363
        expect(json_response.first.keys).not_to include 'is_admin'
      end
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

      context 'exclude_internal param' do
        let_it_be(:internal_user) { User.alert_bot }

        it 'returns all users when it is not set' do
          get api("/users?exclude_internal=false", user)

          expect(response).to match_response_schema('public_api/v4/user/basics')
          expect(response).to include_pagination_headers
          expect(json_response.map { |u| u['id'] }).to include(internal_user.id)
        end

        it 'returns all non internal users when it is set' do
          get api("/users?exclude_internal=true", user)

          expect(response).to match_response_schema('public_api/v4/user/basics')
          expect(response).to include_pagination_headers
          expect(json_response.map { |u| u['id'] }).not_to include(internal_user.id)
        end
      end
Ethan Urie's avatar
Ethan Urie committed
384 385 386 387 388 389 390 391 392 393

      context 'admins param' do
        it 'returns all users' do
          get api("/users?admins=true", user)

          expect(response).to match_response_schema('public_api/v4/user/basics')
          expect(json_response.size).to eq(2)
          expect(json_response.map { |u| u['id'] }).to include(user.id, admin.id)
        end
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
394
    end
395 396

    context "when admin" do
397 398
      context 'when sudo is defined' do
        it 'does not return 500' do
Douwe Maan's avatar
Douwe Maan committed
399 400
          admin_personal_access_token = create(:personal_access_token, user: admin, scopes: [:sudo])
          get api("/users?sudo=#{user.id}", admin, personal_access_token: admin_personal_access_token)
401

402
          expect(response).to have_gitlab_http_status(:success)
403 404 405
        end
      end

406
      it "returns an array of users" do
407
        get api("/users", admin)
408

409
        expect(response).to match_response_schema('public_api/v4/user/admins')
410
        expect(response).to include_pagination_headers
411
      end
412 413 414 415 416 417

      it "returns an array of external users" do
        create(:user, external: true)

        get api("/users?external=true", admin)

418
        expect(response).to match_response_schema('public_api/v4/user/admins')
419
        expect(response).to include_pagination_headers
420 421
        expect(json_response).to all(include('external' => true))
      end
422 423 424 425

      it "returns one user by external UID" do
        get api("/users?extern_uid=#{omniauth_user.identities.first.extern_uid}&provider=#{omniauth_user.identities.first.provider}", admin)

426
        expect(response).to match_response_schema('public_api/v4/user/admins')
427 428 429 430 431 432 433
        expect(json_response.size).to eq(1)
        expect(json_response.first['username']).to eq(omniauth_user.username)
      end

      it "returns 400 error if provider with no extern_uid" do
        get api("/users?extern_uid=#{omniauth_user.identities.first.extern_uid}", admin)

434
        expect(response).to have_gitlab_http_status(:bad_request)
435 436 437 438 439
      end

      it "returns 400 error if provider with no extern_uid" do
        get api("/users?provider=#{omniauth_user.identities.first.provider}", admin)

440
        expect(response).to have_gitlab_http_status(:bad_request)
441
      end
442

James Lopez's avatar
James Lopez committed
443
      it "returns a user created before a specific date" do
James Lopez's avatar
James Lopez committed
444
        user = create(:user, created_at: Date.new(2000, 1, 1))
445 446 447

        get api("/users?created_before=2000-01-02T00:00:00.060Z", admin)

448
        expect(response).to match_response_schema('public_api/v4/user/admins')
449 450 451 452 453
        expect(json_response.size).to eq(1)
        expect(json_response.first['username']).to eq(user.username)
      end

      it "returns no users created before a specific date" do
James Lopez's avatar
James Lopez committed
454
        create(:user, created_at: Date.new(2001, 1, 1))
455 456 457

        get api("/users?created_before=2000-01-02T00:00:00.060Z", admin)

458
        expect(response).to match_response_schema('public_api/v4/user/admins')
459 460 461 462
        expect(json_response.size).to eq(0)
      end

      it "returns users created before and after a specific date" do
James Lopez's avatar
James Lopez committed
463
        user = create(:user, created_at: Date.new(2001, 1, 1))
464 465 466

        get api("/users?created_before=2001-01-02T00:00:00.060Z&created_after=1999-01-02T00:00:00.060", admin)

467
        expect(response).to match_response_schema('public_api/v4/user/admins')
468 469 470
        expect(json_response.size).to eq(1)
        expect(json_response.first['username']).to eq(user.username)
      end
471 472

      it 'returns the correct order when sorted by id' do
473 474 475
        # order of let_it_be definitions:
        # - admin
        # - user
476

blackst0ne's avatar
blackst0ne committed
477
        get api('/users', admin), params: { order_by: 'id', sort: 'asc' }
478 479 480 481 482 483 484

        expect(response).to match_response_schema('public_api/v4/user/admins')
        expect(json_response.size).to eq(2)
        expect(json_response.first['id']).to eq(admin.id)
        expect(json_response.last['id']).to eq(user.id)
      end

485 486 487
      it 'returns users with 2fa enabled' do
        user_with_2fa = create(:user, :two_factor_via_otp)

blackst0ne's avatar
blackst0ne committed
488
        get api('/users', admin), params: { two_factor: 'enabled' }
489 490 491 492 493 494

        expect(response).to match_response_schema('public_api/v4/user/admins')
        expect(json_response.size).to eq(1)
        expect(json_response.first['id']).to eq(user_with_2fa.id)
      end

Blair Lunceford's avatar
Blair Lunceford committed
495 496
      it "returns users without projects" do
        user_without_projects = create(:user)
Blair Lunceford's avatar
Blair Lunceford committed
497 498
        create(:project, namespace: user.namespace)
        create(:project, namespace: admin.namespace)
Blair Lunceford's avatar
Blair Lunceford committed
499 500 501 502 503 504 505 506

        get api('/users', admin), params: { without_projects: true }

        expect(response).to match_response_schema('public_api/v4/user/admins')
        expect(json_response.size).to eq(1)
        expect(json_response.first['id']).to eq(user_without_projects.id)
      end

507
      it 'returns 400 when provided incorrect sort params' do
blackst0ne's avatar
blackst0ne committed
508
        get api('/users', admin), params: { order_by: 'magic', sort: 'asc' }
509

510
        expect(response).to have_gitlab_http_status(:bad_request)
511
      end
512
    end
Ethan Urie's avatar
Ethan Urie committed
513 514 515 516 517 518 519 520 521 522

    context 'admins param' do
      it 'returns only admins' do
        get api("/users?admins=true", admin)

        expect(response).to match_response_schema('public_api/v4/user/basics')
        expect(json_response.size).to eq(1)
        expect(json_response.first['id']).to eq(admin.id)
      end
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
523 524 525
  end

  describe "GET /users/:id" do
526
    it "returns a user by id" do
Robert Speicher's avatar
Robert Speicher committed
527
      get api("/users/#{user.id}", user)
528

529
      expect(response).to match_response_schema('public_api/v4/user/basic')
530
      expect(json_response['username']).to eq(user.username)
Nihad Abbasov's avatar
Nihad Abbasov committed
531 532
    end

533 534 535
    it "does not return the user's `is_admin` flag" do
      get api("/users/#{user.id}", user)

536 537
      expect(response).to match_response_schema('public_api/v4/user/basic')
      expect(json_response.keys).not_to include 'is_admin'
538 539
    end

540 541 542 543 544 545 546
    it "does not return the user's `highest_role`" do
      get api("/users/#{user.id}", user)

      expect(response).to match_response_schema('public_api/v4/user/basic')
      expect(json_response.keys).not_to include 'highest_role'
    end

547 548 549 550 551 552 553 554
    it "does not return the user's sign in IPs" do
      get api("/users/#{user.id}", user)

      expect(response).to match_response_schema('public_api/v4/user/basic')
      expect(json_response.keys).not_to include 'current_sign_in_ip'
      expect(json_response.keys).not_to include 'last_sign_in_ip'
    end

555 556 557 558 559 560 561 562
    it "does not contain plan or trial data" do
      get api("/users/#{user.id}", user)

      expect(response).to match_response_schema('public_api/v4/user/basic')
      expect(json_response.keys).not_to include 'plan'
      expect(json_response.keys).not_to include 'trial'
    end

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    context 'when job title is present' do
      let(:job_title) { 'Fullstack Engineer' }

      before do
        create(:user_detail, user: user, job_title: job_title)
      end

      it 'returns job title of a user' do
        get api("/users/#{user.id}", user)

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response['job_title']).to eq(job_title)
      end
    end

578 579 580 581
    context 'when authenticated as admin' do
      it 'includes the `is_admin` field' do
        get api("/users/#{user.id}", admin)

582
        expect(response).to match_response_schema('public_api/v4/user/admin')
583 584
        expect(json_response['is_admin']).to be(false)
      end
585 586 587 588 589 590 591

      it "includes the `created_at` field for private users" do
        get api("/users/#{private_user.id}", admin)

        expect(response).to match_response_schema('public_api/v4/user/admin')
        expect(json_response.keys).to include 'created_at'
      end
592

593 594 595 596 597 598
      it 'includes the `highest_role` field' do
        get api("/users/#{user.id}", admin)

        expect(response).to match_response_schema('public_api/v4/user/admin')
        expect(json_response['highest_role']).to be(0)
      end
599

600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
      if Gitlab.ee?
        it 'does not include values for plan or trial' do
          get api("/users/#{user.id}", admin)

          expect(response).to match_response_schema('public_api/v4/user/basic')
        end
      else
        it 'does not include plan or trial data' do
          get api("/users/#{user.id}", admin)

          expect(response).to match_response_schema('public_api/v4/user/basic')
          expect(json_response.keys).not_to include 'plan'
          expect(json_response.keys).not_to include 'trial'
        end
      end

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
      context 'when user has not logged in' do
        it 'does not include the sign in IPs' do
          get api("/users/#{user.id}", admin)

          expect(response).to match_response_schema('public_api/v4/user/admin')
          expect(json_response).to include('current_sign_in_ip' => nil, 'last_sign_in_ip' => nil)
        end
      end

      context 'when user has logged in' do
        let_it_be(:signed_in_user) { create(:user, :with_sign_ins) }

        it 'includes the sign in IPs' do
          get api("/users/#{signed_in_user.id}", admin)

          expect(response).to match_response_schema('public_api/v4/user/admin')
          expect(json_response['current_sign_in_ip']).to eq('127.0.0.1')
          expect(json_response['last_sign_in_ip']).to eq('127.0.0.1')
        end
      end
636 637
    end

638 639 640 641
    context 'for an anonymous user' do
      it "returns a user by id" do
        get api("/users/#{user.id}")

642
        expect(response).to match_response_schema('public_api/v4/user/basic')
643 644 645 646 647 648 649 650 651
        expect(json_response['username']).to eq(user.username)
      end

      it "returns a 404 if the target user is present but inaccessible" do
        allow(Ability).to receive(:allowed?).and_call_original
        allow(Ability).to receive(:allowed?).with(nil, :read_user, user).and_return(false)

        get api("/users/#{user.id}")

652
        expect(response).to have_gitlab_http_status(:not_found)
653
      end
654 655 656 657 658 659 660 661 662 663 664 665 666 667

      it "returns the `created_at` field for public users" do
        get api("/users/#{user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).to include 'created_at'
      end

      it "does not return the `created_at` field for private users" do
        get api("/users/#{private_user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).not_to include 'created_at'
      end
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695

      it "returns the `followers` field for public users" do
        get api("/users/#{user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).to include 'followers'
      end

      it "does not return the `followers` field for private users" do
        get api("/users/#{private_user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).not_to include 'followers'
      end

      it "returns the `following` field for public users" do
        get api("/users/#{user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).to include 'following'
      end

      it "does not return the `following` field for private users" do
        get api("/users/#{private_user.id}")

        expect(response).to match_response_schema('public_api/v4/user/basic')
        expect(json_response.keys).not_to include 'following'
      end
696
    end
697

698
    it "returns a 404 error if user id not found" do
699
      get api("/users/0", user)
700

701
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
702
      expect(json_response['message']).to eq('404 User Not Found')
703
    end
704

705
    it "returns a 404 for invalid ID" do
706
      get api("/users/1ASDF", user)
707

708
      expect(response).to have_gitlab_http_status(:not_found)
709
    end
710 711
  end

712 713 714 715 716 717 718 719 720 721 722 723
  describe 'GET /users/:id_or_username/status' do
    context 'when finding the user by id' do
      it_behaves_like 'rendering user status' do
        let(:path) { "/users/#{user.id}/status" }
      end
    end

    context 'when finding the user by username' do
      it_behaves_like 'rendering user status' do
        let(:path) { "/users/#{user.username}/status" }
      end
    end
724 725 726 727 728 729

    context 'when finding the user by username (case insensitive)' do
      it_behaves_like 'rendering user status' do
        let(:path) { "/users/#{user.username.upcase}/status" }
      end
    end
730 731
  end

732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
  describe 'POST /users/:id/follow' do
    let(:followee) { create(:user) }

    context 'on an unfollowed user' do
      it 'follows the user' do
        post api("/users/#{followee.id}/follow", user)

        expect(user.followees).to contain_exactly(followee)
        expect(response).to have_gitlab_http_status(:created)
      end
    end

    context 'on a followed user' do
      before do
        user.follow(followee)
      end

      it 'does not change following' do
        post api("/users/#{followee.id}/follow", user)

        expect(user.followees).to contain_exactly(followee)
        expect(response).to have_gitlab_http_status(:not_modified)
      end
    end
  end

  describe 'POST /users/:id/unfollow' do
    let(:followee) { create(:user) }

    context 'on a followed user' do
      before do
        user.follow(followee)
      end

      it 'unfollow the user' do
        post api("/users/#{followee.id}/unfollow", user)

        expect(user.followees).to be_empty
        expect(response).to have_gitlab_http_status(:created)
      end
    end

    context 'on an unfollowed user' do
      it 'does not change following' do
        post api("/users/#{followee.id}/unfollow", user)

        expect(user.followees).to be_empty
        expect(response).to have_gitlab_http_status(:not_modified)
      end
    end
  end

  describe 'GET /users/:id/followers' do
    let(:follower) { create(:user) }

    context 'user has followers' do
      it 'lists followers' do
        follower.follow(user)

        get api("/users/#{user.id}/followers", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
      end

      it 'do not lists followers if profile is private' do
        follower.follow(private_user)

        get api("/users/#{private_user.id}/followers", user)

        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 User Not Found')
      end
    end

    context 'user does not have any follower' do
      it 'does list nothing' do
        get api("/users/#{user.id}/followers", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to include_pagination_headers
        expect(json_response).to be_empty
      end
    end
  end

  describe 'GET /users/:id/following' do
    let(:followee) { create(:user) }

    context 'user has followers' do
      it 'lists following user' do
        user.follow(followee)

        get api("/users/#{user.id}/following", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
      end

      it 'do not lists following user if profile is private' do
        user.follow(private_user)

        get api("/users/#{private_user.id}/following", user)

        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 User Not Found')
      end
    end

    context 'user does not have any follower' do
      it 'does list nothing' do
        get api("/users/#{user.id}/following", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to include_pagination_headers
        expect(json_response).to be_empty
      end
    end
  end

854
  describe "POST /users" do
855
    it "creates user" do
856
      expect do
blackst0ne's avatar
blackst0ne committed
857
        post api("/users", admin), params: attributes_for(:user, projects_limit: 3)
858
      end.to change { User.count }.by(1)
859 860
    end

861
    it "creates user with correct attributes" do
blackst0ne's avatar
blackst0ne committed
862
      post api('/users', admin), params: attributes_for(:user, admin: true, can_create_group: true)
863
      expect(response).to have_gitlab_http_status(:created)
864 865
      user_id = json_response['id']
      new_user = User.find(user_id)
866 867
      expect(new_user.admin).to eq(true)
      expect(new_user.can_create_group).to eq(true)
868 869
    end

870
    it "creates user with optional attributes" do
871
      optional_attributes = { confirm: true, theme_id: 2, color_scheme_id: 4 }
872 873
      attributes = attributes_for(:user).merge(optional_attributes)

blackst0ne's avatar
blackst0ne committed
874
      post api('/users', admin), params: attributes
875

876
      expect(response).to have_gitlab_http_status(:created)
877 878
    end

879
    it "creates non-admin user" do
blackst0ne's avatar
blackst0ne committed
880
      post api('/users', admin), params: attributes_for(:user, admin: false, can_create_group: false)
881
      expect(response).to have_gitlab_http_status(:created)
882 883
      user_id = json_response['id']
      new_user = User.find(user_id)
884 885
      expect(new_user.admin).to eq(false)
      expect(new_user.can_create_group).to eq(false)
886 887
    end

888
    it "creates non-admin users by default" do
blackst0ne's avatar
blackst0ne committed
889
      post api('/users', admin), params: attributes_for(:user)
890
      expect(response).to have_gitlab_http_status(:created)
891 892
      user_id = json_response['id']
      new_user = User.find(user_id)
893
      expect(new_user.admin).to eq(false)
894 895
    end

896
    it "returns 201 Created on success" do
blackst0ne's avatar
blackst0ne committed
897
      post api("/users", admin), params: attributes_for(:user, projects_limit: 3)
898
      expect(response).to match_response_schema('public_api/v4/user/admin')
899
      expect(response).to have_gitlab_http_status(:created)
900 901
    end

Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg committed
902
    it 'creates non-external users by default' do
blackst0ne's avatar
blackst0ne committed
903
      post api("/users", admin), params: attributes_for(:user)
904
      expect(response).to have_gitlab_http_status(:created)
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg committed
905 906 907 908 909 910

      user_id = json_response['id']
      new_user = User.find(user_id)
      expect(new_user.external).to be_falsy
    end

911
    it 'allows an external user to be created' do
blackst0ne's avatar
blackst0ne committed
912
      post api("/users", admin), params: attributes_for(:user, external: true)
913
      expect(response).to have_gitlab_http_status(:created)
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg committed
914 915 916 917 918 919

      user_id = json_response['id']
      new_user = User.find(user_id)
      expect(new_user.external).to be_truthy
    end

920
    it "creates user with reset password" do
blackst0ne's avatar
blackst0ne committed
921
      post api('/users', admin), params: attributes_for(:user, reset_password: true).except(:password)
922

923
      expect(response).to have_gitlab_http_status(:created)
924 925 926 927

      user_id = json_response['id']
      new_user = User.find(user_id)

928 929 930 931
      expect(new_user.recently_sent_password_reset?).to eq(true)
    end

    it "creates user with random password" do
932 933
      params = attributes_for(:user, force_random_password: true)
      params.delete(:password)
934 935
      post api('/users', admin), params: params

936
      expect(response).to have_gitlab_http_status(:created)
937 938 939 940

      user_id = json_response['id']
      new_user = User.find(user_id)

941
      expect(new_user.encrypted_password).to be_present
942 943
    end

944
    it "creates user with private profile" do
blackst0ne's avatar
blackst0ne committed
945
      post api('/users', admin), params: attributes_for(:user, private_profile: true)
946

947
      expect(response).to have_gitlab_http_status(:created)
948 949 950 951 952 953 954 955

      user_id = json_response['id']
      new_user = User.find(user_id)

      expect(new_user).not_to eq(nil)
      expect(new_user.private_profile?).to eq(true)
    end

956 957 958 959 960 961 962 963 964 965 966 967
    it "creates user with view_diffs_file_by_file" do
      post api('/users', admin), params: attributes_for(:user, view_diffs_file_by_file: true)

      expect(response).to have_gitlab_http_status(:created)

      user_id = json_response['id']
      new_user = User.find(user_id)

      expect(new_user).not_to eq(nil)
      expect(new_user.user_preference.view_diffs_file_by_file?).to eq(true)
    end

968
    it "does not create user with invalid email" do
969
      post api('/users', admin),
970 971 972 973 974
        params: {
          email: 'invalid email',
          password: 'password',
          name: 'test'
        }
975
      expect(response).to have_gitlab_http_status(:bad_request)
976 977
    end

978
    it 'returns 400 error if name not given' do
blackst0ne's avatar
blackst0ne committed
979
      post api('/users', admin), params: attributes_for(:user).except(:name)
980
      expect(response).to have_gitlab_http_status(:bad_request)
981 982
    end

983
    it 'returns 400 error if password not given' do
blackst0ne's avatar
blackst0ne committed
984
      post api('/users', admin), params: attributes_for(:user).except(:password)
985
      expect(response).to have_gitlab_http_status(:bad_request)
986 987
    end

988
    it 'returns 400 error if email not given' do
blackst0ne's avatar
blackst0ne committed
989
      post api('/users', admin), params: attributes_for(:user).except(:email)
990
      expect(response).to have_gitlab_http_status(:bad_request)
991 992
    end

993
    it 'returns 400 error if username not given' do
blackst0ne's avatar
blackst0ne committed
994
      post api('/users', admin), params: attributes_for(:user).except(:username)
995
      expect(response).to have_gitlab_http_status(:bad_request)
996 997
    end

998 999 1000 1001 1002 1003
    it "doesn't create user with invalid optional attributes" do
      optional_attributes = { theme_id: 50, color_scheme_id: 50 }
      attributes = attributes_for(:user).merge(optional_attributes)

      post api('/users', admin), params: attributes

1004
      expect(response).to have_gitlab_http_status(:bad_request)
1005 1006
    end

1007
    it 'returns 400 error if user does not validate' do
1008
      post api('/users', admin),
1009 1010 1011 1012 1013 1014 1015 1016
        params: {
          password: 'pass',
          email: 'test@example.com',
          username: 'test!',
          name: 'test',
          bio: 'g' * 256,
          projects_limit: -1
        }
1017
      expect(response).to have_gitlab_http_status(:bad_request)
1018 1019 1020 1021 1022 1023 1024 1025
      expect(json_response['message']['password'])
        .to eq(['is too short (minimum is 8 characters)'])
      expect(json_response['message']['bio'])
        .to eq(['is too long (maximum is 255 characters)'])
      expect(json_response['message']['projects_limit'])
        .to eq(['must be greater than or equal to 0'])
      expect(json_response['message']['username'])
        .to eq([Gitlab::PathRegex.namespace_format_message])
1026 1027
    end

1028
    it "is not available for non admin users" do
blackst0ne's avatar
blackst0ne committed
1029
      post api("/users", user), params: attributes_for(:user)
1030
      expect(response).to have_gitlab_http_status(:forbidden)
1031
    end
1032

1033 1034 1035
    context 'with existing user' do
      before do
        post api('/users', admin),
1036 1037 1038 1039 1040 1041
          params: {
            email: 'test@example.com',
            password: 'password',
            username: 'test',
            name: 'foo'
          }
1042
      end
1043

1044
      it 'returns 409 conflict error if user with same email exists' do
1045
        expect do
1046
          post api('/users', admin),
1047 1048 1049 1050 1051 1052
            params: {
              name: 'foo',
              email: 'test@example.com',
              password: 'password',
              username: 'foo'
            }
1053
        end.to change { User.count }.by(0)
1054
        expect(response).to have_gitlab_http_status(:conflict)
1055
        expect(json_response['message']).to eq('Email has already been taken')
1056 1057
      end

1058
      it 'returns 409 conflict error if same username exists' do
1059 1060
        expect do
          post api('/users', admin),
1061 1062 1063 1064 1065 1066
            params: {
              name: 'foo',
              email: 'foo@example.com',
              password: 'password',
              username: 'test'
            }
1067
        end.to change { User.count }.by(0)
1068
        expect(response).to have_gitlab_http_status(:conflict)
1069
        expect(json_response['message']).to eq('Username has already been taken')
1070
      end
1071

1072 1073 1074
      it 'returns 409 conflict error if same username exists (case insensitive)' do
        expect do
          post api('/users', admin),
1075 1076 1077 1078 1079 1080
            params: {
              name: 'foo',
              email: 'foo@example.com',
              password: 'password',
              username: 'TEST'
            }
1081
        end.to change { User.count }.by(0)
1082
        expect(response).to have_gitlab_http_status(:conflict)
1083 1084 1085
        expect(json_response['message']).to eq('Username has already been taken')
      end

1086
      it 'creates user with new identity' do
blackst0ne's avatar
blackst0ne committed
1087
        post api("/users", admin), params: attributes_for(:user, provider: 'github', extern_uid: '67890')
1088

1089
        expect(response).to have_gitlab_http_status(:created)
1090 1091 1092
        expect(json_response['identities'].first['extern_uid']).to eq('67890')
        expect(json_response['identities'].first['provider']).to eq('github')
      end
1093
    end
1094 1095 1096 1097 1098 1099 1100 1101

    context "scopes" do
      let(:user) { admin }
      let(:path) { '/users' }
      let(:api_call) { method(:api) }

      include_examples 'does not allow the "read_user" scope'
    end
1102 1103
  end

1104
  describe "PUT /users/:id" do
1105 1106 1107 1108
    it "returns 200 OK on success" do
      put api("/users/#{user.id}", admin), params: { bio: 'new test bio' }

      expect(response).to match_response_schema('public_api/v4/user/admin')
1109
      expect(response).to have_gitlab_http_status(:ok)
1110 1111
    end

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
    context 'updating password' do
      def update_password(user, admin, password = User.random_password)
        put api("/users/#{user.id}", admin), params: { password: password }
      end

      context 'admin updates their own password' do
        it 'does not force reset on next login' do
          update_password(admin, admin)

          expect(response).to have_gitlab_http_status(:ok)
          expect(user.reload.password_expired?).to eq(false)
        end

        it 'does not enqueue the `admin changed your password` email' do
          expect { update_password(admin, admin) }
            .not_to have_enqueued_mail(DeviseMailer, :password_change_by_admin)
        end

        it 'enqueues the `password changed` email' do
          expect { update_password(admin, admin) }
            .to have_enqueued_mail(DeviseMailer, :password_change)
        end
      end

      context 'admin updates the password of another user' do
        it 'forces reset on next login' do
          update_password(user, admin)

          expect(response).to have_gitlab_http_status(:ok)
          expect(user.reload.password_expired?).to eq(true)
        end

        it 'enqueues the `admin changed your password` email' do
          expect { update_password(user, admin) }
            .to have_enqueued_mail(DeviseMailer, :password_change_by_admin)
        end

        it 'does not enqueue the `password changed` email' do
          expect { update_password(user, admin) }
            .not_to have_enqueued_mail(DeviseMailer, :password_change)
        end
      end
    end

1156
    it "updates user with new bio" do
blackst0ne's avatar
blackst0ne committed
1157
      put api("/users/#{user.id}", admin), params: { bio: 'new test bio' }
1158

1159
      expect(response).to have_gitlab_http_status(:ok)
1160 1161
      expect(json_response['bio']).to eq('new test bio')
      expect(user.reload.bio).to eq('new test bio')
1162 1163
    end

1164
    it "updates user with empty bio" do
1165
      user.update!(bio: 'previous bio')
1166 1167 1168 1169 1170 1171 1172 1173

      put api("/users/#{user.id}", admin), params: { bio: '' }

      expect(response).to have_gitlab_http_status(:ok)
      expect(json_response['bio']).to eq('')
      expect(user.reload.bio).to eq('')
    end

1174 1175 1176 1177 1178 1179 1180 1181
    it 'updates user with nil bio' do
      put api("/users/#{user.id}", admin), params: { bio: nil }

      expect(response).to have_gitlab_http_status(:ok)
      expect(json_response['bio']).to eq('')
      expect(user.reload.bio).to eq('')
    end

1182
    it "updates user with organization" do
blackst0ne's avatar
blackst0ne committed
1183
      put api("/users/#{user.id}", admin), params: { organization: 'GitLab' }
1184

1185
      expect(response).to have_gitlab_http_status(:ok)
1186 1187 1188 1189
      expect(json_response['organization']).to eq('GitLab')
      expect(user.reload.organization).to eq('GitLab')
    end

1190
    it 'updates user with avatar' do
blackst0ne's avatar
blackst0ne committed
1191
      put api("/users/#{user.id}", admin), params: { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
1192 1193 1194 1195

      user.reload

      expect(user.avatar).to be_present
1196
      expect(response).to have_gitlab_http_status(:ok)
1197 1198 1199
      expect(json_response['avatar_url']).to include(user.avatar_path)
    end

James Lopez's avatar
James Lopez committed
1200
    it 'updates user with a new email' do
1201 1202
      old_email = user.email
      old_notification_email = user.notification_email
blackst0ne's avatar
blackst0ne committed
1203
      put api("/users/#{user.id}", admin), params: { email: 'new@email.com' }
1204

1205 1206
      user.reload

1207
      expect(response).to have_gitlab_http_status(:ok)
1208 1209 1210 1211
      expect(user).to be_confirmed
      expect(user.email).to eq(old_email)
      expect(user.notification_email).to eq(old_notification_email)
      expect(user.unconfirmed_email).to eq('new@email.com')
James Lopez's avatar
James Lopez committed
1212 1213
    end

Daniel Juarez's avatar
Daniel Juarez committed
1214
    it 'skips reconfirmation when requested' do
blackst0ne's avatar
blackst0ne committed
1215
      put api("/users/#{user.id}", admin), params: { email: 'new@email.com', skip_reconfirmation: true }
Daniel Juarez's avatar
Daniel Juarez committed
1216 1217 1218

      user.reload

1219
      expect(response).to have_gitlab_http_status(:ok)
1220 1221
      expect(user).to be_confirmed
      expect(user.email).to eq('new@email.com')
Daniel Juarez's avatar
Daniel Juarez committed
1222 1223
    end

1224
    it 'updates user with their own username' do
blackst0ne's avatar
blackst0ne committed
1225
      put api("/users/#{user.id}", admin), params: { username: user.username }
1226

1227
      expect(response).to have_gitlab_http_status(:ok)
1228 1229
      expect(json_response['username']).to eq(user.username)
      expect(user.reload.username).to eq(user.username)
1230 1231
    end

1232
    it "updates user's existing identity" do
blackst0ne's avatar
blackst0ne committed
1233
      put api("/users/#{omniauth_user.id}", admin), params: { provider: 'ldapmain', extern_uid: '654321' }
1234

1235
      expect(response).to have_gitlab_http_status(:ok)
1236 1237 1238
      expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
    end

1239
    it 'updates user with new identity' do
blackst0ne's avatar
blackst0ne committed
1240
      put api("/users/#{user.id}", admin), params: { provider: 'github', extern_uid: 'john' }
1241

1242
      expect(response).to have_gitlab_http_status(:ok)
1243
      expect(user.reload.identities.first.extern_uid).to eq('john')
1244 1245 1246
      expect(user.reload.identities.first.provider).to eq('github')
    end

1247
    it "updates admin status" do
blackst0ne's avatar
blackst0ne committed
1248
      put api("/users/#{user.id}", admin), params: { admin: true }
1249

1250
      expect(response).to have_gitlab_http_status(:ok)
1251
      expect(user.reload.admin).to eq(true)
1252 1253
    end

1254
    it "updates external status" do
blackst0ne's avatar
blackst0ne committed
1255
      put api("/users/#{user.id}", admin), params: { external: true }
1256

1257
      expect(response).to have_gitlab_http_status(:ok)
1258 1259 1260 1261
      expect(json_response['external']).to eq(true)
      expect(user.reload.external?).to be_truthy
    end

1262 1263 1264 1265 1266 1267
    it "private profile is false by default" do
      put api("/users/#{user.id}", admin), params: {}

      expect(user.reload.private_profile).to eq(false)
    end

1268 1269 1270 1271 1272 1273 1274
    it "does have default values for theme and color-scheme ID" do
      put api("/users/#{user.id}", admin), params: {}

      expect(user.reload.theme_id).to eq(Gitlab::Themes.default.id)
      expect(user.reload.color_scheme_id).to eq(Gitlab::ColorSchemes.default.id)
    end

1275
    it "updates private profile" do
blackst0ne's avatar
blackst0ne committed
1276
      put api("/users/#{user.id}", admin), params: { private_profile: true }
1277

1278
      expect(response).to have_gitlab_http_status(:ok)
1279 1280 1281
      expect(user.reload.private_profile).to eq(true)
    end

1282 1283 1284 1285 1286 1287 1288
    it "updates viewing diffs file by file" do
      put api("/users/#{user.id}", admin), params: { view_diffs_file_by_file: true }

      expect(response).to have_gitlab_http_status(:ok)
      expect(user.reload.user_preference.view_diffs_file_by_file?).to eq(true)
    end

1289
    it "updates private profile to false when nil is given" do
1290
      user.update!(private_profile: true)
1291 1292 1293

      put api("/users/#{user.id}", admin), params: { private_profile: nil }

1294
      expect(response).to have_gitlab_http_status(:ok)
1295 1296 1297
      expect(user.reload.private_profile).to eq(false)
    end

1298
    it "does not modify private profile when field is not provided" do
1299
      user.update!(private_profile: true)
1300 1301 1302

      put api("/users/#{user.id}", admin), params: {}

1303
      expect(response).to have_gitlab_http_status(:ok)
1304 1305 1306
      expect(user.reload.private_profile).to eq(true)
    end

1307 1308 1309 1310
    it "does not modify theme or color-scheme ID when field is not provided" do
      theme = Gitlab::Themes.each.find { |t| t.id != Gitlab::Themes.default.id }
      scheme = Gitlab::ColorSchemes.each.find { |t| t.id != Gitlab::ColorSchemes.default.id }

1311
      user.update!(theme_id: theme.id, color_scheme_id: scheme.id)
1312 1313 1314 1315 1316 1317 1318 1319

      put api("/users/#{user.id}", admin), params: {}

      expect(response).to have_gitlab_http_status(:ok)
      expect(user.reload.theme_id).to eq(theme.id)
      expect(user.reload.color_scheme_id).to eq(scheme.id)
    end

1320
    it "does not update admin status" do
1321 1322
      admin_user = create(:admin)

blackst0ne's avatar
blackst0ne committed
1323
      put api("/users/#{admin_user.id}", admin), params: { can_create_group: false }
1324

1325
      expect(response).to have_gitlab_http_status(:ok)
1326 1327
      expect(admin_user.reload.admin).to eq(true)
      expect(admin_user.can_create_group).to eq(false)
1328 1329
    end

1330
    it "does not allow invalid update" do
blackst0ne's avatar
blackst0ne committed
1331
      put api("/users/#{user.id}", admin), params: { email: 'invalid email' }
1332

1333
      expect(response).to have_gitlab_http_status(:bad_request)
1334
      expect(user.reload.email).not_to eq('invalid email')
1335 1336
    end

1337 1338 1339
    it "updates theme id" do
      put api("/users/#{user.id}", admin), params: { theme_id: 5 }

1340
      expect(response).to have_gitlab_http_status(:ok)
1341 1342 1343 1344 1345 1346
      expect(user.reload.theme_id).to eq(5)
    end

    it "does not update invalid theme id" do
      put api("/users/#{user.id}", admin), params: { theme_id: 50 }

1347
      expect(response).to have_gitlab_http_status(:bad_request)
1348 1349 1350 1351 1352 1353
      expect(user.reload.theme_id).not_to eq(50)
    end

    it "updates color scheme id" do
      put api("/users/#{user.id}", admin), params: { color_scheme_id: 5 }

1354
      expect(response).to have_gitlab_http_status(:ok)
1355 1356 1357 1358 1359 1360
      expect(user.reload.color_scheme_id).to eq(5)
    end

    it "does not update invalid color scheme id" do
      put api("/users/#{user.id}", admin), params: { color_scheme_id: 50 }

1361
      expect(response).to have_gitlab_http_status(:bad_request)
1362 1363 1364
      expect(user.reload.color_scheme_id).not_to eq(50)
    end

1365 1366 1367
    context 'when the current user is not an admin' do
      it "is not available" do
        expect do
blackst0ne's avatar
blackst0ne committed
1368
          put api("/users/#{user.id}", user), params: attributes_for(:user)
1369 1370
        end.not_to change { user.reload.attributes }

1371
        expect(response).to have_gitlab_http_status(:forbidden)
1372
      end
1373 1374
    end

1375
    it "returns 404 for non-existing user" do
1376
      put api("/users/0", admin), params: { bio: 'update should fail' }
1377

1378
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
1379
      expect(json_response['message']).to eq('404 User Not Found')
1380 1381
    end

1382
    it "returns a 404 if invalid ID" do
1383 1384
      put api("/users/ASDF", admin)

1385
      expect(response).to have_gitlab_http_status(:not_found)
1386 1387
    end

1388
    it 'returns 400 error if user does not validate' do
1389
      put api("/users/#{user.id}", admin),
1390 1391 1392 1393 1394 1395 1396 1397
        params: {
          password: 'pass',
          email: 'test@example.com',
          username: 'test!',
          name: 'test',
          bio: 'g' * 256,
          projects_limit: -1
        }
1398
      expect(response).to have_gitlab_http_status(:bad_request)
1399 1400 1401 1402 1403 1404 1405 1406
      expect(json_response['message']['password'])
        .to eq(['is too short (minimum is 8 characters)'])
      expect(json_response['message']['bio'])
        .to eq(['is too long (maximum is 255 characters)'])
      expect(json_response['message']['projects_limit'])
        .to eq(['must be greater than or equal to 0'])
      expect(json_response['message']['username'])
        .to eq([Gitlab::PathRegex.namespace_format_message])
1407
    end
1408

Robert Schilling's avatar
Robert Schilling committed
1409
    it 'returns 400 if provider is missing for identity update' do
blackst0ne's avatar
blackst0ne committed
1410
      put api("/users/#{omniauth_user.id}", admin), params: { extern_uid: '654321' }
Robert Schilling's avatar
Robert Schilling committed
1411

1412
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
1413 1414 1415
    end

    it 'returns 400 if external UID is missing for identity update' do
blackst0ne's avatar
blackst0ne committed
1416
      put api("/users/#{omniauth_user.id}", admin), params: { provider: 'ldap' }
Robert Schilling's avatar
Robert Schilling committed
1417

1418
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
1419 1420
    end

1421
    context "with existing user" do
1422
      before do
blackst0ne's avatar
blackst0ne committed
1423 1424
        post api("/users", admin), params: { email: 'test@example.com', password: 'password', username: 'test', name: 'test' }
        post api("/users", admin), params: { email: 'foo@bar.com', password: 'password', username: 'john', name: 'john' }
1425
        @user = User.all.last
1426
      end
1427

1428
      it 'returns 409 conflict error if email address exists' do
blackst0ne's avatar
blackst0ne committed
1429
        put api("/users/#{@user.id}", admin), params: { email: 'test@example.com' }
1430

1431
        expect(response).to have_gitlab_http_status(:conflict)
1432
        expect(@user.reload.email).to eq(@user.email)
1433 1434
      end

1435
      it 'returns 409 conflict error if username taken' do
1436
        @user_id = User.all.last.id
blackst0ne's avatar
blackst0ne committed
1437
        put api("/users/#{@user.id}", admin), params: { username: 'test' }
1438

1439
        expect(response).to have_gitlab_http_status(:conflict)
1440
        expect(@user.reload.username).to eq(@user.username)
1441
      end
1442 1443 1444

      it 'returns 409 conflict error if username taken (case insensitive)' do
        @user_id = User.all.last.id
blackst0ne's avatar
blackst0ne committed
1445
        put api("/users/#{@user.id}", admin), params: { username: 'TEST' }
1446

1447
        expect(response).to have_gitlab_http_status(:conflict)
1448 1449
        expect(@user.reload.username).to eq(@user.username)
      end
1450
    end
1451 1452
  end

1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
  describe "PUT /user/:id/credit_card_validation" do
    let(:credit_card_validated_time) { Time.utc(2020, 1, 1) }

    context 'when unauthenticated' do
      it 'returns authentication error' do
        put api("/user/#{user.id}/credit_card_validation"), params: { credit_card_validated_at: credit_card_validated_time }

        expect(response).to have_gitlab_http_status(:unauthorized)
      end
    end

    context 'when authenticated as non-admin' do
      it "does not allow updating user's credit card validation", :aggregate_failures do
        put api("/user/#{user.id}/credit_card_validation", user), params: { credit_card_validated_at: credit_card_validated_time }

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    context 'when authenticated as admin' do
      it "updates user's credit card validation", :aggregate_failures do
        put api("/user/#{user.id}/credit_card_validation", admin), params: { credit_card_validated_at: credit_card_validated_time }

        expect(response).to have_gitlab_http_status(:ok)
        expect(user.reload.credit_card_validated_at).to eq(credit_card_validated_time)
      end

      it "returns 400 error if credit_card_validated_at is missing" do
        put api("/user/#{user.id}/credit_card_validation", admin), params: {}

        expect(response).to have_gitlab_http_status(:bad_request)
      end

      it 'returns 404 error if user not found' do
        put api("/user/#{non_existing_record_id}/credit_card_validation", admin), params: { credit_card_validated_at: credit_card_validated_time }

        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 User Not Found')
      end
    end
  end

1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
  describe "DELETE /users/:id/identities/:provider" do
    let(:test_user) { create(:omniauth_user, provider: 'ldapmain') }

    context 'when unauthenticated' do
      it 'returns authentication error' do
        delete api("/users/#{test_user.id}/identities/ldapmain")

        expect(response).to have_gitlab_http_status(:unauthorized)
      end
    end

    context 'when authenticated' do
      it 'deletes identity of given provider' do
        expect do
          delete api("/users/#{test_user.id}/identities/ldapmain", admin)
        end.to change { test_user.identities.count }.by(-1)
        expect(response).to have_gitlab_http_status(:no_content)
      end

      it_behaves_like '412 response' do
        let(:request) { api("/users/#{test_user.id}/identities/ldapmain", admin) }
      end

      it 'returns 404 error if user not found' do
        delete api("/users/0/identities/ldapmain", admin)

        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 User Not Found')
      end

      it 'returns 404 error if identity not found' do
        delete api("/users/#{test_user.id}/identities/saml", admin)

        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 Identity Not Found')
      end
    end
  end

Angus MacArthur's avatar
Angus MacArthur committed
1534
  describe "POST /users/:id/keys" do
1535
    it "does not create invalid ssh key" do
blackst0ne's avatar
blackst0ne committed
1536
      post api("/users/#{user.id}/keys", admin), params: { title: "invalid key" }
Robert Schilling's avatar
Robert Schilling committed
1537

1538
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
1539
      expect(json_response['error']).to eq('key is missing')
1540 1541
    end

1542
    it 'does not create key without title' do
blackst0ne's avatar
blackst0ne committed
1543
      post api("/users/#{user.id}/keys", admin), params: { key: 'some key' }
Robert Schilling's avatar
Robert Schilling committed
1544

1545
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
1546
      expect(json_response['error']).to eq('title is missing')
Angus MacArthur's avatar
Angus MacArthur committed
1547 1548
    end

1549
    it "creates ssh key" do
Angus MacArthur's avatar
Angus MacArthur committed
1550
      key_attrs = attributes_for :key
1551
      expect do
blackst0ne's avatar
blackst0ne committed
1552
        post api("/users/#{user.id}/keys", admin), params: key_attrs
1553
      end.to change { user.keys.count }.by(1)
Angus MacArthur's avatar
Angus MacArthur committed
1554
    end
1555

1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
    it 'creates SSH key with `expires_at` attribute' do
      optional_attributes = { expires_at: '2016-01-21T00:00:00.000Z' }
      attributes = attributes_for(:key).merge(optional_attributes)

      post api("/users/#{user.id}/keys", admin), params: attributes

      expect(response).to have_gitlab_http_status(:created)
      expect(json_response['expires_at']).to eq(optional_attributes[:expires_at])
    end

1566
    it "returns 400 for invalid ID" do
1567
      post api("/users/0/keys", admin)
1568
      expect(response).to have_gitlab_http_status(:bad_request)
1569
    end
Angus MacArthur's avatar
Angus MacArthur committed
1570 1571
  end

Robert Schilling's avatar
Robert Schilling committed
1572
  describe 'GET /user/:id/keys' do
1573
    it 'returns 404 for non-existing user' do
1574
      get api("/users/#{non_existing_record_id}/keys")
1575

1576
      expect(response).to have_gitlab_http_status(:not_found)
1577 1578
      expect(json_response['message']).to eq('404 User Not Found')
    end
1579

1580 1581
    it 'returns array of ssh keys' do
      user.keys << key
1582

1583
      get api("/users/#{user.id}/keys")
1584

1585
      expect(response).to have_gitlab_http_status(:ok)
Rajendra Kadam's avatar
Rajendra Kadam committed
1586 1587 1588 1589
      expect(response).to include_pagination_headers
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(key.title)
    end
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619

    it 'returns array of ssh keys with comments replaced with'\
      'a simple identifier of username + hostname' do
      get api("/users/#{user.id}/keys")

      expect(response).to have_gitlab_http_status(:ok)
      expect(response).to include_pagination_headers
      expect(json_response).to be_an Array

      keys = json_response.map { |key_detail| key_detail['key'] }
      expect(keys).to all(include("#{user.name} (#{Gitlab.config.gitlab.host}"))
    end

    context 'N+1 queries' do
      before do
        get api("/users/#{user.id}/keys")
      end

      it 'avoids N+1 queries', :request_store do
        control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
          get api("/users/#{user.id}/keys")
        end.count

        create_list(:key, 2, user: user)

        expect do
          get api("/users/#{user.id}/keys")
        end.not_to exceed_all_query_limit(control_count)
      end
    end
Rajendra Kadam's avatar
Rajendra Kadam committed
1620 1621 1622 1623
  end

  describe 'GET /user/:user_id/keys' do
    it 'returns 404 for non-existing user' do
1624
      get api("/users/#{non_existing_record_id}/keys")
Rajendra Kadam's avatar
Rajendra Kadam committed
1625

1626
      expect(response).to have_gitlab_http_status(:not_found)
Rajendra Kadam's avatar
Rajendra Kadam committed
1627 1628 1629 1630 1631 1632 1633 1634
      expect(json_response['message']).to eq('404 User Not Found')
    end

    it 'returns array of ssh keys' do
      user.keys << key

      get api("/users/#{user.username}/keys")

1635
      expect(response).to have_gitlab_http_status(:ok)
1636 1637 1638
      expect(response).to include_pagination_headers
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(key.title)
1639 1640 1641
    end
  end

Robert Schilling's avatar
Robert Schilling committed
1642
  describe 'DELETE /user/:id/keys/:key_id' do
1643
    context 'when unauthenticated' do
1644
      it 'returns authentication error' do
1645
        delete api("/users/#{user.id}/keys/#{non_existing_record_id}")
1646
        expect(response).to have_gitlab_http_status(:unauthorized)
1647 1648 1649 1650
      end
    end

    context 'when authenticated' do
1651
      it 'deletes existing key' do
1652
        user.keys << key
1653

1654
        expect do
1655
          delete api("/users/#{user.id}/keys/#{key.id}", admin)
1656

1657
          expect(response).to have_gitlab_http_status(:no_content)
1658
        end.to change { user.keys.count }.by(-1)
1659 1660
      end

1661 1662 1663 1664
      it_behaves_like '412 response' do
        let(:request) { api("/users/#{user.id}/keys/#{key.id}", admin) }
      end

1665
      it 'returns 404 error if user not found' do
1666
        user.keys << key
1667

1668
        delete api("/users/0/keys/#{key.id}", admin)
1669
        expect(response).to have_gitlab_http_status(:not_found)
1670
        expect(json_response['message']).to eq('404 User Not Found')
1671 1672
      end

1673
      it 'returns 404 error if key not foud' do
1674
        delete api("/users/#{user.id}/keys/#{non_existing_record_id}", admin)
1675
        expect(response).to have_gitlab_http_status(:not_found)
1676
        expect(json_response['message']).to eq('404 Key Not Found')
1677 1678 1679 1680
      end
    end
  end

1681
  describe 'POST /users/:id/gpg_keys' do
1682 1683 1684
    it 'does not create invalid GPG key' do
      post api("/users/#{user.id}/gpg_keys", admin)

1685
      expect(response).to have_gitlab_http_status(:bad_request)
1686 1687 1688 1689
      expect(json_response['error']).to eq('key is missing')
    end

    it 'creates GPG key' do
1690 1691
      key_attrs = attributes_for :gpg_key, key: GpgHelpers::User2.public_key

1692
      expect do
blackst0ne's avatar
blackst0ne committed
1693
        post api("/users/#{user.id}/gpg_keys", admin), params: key_attrs
1694

1695
        expect(response).to have_gitlab_http_status(:created)
1696 1697 1698 1699
      end.to change { user.gpg_keys.count }.by(1)
    end

    it 'returns 400 for invalid ID' do
1700
      post api('/users/0/gpg_keys', admin)
1701

1702
      expect(response).to have_gitlab_http_status(:bad_request)
1703 1704 1705 1706
    end
  end

  describe 'GET /user/:id/gpg_keys' do
1707 1708
    it 'returns 404 for non-existing user' do
      get api('/users/0/gpg_keys')
1709

1710 1711
      expect(response).to have_gitlab_http_status(:not_found)
      expect(json_response['message']).to eq('404 User Not Found')
1712 1713
    end

1714 1715
    it 'returns array of GPG keys' do
      user.gpg_keys << gpg_key
1716

1717
      get api("/users/#{user.id}/gpg_keys")
1718

1719 1720 1721 1722
      expect(response).to have_gitlab_http_status(:ok)
      expect(response).to include_pagination_headers
      expect(json_response).to be_an Array
      expect(json_response.first['key']).to eq(gpg_key.key)
1723 1724 1725
    end
  end

1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
  describe 'GET /user/:id/gpg_keys/:key_id' do
    it 'returns 404 for non-existing user' do
      get api('/users/0/gpg_keys/1')

      expect(response).to have_gitlab_http_status(:not_found)
      expect(json_response['message']).to eq('404 User Not Found')
    end

    it 'returns 404 for non-existing key' do
      get api("/users/#{user.id}/gpg_keys/0")

      expect(response).to have_gitlab_http_status(:not_found)
      expect(json_response['message']).to eq('404 GPG Key Not Found')
    end

    it 'returns a single GPG key' do
      user.gpg_keys << gpg_key

      get api("/users/#{user.id}/gpg_keys/#{gpg_key.id}")

      expect(response).to have_gitlab_http_status(:ok)
      expect(json_response['key']).to eq(gpg_key.key)
    end
  end

1751 1752 1753
  describe 'DELETE /user/:id/gpg_keys/:key_id' do
    context 'when unauthenticated' do
      it 'returns authentication error' do
1754
        delete api("/users/#{user.id}/keys/#{non_existing_record_id}")
1755

1756
        expect(response).to have_gitlab_http_status(:unauthorized)
1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
      end
    end

    context 'when authenticated' do
      it 'deletes existing key' do
        user.gpg_keys << gpg_key

        expect do
          delete api("/users/#{user.id}/gpg_keys/#{gpg_key.id}", admin)

1767
          expect(response).to have_gitlab_http_status(:no_content)
1768 1769 1770 1771 1772 1773
        end.to change { user.gpg_keys.count }.by(-1)
      end

      it 'returns 404 error if user not found' do
        user.keys << key

1774
        delete api("/users/0/gpg_keys/#{gpg_key.id}", admin)
1775

1776
        expect(response).to have_gitlab_http_status(:not_found)
1777 1778 1779 1780
        expect(json_response['message']).to eq('404 User Not Found')
      end

      it 'returns 404 error if key not foud' do
1781
        delete api("/users/#{user.id}/gpg_keys/#{non_existing_record_id}", admin)
1782

1783
        expect(response).to have_gitlab_http_status(:not_found)
1784 1785 1786 1787 1788 1789 1790 1791
        expect(json_response['message']).to eq('404 GPG Key Not Found')
      end
    end
  end

  describe 'POST /user/:id/gpg_keys/:key_id/revoke' do
    context 'when unauthenticated' do
      it 'returns authentication error' do
1792
        post api("/users/#{user.id}/gpg_keys/#{non_existing_record_id}/revoke")
1793

1794
        expect(response).to have_gitlab_http_status(:unauthorized)
1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
      end
    end

    context 'when authenticated' do
      it 'revokes existing key' do
        user.gpg_keys << gpg_key

        expect do
          post api("/users/#{user.id}/gpg_keys/#{gpg_key.id}/revoke", admin)

1805
          expect(response).to have_gitlab_http_status(:accepted)
1806 1807 1808 1809 1810 1811
        end.to change { user.gpg_keys.count }.by(-1)
      end

      it 'returns 404 error if user not found' do
        user.gpg_keys << gpg_key

1812
        post api("/users/0/gpg_keys/#{gpg_key.id}/revoke", admin)
1813

1814
        expect(response).to have_gitlab_http_status(:not_found)
1815 1816 1817 1818
        expect(json_response['message']).to eq('404 User Not Found')
      end

      it 'returns 404 error if key not foud' do
1819
        post api("/users/#{user.id}/gpg_keys/#{non_existing_record_id}/revoke", admin)
1820

1821
        expect(response).to have_gitlab_http_status(:not_found)
1822 1823 1824 1825 1826
        expect(json_response['message']).to eq('404 GPG Key Not Found')
      end
    end
  end

1827
  describe "POST /users/:id/emails" do
1828
    it "does not create invalid email" do
blackst0ne's avatar
blackst0ne committed
1829
      post api("/users/#{user.id}/emails", admin), params: {}
Robert Schilling's avatar
Robert Schilling committed
1830

1831
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
1832
      expect(json_response['error']).to eq('email is missing')
1833 1834
    end

1835
    it "creates unverified email" do
1836 1837
      email_attrs = attributes_for :email
      expect do
blackst0ne's avatar
blackst0ne committed
1838
        post api("/users/#{user.id}/emails", admin), params: email_attrs
1839
      end.to change { user.emails.count }.by(1)
1840

1841
      expect(json_response['confirmed_at']).to be_nil
1842
    end
1843

1844
    it "returns a 400 for invalid ID" do
1845
      post api("/users/0/emails", admin)
1846

1847
      expect(response).to have_gitlab_http_status(:bad_request)
1848
    end
1849 1850 1851 1852 1853

    it "creates verified email" do
      email_attrs = attributes_for :email
      email_attrs[:skip_confirmation] = true

blackst0ne's avatar
blackst0ne committed
1854
      post api("/users/#{user.id}/emails", admin), params: email_attrs
1855

1856
      expect(response).to have_gitlab_http_status(:created)
1857

1858
      expect(json_response['confirmed_at']).not_to be_nil
1859
    end
1860 1861
  end

Robert Schilling's avatar
Robert Schilling committed
1862
  describe 'GET /user/:id/emails' do
1863
    context 'when unauthenticated' do
1864
      it 'returns authentication error' do
1865
        get api("/users/#{user.id}/emails")
1866
        expect(response).to have_gitlab_http_status(:unauthorized)
1867 1868 1869 1870
      end
    end

    context 'when authenticated' do
1871
      it 'returns 404 for non-existing user' do
1872
        get api('/users/0/emails', admin)
1873
        expect(response).to have_gitlab_http_status(:not_found)
1874 1875 1876
        expect(json_response['message']).to eq('404 User Not Found')
      end

1877
      it 'returns array of emails' do
1878
        user.emails << email
1879

1880
        get api("/users/#{user.id}/emails", admin)
1881

1882
        expect(response).to have_gitlab_http_status(:ok)
1883
        expect(response).to include_pagination_headers
1884 1885 1886
        expect(json_response).to be_an Array
        expect(json_response.first['email']).to eq(email.email)
      end
1887

1888
      it "returns a 404 for invalid ID" do
1889
        get api("/users/ASDF/emails", admin)
1890

1891
        expect(response).to have_gitlab_http_status(:not_found)
1892
      end
1893 1894 1895
    end
  end

Robert Schilling's avatar
Robert Schilling committed
1896
  describe 'DELETE /user/:id/emails/:email_id' do
1897
    context 'when unauthenticated' do
1898
      it 'returns authentication error' do
1899
        delete api("/users/#{user.id}/emails/#{non_existing_record_id}")
1900
        expect(response).to have_gitlab_http_status(:unauthorized)
1901 1902 1903 1904
      end
    end

    context 'when authenticated' do
1905
      it 'deletes existing email' do
1906
        user.emails << email
1907

1908 1909
        expect do
          delete api("/users/#{user.id}/emails/#{email.id}", admin)
1910

1911
          expect(response).to have_gitlab_http_status(:no_content)
1912 1913 1914
        end.to change { user.emails.count }.by(-1)
      end

1915 1916 1917 1918
      it_behaves_like '412 response' do
        let(:request) { api("/users/#{user.id}/emails/#{email.id}", admin) }
      end

1919
      it 'returns 404 error if user not found' do
1920
        user.emails << email
1921

1922
        delete api("/users/0/emails/#{email.id}", admin)
1923
        expect(response).to have_gitlab_http_status(:not_found)
1924 1925 1926
        expect(json_response['message']).to eq('404 User Not Found')
      end

1927
      it 'returns 404 error if email not foud' do
1928
        delete api("/users/#{user.id}/emails/#{non_existing_record_id}", admin)
1929
        expect(response).to have_gitlab_http_status(:not_found)
1930 1931
        expect(json_response['message']).to eq('404 Email Not Found')
      end
1932

1933
      it "returns a 404 for invalid ID" do
1934 1935
        delete api("/users/ASDF/emails/bar", admin)

1936
        expect(response).to have_gitlab_http_status(:not_found)
1937
      end
1938 1939 1940
    end
  end

1941
  describe "DELETE /users/:id" do
1942
    let_it_be(:issue) { create(:issue, author: user) }
1943

1944
    it "deletes user", :sidekiq_might_not_need_inline do
1945 1946
      namespace_id = user.namespace.id

1947
      perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
1948

1949
      expect(response).to have_gitlab_http_status(:no_content)
1950
      expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
1951
      expect { Namespace.find(namespace_id) }.to raise_error ActiveRecord::RecordNotFound
1952 1953
    end

1954 1955 1956 1957 1958
    context "sole owner of a group" do
      let!(:group) { create(:group).tap { |group| group.add_owner(user) } }

      context "hard delete disabled" do
        it "does not delete user" do
1959
          perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
1960
          expect(response).to have_gitlab_http_status(:conflict)
1961 1962 1963 1964 1965
        end
      end

      context "hard delete enabled" do
        it "delete user and group", :sidekiq_might_not_need_inline do
1966
          perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
1967
          expect(response).to have_gitlab_http_status(:no_content)
1968 1969 1970 1971 1972
          expect(Group.exists?(group.id)).to be_falsy
        end
      end
    end

1973 1974 1975 1976
    it_behaves_like '412 response' do
      let(:request) { api("/users/#{user.id}", admin) }
    end

1977
    it "does not delete for unauthenticated user" do
1978
      perform_enqueued_jobs { delete api("/users/#{user.id}") }
1979
      expect(response).to have_gitlab_http_status(:unauthorized)
1980 1981
    end

1982
    it "is not available for non admin users" do
1983
      perform_enqueued_jobs { delete api("/users/#{user.id}", user) }
1984
      expect(response).to have_gitlab_http_status(:forbidden)
1985 1986
    end

1987
    it "returns 404 for non-existing user" do
1988
      perform_enqueued_jobs { delete api("/users/0", admin) }
1989
      expect(response).to have_gitlab_http_status(:not_found)
1990
      expect(json_response['message']).to eq('404 User Not Found')
1991
    end
1992

1993
    it "returns a 404 for invalid ID" do
1994
      perform_enqueued_jobs { delete api("/users/ASDF", admin) }
1995

1996
      expect(response).to have_gitlab_http_status(:not_found)
1997
    end
1998 1999

    context "hard delete disabled" do
2000
      it "moves contributions to the ghost user", :sidekiq_might_not_need_inline do
2001
        perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
2002

2003
        expect(response).to have_gitlab_http_status(:no_content)
2004 2005 2006 2007 2008 2009
        expect(issue.reload).to be_persisted
        expect(issue.author.ghost?).to be_truthy
      end
    end

    context "hard delete enabled" do
2010
      it "removes contributions", :sidekiq_might_not_need_inline do
2011
        perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
2012

2013
        expect(response).to have_gitlab_http_status(:no_content)
2014 2015 2016
        expect(Issue.exists?(issue.id)).to be_falsy
      end
    end
2017 2018
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
2019
  describe "GET /user" do
2020 2021 2022
    shared_examples 'get user info' do |version|
      context 'with regular user' do
        context 'with personal access token' do
2023 2024
          let(:personal_access_token) { create(:personal_access_token, user: user).token }

2025 2026
          it 'returns 403 without private token when sudo is defined' do
            get api("/user?private_token=#{personal_access_token}&sudo=123", version: version)
2027

2028
            expect(response).to have_gitlab_http_status(:forbidden)
2029
          end
2030 2031
        end

2032 2033
        it 'returns current user without private token when sudo not defined' do
          get api("/user", user, version: version)
2034

2035
          expect(response).to have_gitlab_http_status(:ok)
2036 2037 2038
          expect(response).to match_response_schema('public_api/v4/user/public')
          expect(json_response['id']).to eq(user.id)
        end
Timothy Andrew's avatar
Timothy Andrew committed
2039

2040 2041 2042
        context "scopes" do
          let(:path) { "/user" }
          let(:api_call) { method(:api) }
Timothy Andrew's avatar
Timothy Andrew committed
2043

2044 2045
          include_examples 'allows the "read_user" scope', version
        end
Timothy Andrew's avatar
Timothy Andrew committed
2046
      end
2047

2048 2049
      context 'with admin' do
        let(:admin_personal_access_token) { create(:personal_access_token, user: admin).token }
2050

2051 2052 2053
        context 'with personal access token' do
          it 'returns 403 without private token when sudo defined' do
            get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}", version: version)
2054

2055
            expect(response).to have_gitlab_http_status(:forbidden)
2056
          end
2057

2058 2059
          it 'returns initial current user without private token but with is_admin when sudo not defined' do
            get api("/user?private_token=#{admin_personal_access_token}", version: version)
2060

2061
            expect(response).to have_gitlab_http_status(:ok)
2062 2063 2064
            expect(response).to match_response_schema('public_api/v4/user/admin')
            expect(json_response['id']).to eq(admin.id)
          end
2065 2066 2067
        end
      end

2068 2069 2070
      context 'with unauthenticated user' do
        it "returns 401 error if user is unauthenticated" do
          get api("/user", version: version)
2071

2072
          expect(response).to have_gitlab_http_status(:unauthorized)
2073
        end
2074
      end
2075
    end
2076 2077 2078

    it_behaves_like 'get user info', 'v3'
    it_behaves_like 'get user info', 'v4'
Nihad Abbasov's avatar
Nihad Abbasov committed
2079
  end
2080

2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
  describe "GET /user/preferences" do
    context "when unauthenticated" do
      it "returns authentication error" do
        get api("/user/preferences")
        expect(response).to have_gitlab_http_status(:unauthorized)
      end
    end

    context "when authenticated" do
      it "returns user preferences" do
        user.user_preference.view_diffs_file_by_file = false
        user.user_preference.show_whitespace_in_diffs = true
        user.save!

        get api("/user/preferences", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(json_response["view_diffs_file_by_file"]).to eq(user.user_preference.view_diffs_file_by_file)
        expect(json_response["show_whitespace_in_diffs"]).to eq(user.user_preference.show_whitespace_in_diffs)
      end
    end
  end

2104 2105
  describe "GET /user/keys" do
    context "when unauthenticated" do
2106
      it "returns authentication error" do
2107
        get api("/user/keys")
2108
        expect(response).to have_gitlab_http_status(:unauthorized)
2109 2110
      end
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
2111

2112
    context "when authenticated" do
2113
      it "returns array of ssh keys" do
2114
        user.keys << key
2115

2116
        get api("/user/keys", user)
2117

2118
        expect(response).to have_gitlab_http_status(:ok)
2119
        expect(response).to include_pagination_headers
2120 2121
        expect(json_response).to be_an Array
        expect(json_response.first["title"]).to eq(key.title)
2122
      end
Timothy Andrew's avatar
Timothy Andrew committed
2123

2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153
      it 'returns array of ssh keys with comments replaced with'\
        'a simple identifier of username + hostname' do
        get api("/user/keys", user)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array

        keys = json_response.map { |key_detail| key_detail['key'] }
        expect(keys).to all(include("#{user.name} (#{Gitlab.config.gitlab.host}"))
      end

      context 'N+1 queries' do
        before do
          get api("/user/keys", user)
        end

        it 'avoids N+1 queries', :request_store do
          control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
            get api("/user/keys", user)
          end.count

          create_list(:key, 2, user: user)

          expect do
            get api("/user/keys", user)
          end.not_to exceed_all_query_limit(control_count)
        end
      end

Timothy Andrew's avatar
Timothy Andrew committed
2154 2155 2156 2157 2158 2159
      context "scopes" do
        let(:path) { "/user/keys" }
        let(:api_call) { method(:api) }

        include_examples 'allows the "read_user" scope'
      end
2160 2161 2162
    end
  end

Robert Schilling's avatar
Robert Schilling committed
2163
  describe "GET /user/keys/:key_id" do
2164
    it "returns single key" do
2165
      user.keys << key
2166

2167
      get api("/user/keys/#{key.id}", user)
2168
      expect(response).to have_gitlab_http_status(:ok)
2169
      expect(json_response["title"]).to eq(key.title)
2170
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
2171

2172 2173 2174 2175 2176 2177 2178
    it 'exposes SSH key comment as a simple identifier of username + hostname' do
      get api("/user/keys/#{key.id}", user)

      expect(response).to have_gitlab_http_status(:ok)
      expect(json_response['key']).to include("#{key.user_name} (#{Gitlab.config.gitlab.host})")
    end

2179
    it "returns 404 Not Found within invalid ID" do
2180
      get api("/user/keys/#{non_existing_record_id}", user)
2181

2182
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2183
      expect(json_response['message']).to eq('404 Key Not Found')
2184 2185
    end

2186
    it "returns 404 error if admin accesses user's ssh key" do
2187 2188
      user.keys << key
      admin
2189

2190
      get api("/user/keys/#{key.id}", admin)
2191
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2192
      expect(json_response['message']).to eq('404 Key Not Found')
2193
    end
2194

2195
    it "returns 404 for invalid ID" do
2196
      get api("/users/keys/ASDF", admin)
2197

2198
      expect(response).to have_gitlab_http_status(:not_found)
2199
    end
Timothy Andrew's avatar
Timothy Andrew committed
2200 2201 2202 2203 2204 2205 2206

    context "scopes" do
      let(:path) { "/user/keys/#{key.id}" }
      let(:api_call) { method(:api) }

      include_examples 'allows the "read_user" scope'
    end
2207
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
2208

2209
  describe "POST /user/keys" do
2210
    it "creates ssh key" do
2211
      key_attrs = attributes_for :key
2212
      expect do
blackst0ne's avatar
blackst0ne committed
2213
        post api("/user/keys", user), params: key_attrs
2214
      end.to change { user.keys.count }.by(1)
2215
      expect(response).to have_gitlab_http_status(:created)
2216 2217
    end

2218 2219 2220 2221 2222 2223 2224 2225 2226 2227
    it 'creates SSH key with `expires_at` attribute' do
      optional_attributes = { expires_at: '2016-01-21T00:00:00.000Z' }
      attributes = attributes_for(:key).merge(optional_attributes)

      post api("/user/keys", user), params: attributes

      expect(response).to have_gitlab_http_status(:created)
      expect(json_response['expires_at']).to eq(optional_attributes[:expires_at])
    end

2228
    it "returns a 401 error if unauthorized" do
blackst0ne's avatar
blackst0ne committed
2229
      post api("/user/keys"), params: { title: 'some title', key: 'some key' }
2230
      expect(response).to have_gitlab_http_status(:unauthorized)
2231 2232
    end

2233
    it "does not create ssh key without key" do
blackst0ne's avatar
blackst0ne committed
2234
      post api("/user/keys", user), params: { title: 'title' }
Robert Schilling's avatar
Robert Schilling committed
2235

2236
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
2237
      expect(json_response['error']).to eq('key is missing')
2238 2239
    end

2240
    it 'does not create ssh key without title' do
blackst0ne's avatar
blackst0ne committed
2241
      post api('/user/keys', user), params: { key: 'some key' }
Robert Schilling's avatar
Robert Schilling committed
2242

2243
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
2244
      expect(json_response['error']).to eq('title is missing')
2245 2246
    end

2247
    it "does not create ssh key without title" do
blackst0ne's avatar
blackst0ne committed
2248
      post api("/user/keys", user), params: { key: "somekey" }
2249
      expect(response).to have_gitlab_http_status(:bad_request)
2250 2251 2252
    end
  end

Robert Schilling's avatar
Robert Schilling committed
2253
  describe "DELETE /user/keys/:key_id" do
2254
    it "deletes existed key" do
2255
      user.keys << key
2256

2257
      expect do
2258
        delete api("/user/keys/#{key.id}", user)
2259

2260
        expect(response).to have_gitlab_http_status(:no_content)
2261
      end.to change { user.keys.count }.by(-1)
2262
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
2263

2264 2265 2266 2267
    it_behaves_like '412 response' do
      let(:request) { api("/user/keys/#{key.id}", user) }
    end

Robert Schilling's avatar
Robert Schilling committed
2268
    it "returns 404 if key ID not found" do
2269
      delete api("/user/keys/#{non_existing_record_id}", user)
Robert Schilling's avatar
Robert Schilling committed
2270

2271
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2272
      expect(json_response['message']).to eq('404 Key Not Found')
2273 2274
    end

2275
    it "returns 401 error if unauthorized" do
2276
      user.keys << key
2277

2278
      delete api("/user/keys/#{key.id}")
2279
      expect(response).to have_gitlab_http_status(:unauthorized)
2280
    end
2281

2282
    it "returns a 404 for invalid ID" do
2283 2284
      delete api("/users/keys/ASDF", admin)

2285
      expect(response).to have_gitlab_http_status(:not_found)
2286
    end
2287
  end
2288

2289 2290 2291 2292 2293
  describe 'GET /user/gpg_keys' do
    context 'when unauthenticated' do
      it 'returns authentication error' do
        get api('/user/gpg_keys')

2294
        expect(response).to have_gitlab_http_status(:unauthorized)
2295 2296 2297 2298 2299 2300 2301 2302 2303
      end
    end

    context 'when authenticated' do
      it 'returns array of GPG keys' do
        user.gpg_keys << gpg_key

        get api('/user/gpg_keys', user)

2304
        expect(response).to have_gitlab_http_status(:ok)
2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
        expect(json_response.first['key']).to eq(gpg_key.key)
      end

      context 'scopes' do
        let(:path) { '/user/gpg_keys' }
        let(:api_call) { method(:api) }

        include_examples 'allows the "read_user" scope'
      end
    end
  end

  describe 'GET /user/gpg_keys/:key_id' do
    it 'returns a single key' do
      user.gpg_keys << gpg_key

      get api("/user/gpg_keys/#{gpg_key.id}", user)

2325
      expect(response).to have_gitlab_http_status(:ok)
2326 2327 2328 2329
      expect(json_response['key']).to eq(gpg_key.key)
    end

    it 'returns 404 Not Found within invalid ID' do
2330
      get api("/user/gpg_keys/#{non_existing_record_id}", user)
2331

2332
      expect(response).to have_gitlab_http_status(:not_found)
2333 2334 2335 2336 2337 2338 2339 2340
      expect(json_response['message']).to eq('404 GPG Key Not Found')
    end

    it "returns 404 error if admin accesses user's GPG key" do
      user.gpg_keys << gpg_key

      get api("/user/gpg_keys/#{gpg_key.id}", admin)

2341
      expect(response).to have_gitlab_http_status(:not_found)
2342 2343 2344 2345 2346 2347
      expect(json_response['message']).to eq('404 GPG Key Not Found')
    end

    it 'returns 404 for invalid ID' do
      get api('/users/gpg_keys/ASDF', admin)

2348
      expect(response).to have_gitlab_http_status(:not_found)
2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360
    end

    context 'scopes' do
      let(:path) { "/user/gpg_keys/#{gpg_key.id}" }
      let(:api_call) { method(:api) }

      include_examples 'allows the "read_user" scope'
    end
  end

  describe 'POST /user/gpg_keys' do
    it 'creates a GPG key' do
2361 2362
      key_attrs = attributes_for :gpg_key, key: GpgHelpers::User2.public_key

2363
      expect do
blackst0ne's avatar
blackst0ne committed
2364
        post api('/user/gpg_keys', user), params: key_attrs
2365

2366
        expect(response).to have_gitlab_http_status(:created)
2367 2368 2369 2370
      end.to change { user.gpg_keys.count }.by(1)
    end

    it 'returns a 401 error if unauthorized' do
blackst0ne's avatar
blackst0ne committed
2371
      post api('/user/gpg_keys'), params: { key: 'some key' }
2372

2373
      expect(response).to have_gitlab_http_status(:unauthorized)
2374 2375 2376 2377 2378
    end

    it 'does not create GPG key without key' do
      post api('/user/gpg_keys', user)

2379
      expect(response).to have_gitlab_http_status(:bad_request)
2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
      expect(json_response['error']).to eq('key is missing')
    end
  end

  describe 'POST /user/gpg_keys/:key_id/revoke' do
    it 'revokes existing GPG key' do
      user.gpg_keys << gpg_key

      expect do
        post api("/user/gpg_keys/#{gpg_key.id}/revoke", user)

2391
        expect(response).to have_gitlab_http_status(:accepted)
2392
      end.to change { user.gpg_keys.count }.by(-1)
2393 2394 2395
    end

    it 'returns 404 if key ID not found' do
2396
      post api("/user/gpg_keys/#{non_existing_record_id}/revoke", user)
2397

2398
      expect(response).to have_gitlab_http_status(:not_found)
2399 2400 2401 2402 2403 2404 2405 2406
      expect(json_response['message']).to eq('404 GPG Key Not Found')
    end

    it 'returns 401 error if unauthorized' do
      user.gpg_keys << gpg_key

      post api("/user/gpg_keys/#{gpg_key.id}/revoke")

2407
      expect(response).to have_gitlab_http_status(:unauthorized)
2408 2409 2410 2411 2412
    end

    it 'returns a 404 for invalid ID' do
      post api('/users/gpg_keys/ASDF/revoke', admin)

2413
      expect(response).to have_gitlab_http_status(:not_found)
2414 2415 2416 2417 2418 2419 2420 2421 2422 2423
    end
  end

  describe 'DELETE /user/gpg_keys/:key_id' do
    it 'deletes existing GPG key' do
      user.gpg_keys << gpg_key

      expect do
        delete api("/user/gpg_keys/#{gpg_key.id}", user)

2424
        expect(response).to have_gitlab_http_status(:no_content)
2425
      end.to change { user.gpg_keys.count }.by(-1)
2426 2427 2428
    end

    it 'returns 404 if key ID not found' do
2429
      delete api("/user/gpg_keys/#{non_existing_record_id}", user)
2430

2431
      expect(response).to have_gitlab_http_status(:not_found)
2432 2433 2434 2435 2436 2437 2438 2439
      expect(json_response['message']).to eq('404 GPG Key Not Found')
    end

    it 'returns 401 error if unauthorized' do
      user.gpg_keys << gpg_key

      delete api("/user/gpg_keys/#{gpg_key.id}")

2440
      expect(response).to have_gitlab_http_status(:unauthorized)
2441 2442 2443 2444 2445
    end

    it 'returns a 404 for invalid ID' do
      delete api('/users/gpg_keys/ASDF', admin)

2446
      expect(response).to have_gitlab_http_status(:not_found)
2447 2448 2449
    end
  end

2450 2451
  describe "GET /user/emails" do
    context "when unauthenticated" do
2452
      it "returns authentication error" do
2453
        get api("/user/emails")
2454
        expect(response).to have_gitlab_http_status(:unauthorized)
2455 2456 2457 2458
      end
    end

    context "when authenticated" do
2459
      it "returns array of emails" do
2460
        user.emails << email
2461

2462
        get api("/user/emails", user)
2463

2464
        expect(response).to have_gitlab_http_status(:ok)
2465
        expect(response).to include_pagination_headers
2466 2467 2468
        expect(json_response).to be_an Array
        expect(json_response.first["email"]).to eq(email.email)
      end
Timothy Andrew's avatar
Timothy Andrew committed
2469 2470 2471 2472 2473 2474 2475

      context "scopes" do
        let(:path) { "/user/emails" }
        let(:api_call) { method(:api) }

        include_examples 'allows the "read_user" scope'
      end
2476 2477 2478
    end
  end

Robert Schilling's avatar
Robert Schilling committed
2479
  describe "GET /user/emails/:email_id" do
2480
    it "returns single email" do
2481
      user.emails << email
2482

2483
      get api("/user/emails/#{email.id}", user)
2484
      expect(response).to have_gitlab_http_status(:ok)
2485 2486 2487
      expect(json_response["email"]).to eq(email.email)
    end

2488
    it "returns 404 Not Found within invalid ID" do
2489
      get api("/user/emails/#{non_existing_record_id}", user)
2490
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2491
      expect(json_response['message']).to eq('404 Email Not Found')
2492 2493
    end

2494
    it "returns 404 error if admin accesses user's email" do
2495 2496
      user.emails << email
      admin
2497

2498
      get api("/user/emails/#{email.id}", admin)
2499
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2500
      expect(json_response['message']).to eq('404 Email Not Found')
2501
    end
2502

2503
    it "returns 404 for invalid ID" do
2504
      get api("/users/emails/ASDF", admin)
2505

2506
      expect(response).to have_gitlab_http_status(:not_found)
2507
    end
Timothy Andrew's avatar
Timothy Andrew committed
2508 2509 2510 2511 2512 2513 2514

    context "scopes" do
      let(:path) { "/user/emails/#{email.id}" }
      let(:api_call) { method(:api) }

      include_examples 'allows the "read_user" scope'
    end
2515 2516 2517
  end

  describe "POST /user/emails" do
2518
    it "creates email" do
2519 2520
      email_attrs = attributes_for :email
      expect do
blackst0ne's avatar
blackst0ne committed
2521
        post api("/user/emails", user), params: email_attrs
2522
      end.to change { user.emails.count }.by(1)
2523
      expect(response).to have_gitlab_http_status(:created)
2524 2525
    end

2526
    it "returns a 401 error if unauthorized" do
blackst0ne's avatar
blackst0ne committed
2527
      post api("/user/emails"), params: { email: 'some email' }
2528
      expect(response).to have_gitlab_http_status(:unauthorized)
2529 2530
    end

2531
    it "does not create email with invalid email" do
blackst0ne's avatar
blackst0ne committed
2532
      post api("/user/emails", user), params: {}
Robert Schilling's avatar
Robert Schilling committed
2533

2534
      expect(response).to have_gitlab_http_status(:bad_request)
Robert Schilling's avatar
Robert Schilling committed
2535
      expect(json_response['error']).to eq('email is missing')
2536 2537 2538
    end
  end

Robert Schilling's avatar
Robert Schilling committed
2539
  describe "DELETE /user/emails/:email_id" do
2540
    it "deletes existed email" do
2541
      user.emails << email
2542

2543 2544
      expect do
        delete api("/user/emails/#{email.id}", user)
2545

2546
        expect(response).to have_gitlab_http_status(:no_content)
2547
      end.to change { user.emails.count }.by(-1)
2548 2549
    end

2550 2551 2552 2553
    it_behaves_like '412 response' do
      let(:request) { api("/user/emails/#{email.id}", user) }
    end

Robert Schilling's avatar
Robert Schilling committed
2554
    it "returns 404 if email ID not found" do
2555
      delete api("/user/emails/#{non_existing_record_id}", user)
Robert Schilling's avatar
Robert Schilling committed
2556

2557
      expect(response).to have_gitlab_http_status(:not_found)
Robert Schilling's avatar
Robert Schilling committed
2558
      expect(json_response['message']).to eq('404 Email Not Found')
2559 2560
    end

2561
    it "returns 401 error if unauthorized" do
2562
      user.emails << email
2563

2564
      delete api("/user/emails/#{email.id}")
2565
      expect(response).to have_gitlab_http_status(:unauthorized)
2566
    end
2567

Robert Schilling's avatar
Robert Schilling committed
2568 2569
    it "returns 400 for invalid ID" do
      delete api("/user/emails/ASDF", admin)
2570

2571
      expect(response).to have_gitlab_http_status(:bad_request)
2572
    end
2573 2574
  end

2575 2576 2577
  context 'activate and deactivate' do
    shared_examples '404' do
      it 'returns 404' do
2578
        expect(response).to have_gitlab_http_status(:not_found)
2579 2580 2581 2582 2583
        expect(json_response['message']).to eq('404 User Not Found')
      end
    end

    describe 'POST /users/:id/activate' do
2584 2585 2586 2587
      subject(:activate) { post api("/users/#{user_id}/activate", api_user) }

      let(:user_id) { user.id }

2588
      context 'performed by a non-admin user' do
2589 2590
        let(:api_user) { user }

2591
        it 'is not authorized to perform the action' do
2592
          activate
2593

2594
          expect(response).to have_gitlab_http_status(:forbidden)
2595 2596 2597 2598
        end
      end

      context 'performed by an admin user' do
2599 2600
        let(:api_user) { admin }

2601 2602 2603 2604 2605 2606
        context 'for a deactivated user' do
          before do
            user.deactivate
          end

          it 'activates a deactivated user' do
2607 2608
            activate

2609
            expect(response).to have_gitlab_http_status(:created)
2610 2611 2612 2613 2614 2615 2616 2617 2618 2619
            expect(user.reload.state).to eq('active')
          end
        end

        context 'for an active user' do
          before do
            user.activate
          end

          it 'returns 201' do
2620 2621
            activate

2622
            expect(response).to have_gitlab_http_status(:created)
2623 2624 2625 2626 2627 2628 2629 2630 2631 2632
            expect(user.reload.state).to eq('active')
          end
        end

        context 'for a blocked user' do
          before do
            user.block
          end

          it 'returns 403' do
2633 2634
            activate

2635
            expect(response).to have_gitlab_http_status(:forbidden)
2636
            expect(json_response['message']).to eq('403 Forbidden - A blocked user must be unblocked to be activated')
2637 2638 2639 2640 2641 2642 2643 2644 2645 2646
            expect(user.reload.state).to eq('blocked')
          end
        end

        context 'for a ldap blocked user' do
          before do
            user.ldap_block
          end

          it 'returns 403' do
2647 2648
            activate

2649
            expect(response).to have_gitlab_http_status(:forbidden)
2650
            expect(json_response['message']).to eq('403 Forbidden - A blocked user must be unblocked to be activated')
2651 2652 2653 2654 2655
            expect(user.reload.state).to eq('ldap_blocked')
          end
        end

        context 'for a user that does not exist' do
2656 2657
          let(:user_id) { 0 }

2658
          before do
2659
            activate
2660 2661 2662 2663 2664 2665 2666 2667
          end

          it_behaves_like '404'
        end
      end
    end

    describe 'POST /users/:id/deactivate' do
2668
      subject(:deactivate) { post api("/users/#{user_id}/deactivate", api_user) }
sfang97's avatar
sfang97 committed
2669 2670

      let(:user_id) { user.id }
sfang97's avatar
sfang97 committed
2671

2672
      context 'performed by a non-admin user' do
sfang97's avatar
sfang97 committed
2673 2674
        let(:api_user) { user }

2675
        it 'is not authorized to perform the action' do
2676
          deactivate
2677

2678
          expect(response).to have_gitlab_http_status(:forbidden)
2679 2680 2681 2682
        end
      end

      context 'performed by an admin user' do
sfang97's avatar
sfang97 committed
2683 2684
        let(:api_user) { admin }

2685 2686
        context 'for an active user' do
          let(:activity) { {} }
2687
          let(:user) { create(:user, **activity) }
2688 2689 2690 2691 2692

          context 'with no recent activity' do
            let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } }

            it 'deactivates an active user' do
2693
              deactivate
sfang97's avatar
sfang97 committed
2694

2695
              expect(response).to have_gitlab_http_status(:created)
2696 2697 2698 2699 2700 2701 2702 2703
              expect(user.reload.state).to eq('deactivated')
            end
          end

          context 'with recent activity' do
            let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } }

            it 'does not deactivate an active user' do
2704
              deactivate
sfang97's avatar
sfang97 committed
2705

2706
              expect(response).to have_gitlab_http_status(:forbidden)
2707
              expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718
              expect(user.reload.state).to eq('active')
            end
          end
        end

        context 'for a deactivated user' do
          before do
            user.deactivate
          end

          it 'returns 201' do
2719
            deactivate
sfang97's avatar
sfang97 committed
2720

2721
            expect(response).to have_gitlab_http_status(:created)
2722 2723 2724 2725 2726 2727 2728 2729 2730 2731
            expect(user.reload.state).to eq('deactivated')
          end
        end

        context 'for a blocked user' do
          before do
            user.block
          end

          it 'returns 403' do
2732
            deactivate
sfang97's avatar
sfang97 committed
2733

2734
            expect(response).to have_gitlab_http_status(:forbidden)
2735
            expect(json_response['message']).to eq('403 Forbidden - A blocked user cannot be deactivated by the API')
2736 2737 2738 2739 2740 2741 2742 2743 2744 2745
            expect(user.reload.state).to eq('blocked')
          end
        end

        context 'for a ldap blocked user' do
          before do
            user.ldap_block
          end

          it 'returns 403' do
2746
            deactivate
sfang97's avatar
sfang97 committed
2747

2748
            expect(response).to have_gitlab_http_status(:forbidden)
2749
            expect(json_response['message']).to eq('403 Forbidden - A blocked user cannot be deactivated by the API')
2750 2751 2752 2753
            expect(user.reload.state).to eq('ldap_blocked')
          end
        end

2754
        context 'for an internal user' do
2755
          let(:user) { User.alert_bot }
2756

2757
          it 'returns 403' do
2758
            deactivate
2759 2760

            expect(response).to have_gitlab_http_status(:forbidden)
2761
            expect(json_response['message']).to eq('403 Forbidden - An internal user cannot be deactivated by the API')
2762 2763 2764
          end
        end

2765
        context 'for a user that does not exist' do
sfang97's avatar
sfang97 committed
2766 2767
          let(:user_id) { 0 }

2768
          before do
2769
            deactivate
2770 2771 2772 2773 2774 2775 2776 2777
          end

          it_behaves_like '404'
        end
      end
    end
  end

2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812
  context 'approve pending user' do
    shared_examples '404' do
      it 'returns 404' do
        expect(response).to have_gitlab_http_status(:not_found)
        expect(json_response['message']).to eq('404 User Not Found')
      end
    end

    describe 'POST /users/:id/approve' do
      subject(:approve) { post api("/users/#{user_id}/approve", api_user) }

      let_it_be(:pending_user) { create(:user, :blocked_pending_approval) }
      let_it_be(:deactivated_user) { create(:user, :deactivated) }
      let_it_be(:blocked_user) { create(:user, :blocked) }

      context 'performed by a non-admin user' do
        let(:api_user) { user }
        let(:user_id) { pending_user.id }

        it 'is not authorized to perform the action' do
          expect { approve }.not_to change { pending_user.reload.state }
          expect(response).to have_gitlab_http_status(:forbidden)
          expect(json_response['message']).to eq('You are not allowed to approve a user')
        end
      end

      context 'performed by an admin user' do
        let(:api_user) { admin }

        context 'for a deactivated user' do
          let(:user_id) { deactivated_user.id }

          it 'does not approve a deactivated user' do
            expect { approve }.not_to change { deactivated_user.reload.state }
            expect(response).to have_gitlab_http_status(:conflict)
2813
            expect(json_response['message']).to eq('The user you are trying to approve is not pending approval')
2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832
          end
        end

        context 'for an pending approval user' do
          let(:user_id) { pending_user.id }

          it 'returns 201' do
            expect { approve }.to change { pending_user.reload.state }.to('active')
            expect(response).to have_gitlab_http_status(:created)
            expect(json_response['message']).to eq('Success')
          end
        end

        context 'for an active user' do
          let(:user_id) { user.id }

          it 'returns 201' do
            expect { approve }.not_to change { user.reload.state }
            expect(response).to have_gitlab_http_status(:conflict)
2833
            expect(json_response['message']).to eq('The user you are trying to approve is not pending approval')
2834 2835 2836 2837 2838 2839 2840 2841 2842
          end
        end

        context 'for a blocked user' do
          let(:user_id) { blocked_user.id }

          it 'returns 403' do
            expect { approve }.not_to change { blocked_user.reload.state }
            expect(response).to have_gitlab_http_status(:conflict)
2843
            expect(json_response['message']).to eq('The user you are trying to approve is not pending approval')
2844 2845 2846 2847 2848 2849 2850 2851 2852
          end
        end

        context 'for a ldap blocked user' do
          let(:user_id) { ldap_blocked_user.id }

          it 'returns 403' do
            expect { approve }.not_to change { ldap_blocked_user.reload.state }
            expect(response).to have_gitlab_http_status(:conflict)
2853
            expect(json_response['message']).to eq('The user you are trying to approve is not pending approval')
2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869
          end
        end

        context 'for a user that does not exist' do
          let(:user_id) { non_existing_record_id }

          before do
            approve
          end

          it_behaves_like '404'
        end
      end
    end
  end

2870
  describe 'POST /users/:id/block' do
2871 2872
    let(:blocked_user) { create(:user, state: 'blocked') }

2873
    it 'blocks existing user' do
2874
      post api("/users/#{user.id}/block", admin)
2875 2876 2877 2878 2879 2880

      aggregate_failures do
        expect(response).to have_gitlab_http_status(:created)
        expect(response.body).to eq('true')
        expect(user.reload.state).to eq('blocked')
      end
2881 2882
    end

2883
    it 'does not re-block ldap blocked users' do
2884
      post api("/users/#{ldap_blocked_user.id}/block", admin)
2885
      expect(response).to have_gitlab_http_status(:forbidden)
2886 2887 2888
      expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
    end

2889
    it 'does not be available for non admin users' do
2890
      post api("/users/#{user.id}/block", user)
2891
      expect(response).to have_gitlab_http_status(:forbidden)
2892 2893 2894
      expect(user.reload.state).to eq('active')
    end

2895
    it 'returns a 404 error if user id not found' do
2896
      post api('/users/0/block', admin)
2897
      expect(response).to have_gitlab_http_status(:not_found)
2898 2899
      expect(json_response['message']).to eq('404 User Not Found')
    end
2900

2901 2902 2903 2904 2905 2906 2907 2908 2909
    it 'returns a 403 error if user is internal' do
      internal_user = create(:user, :bot)

      post api("/users/#{internal_user.id}/block", admin)

      expect(response).to have_gitlab_http_status(:forbidden)
      expect(json_response['message']).to eq('An internal user cannot be blocked')
    end

2910 2911 2912 2913 2914 2915 2916 2917
    it 'returns a 201 if user is already blocked' do
      post api("/users/#{blocked_user.id}/block", admin)

      aggregate_failures do
        expect(response).to have_gitlab_http_status(:created)
        expect(response.body).to eq('null')
      end
    end
2918 2919
  end

2920
  describe 'POST /users/:id/unblock' do
2921
    let(:blocked_user) { create(:user, state: 'blocked') }
2922
    let(:deactivated_user) { create(:user, state: 'deactivated') }
2923

2924
    it 'unblocks existing user' do
2925
      post api("/users/#{user.id}/unblock", admin)
2926
      expect(response).to have_gitlab_http_status(:created)
2927 2928 2929
      expect(user.reload.state).to eq('active')
    end

2930
    it 'unblocks a blocked user' do
2931
      post api("/users/#{blocked_user.id}/unblock", admin)
2932
      expect(response).to have_gitlab_http_status(:created)
2933 2934 2935
      expect(blocked_user.reload.state).to eq('active')
    end

2936
    it 'does not unblock ldap blocked users' do
2937
      post api("/users/#{ldap_blocked_user.id}/unblock", admin)
2938
      expect(response).to have_gitlab_http_status(:forbidden)
2939
      expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
2940 2941
    end

2942 2943
    it 'does not unblock deactivated users' do
      post api("/users/#{deactivated_user.id}/unblock", admin)
2944
      expect(response).to have_gitlab_http_status(:forbidden)
2945 2946 2947 2948
      expect(deactivated_user.reload.state).to eq('deactivated')
    end

    it 'is not available for non admin users' do
2949
      post api("/users/#{user.id}/unblock", user)
2950
      expect(response).to have_gitlab_http_status(:forbidden)
2951 2952 2953
      expect(user.reload.state).to eq('active')
    end

2954
    it 'returns a 404 error if user id not found' do
2955
      post api('/users/0/block', admin)
2956
      expect(response).to have_gitlab_http_status(:not_found)
2957 2958
      expect(json_response['message']).to eq('404 User Not Found')
    end
2959

2960
    it "returns a 404 for invalid ID" do
2961
      post api("/users/ASDF/block", admin)
2962

2963
      expect(response).to have_gitlab_http_status(:not_found)
2964
    end
2965
  end
2966

2967 2968 2969 2970
  describe "GET /users/:id/memberships" do
    let_it_be(:user) { create(:user) }
    let_it_be(:project) { create(:project) }
    let_it_be(:group) { create(:group) }
2971

2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044
    let(:requesting_user) { create(:user) }

    before_all do
      project.add_guest(user)
      group.add_guest(user)
    end

    it "responses with 403" do
      get api("/users/#{user.id}/memberships", requesting_user)

      expect(response).to have_gitlab_http_status(:forbidden)
    end

    context 'requested by admin user' do
      let(:requesting_user) { create(:user, :admin) }

      it "responses successfully" do
        get api("/users/#{user.id}/memberships", requesting_user)

        aggregate_failures 'expect successful response including groups and projects' do
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('public_api/v4/memberships')
          expect(response).to include_pagination_headers
          expect(json_response).to contain_exactly(
            a_hash_including('source_type' => 'Project'),
            a_hash_including('source_type' => 'Namespace')
          )
        end
      end

      it 'does not submit N+1 DB queries' do
        # Avoid setup queries
        get api("/users/#{user.id}/memberships", requesting_user)

        control = ActiveRecord::QueryRecorder.new do
          get api("/users/#{user.id}/memberships", requesting_user)
        end

        create_list(:project, 5).map { |project| project.add_guest(user) }

        expect do
          get api("/users/#{user.id}/memberships", requesting_user)
        end.not_to exceed_query_limit(control)
      end

      context 'with type filter' do
        it "only returns project memberships" do
          get api("/users/#{user.id}/memberships?type=Project", requesting_user)

          aggregate_failures do
            expect(json_response).to contain_exactly(a_hash_including('source_type' => 'Project'))
            expect(json_response).not_to include(a_hash_including('source_type' => 'Namespace'))
          end
        end

        it "only returns group memberships" do
          get api("/users/#{user.id}/memberships?type=Namespace", requesting_user)

          aggregate_failures do
            expect(json_response).to contain_exactly(a_hash_including('source_type' => 'Namespace'))
            expect(json_response).not_to include(a_hash_including('source_type' => 'Project'))
          end
        end

        it "recognizes unsupported types" do
          get api("/users/#{user.id}/memberships?type=foo", requesting_user)

          expect(response).to have_gitlab_http_status(:bad_request)
        end
      end
    end
  end

3045
  context "user activities", :clean_gitlab_redis_shared_state do
3046 3047
    let_it_be(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) }
    let_it_be(:newly_active_user) { create(:user, last_activity_on: 2.days.ago.midday) }
3048 3049 3050 3051 3052

    context 'last activity as normal user' do
      it 'has no permission' do
        get api("/user/activities", user)

3053
        expect(response).to have_gitlab_http_status(:forbidden)
3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087
      end
    end

    context 'as admin' do
      it 'returns the activities from the last 6 months' do
        get api("/user/activities", admin)

        expect(response).to include_pagination_headers
        expect(json_response.size).to eq(1)

        activity = json_response.last

        expect(activity['username']).to eq(newly_active_user.username)
        expect(activity['last_activity_on']).to eq(2.days.ago.to_date.to_s)
        expect(activity['last_activity_at']).to eq(2.days.ago.to_date.to_s)
      end

      context 'passing a :from parameter' do
        it 'returns the activities from the given date' do
          get api("/user/activities?from=2000-1-1", admin)

          expect(response).to include_pagination_headers
          expect(json_response.size).to eq(2)

          activity = json_response.first

          expect(activity['username']).to eq(old_active_user.username)
          expect(activity['last_activity_on']).to eq(Time.utc(2000, 1, 1).to_date.to_s)
          expect(activity['last_activity_at']).to eq(Time.utc(2000, 1, 1).to_date.to_s)
        end
      end
    end
  end

3088 3089
  describe 'GET /user/status' do
    let(:path) { '/user/status' }
3090

3091 3092 3093 3094 3095
    it_behaves_like 'rendering user status'
  end

  describe 'PUT /user/status' do
    it 'saves the status' do
blackst0ne's avatar
blackst0ne committed
3096
      put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world' }
3097 3098 3099 3100 3101 3102

      expect(response).to have_gitlab_http_status(:success)
      expect(json_response['emoji']).to eq('smirk')
    end

    it 'renders errors when the status was invalid' do
blackst0ne's avatar
blackst0ne committed
3103
      put api('/user/status', user), params: { emoji: 'does not exist', message: 'hello world' }
3104

3105
      expect(response).to have_gitlab_http_status(:bad_request)
3106 3107 3108 3109 3110 3111 3112 3113 3114
      expect(json_response['message']['emoji']).to be_present
    end

    it 'deletes the status when passing empty values' do
      put api('/user/status', user)

      expect(response).to have_gitlab_http_status(:success)
      expect(user.reload.status).to be_nil
    end
Adam Hegyi's avatar
Adam Hegyi committed
3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143

    context 'when clear_status_after is given' do
      it 'sets the clear_status_at column' do
        freeze_time do
          expected_clear_status_at = 3.hours.from_now

          put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: '3_hours' }

          expect(response).to have_gitlab_http_status(:success)
          expect(user.status.reload.clear_status_at).to be_within(1.minute).of(expected_clear_status_at)
          expect(Time.parse(json_response["clear_status_at"])).to be_within(1.minute).of(expected_clear_status_at)
        end
      end

      it 'unsets the clear_status_at column' do
        user.create_status!(clear_status_at: 5.hours.ago)

        put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: nil }

        expect(response).to have_gitlab_http_status(:success)
        expect(user.status.reload.clear_status_at).to be_nil
      end

      it 'raises error when unknown status value is given' do
        put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: 'wrong' }

        expect(response).to have_gitlab_http_status(:bad_request)
      end
    end
3144 3145
  end

3146 3147 3148 3149 3150
  describe 'POST /users/:user_id/personal_access_tokens' do
    let(:name) { 'new pat' }
    let(:expires_at) { 3.days.from_now.to_date.to_s }
    let(:scopes) { %w(api read_user) }

3151 3152
    it 'returns error if required attributes are missing' do
      post api("/users/#{user.id}/personal_access_tokens", admin)
3153

3154 3155 3156
      expect(response).to have_gitlab_http_status(:bad_request)
      expect(json_response['error']).to eq('name is missing, scopes is missing, scopes does not have a valid value')
    end
3157

3158 3159 3160 3161 3162 3163 3164
    it 'returns a 404 error if user not found' do
      post api("/users/#{non_existing_record_id}/personal_access_tokens", admin),
        params: {
          name: name,
          scopes: scopes,
          expires_at: expires_at
        }
3165

3166 3167 3168
      expect(response).to have_gitlab_http_status(:not_found)
      expect(json_response['message']).to eq('404 User Not Found')
    end
3169

3170 3171 3172 3173 3174 3175 3176
    it 'returns a 401 error when not authenticated' do
      post api("/users/#{user.id}/personal_access_tokens"),
        params: {
          name: name,
          scopes: scopes,
          expires_at: expires_at
        }
3177

3178 3179 3180
      expect(response).to have_gitlab_http_status(:unauthorized)
      expect(json_response['message']).to eq('401 Unauthorized')
    end
3181

3182 3183 3184 3185 3186 3187 3188
    it 'returns a 403 error when authenticated as normal user' do
      post api("/users/#{user.id}/personal_access_tokens", user),
        params: {
          name: name,
          scopes: scopes,
          expires_at: expires_at
        }
3189

3190 3191 3192
      expect(response).to have_gitlab_http_status(:forbidden)
      expect(json_response['message']).to eq('403 Forbidden')
    end
3193

3194 3195 3196 3197 3198 3199 3200
    it 'creates a personal access token when authenticated as admin' do
      post api("/users/#{user.id}/personal_access_tokens", admin),
        params: {
          name: name,
          expires_at: expires_at,
          scopes: scopes
        }
3201

3202 3203 3204 3205 3206 3207 3208 3209 3210 3211
      expect(response).to have_gitlab_http_status(:created)
      expect(json_response['name']).to eq(name)
      expect(json_response['scopes']).to eq(scopes)
      expect(json_response['expires_at']).to eq(expires_at)
      expect(json_response['id']).to be_present
      expect(json_response['created_at']).to be_present
      expect(json_response['active']).to be_truthy
      expect(json_response['revoked']).to be_falsey
      expect(json_response['token']).to be_present
    end
3212

3213 3214 3215
    context 'when an error is thrown by the model' do
      let!(:admin_personal_access_token) { create(:personal_access_token, user: admin) }
      let(:error_message) { 'error message' }
3216

3217 3218 3219 3220
      before do
        allow_next_instance_of(PersonalAccessToken) do |personal_access_token|
          allow(personal_access_token).to receive_message_chain(:errors, :full_messages)
                                            .and_return([error_message])
3221

3222
          allow(personal_access_token).to receive(:save).and_return(false)
3223 3224 3225
        end
      end

3226 3227
      it 'returns the error' do
        post api("/users/#{user.id}/personal_access_tokens", personal_access_token: admin_personal_access_token),
3228 3229 3230 3231 3232 3233
          params: {
            name: name,
            expires_at: expires_at,
            scopes: scopes
          }

3234 3235
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
        expect(json_response['message']).to eq(error_message)
3236 3237 3238 3239
      end
    end
  end

3240
  describe 'GET /users/:user_id/impersonation_tokens' do
3241 3242 3243 3244 3245
    let_it_be(:active_personal_access_token) { create(:personal_access_token, user: user) }
    let_it_be(:revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
    let_it_be(:expired_personal_access_token) { create(:personal_access_token, :expired, user: user) }
    let_it_be(:impersonation_token) { create(:personal_access_token, :impersonation, user: user) }
    let_it_be(:revoked_impersonation_token) { create(:personal_access_token, :impersonation, :revoked, user: user) }
3246 3247

    it 'returns a 404 error if user not found' do
3248
      get api("/users/#{non_existing_record_id}/impersonation_tokens", admin)
3249

3250
      expect(response).to have_gitlab_http_status(:not_found)
3251 3252 3253 3254
      expect(json_response['message']).to eq('404 User Not Found')
    end

    it 'returns a 403 error when authenticated as normal user' do
3255
      get api("/users/#{non_existing_record_id}/impersonation_tokens", user)
3256

3257
      expect(response).to have_gitlab_http_status(:forbidden)
3258 3259 3260
      expect(json_response['message']).to eq('403 Forbidden')
    end

3261 3262
    it 'returns an array of all impersonated tokens' do
      get api("/users/#{user.id}/impersonation_tokens", admin)
3263

3264
      expect(response).to have_gitlab_http_status(:ok)
3265
      expect(response).to include_pagination_headers
3266
      expect(json_response).to be_an Array
3267
      expect(json_response.size).to eq(2)
3268 3269
    end

3270 3271
    it 'returns an array of active impersonation tokens if state active' do
      get api("/users/#{user.id}/impersonation_tokens?state=active", admin)
3272

3273
      expect(response).to have_gitlab_http_status(:ok)
3274
      expect(response).to include_pagination_headers
3275
      expect(json_response).to be_an Array
3276
      expect(json_response.size).to eq(1)
3277 3278 3279 3280
      expect(json_response).to all(include('active' => true))
    end

    it 'returns an array of inactive personal access tokens if active is set to false' do
3281
      get api("/users/#{user.id}/impersonation_tokens?state=inactive", admin)
3282

3283
      expect(response).to have_gitlab_http_status(:ok)
3284
      expect(json_response).to be_an Array
3285
      expect(json_response.size).to eq(1)
3286 3287 3288 3289
      expect(json_response).to all(include('active' => false))
    end
  end

3290
  describe 'POST /users/:user_id/impersonation_tokens' do
3291 3292
    let(:name) { 'my new pat' }
    let(:expires_at) { '2016-12-28' }
3293
    let(:scopes) { %w(api read_user) }
Simon Vocella's avatar
Simon Vocella committed
3294
    let(:impersonation) { true }
3295

3296 3297
    it 'returns validation error if impersonation token misses some attributes' do
      post api("/users/#{user.id}/impersonation_tokens", admin)
3298

3299
      expect(response).to have_gitlab_http_status(:bad_request)
3300 3301 3302 3303
      expect(json_response['error']).to eq('name is missing')
    end

    it 'returns a 404 error if user not found' do
3304
      post api("/users/#{non_existing_record_id}/impersonation_tokens", admin),
blackst0ne's avatar
blackst0ne committed
3305 3306 3307 3308
        params: {
          name: name,
          expires_at: expires_at
        }
3309

3310
      expect(response).to have_gitlab_http_status(:not_found)
3311 3312 3313 3314
      expect(json_response['message']).to eq('404 User Not Found')
    end

    it 'returns a 403 error when authenticated as normal user' do
3315
      post api("/users/#{user.id}/impersonation_tokens", user),
blackst0ne's avatar
blackst0ne committed
3316 3317 3318 3319
        params: {
          name: name,
          expires_at: expires_at
        }
3320

3321
      expect(response).to have_gitlab_http_status(:forbidden)
3322 3323 3324
      expect(json_response['message']).to eq('403 Forbidden')
    end

3325 3326
    it 'creates a impersonation token' do
      post api("/users/#{user.id}/impersonation_tokens", admin),
blackst0ne's avatar
blackst0ne committed
3327 3328 3329 3330 3331 3332
        params: {
          name: name,
          expires_at: expires_at,
          scopes: scopes,
          impersonation: impersonation
        }
3333

3334
      expect(response).to have_gitlab_http_status(:created)
3335 3336 3337 3338 3339
      expect(json_response['name']).to eq(name)
      expect(json_response['scopes']).to eq(scopes)
      expect(json_response['expires_at']).to eq(expires_at)
      expect(json_response['id']).to be_present
      expect(json_response['created_at']).to be_present
3340 3341
      expect(json_response['active']).to be_falsey
      expect(json_response['revoked']).to be_falsey
3342
      expect(json_response['token']).to be_present
Simon Vocella's avatar
Simon Vocella committed
3343
      expect(json_response['impersonation']).to eq(impersonation)
3344 3345 3346
    end
  end

3347
  describe 'GET /users/:user_id/impersonation_tokens/:impersonation_token_id' do
3348 3349
    let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
    let_it_be(:impersonation_token) { create(:personal_access_token, :impersonation, user: user) }
3350 3351

    it 'returns 404 error if user not found' do
3352
      get api("/users/#{non_existing_record_id}/impersonation_tokens/1", admin)
3353

3354
      expect(response).to have_gitlab_http_status(:not_found)
3355 3356 3357
      expect(json_response['message']).to eq('404 User Not Found')
    end

3358
    it 'returns a 404 error if impersonation token not found' do
3359
      get api("/users/#{user.id}/impersonation_tokens/#{non_existing_record_id}", admin)
3360

3361
      expect(response).to have_gitlab_http_status(:not_found)
3362 3363 3364 3365 3366 3367
      expect(json_response['message']).to eq('404 Impersonation Token Not Found')
    end

    it 'returns a 404 error if token is not impersonation token' do
      get api("/users/#{user.id}/impersonation_tokens/#{personal_access_token.id}", admin)

3368
      expect(response).to have_gitlab_http_status(:not_found)
3369
      expect(json_response['message']).to eq('404 Impersonation Token Not Found')
3370 3371 3372
    end

    it 'returns a 403 error when authenticated as normal user' do
3373
      get api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", user)
3374

3375
      expect(response).to have_gitlab_http_status(:forbidden)
3376 3377 3378
      expect(json_response['message']).to eq('403 Forbidden')
    end

3379
    it 'returns an impersonation token' do
3380
      get api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin)
3381

3382
      expect(response).to have_gitlab_http_status(:ok)
3383
      expect(json_response['token']).not_to be_present
3384
      expect(json_response['impersonation']).to be_truthy
3385 3386 3387
    end
  end

3388
  describe 'DELETE /users/:user_id/impersonation_tokens/:impersonation_token_id' do
3389 3390
    let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
    let_it_be(:impersonation_token) { create(:personal_access_token, :impersonation, user: user) }
3391 3392

    it 'returns a 404 error if user not found' do
3393
      delete api("/users/#{non_existing_record_id}/impersonation_tokens/1", admin)
3394

3395
      expect(response).to have_gitlab_http_status(:not_found)
3396 3397 3398
      expect(json_response['message']).to eq('404 User Not Found')
    end

3399
    it 'returns a 404 error if impersonation token not found' do
3400
      delete api("/users/#{user.id}/impersonation_tokens/#{non_existing_record_id}", admin)
3401

3402
      expect(response).to have_gitlab_http_status(:not_found)
3403 3404 3405 3406 3407
      expect(json_response['message']).to eq('404 Impersonation Token Not Found')
    end

    it 'returns a 404 error if token is not impersonation token' do
      delete api("/users/#{user.id}/impersonation_tokens/#{personal_access_token.id}", admin)
3408

3409
      expect(response).to have_gitlab_http_status(:not_found)
3410
      expect(json_response['message']).to eq('404 Impersonation Token Not Found')
3411 3412 3413
    end

    it 'returns a 403 error when authenticated as normal user' do
3414
      delete api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", user)
3415

3416
      expect(response).to have_gitlab_http_status(:forbidden)
3417 3418 3419
      expect(json_response['message']).to eq('403 Forbidden')
    end

3420 3421 3422 3423
    it_behaves_like '412 response' do
      let(:request) { api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin) }
    end

3424 3425
    it 'revokes a impersonation token' do
      delete api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin)
3426

3427
      expect(response).to have_gitlab_http_status(:no_content)
3428 3429
      expect(impersonation_token.revoked).to be_falsey
      expect(impersonation_token.reload.revoked).to be_truthy
3430 3431
    end
  end
3432

3433
  it_behaves_like 'custom attributes endpoints', 'users' do
3434
    let(:attributable) { user }
3435
    let(:other_attributable) { admin }
3436
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
3437
end