admin_users_spec.rb 19.2 KB
Newer Older
1 2
# frozen_string_literal: true

gitlabhq's avatar
gitlabhq committed
3 4
require 'spec_helper'

5
describe "Admin::Users" do
6
  include Spec::Support::Helpers::Features::ResponsiveTableHelpers
7

8 9 10 11
  let!(:user) do
    create(:omniauth_user, provider: 'twitter', extern_uid: '123456')
  end

12
  let!(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
13 14 15 16

  before do
    sign_in(current_user)
  end
gitlabhq's avatar
gitlabhq committed
17 18

  describe "GET /admin/users" do
Nihad Abbasov's avatar
Nihad Abbasov committed
19
    before do
gitlabhq's avatar
gitlabhq committed
20 21 22
      visit admin_users_path
    end

23
    it "is ok" do
24
      expect(current_path).to eq(admin_users_path)
gitlabhq's avatar
gitlabhq committed
25 26
    end

27
    it "has users list" do
28 29
      expect(page).to have_content(current_user.email)
      expect(page).to have_content(current_user.name)
30 31
      expect(page).to have_content(current_user.created_at.strftime("%e %b, %Y"))
      expect(page).to have_content(current_user.last_activity_on.strftime("%e %b, %Y"))
32 33
      expect(page).to have_content(user.email)
      expect(page).to have_content(user.name)
34
      expect(page).to have_link('Block', href: block_admin_user_path(user))
35 36
      expect(page).to have_button('Delete user')
      expect(page).to have_button('Delete user and contributions')
gitlabhq's avatar
gitlabhq committed
37
    end
38

39 40
    describe "view extra user information" do
      it 'shows the user popover on hover', :js, :quarantine do
41 42 43 44 45 46 47 48 49
        expect(page).not_to have_selector('#__BV_popover_1__')

        first_user_link = page.first('.js-user-link')
        first_user_link.hover

        expect(page).to have_selector('#__BV_popover_1__')
      end
    end

50 51
    describe 'search and sort' do
      before do
52 53
        create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
        create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
54 55 56
        create(:user, name: 'Dmitriy')
      end

57
      it 'searches users by name' do
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
        visit admin_users_path(search_query: 'Foo')

        expect(page).to have_content('Foo Bar')
        expect(page).to have_content('Foo Baz')
        expect(page).not_to have_content('Dmitriy')
      end

      it 'sorts users by name' do
        visit admin_users_path

        sort_by('Name')

        expect(first_row.text).to include('Dmitriy')
        expect(second_row.text).to include('Foo Bar')
      end

      it 'sorts search results only' do
        visit admin_users_path(search_query: 'Foo')

        sort_by('Name')

        expect(page).not_to have_content('Dmitriy')
        expect(first_row.text).to include('Foo Bar')
        expect(second_row.text).to include('Foo Baz')
      end
83 84 85 86 87 88 89 90 91 92

      it 'searches with respect of sorting' do
        visit admin_users_path(sort: 'Name')

        fill_in :search_query, with: 'Foo'
        click_button('Search users')

        expect(first_row.text).to include('Foo Bar')
        expect(second_row.text).to include('Foo Baz')
      end
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

      it 'sorts users by recent last activity' do
        visit admin_users_path(search_query: 'Foo')

        sort_by('Recent last activity')

        expect(first_row.text).to include('Foo Baz')
        expect(second_row.text).to include('Foo Bar')
      end

      it 'sorts users by oldest last activity' do
        visit admin_users_path(search_query: 'Foo')

        sort_by('Oldest last activity')

        expect(first_row.text).to include('Foo Bar')
        expect(second_row.text).to include('Foo Baz')
      end
111 112
    end

113 114
    describe 'Two-factor Authentication filters' do
      it 'counts users who have enabled 2FA' do
115
        create(:user, :two_factor)
116 117 118 119 120 121 122 123 124

        visit admin_users_path

        page.within('.filter-two-factor-enabled small') do
          expect(page).to have_content('1')
        end
      end

      it 'filters by users who have enabled 2FA' do
125
        user = create(:user, :two_factor)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

        visit admin_users_path
        click_link '2FA Enabled'

        expect(page).to have_content(user.email)
      end

      it 'counts users who have not enabled 2FA' do
        visit admin_users_path

        page.within('.filter-two-factor-disabled small') do
          expect(page).to have_content('2') # Including admin
        end
      end

      it 'filters by users who have not enabled 2FA' do
        visit admin_users_path
        click_link '2FA Disabled'

        expect(page).to have_content(user.email)
      end
    end
gitlabhq's avatar
gitlabhq committed
148 149
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
150
  describe "GET /admin/users/new" do
151 152
    let(:user_username) { 'bang' }

Nihad Abbasov's avatar
Nihad Abbasov committed
153
    before do
gitlabhq's avatar
gitlabhq committed
154
      visit new_admin_user_path
155
      fill_in "user_name", with: "Big Bang"
156
      fill_in "user_username", with: user_username
157
      fill_in "user_email", with: "bigbang@mail.com"
gitlabhq's avatar
gitlabhq committed
158 159
    end

160
    it "creates new user" do
161
      expect { click_button "Create user" }.to change {User.count}.by(1)
gitlabhq's avatar
gitlabhq committed
162 163
    end

164
    it "applies defaults to user" do
165
      click_button "Create user"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
166
      user = User.find_by(username: 'bang')
167 168 169 170
      expect(user.projects_limit)
        .to eq(Gitlab.config.gitlab.default_projects_limit)
      expect(user.can_create_group)
        .to eq(Gitlab.config.gitlab.default_can_create_group)
171 172
    end

173
    it "creates user with valid data" do
174
      click_button "Create user"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
175
      user = User.find_by(username: 'bang')
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
176 177
      expect(user.name).to eq('Big Bang')
      expect(user.email).to eq('bigbang@mail.com')
gitlabhq's avatar
gitlabhq committed
178 179
    end

180
    it "calls send mail" do
Valery Sizov's avatar
Valery Sizov committed
181
      expect_any_instance_of(NotificationService).to receive(:new_user)
182

183
      click_button "Create user"
gitlabhq's avatar
gitlabhq committed
184 185
    end

186
    it "sends valid email to user with email & password" do
Valery Sizov's avatar
Valery Sizov committed
187 188 189 190
      perform_enqueued_jobs do
        click_button "Create user"
      end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
191
      user = User.find_by(username: 'bang')
192
      email = ActionMailer::Base.deliveries.last
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
193
      expect(email.subject).to have_content('Account was created')
194 195
      expect(email.text_part.body).to have_content(user.email)
      expect(email.text_part.body).to have_content('password')
Marin Jankovski's avatar
Marin Jankovski committed
196
    end
197 198 199 200 201 202 203 204 205 206 207

    context 'username contains spaces' do
      let(:user_username) { 'Bing bang' }

      it "doesn't create the user and shows an error message" do
        expect { click_button "Create user" }.to change {User.count}.by(0)

        expect(page).to have_content('The form contains the following error')
        expect(page).to have_content('Username can contain only letters, digits')
      end
    end
208 209 210 211 212

    context 'with new users set to external enabled' do
      context 'with regex to match internal user email address set', :js do
        before do
          stub_application_setting(user_default_external: true)
213
          stub_application_setting(user_default_internal_regex: '\.internal@')
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

          visit new_admin_user_path
        end

        def expects_external_to_be_checked
          expect(find('#user_external')).to be_checked
        end

        def expects_external_to_be_unchecked
          expect(find('#user_external')).not_to be_checked
        end

        def expects_warning_to_be_hidden
          expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
        end

        def expects_warning_to_be_shown
          expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
        end

        it 'automatically unchecks external for matching email' do
          expects_external_to_be_checked
          expects_warning_to_be_hidden

          fill_in 'user_email', with: 'test.internal@domain.ch'

          expects_external_to_be_unchecked
          expects_warning_to_be_shown

          fill_in 'user_email', with: 'test@domain.ch'

          expects_external_to_be_checked
          expects_warning_to_be_hidden

          uncheck 'user_external'

          expects_warning_to_be_hidden
        end
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

        it 'creates an internal user' do
          user_name = 'tester1'
          fill_in 'user_email', with: 'test.internal@domain.ch'
          fill_in 'user_name', with: 'tester1 name'
          fill_in 'user_username', with: user_name

          expects_external_to_be_unchecked
          expects_warning_to_be_shown

          click_button 'Create user'

          new_user = User.find_by(username: user_name)

          expect(new_user.external).to be_falsy
        end
268 269
      end
    end
gitlabhq's avatar
gitlabhq committed
270 271
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
272
  describe "GET /admin/users/:id" do
273
    it "has user info" do
gitlabhq's avatar
gitlabhq committed
274
      visit admin_users_path
275
      click_link user.name
gitlabhq's avatar
gitlabhq committed
276

277 278
      expect(page).to have_content(user.email)
      expect(page).to have_content(user.name)
279
      expect(page).to have_content(user.id)
280
      expect(page).to have_link('Block user', href: block_admin_user_path(user))
281 282
      expect(page).to have_button('Delete user')
      expect(page).to have_button('Delete user and contributions')
gitlabhq's avatar
gitlabhq committed
283
    end
284

285 286
    describe 'Impersonation' do
      let(:another_user) { create(:user) }
287

288
      context 'before impersonating' do
289 290 291 292 293 294 295 296 297 298
        subject { visit admin_user_path(user_to_visit) }

        let(:user_to_visit) { another_user }

        context 'for other users' do
          it 'shows impersonate button for other users' do
            subject

            expect(page).to have_content('Impersonate')
          end
299
        end
300

301 302
        context 'for admin itself' do
          let(:user_to_visit) { current_user }
303

304 305 306 307 308
          it 'does not show impersonate button for admin itself' do
            subject

            expect(page).not_to have_content('Impersonate')
          end
309
        end
310

311 312 313 314
        context 'for blocked user' do
          before do
            another_user.block
          end
315

316 317
          it 'does not show impersonate button for blocked user' do
            subject
318

319 320 321 322 323 324 325 326
            expect(page).not_to have_content('Impersonate')
          end
        end

        context 'when impersonation is disabled' do
          before do
            stub_config_setting(impersonation_enabled: false)
          end
327

328 329 330 331 332
          it 'does not show impersonate button' do
            subject

            expect(page).not_to have_content('Impersonate')
          end
333
        end
334 335
      end

336
      context 'when impersonating' do
337 338
        subject { click_link 'Impersonate' }

339
        before do
340
          visit admin_user_path(another_user)
341
        end
342 343

        it 'logs in as the user when impersonate is clicked' do
344 345
          subject

346
          expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
347 348 349
        end

        it 'sees impersonation log out icon' do
350
          subject
351

352
          icon = first('.fa.fa-user-secret')
353
          expect(icon).not_to be nil
354 355
        end

356 357 358 359
        context 'a user with an expired password' do
          before do
            another_user.update(password_expires_at: Time.now - 5.minutes)
          end
360

361 362
          it 'does not redirect to password change page' do
            subject
363

364 365
            expect(current_path).to eq('/')
          end
366 367 368
        end
      end

369 370 371
      context 'ending impersonation' do
        subject { find(:css, 'li.impersonation a').click }

372
        before do
373
          visit admin_user_path(another_user)
374 375 376
          click_link 'Impersonate'
        end

377 378 379 380
        it 'logs out of impersonated user back to original user' do
          subject

          expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username)
