diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-29 06:08:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-29 06:08:16 +0000 |
commit | 2958635884b1dc5c8c01b5f00f0b7b7359556c37 (patch) | |
tree | fc875db03082af0ea8861a9cbf32d7c58e98d461 /spec/frontend/authentication | |
parent | c70ed55242619ebd139c029a1f3c531d6b81a327 (diff) | |
download | gitlab-ce-2958635884b1dc5c8c01b5f00f0b7b7359556c37.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/authentication')
-rw-r--r-- | spec/frontend/authentication/u2f/authenticate_spec.js | 109 | ||||
-rw-r--r-- | spec/frontend/authentication/u2f/mock_u2f_device.js | 23 | ||||
-rw-r--r-- | spec/frontend/authentication/u2f/register_spec.js | 83 | ||||
-rw-r--r-- | spec/frontend/authentication/u2f/util_spec.js | 61 |
4 files changed, 276 insertions, 0 deletions
diff --git a/spec/frontend/authentication/u2f/authenticate_spec.js b/spec/frontend/authentication/u2f/authenticate_spec.js new file mode 100644 index 00000000000..36cc6fb7c63 --- /dev/null +++ b/spec/frontend/authentication/u2f/authenticate_spec.js @@ -0,0 +1,109 @@ +import $ from 'jquery'; +import U2FAuthenticate from '~/authentication/u2f/authenticate'; +import 'vendor/u2f'; +import MockU2FDevice from './mock_u2f_device'; + +describe('U2FAuthenticate', () => { + let u2fDevice; + let container; + let component; + + preloadFixtures('u2f/authenticate.html'); + + beforeEach(() => { + loadFixtures('u2f/authenticate.html'); + u2fDevice = new MockU2FDevice(); + container = $('#js-authenticate-u2f'); + component = new U2FAuthenticate( + container, + '#js-login-u2f-form', + { + sign_requests: [], + }, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form'), + ); + }); + + describe('with u2f unavailable', () => { + let oldu2f; + + beforeEach(() => { + jest.spyOn(component, 'switchToFallbackUI').mockImplementation(() => {}); + oldu2f = window.u2f; + window.u2f = null; + }); + + afterEach(() => { + window.u2f = oldu2f; + }); + + it('falls back to normal 2fa', done => { + component + .start() + .then(() => { + expect(component.switchToFallbackUI).toHaveBeenCalled(); + done(); + }) + .catch(done.fail); + }); + }); + + describe('with u2f available', () => { + beforeEach(done => { + // bypass automatic form submission within renderAuthenticated + jest.spyOn(component, 'renderAuthenticated').mockReturnValue(true); + u2fDevice = new MockU2FDevice(); + + component + .start() + .then(done) + .catch(done.fail); + }); + + it('allows authenticating via a U2F device', () => { + const inProgressMessage = container.find('p'); + + expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); + u2fDevice.respondToAuthenticateRequest({ + deviceData: 'this is data from the device', + }); + + expect(component.renderAuthenticated).toHaveBeenCalledWith( + '{"deviceData":"this is data from the device"}', + ); + }); + + describe('errors', () => { + it('displays an error message', () => { + const setupButton = container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToAuthenticateRequest({ + errorCode: 'error!', + }); + const errorMessage = container.find('p'); + + expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + }); + + it('allows retrying authentication after an error', () => { + let setupButton = container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToAuthenticateRequest({ + errorCode: 'error!', + }); + const retryButton = container.find('#js-u2f-try-again'); + retryButton.trigger('click'); + setupButton = container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToAuthenticateRequest({ + deviceData: 'this is data from the device', + }); + + expect(component.renderAuthenticated).toHaveBeenCalledWith( + '{"deviceData":"this is data from the device"}', + ); + }); + }); + }); +}); diff --git a/spec/frontend/authentication/u2f/mock_u2f_device.js b/spec/frontend/authentication/u2f/mock_u2f_device.js new file mode 100644 index 00000000000..ec8425a4e3e --- /dev/null +++ b/spec/frontend/authentication/u2f/mock_u2f_device.js @@ -0,0 +1,23 @@ +/* eslint-disable no-unused-expressions */ + +export default class MockU2FDevice { + constructor() { + this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this); + this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this); + window.u2f || (window.u2f = {}); + window.u2f.register = (appId, registerRequests, signRequests, callback) => { + this.registerCallback = callback; + }; + window.u2f.sign = (appId, challenges, signRequests, callback) => { + this.authenticateCallback = callback; + }; + } + + respondToRegisterRequest(params) { + return this.registerCallback(params); + } + + respondToAuthenticateRequest(params) { + return this.authenticateCallback(params); + } +} diff --git a/spec/frontend/authentication/u2f/register_spec.js b/spec/frontend/authentication/u2f/register_spec.js new file mode 100644 index 00000000000..3c2ecdbba66 --- /dev/null +++ b/spec/frontend/authentication/u2f/register_spec.js @@ -0,0 +1,83 @@ +import $ from 'jquery'; +import U2FRegister from '~/authentication/u2f/register'; +import 'vendor/u2f'; +import MockU2FDevice from './mock_u2f_device'; + +describe('U2FRegister', () => { + let u2fDevice; + let container; + let component; + + preloadFixtures('u2f/register.html'); + + beforeEach(done => { + loadFixtures('u2f/register.html'); + u2fDevice = new MockU2FDevice(); + container = $('#js-register-u2f'); + component = new U2FRegister(container, $('#js-register-u2f-templates'), {}, 'token'); + component + .start() + .then(done) + .catch(done.fail); + }); + + it('allows registering a U2F device', () => { + const setupButton = container.find('#js-setup-u2f-device'); + + expect(setupButton.text()).toBe('Set up new U2F device'); + setupButton.trigger('click'); + const inProgressMessage = container.children('p'); + + expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); + u2fDevice.respondToRegisterRequest({ + deviceData: 'this is data from the device', + }); + const registeredMessage = container.find('p'); + const deviceResponse = container.find('#js-device-response'); + + expect(registeredMessage.text()).toContain('Your device was successfully set up!'); + expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + }); + + describe('errors', () => { + it("doesn't allow the same device to be registered twice (for the same user", () => { + const setupButton = container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToRegisterRequest({ + errorCode: 4, + }); + const errorMessage = container.find('p'); + + expect(errorMessage.text()).toContain('already been registered with us'); + }); + + it('displays an error message for other errors', () => { + const setupButton = container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToRegisterRequest({ + errorCode: 'error!', + }); + const errorMessage = container.find('p'); + + expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + }); + + it('allows retrying registration after an error', () => { + let setupButton = container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToRegisterRequest({ + errorCode: 'error!', + }); + const retryButton = container.find('#U2FTryAgain'); + retryButton.trigger('click'); + setupButton = container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + u2fDevice.respondToRegisterRequest({ + deviceData: 'this is data from the device', + }); + const registeredMessage = container.find('p'); + + expect(registeredMessage.text()).toContain('Your device was successfully set up!'); + }); + }); +}); diff --git a/spec/frontend/authentication/u2f/util_spec.js b/spec/frontend/authentication/u2f/util_spec.js new file mode 100644 index 00000000000..67fd4c73243 --- /dev/null +++ b/spec/frontend/authentication/u2f/util_spec.js @@ -0,0 +1,61 @@ +import { canInjectU2fApi } from '~/authentication/u2f/util'; + +describe('U2F Utils', () => { + describe('canInjectU2fApi', () => { + it('returns false for Chrome < 41', () => { + const userAgent = + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.28 Safari/537.36'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + + it('returns true for Chrome >= 41', () => { + const userAgent = + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'; + + expect(canInjectU2fApi(userAgent)).toBe(true); + }); + + it('returns false for Opera < 40', () => { + const userAgent = + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + + it('returns true for Opera >= 40', () => { + const userAgent = + 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991'; + + expect(canInjectU2fApi(userAgent)).toBe(true); + }); + + it('returns false for Safari', () => { + const userAgent = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + + it('returns false for Chrome on Android', () => { + const userAgent = + 'Mozilla/5.0 (Linux; Android 7.0; VS988 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3145.0 Mobile Safari/537.36'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + + it('returns false for Chrome on iOS', () => { + const userAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + + it('returns false for Safari on iOS', () => { + const userAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1'; + + expect(canInjectU2fApi(userAgent)).toBe(false); + }); + }); +}); |