Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-07 15:08:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-07 15:08:27 +0300
commita8653790086d284cecffdc35892bb422cd6c9a7b (patch)
tree8d1f4dc69026a42a47b1026eef2566c7461a52fe /spec/features
parent444f662b8d8cbe47a8f3fa1db6ed926d64f3def3 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/u2f_spec.rb115
-rw-r--r--spec/features/webauthn_spec.rb234
2 files changed, 245 insertions, 104 deletions
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 8dbedc0a7ee..5762a54a717 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -3,22 +3,14 @@
require 'spec_helper'
RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
- def manage_two_factor_authentication
- click_on 'Manage two-factor authentication'
- expect(page).to have_content("Set up new U2F device")
- wait_for_requests
- end
+ include Spec::Support::Helpers::Features::TwoFactorHelpers
- def register_u2f_device(u2f_device = nil, name: 'My device')
- u2f_device ||= FakeU2fDevice.new(page, name)
- u2f_device.respond_to_u2f_registration
- click_on 'Set up new U2F device'
- expect(page).to have_content('Your device was successfully set up')
- fill_in "Pick a name", with: name
- click_on 'Register U2F device'
- u2f_device
+ before do
+ stub_feature_flags(webauthn: false)
end
+ it_behaves_like 'hardware device for 2fa', 'U2F'
+
describe "registration" do
let(:user) { create(:user) }
@@ -27,31 +19,7 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
user.update_attribute(:otp_required_for_login, true)
end
- describe 'when 2FA via OTP is disabled' do
- before do
- user.update_attribute(:otp_required_for_login, false)
- end
-
- it 'does not allow registering a new device' do
- visit profile_account_path
- click_on 'Enable two-factor authentication'
-
- expect(page).to have_button('Set up new U2F device', disabled: true)
- end
- end
-
describe 'when 2FA via OTP is enabled' do
- it 'allows registering a new device with a name' do
- visit profile_account_path
- manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
-
- u2f_device = register_u2f_device
-
- expect(page).to have_content(u2f_device.name)
- expect(page).to have_content('Your U2F device was registered')
- end
-
it 'allows registering more than one device' do
visit profile_account_path
@@ -68,21 +36,6 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
expect(page).to have_content(second_device.name)
expect(U2fRegistration.count).to eq(2)
end
-
- it 'allows deleting a device' do
- visit profile_account_path
- manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
-
- first_u2f_device = register_u2f_device
- second_u2f_device = register_u2f_device(name: 'My other device')
-
- accept_confirm { click_on "Delete", match: :first }
-
- expect(page).to have_content('Successfully deleted')
- expect(page.body).not_to match(first_u2f_device.name)
- expect(page).to have_content(second_u2f_device.name)
- end
end
it 'allows the same device to be registered for multiple users' do
@@ -111,9 +64,9 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
# Have the "u2f device" respond with bad data
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Set up new U2F device'
+ click_on 'Set up new device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F device'
+ click_on 'Register device'
expect(U2fRegistration.count).to eq(0)
expect(page).to have_content("The form contains the following error")
@@ -126,9 +79,9 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
# Failed registration
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Set up new U2F device'
+ click_on 'Set up new device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F device'
+ click_on 'Register device'
expect(page).to have_content("The form contains the following error")
# Successful registration
@@ -228,12 +181,12 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
user = gitlab_sign_in(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_two_factor_auth_path
- expect(page).to have_content("Your U2F device needs to be set up.")
+ expect(page).to have_content("Your device needs to be set up.")
first_device = register_u2f_device
# Register second device
visit profile_two_factor_auth_path
- expect(page).to have_content("Your U2F device needs to be set up.")
+ expect(page).to have_content("Your device needs to be set up.")
second_device = register_u2f_device(name: 'My other device')
gitlab_sign_out
@@ -249,50 +202,4 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
end
end
end
-
- describe 'fallback code authentication' do
- let(:user) { create(:user) }
-
- def assert_fallback_ui(page)
- expect(page).to have_button('Verify code')
- expect(page).to have_css('#user_otp_attempt')
- expect(page).not_to have_link('Sign in via 2FA code')
- expect(page).not_to have_css('#js-authenticate-token-2fa')
- end
-
- before do
- # Register and logout
- gitlab_sign_in(user)
- user.update_attribute(:otp_required_for_login, true)
- visit profile_account_path
- end
-
- describe 'when no u2f device is registered' do
- before do
- gitlab_sign_out
- gitlab_sign_in(user)
- end
-
- it 'shows the fallback otp code UI' do
- assert_fallback_ui(page)
- end
- end
-
- describe 'when a u2f device is registered' do
- before do
- manage_two_factor_authentication
- @u2f_device = register_u2f_device
- gitlab_sign_out
- gitlab_sign_in(user)
- end
-
- it 'provides a button that shows the fallback otp code UI' do
- expect(page).to have_link('Sign in via 2FA code')
-
- click_link('Sign in via 2FA code')
-
- assert_fallback_ui(page)
- end
- end
- end
end
diff --git a/spec/features/webauthn_spec.rb b/spec/features/webauthn_spec.rb
new file mode 100644
index 00000000000..2ffb6bb3477
--- /dev/null
+++ b/spec/features/webauthn_spec.rb
@@ -0,0 +1,234 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Using WebAuthn Devices for Authentication', :js do
+ include Spec::Support::Helpers::Features::TwoFactorHelpers
+ let(:app_id) { "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}" }
+
+ before do
+ WebAuthn.configuration.origin = app_id
+ end
+
+ it_behaves_like 'hardware device for 2fa', 'WebAuthn'
+
+ describe 'registration' do
+ let(:user) { create(:user) }
+
+ before do
+ gitlab_sign_in(user)
+ user.update_attribute(:otp_required_for_login, true)
+ end
+
+ describe 'when 2FA via OTP is enabled' do
+ it 'allows registering more than one device' do
+ visit profile_account_path
+
+ # First device
+ manage_two_factor_authentication
+ first_device = register_webauthn_device
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ # Second device
+ second_device = register_webauthn_device(name: 'My other device')
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ expect(page).to have_content(first_device.name)
+ expect(page).to have_content(second_device.name)
+ expect(WebauthnRegistration.count).to eq(2)
+ end
+ end
+
+ it 'allows the same device to be registered for multiple users' do
+ # First user
+ visit profile_account_path
+ manage_two_factor_authentication
+ webauthn_device = register_webauthn_device
+ expect(page).to have_content('Your WebAuthn device was registered')
+ gitlab_sign_out
+
+ # Second user
+ user = gitlab_sign_in(:user)
+ user.update_attribute(:otp_required_for_login, true)
+ visit profile_account_path
+ manage_two_factor_authentication
+ register_webauthn_device(webauthn_device, name: 'My other device')
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ expect(WebauthnRegistration.count).to eq(2)
+ end
+
+ context 'when there are form errors' do
+ let(:mock_register_js) do
+ <<~JS
+ const mockResponse = {
+ type: 'public-key',
+ id: '',
+ rawId: '',
+ response: {
+ clientDataJSON: '',
+ attestationObject: '',
+ },
+ getClientExtensionResults: () => {},
+ };
+ navigator.credentials.create = function(_) {return Promise.resolve(mockResponse);}
+ JS
+ end
+
+ it "doesn't register the device if there are errors" do
+ visit profile_account_path
+ manage_two_factor_authentication
+
+ # Have the "webauthn device" respond with bad data
+ page.execute_script(mock_register_js)
+ click_on 'Set up new device'
+ expect(page).to have_content('Your device was successfully set up')
+ click_on 'Register device'
+
+ expect(WebauthnRegistration.count).to eq(0)
+ expect(page).to have_content('The form contains the following error')
+ expect(page).to have_content('did not send a valid JSON response')
+ end
+
+ it 'allows retrying registration' do
+ visit profile_account_path
+ manage_two_factor_authentication
+
+ # Failed registration
+ page.execute_script(mock_register_js)
+ click_on 'Set up new device'
+ expect(page).to have_content('Your device was successfully set up')
+ click_on 'Register device'
+ expect(page).to have_content('The form contains the following error')
+
+ # Successful registration
+ register_webauthn_device
+
+ expect(page).to have_content('Your WebAuthn device was registered')
+ expect(WebauthnRegistration.count).to eq(1)
+ end
+ end
+ end
+
+ describe 'authentication' do
+ let(:otp_required_for_login) { true }
+ let(:user) { create(:user, webauthn_xid: WebAuthn.generate_user_id, otp_required_for_login: otp_required_for_login) }
+
+ describe 'when there is only an U2F device' do
+ let!(:u2f_device) do
+ fake_device = U2F::FakeU2F.new(app_id) # "Client"
+ u2f = U2F::U2F.new(app_id) # "Server"
+
+ challenges = u2f.registration_requests.map(&:challenge)
+ device_response = fake_device.register_response(challenges[0])
+ device_registration_params = { device_response: device_response,
+ name: 'My device' }
+
+ U2fRegistration.register(user, app_id, device_registration_params, challenges)
+ FakeU2fDevice.new(page, 'My device', fake_device)
+ end
+
+ it 'falls back to U2F' do
+ gitlab_sign_in(user)
+
+ u2f_device.respond_to_u2f_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when there is a WebAuthn device' do
+ let!(:webauthn_device) do
+ add_webauthn_device(app_id, user)
+ end
+
+ describe 'when 2FA via OTP is disabled' do
+ let(:otp_required_for_login) { false }
+
+ it 'allows logging in with the WebAuthn device' do
+ gitlab_sign_in(user)
+
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when 2FA via OTP is enabled' do
+ it 'allows logging in with the WebAuthn device' do
+ gitlab_sign_in(user)
+
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when a given WebAuthn device has already been registered by another user' do
+ describe 'but not the current user' do
+ let(:other_user) { create(:user, webauthn_xid: WebAuthn.generate_user_id, otp_required_for_login: otp_required_for_login) }
+
+ it 'does not allow logging in with that particular device' do
+ # Register other user with a different WebAuthn device
+ other_device = add_webauthn_device(app_id, other_user)
+
+ # Try authenticating user with the old WebAuthn device
+ gitlab_sign_in(user)
+ other_device.respond_to_webauthn_authentication
+ expect(page).to have_content('Authentication via WebAuthn device failed')
+ end
+ end
+
+ describe "and also the current user" do
+ # TODO Uncomment once WebAuthn::FakeClient supports passing credential options
+ # (especially allow_credentials, as this is needed to specify which credential the
+ # fake client should use. Currently, the first credential is always used).
+ # There is an issue open for this: https://github.com/cedarcode/webauthn-ruby/issues/259
+ it "allows logging in with that particular device" do
+ pending("support for passing credential options in FakeClient")
+ # Register current user with the same WebAuthn device
+ current_user = gitlab_sign_in(:user)
+ visit profile_account_path
+ manage_two_factor_authentication
+ register_webauthn_device(webauthn_device)
+ gitlab_sign_out
+
+ # Try authenticating user with the same WebAuthn device
+ gitlab_sign_in(current_user)
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+ end
+
+ describe 'when a given WebAuthn device has not been registered' do
+ it 'does not allow logging in with that particular device' do
+ unregistered_device = FakeWebauthnDevice.new(page, 'My device')
+ gitlab_sign_in(user)
+ unregistered_device.respond_to_webauthn_authentication
+
+ expect(page).to have_content('Authentication via WebAuthn device failed')
+ end
+ end
+
+ describe 'when more than one device has been registered by the same user' do
+ it 'allows logging in with either device' do
+ first_device = add_webauthn_device(app_id, user)
+ second_device = add_webauthn_device(app_id, user)
+
+ # Authenticate as both devices
+ [first_device, second_device].each do |device|
+ gitlab_sign_in(user)
+ # register_webauthn_device(device)
+ device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+
+ gitlab_sign_out
+ end
+ end
+ end
+ end
+ end
+end