381 382 383
        end

        it 'is redirected back to the impersonated users page in the admin after stopping' do
384
          subject
385 386

          expect(current_path).to eq("/admin/users/#{another_user.username}")
387
        end
388 389 390 391 392 393 394 395 396 397 398 399

        context 'a user with an expired password' do
          before do
            another_user.update(password_expires_at: Time.now - 5.minutes)
          end

          it 'is redirected back to the impersonated users page in the admin after stopping' do
            subject

            expect(current_path).to eq("/admin/users/#{another_user.username}")
          end
        end
400 401 402
      end
    end

403 404
    describe 'Two-factor Authentication status' do
      it 'shows when enabled' do
405
        user.update_attribute(:otp_required_for_login, true)
406

407
        visit admin_user_path(user)
408 409 410 411 412

        expect_two_factor_status('Enabled')
      end

      it 'shows when disabled' do
413
        visit admin_user_path(user)
414 415 416 417 418 419 420 421 422 423

        expect_two_factor_status('Disabled')
      end

      def expect_two_factor_status(status)
        page.within('.two-factor-status') do
          expect(page).to have_content(status)
        end
      end
    end
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

    describe 'Email verification status' do
      let!(:secondary_email) do
        create :email, email: 'secondary@example.com', user: user
      end

      it 'displays the correct status for an unverified email address' do
        user.update(confirmed_at: nil, unconfirmed_email: user.email)
        visit admin_user_path(user)

        expect(page).to have_content("#{user.email} Unverified")

        expect(page).to have_content("#{secondary_email.email} Unverified")
      end

      it 'displays the correct status for a verified email address' do
        visit admin_user_path(user)
        expect(page).to have_content("#{user.email} Verified")

        secondary_email.confirm
        expect(secondary_email.confirmed?).to be_truthy

        visit admin_user_path(user)
        expect(page).to have_content("#{secondary_email.email} Verified")
      end
    end
