diff --git a/code/lib/manager-api/src/modules/refs.ts b/code/lib/manager-api/src/modules/refs.ts index 166994b956e6..e8016da9e80d 100644 --- a/code/lib/manager-api/src/modules/refs.ts +++ b/code/lib/manager-api/src/modules/refs.ts @@ -88,6 +88,27 @@ async function handleRequest( } } +interface UrlParseResult { + url: string; + authorization: string | undefined; +} + +const parseUrl = (url: string): UrlParseResult => { + const credentialsRegex = /https?:\/\/(.+:.+)@/; + let cleanUrl = url; + let authorization; + const [, credentials] = url.match(credentialsRegex) || []; + + if (credentials) { + cleanUrl = url.replace(`${credentials}@`, ''); + authorization = btoa(`${credentials}`); + } + return { + url: cleanUrl, + authorization, + }; +}; + const map = ( input: SetStoriesStoryData, ref: API_ComposedRef, @@ -146,11 +167,22 @@ export const init: ModuleFn = ( const loadedData: API_SetRefData = {}; const query = version ? `?version=${version}` : ''; const credentials = isPublic ? 'omit' : 'include'; + const urlParseResult = parseUrl(url); + + const headers: HeadersInit = { + Accept: 'application/json', + }; + + if (urlParseResult.authorization) { + Object.assign(headers, { + Authorization: `Basic ${urlParseResult.authorization}`, + }); + } const [indexFetch, storiesFetch] = await Promise.all( ['index.json', 'stories.json'].map(async (file) => - fetch(`${url}/${file}${query}`, { - headers: { Accept: 'application/json' }, + fetch(`${urlParseResult.url}/${file}${query}`, { + headers, credentials, }) ) @@ -160,10 +192,8 @@ export const init: ModuleFn = ( const [index, metadata] = await Promise.all([ indexFetch.ok ? handleRequest(indexFetch) : handleRequest(storiesFetch), handleRequest( - fetch(`${url}/metadata.json${query}`, { - headers: { - Accept: 'application/json', - }, + fetch(`${urlParseResult.url}/metadata.json${query}`, { + headers, credentials, cache: 'no-cache', }).catch(() => false) @@ -180,7 +210,7 @@ export const init: ModuleFn = ( Error: Loading of ref failed at fetch (lib/api/src/modules/refs.ts) - URL: ${url} + URL: ${urlParseResult.url} We weren't able to load the above URL, it's possible a CORS error happened. @@ -195,7 +225,7 @@ export const init: ModuleFn = ( await api.setRef(id, { id, - url, + url: urlParseResult.url, ...loadedData, ...(versions ? { versions } : {}), type: !loadedData.storyIndex ? 'auto-inject' : 'lazy', diff --git a/code/lib/manager-api/src/tests/refs.test.js b/code/lib/manager-api/src/tests/refs.test.js index 17a7abe9bf90..8e7d542da427 100644 --- a/code/lib/manager-api/src/tests/refs.test.js +++ b/code/lib/manager-api/src/tests/refs.test.js @@ -508,6 +508,68 @@ describe('Refs API', () => { `); }); + it('checks refs (basic-auth)', async () => { + // given + const { api } = initRefs({ provider, store }, { runCheck: false }); + + setupResponses({ + indexPrivate: { + ok: true, + response: async () => ({ v: 4, entries: {} }), + }, + storiesPrivate: { + ok: true, + response: async () => ({ v: 3, stories: {} }), + }, + metadata: { + ok: true, + response: async () => ({ versions: {} }), + }, + }); + + await api.checkRef({ + id: 'fake', + url: 'https://user:pass@example.com', + title: 'Fake', + }); + + expect(fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "https://example.com/index.json", + Object { + "credentials": "include", + "headers": Object { + "Accept": "application/json", + "Authorization": "Basic dXNlcjpwYXNz", + }, + }, + ], + Array [ + "https://example.com/stories.json", + Object { + "credentials": "include", + "headers": Object { + "Accept": "application/json", + "Authorization": "Basic dXNlcjpwYXNz", + }, + }, + ], + Array [ + "https://example.com/metadata.json", + Object { + "cache": "no-cache", + "credentials": "include", + "headers": Object { + "Accept": "application/json", + "Authorization": "Basic dXNlcjpwYXNz", + }, + }, + ], + ] + `); + }); + it('checks refs (mixed)', async () => { // given const { api } = initRefs({ provider, store }, { runCheck: false });