UNPKG

@thoughtspot/visual-embed-sdk

Version:
554 lines 25.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mockSessionInfoApiResponse = exports.embedConfig = void 0; const tslib_1 = require("tslib"); require("jest-fetch-mock"); const authInstance = tslib_1.__importStar(require("./auth")); const authTokenService = tslib_1.__importStar(require("./authToken")); const EmbedConfig = tslib_1.__importStar(require("./embed/embedConfig")); const mixPanelService = tslib_1.__importStar(require("./mixpanel-service")); const test_utils_1 = require("./test/test-utils"); const types_1 = require("./types"); const checkReleaseVersionInBetaInstance = tslib_1.__importStar(require("./utils")); const authService = tslib_1.__importStar(require("./utils/authService/authService")); const tokenAuthService = tslib_1.__importStar(require("./utils/authService/tokenizedAuthService")); const logger_1 = require("./utils/logger"); const SessionService = tslib_1.__importStar(require("./utils/sessionInfoService")); const thoughtSpotHost = 'http://localhost:3000'; const username = 'tsuser'; const password = '12345678'; const samalLoginUrl = `${thoughtSpotHost}/callosum/v1/saml/login?targetURLPath=%23%3FtsSSOMarker%3D5e16222e-ef02-43e9-9fbd-24226bf3ce5b`; exports.embedConfig = { doTokenAuthSuccess: (token) => ({ thoughtSpotHost, username, authEndpoint: 'auth', authType: types_1.AuthType.TrustedAuthToken, getAuthToken: jest.fn(() => Promise.resolve(token)), }), doTokenAuthWithCookieDetect: { thoughtSpotHost, username, authEndpoint: 'auth', detectCookieAccessSlow: true, }, doTokenAuthFailureWithoutAuthEndPoint: { thoughtSpotHost, username, authEndpoint: '', getAuthToken: null, }, doTokenAuthFailureWithoutGetAuthToken: { thoughtSpotHost, username, authEndpoint: 'auth', getAuthToken: null, }, doBasicAuth: { thoughtSpotHost, username, password, }, doSamlAuth: { thoughtSpotHost, }, doSamlAuthNoRedirect: { thoughtSpotHost, inPopup: true, authTriggerContainer: document.body, authTriggerText: 'auth', }, doOidcAuth: { thoughtSpotHost, }, SSOAuth: { authType: types_1.AuthType.SSO, }, SAMLAuth: { authType: types_1.AuthType.SAML, }, OIDCAuth: { authType: types_1.AuthType.OIDC, }, authServerFailure: { thoughtSpotHost, username, authEndpoint: '', getAuthToken: null, authType: types_1.AuthType.AuthServer, }, authServerCookielessFailure: { thoughtSpotHost, username, authEndpoint: '', getAuthToken: null, authType: types_1.AuthType.TrustedAuthTokenCookieless, }, basicAuthSuccess: { thoughtSpotHost, username, password, authType: types_1.AuthType.Basic, }, nonAuthSucess: { thoughtSpotHost, username, password, authType: types_1.AuthType.None, }, doCookielessAuth: (token) => ({ thoughtSpotHost, username, authType: types_1.AuthType.TrustedAuthTokenCookieless, getAuthToken: jest.fn(() => Promise.resolve(token)), }), }; const originalWindow = window; exports.mockSessionInfoApiResponse = { userGUID: '1234', releaseVersion: 'test', configInfo: { isPublicUser: false, mixpanelConfig: { production: true, devSdkKey: 'devKey', prodSdkKey: 'prodKey', }, }, }; describe('Unit test for auth', () => { beforeEach(() => { jest.resetAllMocks(); global.fetch = window.fetch; }); afterEach(() => { authTokenService.resetCachedAuthToken(); SessionService.resetCachedSessionInfo(); jest.resetAllMocks(); }); test('endpoints, SAML_LOGIN_TEMPLATE', () => { const ssoTemplateUrl = authService.EndPoints.SAML_LOGIN_TEMPLATE(thoughtSpotHost); expect(ssoTemplateUrl).toBe(`/callosum/v1/saml/login?targetURLPath=${thoughtSpotHost}`); }); test('when session info giving response, it is cached', async () => { jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockResolvedValueOnce(exports.mockSessionInfoApiResponse); const sessionInfo = await SessionService.getSessionInfo(); expect(sessionInfo.mixpanelToken).toEqual('prodKey'); expect(sessionInfo.isPublicUser).toEqual(false); await SessionService.getSessionInfo(); const cachedInfo = SessionService.getCachedSessionInfo(); expect(cachedInfo).toEqual(sessionInfo); expect(tokenAuthService.fetchSessionInfoService).toHaveBeenCalledTimes(1); }); test('Disable mixpanel when disableSDKTracking flag is set', () => { jest.spyOn(mixPanelService, 'initMixpanel'); jest.spyOn(SessionService, 'getSessionInfo').mockReturnValue(test_utils_1.mockSessionInfo); jest.spyOn(EmbedConfig, 'getEmbedConfig').mockReturnValue({ disableSDKTracking: true }); authInstance.postLoginService(); expect(mixPanelService.initMixpanel).not.toBeCalled(); }); test('Log error is postLogin faild', async () => { jest.spyOn(mixPanelService, 'initMixpanel'); jest.spyOn(SessionService, 'getSessionInfo').mockRejectedValueOnce(test_utils_1.mockSessionInfo); jest.spyOn(EmbedConfig, 'getEmbedConfig').mockReturnValue({ disableSDKTracking: true }); jest.spyOn(logger_1.logger, 'error').mockResolvedValue(true); await authInstance.postLoginService(); expect(mixPanelService.initMixpanel).not.toBeCalled(); expect(logger_1.logger.error).toBeCalled(); }); test('doCookielessTokenAuth: when authEndpoint and getAuthToken are not there, it throw error', async () => { try { await authInstance.doCookielessTokenAuth(exports.embedConfig.doTokenAuthFailureWithoutAuthEndPoint); } catch (e) { expect(e.message).toBe('Either auth endpoint or getAuthToken function must be provided'); } }); test('doTokenAuth: when authEndpoint and getAuthToken are not there, it throw error', async () => { try { await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthFailureWithoutAuthEndPoint); } catch (e) { expect(e.message).toBe('Either auth endpoint or getAuthToken function must be provided'); } }); test('doTokenAuth: when user is loggedIn', async () => { const getAuthenticationTokenMock = jest.spyOn(authTokenService, 'getAuthenticationToken'); jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => true); await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthSuccess('authToken')); expect(authTokenService.getAuthenticationToken).not.toBeCalled(); expect(authInstance.loggedInStatus).toBe(true); getAuthenticationTokenMock.mockRestore(); }); test('doTokenAuth: when user is not loggedIn & getAuthToken have response', async () => { jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false); jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true, })); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthSuccess('authToken2')); expect(authService.fetchAuthService).toBeCalledWith(thoughtSpotHost, username, 'authToken2'); }); test('doTokenAuth: when user is not loggedIn & getAuthToken not present, isLoggedIn should called', async () => { fetchMock.mockResponse(JSON.stringify({ mixpanelAccessToken: '' })); jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false); jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({ text: () => Promise.resolve('abc'), })); jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true, })); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthFailureWithoutGetAuthToken); expect(authService.fetchAuthTokenService).toBeCalledWith('auth'); await (0, test_utils_1.executeAfterWait)(() => { expect(authInstance.loggedInStatus).toBe(true); expect(authService.fetchAuthService).toBeCalledWith(thoughtSpotHost, username, 'abc'); }); }); test('doTokenAuth: Should raise error when duplicate token is used', async () => { jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockResolvedValue({ status: 401, }); jest.spyOn(window, 'alert').mockClear(); jest.spyOn(window, 'alert').mockReturnValue(undefined); jest.spyOn(authService, 'fetchAuthService').mockReset(); jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true, })); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthSuccess('authToken3')); try { jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(false); await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthSuccess('authToken3')); expect(false).toBe(true); } catch (e) { expect(e.message).toContain('Duplicate token'); } await (0, test_utils_1.executeAfterWait)(() => { expect(authInstance.loggedInStatus).toBe(false); expect(window.alert).toBeCalled(); expect(authService.fetchAuthService).toHaveBeenCalledTimes(1); }); }); test('doTokenAuth: Should set loggedInStatus if detectThirdPartyCookieAccess is true and the second info call fails', async () => { jest.spyOn(tokenAuthService, 'fetchSessionInfoService') .mockResolvedValue({ status: 401, }) .mockClear(); jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({ text: () => Promise.resolve('abc'), })); jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true, })); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(false); jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(false); const isLoggedIn = await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthWithCookieDetect); expect(tokenAuthService.isActiveService).toHaveBeenCalledTimes(2); expect(isLoggedIn).toBe(false); }); test('doTokenAuth: when user is not loggedIn & fetchAuthPostService failed than fetchAuthService should call', async () => { jest.spyOn(window, 'alert').mockImplementation(() => undefined); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => false); jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({ text: () => Promise.resolve('abc'), })); jest.spyOn(authService, 'fetchAuthPostService').mockImplementation(() => // eslint-disable-next-line prefer-promise-reject-errors, implicit-arrow-linebreak Promise.reject({ status: 500, })); jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({ status: 200, type: 'opaqueredirect', })); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); expect(await authInstance.doTokenAuth(exports.embedConfig.doTokenAuthSuccess('authToken2'))).toBe(true); expect(authService.fetchAuthPostService).toBeCalledWith(thoughtSpotHost, username, 'authToken2'); expect(authService.fetchAuthService).toBeCalledWith(thoughtSpotHost, username, 'authToken2'); }); describe('doBasicAuth', () => { beforeEach(() => { global.fetch = window.fetch; }); it('when user is loggedIn', async () => { jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(true); await authInstance.doBasicAuth(exports.embedConfig.doBasicAuth); expect(authInstance.loggedInStatus).toBe(true); }); it('when user is not loggedIn', async () => { jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject()); jest.spyOn(authService, 'fetchBasicAuthService').mockImplementation(() => ({ status: 200, ok: true, })); await authInstance.doBasicAuth(exports.embedConfig.doBasicAuth); // expect(tokenAuthService.fetchSessionInfoService).toBeCalled(); expect(authService.fetchBasicAuthService).toBeCalled(); expect(authInstance.loggedInStatus).toBe(true); }); }); describe('doSamlAuth', () => { afterEach(() => { delete global.window; global.window = Object.create(originalWindow); global.window.open = jest.fn(); global.fetch = window.fetch; }); it('when user is loggedIn & isAtSSORedirectUrl is true', async () => { spyOn(checkReleaseVersionInBetaInstance, 'checkReleaseVersionInBeta'); Object.defineProperty(window, 'location', { value: { href: `asd.com#?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, hash: `?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, }, }); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(async () => ({ json: () => test_utils_1.mockSessionInfo, status: 200, })); jest.spyOn(tokenAuthService, 'isActiveService').mockReturnValue(true); await authInstance.doSamlAuth(exports.embedConfig.doSamlAuth); expect(window.location.hash).toBe(''); expect(authInstance.loggedInStatus).toBe(true); }); it('when user is not loggedIn & isAtSSORedirectUrl is true', async () => { Object.defineProperty(window, 'location', { value: { href: `asd.com#?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, hash: `?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, }, }); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject()); await authInstance.doSamlAuth(exports.embedConfig.doSamlAuth); expect(window.location.hash).toBe(''); expect(authInstance.loggedInStatus).toBe(false); }); it('when user is not loggedIn, in config noRedirect is false and isAtSSORedirectUrl is false', async () => { Object.defineProperty(window, 'location', { value: { href: '', hash: '', }, }); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject()); await authInstance.doSamlAuth(exports.embedConfig.doSamlAuth); expect(global.window.location.href).toBe(samalLoginUrl); }); it('should emit SAML_POPUP_CLOSED_NO_AUTH when popup window is closed', async () => { jest.useFakeTimers(); const mockPopupWindow = { closed: false, focus: jest.fn(), close: jest.fn() }; global.window.open = jest.fn().mockReturnValue(mockPopupWindow); Object.defineProperty(window, 'location', { value: { href: '', hash: '', }, }); spyOn(authInstance, 'samlCompletionPromise').and.returnValue(Promise.resolve(false)); const emitSpy = jest.fn(); const mockEventEmitter = { emit: emitSpy, once: jest.fn(), on: jest.fn() }; authInstance.setAuthEE(mockEventEmitter); jest.spyOn(tokenAuthService, 'isActiveService') .mockReturnValueOnce(false) .mockReturnValueOnce(true); expect(await authInstance.doSamlAuth({ ...exports.embedConfig.doSamlAuthNoRedirect, })).toBe(true); document.getElementById('ts-auth-btn').click(); mockPopupWindow.closed = true; jest.advanceTimersByTime(1000); window.postMessage({ type: types_1.EmbedEvent.SAMLComplete }, '*'); await authInstance.samlCompletionPromise; expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH); jest.useRealTimers(); authInstance.setAuthEE(null); }); it('when user is not loggedIn, in config noRedirect is true and isAtSSORedirectUrl is false', async () => { Object.defineProperty(window, 'location', { value: { href: '', hash: '', }, }); spyOn(authInstance, 'samlCompletionPromise'); global.window.open = jest.fn(); jest.spyOn(tokenAuthService, 'isActiveService') .mockReturnValueOnce(false) .mockReturnValueOnce(true); expect(await authInstance.samlCompletionPromise).not.toBe(null); expect(await authInstance.doSamlAuth({ ...exports.embedConfig.doSamlAuthNoRedirect, })).toBe(true); document.getElementById('ts-auth-btn').click(); window.postMessage({ type: types_1.EmbedEvent.SAMLComplete }, '*'); await authInstance.samlCompletionPromise; expect(authInstance.loggedInStatus).toBe(true); }); it('should support emitting SAML_POPUP_CLOSED_NO_AUTH event', () => { const emitSpy = jest.fn(); const mockEventEmitter = { emit: emitSpy, once: jest.fn() }; authInstance.setAuthEE(mockEventEmitter); authInstance.getAuthEE().emit(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH); expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH); authInstance.setAuthEE(null); }); }); describe('doOIDCAuth', () => { afterEach(() => { authTokenService.resetCachedAuthToken(); delete global.window; global.window = Object.create(originalWindow); global.window.open = jest.fn(); global.fetch = window.fetch; }); it('when user is not loggedIn & isAtSSORedirectUrl is true', async () => { Object.defineProperty(window, 'location', { value: { href: `asd.com#?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, hash: `?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, }, }); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject()); await authInstance.doOIDCAuth(exports.embedConfig.doOidcAuth); expect(window.location.hash).toBe(''); expect(authInstance.loggedInStatus).toBe(false); }); }); it('authenticate: when authType is SSO', async () => { jest.spyOn(authInstance, 'doSamlAuth'); await authInstance.authenticate(exports.embedConfig.SSOAuth); expect(window.location.hash).toBe(''); expect(authInstance.doSamlAuth).toBeCalled(); }); it('authenticate: when authType is SMAL', async () => { jest.spyOn(authInstance, 'doSamlAuth'); await authInstance.authenticate(exports.embedConfig.SAMLAuth); expect(window.location.hash).toBe(''); expect(authInstance.doSamlAuth).toBeCalled(); }); it('authenticate: when authType is OIDC', async () => { jest.spyOn(authInstance, 'doOIDCAuth'); await authInstance.authenticate(exports.embedConfig.OIDCAuth); expect(window.location.hash).toBe(''); expect(authInstance.doOIDCAuth).toBeCalled(); }); it('authenticate: when authType is AuthServer', async () => { spyOn(authInstance, 'doTokenAuth'); await authInstance.authenticate(exports.embedConfig.authServerFailure); expect(window.location.hash).toBe(''); expect(authInstance.doTokenAuth).toBeCalled(); }); it('authenticate: when authType is AuthServerCookieless', async () => { spyOn(authInstance, 'doCookielessTokenAuth'); await authInstance.authenticate(exports.embedConfig.authServerCookielessFailure); expect(window.location.hash).toBe(''); expect(authInstance.doCookielessTokenAuth).toBeCalled(); }); it('authenticate: when authType is Basic', async () => { jest.spyOn(authInstance, 'doBasicAuth'); jest.spyOn(authService, 'fetchBasicAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true })); await authInstance.authenticate(exports.embedConfig.basicAuthSuccess); expect(authInstance.doBasicAuth).toBeCalled(); expect(authInstance.loggedInStatus).toBe(true); }); it('authenticate: when authType is None', async () => { expect(await authInstance.authenticate(exports.embedConfig.nonAuthSucess)).not.toBeInstanceOf(Error); }); it('user is authenticated when loggedInStatus is true', () => { expect(authInstance.isAuthenticated()).toBe(authInstance.loggedInStatus); }); it('doCookielessTokenAuth should resolve to true if valid token is passed', async () => { jest.clearAllMocks(); jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true); const isLoggedIn = await authInstance.doCookielessTokenAuth(exports.embedConfig.doCookielessAuth('testToken')); expect(isLoggedIn).toBe(true); }); it('doCookielessTokenAuth should resolve to false if valid token is not passed', async () => { jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(false); const isLoggedIn = await authInstance.doCookielessTokenAuth(exports.embedConfig.doCookielessAuth('testToken')); expect(isLoggedIn).toBe(false); }); it('get AuthEE should return proper value', () => { const testObject = { test: 'true' }; authInstance.setAuthEE(testObject); expect(authInstance.getAuthEE()).toBe(testObject); }); it('getSessionDetails returns the correct details given sessionInfo', async () => { jest.clearAllMocks(); jest.restoreAllMocks(); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockReturnValue({ userGUID: '1234', releaseVersion: '1', configInfo: { mixpanelConfig: { devSdkKey: 'devKey', prodSdkKey: 'prodKey', production: false, }, }, }); const details = await SessionService.getSessionInfo(); expect(details).toEqual(expect.objectContaining({ mixpanelToken: 'devKey', })); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockReturnValue({ configInfo: { mixpanelConfig: { devSdkKey: 'devKey', prodSdkKey: 'prodKey', production: true, }, }, }); SessionService.resetCachedSessionInfo(); const details2 = await SessionService.getSessionInfo(); expect(details2).toEqual(expect.objectContaining({ mixpanelToken: 'prodKey', })); }); test('notifyAuthSuccess if getSessionInfo returns data', async () => { const dummyInfo = { test: 'dummy' }; jest.spyOn(SessionService, 'getSessionInfo').mockResolvedValueOnce(dummyInfo); jest.spyOn(logger_1.logger, 'error').mockResolvedValueOnce(true); const emitSpy = jest.fn(); authInstance.setAuthEE({ emit: emitSpy }); await authInstance.notifyAuthSuccess(); expect(logger_1.logger.error).not.toBeCalled(); expect(emitSpy).toBeCalledWith(authInstance.AuthStatus.SUCCESS, dummyInfo); authInstance.setAuthEE(null); }); test('notifyAuthSuccess if getSessionInfo fails', async () => { jest.spyOn(SessionService, 'getSessionInfo').mockImplementation(() => { throw new Error('error'); }); jest.spyOn(logger_1.logger, 'error'); const emitSpy = jest.fn(); authInstance.setAuthEE({ emit: emitSpy }); await authInstance.notifyAuthSuccess(); expect(logger_1.logger.error).toBeCalled(); expect(emitSpy).not.toBeCalled(); authInstance.setAuthEE(null); }); }); //# sourceMappingURL=auth.spec.js.map