gitlabhq's avatar
gitlabhq committed
450 451
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
452 453
  describe "GET /admin/users/:id/edit" do
    before do
gitlabhq's avatar
gitlabhq committed
454
      visit admin_users_path
455
      click_link "edit_user_#{user.id}"
gitlabhq's avatar
gitlabhq committed
456 457
    end

458
    it "has user edit page" do
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
459 460
      expect(page).to have_content('Name')
      expect(page).to have_content('Password')
gitlabhq's avatar
gitlabhq committed
461 462 463
    end

    describe "Update user" do
Nihad Abbasov's avatar
Nihad Abbasov committed
464
      before do
465 466
        fill_in "user_name", with: "Big Bang"
        fill_in "user_email", with: "bigbang@mail.com"
467 468
        fill_in "user_password", with: "AValidPassword1"
        fill_in "user_password_confirmation", with: "AValidPassword1"
469
        choose "user_access_level_admin"
470
        click_button "Save changes"
gitlabhq's avatar
gitlabhq committed
471 472
      end

473
      it "shows page with new data" do
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
474 475
        expect(page).to have_content('bigbang@mail.com')
        expect(page).to have_content('Big Bang')
gitlabhq's avatar
gitlabhq committed
476 477
      end

478
      it "changes user entry" do
479 480
        user.reload
        expect(user.name).to eq('Big Bang')
