diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index 7e7f5d3ac..4b0a324ef 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -72,6 +72,45 @@ const prepareRequest = (item = {}, collection = {}) => { password: get(collectionAuth, 'digest.password') }; } + + if (collectionAuth.mode === 'awsv4') { + axiosRequest.awsv4config = { + accessKeyId: get(collectionAuth, 'awsv4.accessKeyId'), + secretAccessKey: get(collectionAuth, 'awsv4.secretAccessKey'), + sessionToken: get(collectionAuth, 'awsv4.sessionToken'), + service: get(collectionAuth, 'awsv4.service'), + region: get(collectionAuth, 'awsv4.region'), + profileName: get(collectionAuth, 'awsv4.profileName') + }; + } + + if (collectionAuth.mode === 'ntlm') { + axiosRequest.ntlmConfig = { + username: get(collectionAuth, 'ntlm.username'), + password: get(collectionAuth, 'ntlm.password'), + domain: get(collectionAuth, 'ntlm.domain') + }; + } + + if (collectionAuth.mode === 'wsse') { + const username = get(collectionAuth, 'wsse.username', ''); + const password = get(collectionAuth, 'wsse.password', ''); + + const ts = new Date().toISOString(); + const nonce = crypto.randomBytes(16).toString('hex'); + + // Create the password digest using SHA-1 as required for WSSE + const hash = crypto.createHash('sha1'); + hash.update(nonce + ts + password); + const digest = Buffer.from(hash.digest('hex').toString('utf8')).toString('base64'); + + // Construct the WSSE header + axiosRequest.headers[ + 'X-WSSE' + ] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`; + } + + console.log('axiosRequest', axiosRequest); } if (request.auth && request.auth.mode !== 'inherit') { @@ -129,6 +168,24 @@ const prepareRequest = (item = {}, collection = {}) => { password: get(request, 'auth.digest.password') }; } + + if (request.auth.mode === 'apikey') { + if (request.auth.apikey?.placement === 'header') { + axiosRequest.headers[request.auth.apikey?.key] = request.auth.apikey?.value; + } + + if (request.auth.apikey?.placement === 'queryparams') { + if (axiosRequest.url && request.auth.apikey?.key) { + try { + const urlObj = new URL(request.url); + urlObj.searchParams.set(request.auth.apikey?.key, request.auth.apikey?.value); + axiosRequest.url = urlObj.toString(); + } catch (error) { + console.error('Invalid URL:', request.url, error); + } + } + } + } } request.body = request.body || {}; diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 39b79972f..4c6e86c69 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -150,6 +150,92 @@ describe('prepare-request: prepareRequest', () => { }); }); + describe('AWS v4 Authentication', () => { + it('If collection auth is AWS v4', () => { + collection.root.request.auth = { + mode: 'awsv4', + awsv4: { + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + sessionToken: 'session-token', + service: 's3', + region: 'us-west-2', + profileName: 'default' + } + }; + + const result = prepareRequest(item, collection); + const expected = { + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + sessionToken: 'session-token', + service: 's3', + region: 'us-west-2', + profileName: 'default' + }; + expect(result.awsv4config).toEqual(expected); + }); + }); + + describe('NTLM Authentication', () => { + it('If collection auth is NTLM', () => { + collection.root.request.auth = { + mode: 'ntlm', + ntlm: { + username: 'testUser', + password: 'testPass123', + domain: 'testDomain' + } + }; + + const result = prepareRequest(item, collection); + const expected = { + username: 'testUser', + password: 'testPass123', + domain: 'testDomain' + }; + expect(result.ntlmConfig).toEqual(expected); + }); + }); + + describe('WSSE Authentication', () => { + it('If collection auth is WSSE', () => { + collection.root.request.auth = { + mode: 'wsse', + wsse: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(item, collection); + expect(result.headers).toHaveProperty('X-WSSE'); + expect(result.headers['X-WSSE']).toContain('UsernameToken Username="testUser"'); + expect(result.headers['X-WSSE']).toContain('PasswordDigest="'); + expect(result.headers['X-WSSE']).toContain('Nonce="'); + expect(result.headers['X-WSSE']).toContain('Created="'); + }); + }); + + describe('Digest Authentication', () => { + it('If collection auth is digest auth', () => { + collection.root.request.auth = { + mode: 'digest', + digest: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(item, collection); + const expected = { + username: 'testUser', + password: 'testPass123' + }; + expect(result.digestConfig).toEqual(expected); + }); + }); + describe('No Authentication', () => { it('If request does not have auth configured', () => { delete item.request.auth; @@ -161,4 +247,209 @@ describe('prepare-request: prepareRequest', () => { }); }); }); + + describe('Properly maps request-level auth', () => { + let item; + + beforeEach(() => { + item = { + name: 'Test Request', + type: 'http-request', + request: { + method: 'GET', + headers: [], + params: [], + url: 'https://usebruno.com', + auth: { + mode: 'basic' // Will be overridden in each test + }, + script: { + req: 'console.log("Pre Request")', + res: 'console.log("Post Response")' + } + } + }; + }); + + describe('API Key Authentication', () => { + it('If request auth is apikey in header', () => { + item.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + const result = prepareRequest(item); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If request auth is apikey in header and request has existing headers', () => { + item.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true }); + const result = prepareRequest(item); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If request auth is apikey in query parameters', () => { + item.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "queryparams" + } + }; + + const urlObj = new URL(item.request.url); + urlObj.searchParams.set(item.request.auth.apikey.key, item.request.auth.apikey.value); + + const expected = urlObj.toString(); + const result = prepareRequest(item); + expect(result.url).toEqual(expected); + }); + }); + + describe('Basic Authentication', () => { + it('If request auth is basic auth', () => { + item.request.auth = { + mode: 'basic', + basic: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(item); + const expected = { username: 'testUser', password: 'testPass123' }; + expect(result.basicAuth).toEqual(expected); + }); + }); + + describe('Bearer Token Authentication', () => { + it('If request auth is bearer token', () => { + item.request.auth = { + mode: 'bearer', + bearer: { + token: 'token123' + } + }; + + const result = prepareRequest(item); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token123'); + }); + + it('If request auth is bearer token and request has existing headers', () => { + item.request.auth = { + mode: 'bearer', + bearer: { + token: 'token123' + } + }; + + item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true }); + + const result = prepareRequest(item); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token123'); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + }); + }); + + describe('AWS v4 Authentication', () => { + it('If request auth is AWS v4', () => { + item.request.auth = { + mode: 'awsv4', + awsv4: { + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + sessionToken: 'request-session-token', + service: 'dynamodb', + region: 'us-east-1', + profileName: 'dev' + } + }; + + const result = prepareRequest(item); + const expected = { + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + sessionToken: 'request-session-token', + service: 'dynamodb', + region: 'us-east-1', + profileName: 'dev' + }; + expect(result.awsv4config).toEqual(expected); + }); + }); + + describe('NTLM Authentication', () => { + it('If request auth is NTLM', () => { + item.request.auth = { + mode: 'ntlm', + ntlm: { + username: 'testUser', + password: 'testPass123', + domain: 'testDomain' + } + }; + + const result = prepareRequest(item); + const expected = { + username: 'testUser', + password: 'testPass123', + domain: 'testDomain' + }; + expect(result.ntlmConfig).toEqual(expected); + }); + }); + + describe('WSSE Authentication', () => { + it('If request auth is WSSE', () => { + item.request.auth = { + mode: 'wsse', + wsse: { + username: 'requestUser', + password: 'requestPass' + } + }; + + const result = prepareRequest(item); + expect(result.headers).toHaveProperty('X-WSSE'); + expect(result.headers['X-WSSE']).toContain('UsernameToken Username="requestUser"'); + expect(result.headers['X-WSSE']).toContain('PasswordDigest="'); + expect(result.headers['X-WSSE']).toContain('Nonce="'); + expect(result.headers['X-WSSE']).toContain('Created="'); + }); + }); + + describe('Digest Authentication', () => { + it('If request auth is digest auth', () => { + item.request.auth = { + mode: 'digest', + digest: { + username: 'requestUser', + password: 'requestPass123' + } + }; + + const result = prepareRequest(item); + const expected = { + username: 'requestUser', + password: 'requestPass123' + }; + expect(result.digestConfig).toEqual(expected); + }); + }); + }); });