diff --git a/packages/bruno-app/src/components/FolderSettings/Auth/index.js b/packages/bruno-app/src/components/FolderSettings/Auth/index.js index cbdbba57d..2c852af4b 100644 --- a/packages/bruno-app/src/components/FolderSettings/Auth/index.js +++ b/packages/bruno-app/src/components/FolderSettings/Auth/index.js @@ -77,7 +77,7 @@ const Auth = ({ collection, folder }) => { const parentFolder = folderTreePath[i]; if (parentFolder.type === 'folder') { const folderAuth = get(parentFolder, 'root.request.auth'); - if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') { + if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') { effectiveSource = { type: 'folder', name: parentFolder.name, diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js index 8ca23ab8d..c16f7bb68 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js @@ -54,7 +54,7 @@ const Auth = ({ item, collection }) => { for (let i of [...requestTreePath].reverse()) { if (i.type === 'folder') { const folderAuth = get(i, 'root.request.auth'); - if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') { + if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') { effectiveSource = { type: 'folder', name: i.name, diff --git a/packages/bruno-converters/src/postman/postman-to-bruno.js b/packages/bruno-converters/src/postman/postman-to-bruno.js index 9fa85c625..ca07572f5 100644 --- a/packages/bruno-converters/src/postman/postman-to-bruno.js +++ b/packages/bruno-converters/src/postman/postman-to-bruno.js @@ -2,7 +2,7 @@ import get from 'lodash/get'; import { validateSchema, transformItemsInCollection, hydrateSeqInCollection, uuid } from '../common'; import each from 'lodash/each'; import postmanTranslation from './postman-translations'; -import { invalidVariableCharacterRegex } from '../constants/index'; +import { invalidVariableCharacterRegex } from '../constants/index'; const AUTH_TYPES = Object.freeze({ BASIC: 'basic', @@ -56,7 +56,7 @@ const convertV21Auth = (array) => { const constructUrlFromParts = (url) => { if (!url) return ''; - + const { protocol = 'http', host, path, port, query, hash } = url || {}; const hostStr = Array.isArray(host) ? host.filter(Boolean).join('.') : host || ''; const pathStr = Array.isArray(path) ? path.filter(Boolean).join('/') : path || ''; @@ -64,9 +64,9 @@ const constructUrlFromParts = (url) => { const queryStr = query && Array.isArray(query) && query.length > 0 ? `?${query - .filter((q) => q && q.key) - .map((q) => `${q.key}=${q.value || ''}`) - .join('&')}` + .filter((q) => q && q.key) + .map((q) => `${q.key}=${q.value || ''}`) + .join('&')}` : ''; const urlStr = `${protocol}://${hostStr}${portStr}${pathStr ? `/${pathStr}` : ''}${queryStr}`; return urlStr; @@ -140,39 +140,47 @@ const importCollectionLevelVariables = (variables, requestObject) => { requestObject.vars.req = vars; }; -export const processAuth = (auth, requestObject) => { - if (!auth || !auth.type || auth.type === AUTH_TYPES.NOAUTH) { +export const processAuth = (auth, requestObject, collection = false) => { + // As of 14/05/2025 + // When collections are set to "No Auth" in Postman, the auth object is null. + // When folders and requests are set to "Inherit" in Postman, the auth object is null. + // When folders and requests are set to "No Auth" in Postman, the auth object is present. + + // Handle collection-specific "No Auth" + if (collection && !auth) return; // Return as requestObject is a collection and has a default mode = none + + // Handle "Inherit Auth" (typically for non-collections when postmanAuth is null) + if (!auth) { + requestObject.auth.mode = AUTH_TYPES.INHERIT; return; } - let authValues = auth[auth.type]; - - if(!authValues) { - console.warn('Unexpected auth.type, auth object doesn\'t have the key', auth.type); - requestObject.auth.mode = auth.type; - authValues = {}; + // Handle explicit "No Auth" + if (auth.type === AUTH_TYPES.NOAUTH) { + requestObject.auth.mode = 'none'; + return; } - + + let authValues = auth[auth.type] ?? []; if (Array.isArray(authValues)) { authValues = convertV21Auth(authValues); } + requestObject.auth.mode = auth.type; // Set the mode based on Postman's auth type + switch (auth.type) { case AUTH_TYPES.BASIC: - requestObject.auth.mode = AUTH_TYPES.BASIC; requestObject.auth.basic = { username: authValues.username || '', password: authValues.password || '' }; break; case AUTH_TYPES.BEARER: - requestObject.auth.mode = AUTH_TYPES.BEARER; requestObject.auth.bearer = { token: authValues.token || '' }; break; case AUTH_TYPES.AWSV4: - requestObject.auth.mode = AUTH_TYPES.AWSV4; requestObject.auth.awsv4 = { accessKeyId: authValues.accessKey || '', secretAccessKey: authValues.secretKey || '', @@ -183,7 +191,6 @@ export const processAuth = (auth, requestObject) => { }; break; case AUTH_TYPES.APIKEY: - requestObject.auth.mode = AUTH_TYPES.APIKEY; requestObject.auth.apikey = { key: authValues.key || '', value: authValues.value?.toString() || '', // Convert the value to a string as Postman's schema does not rigidly define the type of it, @@ -191,75 +198,78 @@ export const processAuth = (auth, requestObject) => { }; break; case AUTH_TYPES.DIGEST: - requestObject.auth.mode = AUTH_TYPES.DIGEST; requestObject.auth.digest = { username: authValues.username || '', password: authValues.password || '' }; break; case AUTH_TYPES.OAUTH2: - const findValueUsingKey = (key) => { - return authValues[key] || ''; - }; + const findValueUsingKey = (key) => authValues[key] || ''; + + // Maps Postman's grant_type to the Bruno's grantType string expected in the target object const oauth2GrantTypeMaps = { authorization_code_with_pkce: 'authorization_code', authorization_code: 'authorization_code', client_credentials: 'client_credentials', - password_credentials: 'password_credentials' + password_credentials: 'password' }; - const grantType = oauth2GrantTypeMaps[findValueUsingKey('grant_type')] || 'authorization_code'; - requestObject.auth.mode = AUTH_TYPES.OAUTH2; - if (grantType === 'authorization_code') { - requestObject.auth.oauth2 = { - grantType: 'authorization_code', - authorizationUrl: findValueUsingKey('authUrl'), - callbackUrl: findValueUsingKey('redirect_uri'), - accessTokenUrl: findValueUsingKey('accessTokenUrl'), - refreshTokenUrl: findValueUsingKey('refreshTokenUrl'), - clientId: findValueUsingKey('clientId'), - clientSecret: findValueUsingKey('clientSecret'), - scope: findValueUsingKey('scope'), - state: findValueUsingKey('state'), - pkce: Boolean(findValueUsingKey('grant_type') == 'authorization_code_with_pkce'), - tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url', - credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header' - }; - } else if (grantType === 'password_credentials') { - requestObject.auth.oauth2 = { - grantType: 'password', - accessTokenUrl: findValueUsingKey('accessTokenUrl'), - refreshTokenUrl: findValueUsingKey('refreshTokenUrl'), - username: findValueUsingKey('username'), - password: findValueUsingKey('password'), - clientId: findValueUsingKey('clientId'), - clientSecret: findValueUsingKey('clientSecret'), - scope: findValueUsingKey('scope'), - state: findValueUsingKey('state'), - tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url', - credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header' - }; - } else if (grantType === 'client_credentials') { - requestObject.auth.oauth2 = { - grantType: 'client_credentials', - accessTokenUrl: findValueUsingKey('accessTokenUrl'), - refreshTokenUrl: findValueUsingKey('refreshTokenUrl'), - clientId: findValueUsingKey('clientId'), - clientSecret: findValueUsingKey('clientSecret'), - scope: findValueUsingKey('scope'), - state: findValueUsingKey('state'), - tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url', - credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header' - }; + const postmanGrantType = findValueUsingKey('grant_type'); + const targetGrantType = oauth2GrantTypeMaps[postmanGrantType] ?? 'client_credentials'; // Default + + // Common properties for all OAuth2 grant types + const baseOAuth2Config = { + grantType: targetGrantType, + accessTokenUrl: findValueUsingKey('accessTokenUrl'), + refreshTokenUrl: findValueUsingKey('refreshTokenUrl'), + clientId: findValueUsingKey('clientId'), + clientSecret: findValueUsingKey('clientSecret'), + scope: findValueUsingKey('scope'), + state: findValueUsingKey('state'), + tokenPlacement: findValueUsingKey('addTokenTo') === 'header' ? 'header' : 'url', + credentialsPlacement: findValueUsingKey('client_authentication') === 'body' ? 'body' : 'basic_auth_header' + }; + + switch (postmanGrantType) { + case 'authorization_code': + requestObject.auth.oauth2 = { + ...baseOAuth2Config, + authorizationUrl: findValueUsingKey('authUrl'), + callbackUrl: findValueUsingKey('redirect_uri'), + pkce: false // PKCE is not used for standard authorization_code + }; + break; + case 'authorization_code_with_pkce': + requestObject.auth.oauth2 = { + ...baseOAuth2Config, + authorizationUrl: findValueUsingKey('authUrl'), + callbackUrl: findValueUsingKey('redirect_uri'), + pkce: true, // Explicitly set pkce to true for this grant type + }; + break; + case 'password_credentials': + requestObject.auth.oauth2 = { + ...baseOAuth2Config, + username: findValueUsingKey('username'), + password: findValueUsingKey('password'), + }; + break; + case 'client_credentials': + requestObject.auth.oauth2 = baseOAuth2Config; + break; + default: + console.warn('Unexpected OAuth2 grant type after mapping:', targetGrantType); + requestObject.auth.oauth2 = baseOAuth2Config; // Fallback to default which is Client Credentials + break; } break; default: - requestObject.auth.mode = AUTH_TYPES.NONE; - console.warn('Unexpected auth.type', auth.type); + console.warn('Unexpected auth.type:', auth.type, '- Mode set, but no specific config generated.'); + break; } }; -const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorkers = false } = {}, scriptMap)=> { +const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } = {}, scriptMap) => { brunoParent.items = brunoParent.items || []; const folderMap = {}; const requestMap = {}; @@ -289,7 +299,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke }, request: { auth: { - mode: 'none', + mode: 'inherit', basic: null, bearer: null, awsv4: null, @@ -308,19 +318,14 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke brunoParent.items.push(brunoFolderItem); // Folder level auth - if (i.auth) { - processAuth(i.auth, brunoFolderItem.root.request); - } else if (parentAuth) { - // Inherit parent auth if folder doesn't define its own - processAuth(parentAuth, brunoFolderItem.root.request); - } + processAuth(i.auth, brunoFolderItem.root.request); if (i.item && i.item.length) { - importPostmanV2CollectionItem(brunoFolderItem, i.item, i.auth ?? parentAuth, { useWorkers }, scriptMap); + importPostmanV2CollectionItem(brunoFolderItem, i.item, { useWorkers }, scriptMap); } if (i.event) { - if(useWorkers) { + if (useWorkers) { scriptMap.set(brunoFolderItem.uid, { events: i.event, request: brunoFolderItem.root.request @@ -353,12 +358,12 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke uid: uuid(), name: requestName, type: 'http-request', - seq: index + 1, + seq: index + 1, request: { url: url, method: i?.request?.method?.toUpperCase(), auth: { - mode: 'none', + mode: 'inherit', basic: null, bearer: null, awsv4: null, @@ -389,8 +394,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke brunoParent.items.push(brunoRequestItem); if (i.event) { - if(useWorkers) { - scriptMap.set(brunoRequestItem.uid, { + if (useWorkers) { + scriptMap.set(brunoRequestItem.uid, { events: i.event, request: brunoRequestItem.request }); @@ -501,9 +506,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke }); }); - // Handle request-level auth or inherit from parent - const auth = i.request.auth ?? parentAuth; - processAuth(auth, brunoRequestItem.request); + // Request-level auth + processAuth(i.request.auth, brunoRequestItem.request); each(get(i, 'request.url.query'), (param) => { brunoRequestItem.request.params.push({ @@ -537,7 +541,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke }); }; - + const searchLanguageByHeader = (headers) => { let contentType; each(headers, (header) => { @@ -592,19 +596,19 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) => } // Collection level auth - processAuth(collection.auth, brunoCollection.root.request); + processAuth(collection.auth, brunoCollection.root.request, true); // Create a single scriptMap for all items const scriptMap = useWorkers ? new Map() : null; - - importPostmanV2CollectionItem(brunoCollection, collection.item, collection.auth, { useWorkers }, scriptMap); - + + importPostmanV2CollectionItem(brunoCollection, collection.item, { useWorkers }, scriptMap); + // Process all scripts in a single call at the top level if (useWorkers && scriptMap && scriptMap.size > 0) { try { - const { default: scriptTranslationWorker } = await import('../workers/postman-translator-worker'); + const { default: scriptTranslationWorker } = await import('../workers/postman-translator-worker'); const translatedScripts = await scriptTranslationWorker(scriptMap); - + // Apply translated scripts to all items in the collection const applyScriptsToItems = (items) => { items.forEach(item => { @@ -614,14 +618,17 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) => if (!item.root.request.script) { item.root.request.script = {}; } - + if (!item.root.request.tests) { + item.root.request.tests = ''; + } + const script = translatedScripts.get(item.uid).request?.script?.req; const tests = translatedScripts.get(item.uid).request?.script?.res; - + item.root.request.script.req = script && script.length > 0 ? script : ''; item.root.request.script.res = tests && tests.length > 0 ? tests : ''; } - + // Recursively apply to nested items if (item.items && item.items.length > 0) { applyScriptsToItems(item.items); @@ -631,26 +638,29 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) => if (!item.request.script) { item.request.script = {}; } - + if (!item.request.tests) { + item.request.tests = ''; + } + const script = translatedScripts.get(item.uid).request?.script?.req; const tests = translatedScripts.get(item.uid).request?.script?.res; - + item.request.script.req = script && script.length > 0 ? script : ''; item.request.script.res = tests && tests.length > 0 ? tests : ''; } } }); }; - + applyScriptsToItems(brunoCollection.items); - + } catch (error) { console.error('Error in script translation worker:', error); } finally { scriptMap.clear(); } } - + return brunoCollection; }; diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/collection-auth.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/collection-auth.spec.js index 77b2ea7d4..67cd32cd8 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/collection-auth.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/collection-auth.spec.js @@ -2,7 +2,48 @@ import { describe, it, expect } from '@jest/globals'; import postmanToBruno from '../../../src/postman/postman-to-bruno'; describe('Collection Authentication', () => { - it('should handle basic auth at collection level', async() => { + it('should handle no auth at collection level (when auth property is absent)', async () => { + const postmanCollection = { + info: { + name: 'Collection level no auth', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [], + event: [ + { + listen: 'prerequest', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + }, + { + listen: 'test', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + // console.log('result', JSON.stringify(result, null, 2)); + + expect(result.root.request.auth).toEqual({ + mode: 'none', + basic: null, + bearer: null, + awsv4: null, + apikey: null, + oauth2: null, + digest: null + }); + }); + + it('should handle basic auth at collection level', async () => { const postmanCollection = { info: { name: 'Collection level basic auth', @@ -61,7 +102,7 @@ describe('Collection Authentication', () => { }); }); - it('should handle bearer token auth at collection level', async() => { + it('should handle bearer token auth at collection level', async () => { const postmanCollection = { info: { name: 'Collection level bearer token', @@ -112,9 +153,9 @@ describe('Collection Authentication', () => { oauth2: null, digest: null }); - }); + }); - it('should handle API key auth at collection level', async() => { + it('should handle API key auth at collection level', async () => { const postmanCollection = { info: { name: 'Collection level api key', @@ -173,7 +214,7 @@ describe('Collection Authentication', () => { }); }); - it('should handle digest auth at collection level', async() => { + it('should handle digest auth at collection level', async () => { const postmanCollection = { info: { name: 'Collection level digest auth', @@ -235,8 +276,7 @@ describe('Collection Authentication', () => { } }); }); - - it('should handle missing auth values when auth.type exists', async() => { + it('should handle missing auth values when auth.type exists', async () => { const postmanCollection = { info: { name: 'Collection with missing auth values', @@ -283,7 +323,7 @@ describe('Collection Authentication', () => { }); }); - it('should handle missing auth values for different auth types', async() => { + it('should handle missing auth values for different auth types', async () => { const postmanCollection = { info: { name: 'Collection with missing auth values for different types', diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/folder-auth.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/folder-auth.spec.js index 7e273826e..9ab0534eb 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/folder-auth.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/folder-auth.spec.js @@ -2,7 +2,130 @@ import { describe, it, expect } from '@jest/globals'; import postmanToBruno from '../../../src/postman/postman-to-bruno'; describe('Folder Authentication', () => { - it('should handle basic auth at folder level', async() => { + it('should handle "Inherit Auth" at folder level (auth property absent)', async () => { + const postmanCollection = { + info: { + name: 'Folder Inherit Auth', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [ + { + name: 'Inheriting Folder', + items: [] + } + ], + auth: { + type: 'basic', + basic: [ + { + key: 'password', + value: 'testpass', + type: 'string' + }, + { + key: 'username', + value: 'testuser', + type: 'string' + } + ] + }, + event: [ + { + listen: 'prerequest', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + }, + { + listen: 'test', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + expect(result.items[0].root.request.auth).toEqual({ + mode: 'inherit', + basic: null, + bearer: null, + awsv4: null, + apikey: null, + oauth2: null, + digest: null + }); + }); + + it('should handle explicit "No Auth" at folder level', async () => { + const postmanCollection = { + info: { + name: 'Folder No Auth', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [ + { + name: 'No Auth Folder', + item: [], + auth: { + type: 'noauth' + } + } + ], + auth: { + type: 'basic', + basic: [ + { + key: 'password', + value: 'testpass', + type: 'string' + }, + { + key: 'username', + value: 'testuser', + type: 'string' + } + ] + }, + event: [ + { + listen: 'prerequest', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + }, + { + listen: 'test', + script: { + type: 'text/javascript', + packages: {}, + exec: [''] + } + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + expect(result.items[0].root.request.auth).toEqual({ + mode: 'none', + basic: null, + bearer: null, + awsv4: null, + apikey: null, + oauth2: null, + digest: null + }); + }); + + it('should handle basic auth at folder level', async () => { const postmanCollection = { info: { name: 'Folder level basic auth', @@ -65,7 +188,7 @@ describe('Folder Authentication', () => { }); }); - it('should handle bearer token auth at folder level', async() => { + it('should handle bearer token auth at folder level', async () => { const postmanCollection = { info: { name: 'Folder level bearer token', @@ -120,7 +243,7 @@ describe('Folder Authentication', () => { }); }); - it('should handle API key auth at folder level', async() => { + it('should handle API key auth at folder level', async () => { const postmanCollection = { info: { name: 'Folder level API key', @@ -180,7 +303,7 @@ describe('Folder Authentication', () => { }); }); - it('should handle digest auth at folder level', async() => { + it('should handle digest auth at folder level', async () => { const postmanCollection = { info: { name: 'Folder level digest auth', @@ -245,7 +368,7 @@ describe('Folder Authentication', () => { }); }); - it('should handle missing auth values in folder level auth', async() => { + it('should handle missing auth values in folder level auth', async () => { const postmanCollection = { info: { name: 'Folder with missing auth values', diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js index e303ab3d3..84a3ccba9 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js @@ -154,7 +154,7 @@ const expectedOutput = { "url": "https://usebruno.com", "method": "GET", "auth": { - "mode": "none", + "mode": "inherit", "basic": null, "bearer": null, "awsv4": null, @@ -183,7 +183,7 @@ const expectedOutput = { }, "request": { "auth": { - "mode": "none", + "mode": "inherit", "basic": null, "bearer": null, "awsv4": null, @@ -207,7 +207,7 @@ const expectedOutput = { "url": "https://usebruno.com", "method": "GET", "auth": { - "mode": "none", + "mode": "inherit", "basic": null, "bearer": null, "awsv4": null, diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/process-auth.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/process-auth.spec.js index 8bbf697a5..2b4d1c7b4 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/process-auth.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/process-auth.spec.js @@ -354,16 +354,13 @@ describe('processAuth', () => { processAuth(auth, requestObject); expect(requestObject.auth.mode).toBe('oauth2'); expect(requestObject.auth.oauth2).toEqual({ - grantType: 'authorization_code', - authorizationUrl: '', - callbackUrl: '', + grantType: 'client_credentials', accessTokenUrl: '', refreshTokenUrl: '', clientId: '', clientSecret: '', scope: '', state: '', - pkce: false, tokenPlacement: 'url', credentialsPlacement: 'basic_auth_header' }); @@ -376,16 +373,13 @@ describe('processAuth', () => { processAuth(auth, requestObject); expect(requestObject.auth.mode).toBe('oauth2'); expect(requestObject.auth.oauth2).toEqual({ - grantType: 'authorization_code', - authorizationUrl: '', - callbackUrl: '', + grantType: 'client_credentials', accessTokenUrl: '', refreshTokenUrl: '', clientId: '', clientSecret: '', scope: '', state: '', - pkce: false, tokenPlacement: 'url', credentialsPlacement: 'basic_auth_header' }); diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/request-auth.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/request-auth.spec.js index 8a1c5fa27..09cc712c9 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/request-auth.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/request-auth.spec.js @@ -2,7 +2,9 @@ import { describe, it, expect } from '@jest/globals'; import postmanToBruno from '../../../src/postman/postman-to-bruno'; describe('Request Authentication', () => { - it('should handle basic auth at request level', async() => { + + + it('should handle basic auth at request level', async () => { const postmanCollection = { info: { name: 'Request Auth Collection', @@ -42,7 +44,7 @@ describe('Request Authentication', () => { }); }); - it('should inherit folder auth when request has no auth', async() => { + it('should inherit folder auth when request has no auth', async () => { const postmanCollection = { info: { name: 'Inherit Request Auth Collection', @@ -57,7 +59,7 @@ describe('Request Authentication', () => { }, item: [ { - name: 'No Auth Request', + name: 'Inherit Auth Request', request: { method: 'GET', url: 'https://api.example.com/test' @@ -71,11 +73,9 @@ describe('Request Authentication', () => { const result = await postmanToBruno(postmanCollection); expect(result.items[0].items[0].request.auth).toEqual({ - mode: 'bearer', + mode: 'inherit', basic: null, - bearer: { - token: 'foldertoken' - }, + bearer: null, awsv4: null, apikey: null, oauth2: null, @@ -83,31 +83,113 @@ describe('Request Authentication', () => { }); }); - it('should override folder auth with request auth', async() => { + it('should handle "Inherit Auth" for request (auth property absent, inherits from folder)', async () => { const postmanCollection = { info: { - name: 'Override Request Auth Collection', + name: 'Request Inherit Auth from Folder', schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' }, item: [ { name: 'Auth Folder', - auth: { - type: 'basic', - basic: [ - { key: 'username', value: 'folderuser' }, - { key: 'password', value: 'folderpass' } - ] + auth: { // Folder has auth + type: 'bearer', + bearer: [{ key: 'token', value: 'foldertoken' }] }, item: [ { - name: 'Override Auth Request', + name: 'Inheriting Request', + request: { + method: 'GET', + url: 'https://api.example.com/test' + // auth property is ABSENT for this request, meaning "Inherit auth from parent" + } + } + ] + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + expect(result.items[0].items[0].request.auth).toEqual({ + mode: 'inherit', + basic: null, + bearer: null, // It should NOT have the folder's token directly here after import + awsv4: null, + apikey: null, + oauth2: null, + digest: null + }); + }); + + it('should handle "Inherit Auth" for request (auth property absent, inherits from collection if folder also inherits)', async () => { + const postmanCollection = { + info: { + name: 'Request Inherit Auth from Collection', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + auth: { // Collection has auth + type: 'basic', + basic: [ + + { key: 'username', value: 'requestuser' }, + { key: 'password', value: 'requestpass' } + ] + }, + item: [ + { + name: 'Inheriting Folder', + // auth property is ABSENT for this folder + item: [ + { + name: 'Inheriting Request', + request: { + method: 'GET', + url: 'https://api.example.com/test' + // auth property is ABSENT for this request + } + } + ] + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + // Check folder first + expect(result.items[0].root.request.auth).toEqual({ + mode: 'inherit', + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + // Then check request + expect(result.items[0].items[0].request.auth).toEqual({ + mode: 'inherit', + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + }); + + it('should handle explicit "No Auth" at request level', async () => { + const postmanCollection = { + info: { + name: 'Request No Auth', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [ + { + name: 'Auth Folder', // Parent folder might have auth + auth: { + type: 'bearer', + bearer: [{ key: 'token', value: 'foldertoken' }] + }, + item: [ + { + name: 'Explicit No Auth Request', request: { method: 'GET', url: 'https://api.example.com/test', - auth: { - type: 'bearer', - bearer: [{ key: 'token', value: 'requesttoken' }] + auth: { // Request explicitly set to "No Auth" + type: 'noauth' } } } @@ -119,46 +201,8 @@ describe('Request Authentication', () => { const result = await postmanToBruno(postmanCollection); expect(result.items[0].items[0].request.auth).toEqual({ - mode: 'bearer', + mode: 'none', // <<<< KEY CHECK basic: null, - bearer: { - token: 'requesttoken' - }, - awsv4: null, - apikey: null, - oauth2: null, - digest: null - }); - }); - - it('should handle missing basic auth values in request level', async() => { - const postmanCollection = { - info: { - name: 'Missing Auth Request Collection', - schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' - }, - item: [ - { - name: 'Missing Auth Request', - request: { - method: 'GET', - url: 'https://api.example.com/test', - auth: { - type: 'basic' - } - } - } - ] - }; - - const result = await postmanToBruno(postmanCollection); - - expect(result.items[0].request.auth).toEqual({ - mode: 'basic', - basic: { - username: '', - password: '' - }, bearer: null, awsv4: null, apikey: null, @@ -166,4 +210,186 @@ describe('Request Authentication', () => { digest: null }); }); + + it('should handle "Inherit Auth" for a request nested under multiple inheriting folders', async () => { + const postmanCollection = { + info: { + name: 'Multi-Level Inherit Auth', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + auth: { // Collection level auth + type: 'basic', + basic: [ + { key: 'username', value: 'collectionUser' }, + { key: 'password', value: 'collectionPass' } + ] + }, + item: [ + { + name: 'Folder Level 1 (Inherit)', + // auth property is ABSENT for this folder, meaning "Inherit" + item: [ + { + name: 'Folder Level 2 (Inherit)', + // auth property is ABSENT for this folder, meaning "Inherit" + item: [ + { + name: 'Deeply Nested Request (Inherit)', + request: { + method: 'GET', + url: 'https://api.example.com/deep' + // auth property is ABSENT for this request, meaning "Inherit" + } + } + ] + } + ] + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + // Check Folder Level 1 + expect(result.items[0].root.request.auth).toEqual({ + mode: 'inherit', + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check Folder Level 2 + expect(result.items[0].items[0].root.request.auth).toEqual({ + mode: 'inherit', + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check the Request + expect(result.items[0].items[0].items[0].request.auth).toEqual({ + mode: 'inherit', + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + }); + + it('should handle "Inherit Auth" where an intermediate folder has explicit auth', async () => { + const postmanCollection = { + info: { + name: 'Multi-Level Inherit with Override', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + auth: { // Collection level auth + type: 'basic', + basic: [ + { key: 'username', value: 'collectionUser' }, + { key: 'password', value: 'collectionPass' } + ] + }, + item: [ + { + name: 'Folder Level 1 (Explicit Bearer)', + auth: { // This folder has its own auth + type: 'bearer', + bearer: [{ key: 'token', value: 'folder1Token' }] + }, + item: [ + { + name: 'Folder Level 2 (Inherit from Folder 1)', + // auth property is ABSENT for this folder, meaning "Inherit" + item: [ + { + name: 'Deeply Nested Request (Inherit from Folder 1 via Folder 2)', + request: { + method: 'GET', + url: 'https://api.example.com/deep_override' + // auth property is ABSENT for this request, meaning "Inherit" + } + } + ] + } + ] + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + // Check Folder Level 1 + expect(result.items[0].root.request.auth).toEqual({ + mode: 'bearer', + basic: null, + bearer: { token: 'folder1Token' }, // Explicitly set + awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check Folder Level 2 + expect(result.items[0].items[0].root.request.auth).toEqual({ + mode: 'inherit', // Inherits from Folder 1 + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check the Request + expect(result.items[0].items[0].items[0].request.auth).toEqual({ + mode: 'inherit', // Inherits from Folder 1 + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + }); + + it('should handle "Inherit Auth" where an intermediate folder has explicit "No Auth"', async () => { + const postmanCollection = { + info: { + name: 'Multi-Level Inherit with No Auth Stop', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + auth: { // Collection level auth + type: 'basic', + basic: [ + { key: 'username', value: 'collectionUser' }, + { key: 'password', value: 'collectionPass' } + ] + }, + item: [ + { + name: 'Folder Level 1 (Explicit No Auth)', + auth: { // This folder is explicitly "No Auth" + type: 'noauth' + }, + item: [ + { + name: 'Folder Level 2 (Inherit from Folder 1 - so No Auth)', + // auth property is ABSENT for this folder, meaning "Inherit" + item: [ + { + name: 'Deeply Nested Request (Inherit from Folder 1 via Folder 2 - so No Auth)', + request: { + method: 'GET', + url: 'https://api.example.com/deep_no_auth_stop' + // auth property is ABSENT for this request, meaning "Inherit" + } + } + ] + } + ] + } + ] + }; + + const result = await postmanToBruno(postmanCollection); + + // Check Folder Level 1 + expect(result.items[0].root.request.auth).toEqual({ + mode: 'none', // Explicitly "No Auth" + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check Folder Level 2 + expect(result.items[0].items[0].root.request.auth).toEqual({ + mode: 'inherit', // Inherits from Folder 1 + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + + // Check the Request + expect(result.items[0].items[0].items[0].request.auth).toEqual({ + mode: 'inherit', // Inherits from Folder 1 + basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null + }); + }); + });