481
        expect(user.admin?).to be_truthy
482 483 484 485 486 487 488 489 490 491 492 493 494 495
        expect(user.password_expires_at).to be <= Time.now
      end
    end

    describe 'update username to non ascii char' do
      it do
        fill_in 'user_username', with: '\u3042\u3044'
        click_button('Save')

        page.within '#error_explanation' do
          expect(page).to have_content('Username')
        end

        expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
gitlabhq's avatar
gitlabhq committed
496 497 498
      end
    end
  end
499 500

  describe "GET /admin/users/:id/projects" do
501
    let(:group) { create(:group) }
502
    let!(:project) { create(:project, group: group) }
503

504
    before do
505
      group.add_developer(user)
506

507
      visit projects_admin_user_path(user)
508 509 510
    end

    it "lists group projects" do
511
      within(:css, '.append-bottom-default + .card') do
512
        expect(page).to have_content 'Group projects'
513
        expect(page).to have_link group.name, href: admin_group_path(group)
514 515 516 517
      end
    end

    it 'allows navigation to the group details' do
518
      within(:css, '.append-bottom-default + .card') do
519
        click_link group.name
520 521
      end
      within(:css, 'h3.page-title') do
522
        expect(page).to have_content "Group: #{group.name}"
523
      end
524
      expect(page).to have_content project.name
