From 5a1540a314597213982178f79453e74f0f6ad3f6 Mon Sep 17 00:00:00 2001 From: jbhanu-thoughtspot <53254394+jbhanu-thoughtspot@users.noreply.github.com> Date: Thu, 10 Oct 2024 18:17:10 -0700 Subject: [PATCH] SCAL-227728 - Updated sso marker to differ from hash paths (#36) --- src/auth.spec.ts | 18 +++++++++++++++--- src/auth.ts | 10 +++++++--- src/css-variables.ts | 4 ++-- src/embed/liveboard.ts | 3 --- src/tokenizedFetch.ts | 1 - src/utils.spec.ts | 12 ++++++------ src/utils.ts | 16 ++++++++++++++-- src/utils/processData.ts | 1 - src/utils/sessionInfoService.ts | 4 ---- 9 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/auth.spec.ts b/src/auth.spec.ts index 52450f95..b891fa3d 100644 --- a/src/auth.spec.ts +++ b/src/auth.spec.ts @@ -14,7 +14,7 @@ import * as SessionService from './utils/sessionInfoService'; const thoughtSpotHost = 'http://localhost:3000'; const username = 'tsuser'; const password = '12345678'; -const samalLoginUrl = `${thoughtSpotHost}/callosum/v1/saml/login?targetURLPath=%235e16222e-ef02-43e9-9fbd-24226bf3ce5b`; +const samalLoginUrl = `${thoughtSpotHost}/callosum/v1/saml/login?targetURLPath=%23%3FtsSSOMarker%3D5e16222e-ef02-43e9-9fbd-24226bf3ce5b`; export const embedConfig: any = { doTokenAuthSuccess: (token: string) => ({ @@ -331,8 +331,8 @@ describe('Unit test for auth', () => { spyOn(checkReleaseVersionInBetaInstance, 'checkReleaseVersionInBeta'); Object.defineProperty(window, 'location', { value: { - href: authInstance.SSO_REDIRECTION_MARKER_GUID, - hash: '', + href: `asd.com#?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, + hash: `?tsSSOMarker=${authInstance.SSO_REDIRECTION_MARKER_GUID}`, }, }); jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation( @@ -348,6 +348,12 @@ describe('Unit test for auth', () => { }); 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(embedConfig.doSamlAuth); expect(window.location.hash).toBe(''); @@ -401,6 +407,12 @@ describe('Unit test for auth', () => { }); 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(embedConfig.doOidcAuth); expect(window.location.hash).toBe(''); diff --git a/src/auth.ts b/src/auth.ts index 340f3299..970e2ba8 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -5,7 +5,7 @@ import { initMixpanel } from './mixpanel-service'; import { AuthType, DOMSelector, EmbedConfig, EmbedEvent, } from './types'; -import { getDOMNode, getRedirectUrl } from './utils'; +import { getDOMNode, getRedirectUrl, getSSOMarker } from './utils'; import { EndPoints, fetchAuthPostService, @@ -245,7 +245,7 @@ export function getReleaseVersion() { * Check if we are stuck at the SSO redirect URL */ function isAtSSORedirectUrl(): boolean { - return window.location.href.indexOf(SSO_REDIRECTION_MARKER_GUID) >= 0; + return window.location.href.indexOf(getSSOMarker(SSO_REDIRECTION_MARKER_GUID)) >= 0; } /** @@ -257,7 +257,11 @@ function removeSSORedirectUrlMarker(): void { // reload the page which we don't want. We'll live with adding an // unnecessary hash to the parent page URL until we find any use case where // that creates an issue. - window.location.hash = window.location.hash.replace(SSO_REDIRECTION_MARKER_GUID, ''); + + // Replace any occurences of ?ssoMarker=guid or &ssoMarker=guid. + let updatedHash = window.location.hash.replace(`?${getSSOMarker(SSO_REDIRECTION_MARKER_GUID)}`, ''); + updatedHash = updatedHash.replace(`&${getSSOMarker(SSO_REDIRECTION_MARKER_GUID)}`, ''); + window.location.hash = updatedHash; } /** diff --git a/src/css-variables.ts b/src/css-variables.ts index eae6c89a..427fea91 100644 --- a/src/css-variables.ts +++ b/src/css-variables.ts @@ -26,8 +26,8 @@ export interface CustomCssVariables { /** * Font color of the text on toggle buttons such as * **All**, **Answers**, and **Liveboards** on the Home page (Classic experience), - * the text color of the chart and table tiles on Home page (New modular Homepage experience), - * and title text on the AI-generated charts and tables. + * the text color of the chart and table tiles on Home page (New modular Homepage + * experience), and title text on the AI-generated charts and tables. * The default color code is #2770EF. * */ diff --git a/src/embed/liveboard.ts b/src/embed/liveboard.ts index feaec6df..40837e39 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -270,7 +270,6 @@ export interface LiveboardViewConfig * * Since, this will show preview images, be careful that it may show * undesired data to the user when using row level security. - * * @example * ```js * const embed = new LiveboardEmbed('#embed-container', { @@ -280,7 +279,6 @@ export interface LiveboardViewConfig * }); * embed.render(); * ``` - * * @version SDK: 1.32.0 | ThoughtSpot: 10.0.0.cl */ showPreviewLoader?: boolean; @@ -613,7 +611,6 @@ export class LiveboardEmbed extends V1Embed { /** * Returns the full url of the liveboard/viz which can be used to open * this liveboard inside the full Thoughtspot application in a new tab. - * * @returns url string */ public getLiveboardUrl(): string { diff --git a/src/tokenizedFetch.ts b/src/tokenizedFetch.ts index 69bf462b..1d16bd04 100644 --- a/src/tokenizedFetch.ts +++ b/src/tokenizedFetch.ts @@ -7,7 +7,6 @@ import { AuthType } from './types'; * Fetch wrapper that adds the authentication token to the request. * Use this to call the ThoughtSpot APIs when using the visual embed sdk. * The interface for this method is the same as Web `Fetch`. - * * @param input * @param init * @example diff --git a/src/utils.spec.ts b/src/utils.spec.ts index fd146d71..772b2a6e 100644 --- a/src/utils.spec.ts +++ b/src/utils.spec.ts @@ -119,9 +119,9 @@ describe('unit test for utils', () => { test('appendToUrlHash', () => { expect(appendToUrlHash('http://myhost:3000', 'hashFrag')).toBe( - 'http://myhost:3000#hashFrag', + 'http://myhost:3000#?tsSSOMarker=hashFrag', ); - expect(appendToUrlHash('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foobar'); + expect(appendToUrlHash('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foo?tsSSOMarker=bar'); }); describe('getRedirectURL', () => { @@ -135,9 +135,9 @@ describe('unit test for utils', () => { test('Should return correct value when path is undefined', () => { expect(getRedirectUrl('http://myhost:3000', 'hashFrag')).toBe( - 'http://myhost:3000#hashFrag', + 'http://myhost:3000#?tsSSOMarker=hashFrag', ); - expect(getRedirectUrl('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foobar'); + expect(getRedirectUrl('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foo?tsSSOMarker=bar'); }); test('Should return correct value when path is set', () => { @@ -148,11 +148,11 @@ describe('unit test for utils', () => { })); expect(getRedirectUrl('http://myhost:3000/', 'hashFrag', '/bar')).toBe( - 'http://myhost:3000/bar#hashFrag', + 'http://myhost:3000/bar#?tsSSOMarker=hashFrag', ); expect(getRedirectUrl('http://myhost:3000/#/foo', 'hashFrag', '#/bar')).toBe( - 'http://myhost:3000/#/barhashFrag', + 'http://myhost:3000/#/bar?tsSSOMarker=hashFrag', ); }); }); diff --git a/src/utils.ts b/src/utils.ts index 73a60677..69eb13d4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -131,6 +131,11 @@ export const getCssDimension = (value: number | string): string => { return value; }; +export const getSSOMarker = (markerId: string) => { + const encStringToAppend = encodeURIComponent(markerId); + return `tsSSOMarker=${encStringToAppend}`; +}; + /** * Append a string to a URL's hash fragment * @param url A URL @@ -140,11 +145,18 @@ export const appendToUrlHash = (url: string, stringToAppend: string) => { let outputUrl = url; const encStringToAppend = encodeURIComponent(stringToAppend); + const marker = `tsSSOMarker=${encStringToAppend}`; + + let splitAdder = ''; + if (url.indexOf('#') >= 0) { - outputUrl = `${outputUrl}${encStringToAppend}`; + // If second half of hash contains a '?' already add a '&' instead of + // '?' which appends to query params. + splitAdder = url.split('#')[1].indexOf('?') >= 0 ? '&' : '?'; } else { - outputUrl = `${outputUrl}#${encStringToAppend}`; + splitAdder = '#?'; } + outputUrl = `${outputUrl}${splitAdder}${marker}`; return outputUrl; }; diff --git a/src/utils/processData.ts b/src/utils/processData.ts index aaa0648a..961ada9a 100644 --- a/src/utils/processData.ts +++ b/src/utils/processData.ts @@ -32,7 +32,6 @@ export function processCustomAction(e: any, thoughtSpotHost: string) { /** * Responds to AuthInit sent from host signifying successful authentication in host. - * * @param e * @returns {any} */ diff --git a/src/utils/sessionInfoService.ts b/src/utils/sessionInfoService.ts index 551b3950..a5293b2d 100644 --- a/src/utils/sessionInfoService.ts +++ b/src/utils/sessionInfoService.ts @@ -19,7 +19,6 @@ let sessionInfo: null | SessionInfo = null; * Returns the session info object and caches it for future use. * Once fetched the session info object is cached and returned from the cache on * subsequent calls. - * * @example ```js * const sessionInfo = await getSessionInfo(); * console.log(sessionInfo); @@ -40,7 +39,6 @@ export async function getSessionInfo(): Promise { /** * Returns the cached session info object. If the client is not authenticated the * function will return null. - * * @example ```js * const sessionInfo = getCachedSessionInfo(); * if (sessionInfo) { @@ -58,7 +56,6 @@ export function getCachedSessionInfo(): SessionInfo | null { /** * Processes the session info response and returns the session info object. - * * @param sessionInfoResp {any} Response from the session info API. * @returns {SessionInfo} The session info object. * @example ```js @@ -87,7 +84,6 @@ export const getSessionDetails = (sessionInfoResp: any): SessionInfo => { /** * Resets the cached session info object and forces a new fetch on the next call. - * * @example ```js * resetCachedSessionInfo(); * const sessionInfo = await getSessionInfo();