525
    end
526 527

    it 'shows the group access level' do
528
      within(:css, '.append-bottom-default + .card') do
529 530 531 532
        expect(page).to have_content 'Developer'
      end
    end

533
    it 'allows group membership to be revoked', :js do
534
      page.within(first('.group_member')) do
535
        accept_confirm { find('.btn-remove').click }
536
      end
537
      wait_for_requests
538 539 540

      expect(page).not_to have_selector('.group_member')
    end
541
  end
542

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
  describe 'show breadcrumbs' do
    it do
      visit admin_user_path(user)

      check_breadcrumb(user.name)

      visit projects_admin_user_path(user)

      check_breadcrumb(user.name)

      visit keys_admin_user_path(user)

      check_breadcrumb(user.name)

      visit admin_user_impersonation_tokens_path(user)

      check_breadcrumb(user.name)

      visit admin_user_identities_path(user)

      check_breadcrumb(user.name)

      visit new_admin_user_identity_path(user)

      check_breadcrumb("New Identity")

      visit admin_user_identities_path(user)

      find('.table').find(:link, 'Edit').click

      check_breadcrumb("Edit Identity")
    end
  end

577 578 579 580 581 582 583 584 585 586 587
  describe 'show user attributes' do
    it do
      visit admin_users_path

      click_link user.name

      expect(page).to have_content 'Account'
      expect(page).to have_content 'Personal projects limit'
    end
  end

588
  describe 'remove users secondary email', :js do
589 590 591 592 593 594 595 596 597
    let!(:secondary_email) do
      create :email, email: 'secondary@example.com', user: user
    end

    it do
      visit admin_user_path(user.username)

      expect(page).to have_content("Secondary email: #{secondary_email.email}")

598
      accept_confirm { find("#remove_email_#{secondary_email.id}").click }
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

      expect(page).not_to have_content(secondary_email.email)
    end
  end

  describe 'show user keys' do
    let!(:key1) do
      create(:key, user: user, title: "ssh-rsa Key1", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1")
    end

    let!(:key2) do
      create(:key, user: user, title: "ssh-rsa Key2", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2")
    end

    it do
      visit admin_users_path

      click_link user.name
      click_link 'SSH keys'

      expect(page).to have_content(key1.title)
      expect(page).to have_content(key2.title)

      click_link key2.title

      expect(page).to have_content(key2.title)
      expect(page).to have_content(key2.key)

      click_link 'Remove'

      expect(page).not_to have_content(key2.title)
    end
  end

  describe 'show user identities' do
    it 'shows user identities' do
      visit admin_user_identities_path(user)

      expect(page).to have_content(user.name)
      expect(page).to have_content('twitter')
    end
  end

  describe 'update user identities' do
    before do
644
      allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
    end

    it 'modifies twitter identity' do
      visit admin_user_identities_path(user)

      find('.table').find(:link, 'Edit').click
      fill_in 'identity_extern_uid', with: '654321'
      select 'twitter_updated', from: 'identity_provider'
      click_button 'Save changes'

      expect(page).to have_content(user.name)
      expect(page).to have_content('twitter_updated')
      expect(page).to have_content('654321')
    end
  end

  describe 'remove user with identities' do
    it 'removes user with twitter identity' do
      visit admin_user_identities_path(user)

      click_link 'Delete'

      expect(page).to have_content(user.name)
      expect(page).not_to have_content('twitter')
    end
  end
671 672 673 674

  def check_breadcrumb(content)
    expect(find('.breadcrumbs-sub-title')).to have_content(content)
  end
675 676 677 678 679 680

  def sort_by(text)
    page.within('.user-sort-dropdown') do
      click_link text
    end
  end
gitlabhq's avatar
gitlabhq committed
681
end