diff --git a/package-lock.json b/package-lock.json index e2a22b456a..47beab0320 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "packages/bruno-graphql-docs" ], "dependencies": { + "axios-ntlm": "^1.4.2", "json-bigint": "^1.0.0", "lossless-json": "^4.0.1" }, @@ -7158,6 +7159,18 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-ntlm": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.2.tgz", + "integrity": "sha512-8mS/uhmSWiRBiFKQvysPbX1eDBp6e+eXskmasuAXRHrn1Zjgji3O/oGXzXLw7tOhyD9nho1vGjZ2OYOD3cCvHg==", + "license": "MIT", + "dependencies": { + "axios": "^1.6.1", + "des.js": "^1.1.0", + "dev-null": "^0.1.1", + "js-md4": "^0.3.2" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -9240,6 +9253,16 @@ "node": ">=6" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -9279,6 +9302,12 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, + "node_modules/dev-null": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", + "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -13146,6 +13175,12 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -14033,6 +14068,12 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -19830,7 +19871,7 @@ }, "packages/bruno-electron": { "name": "bruno", - "version": "v1.33.0", + "version": "v1.33.1", "dependencies": { "@aws-sdk/credential-providers": "3.658.1", "@usebruno/common": "0.1.0", diff --git a/package.json b/package.json index 3d8a31df2b..1732bb1769 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,10 @@ "prepare": "husky install" }, "overrides": { - "rollup":"3.29.5" + "rollup": "3.29.5" }, "dependencies": { + "axios-ntlm": "^1.4.2", "json-bigint": "^1.0.0", "lossless-json": "^4.0.1" } diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js index 7dabb4c71a..2c541cc289 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js @@ -79,6 +79,15 @@ const AuthMode = ({ collection }) => { > Digest Auth +
{ + dropdownTippyRef.current.hide(); + onModeChange('ntlm'); + }} + > + NTLM Auth +
{ diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/StyledWrapper.js new file mode 100644 index 0000000000..316d3a7c5f --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/StyledWrapper.js @@ -0,0 +1,17 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + + .single-line-editor-wrapper { + max-width: 400px; + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js new file mode 100644 index 0000000000..341c805dcf --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js @@ -0,0 +1,110 @@ +import React from 'react'; +import get from 'lodash/get'; +import { useTheme } from 'providers/Theme'; +import { useDispatch } from 'react-redux'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections'; +import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; + + + + + +const NTLMAuth = ({ collection }) => { + + + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + + const ntlmAuth = get(collection, 'root.request.auth.ntlm', {}); + + const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); + + + const handleUsernameChange = (username) => { + dispatch( + updateCollectionAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + content: { + username: username, + password: ntlmAuth.password, + domain: ntlmAuth.domain + + } + }) + ); + }; + + const handlePasswordChange = (password) => { + dispatch( + updateCollectionAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + content: { + username: ntlmAuth.username, + password: password, + domain: ntlmAuth.domain + } + }) + ); + }; + + const handleDomainChange = (domain) => { + dispatch( + updateCollectionAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + content: { + username: ntlmAuth.username, + password: ntlmAuth.password, + domain: domain + } + }) + ); + }; + + + + + return ( + + +
+ handleUsernameChange(val)} + collection={collection} + /> +
+ + +
+ handlePasswordChange(val)} + collection={collection} + isSecret={true} + /> +
+ + +
+ handleDomainChange(val)} + collection={collection} + /> +
+
+ ); +}; + +export default NTLMAuth; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/index.js index 05efc17b23..c19ae98738 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/index.js @@ -11,6 +11,8 @@ import ApiKeyAuth from './ApiKeyAuth/'; import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import OAuth2 from './OAuth2'; +import NTLMAuth from './NTLMAuth'; + const Auth = ({ collection }) => { const authMode = get(collection, 'root.request.auth.mode'); @@ -32,6 +34,9 @@ const Auth = ({ collection }) => { case 'digest': { return ; } + case 'ntlm': { + return ; + } case 'oauth2': { return ; } diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js index dfbaba7fa0..1e3bedc2f3 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js @@ -70,6 +70,15 @@ const AuthMode = ({ item, collection }) => { > Digest Auth
+
{ + dropdownTippyRef?.current?.hide(); + onModeChange('ntlm'); + }} + > + NTLM Auth +
{ diff --git a/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/StyledWrapper.js new file mode 100644 index 0000000000..316d3a7c5f --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/StyledWrapper.js @@ -0,0 +1,17 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + + .single-line-editor-wrapper { + max-width: 400px; + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js new file mode 100644 index 0000000000..65e7560418 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js @@ -0,0 +1,110 @@ +import React from 'react'; +import get from 'lodash/get'; +import { useTheme } from 'providers/Theme'; +import { useDispatch } from 'react-redux'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { updateAuth } from 'providers/ReduxStore/slices/collections'; +import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; + +const NTLMAuth = ({ item, collection }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + + const ntlmAuth = item.draft ? get(item, 'draft.request.auth.ntlm', {}) : get(item, 'request.auth.ntlm', {}); + + const handleRun = () => dispatch(sendRequest(item, collection.uid)); + const handleSave = () => dispatch(saveRequest(item.uid, collection.uid)); + + const handleUsernameChange = (username) => { + dispatch( + updateAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + username: username, + password: ntlmAuth.password, + domain: ntlmAuth.domain + + } + }) + ); + }; + + const handlePasswordChange = (password) => { + dispatch( + updateAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + username: ntlmAuth.username, + password: password, + domain: ntlmAuth.domain + } + }) + ); + }; + + const handleDomainChange = (domain) => { + dispatch( + updateAuth({ + mode: 'ntlm', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + username: ntlmAuth.username, + password: ntlmAuth.password, + domain: domain + } + }) + ); + }; + + return ( + + +
+ handleUsernameChange(val)} + onRun={handleRun} + collection={collection} + item={item} + /> +
+ + +
+ handlePasswordChange(val)} + onRun={handleRun} + collection={collection} + item={item} + isSecret={true} + /> +
+ + +
+ handleDomainChange(val)} + onRun={handleRun} + collection={collection} + item={item} + /> +
+
+ ); +}; + +export default NTLMAuth; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js index 1515e5224f..743d23267e 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js @@ -6,6 +6,8 @@ import BearerAuth from './BearerAuth'; import BasicAuth from './BasicAuth'; import DigestAuth from './DigestAuth'; import WsseAuth from './WsseAuth'; +import NTLMAuth from './NTLMAuth'; + import ApiKeyAuth from './ApiKeyAuth'; import StyledWrapper from './StyledWrapper'; import { humanizeRequestAuthMode } from 'utils/collections/index'; @@ -31,6 +33,9 @@ const Auth = ({ item, collection }) => { case 'digest': { return ; } + case 'ntlm': { + return ; + } case 'oauth2': { return ; } diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index b7ef2f86e5..1f7b026e17 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -473,6 +473,10 @@ export const collectionsSlice = createSlice({ item.draft.request.auth.mode = 'digest'; item.draft.request.auth.digest = action.payload.content; break; + case 'ntlm': + item.draft.request.auth.mode = 'ntlm'; + item.draft.request.auth.ntlm = action.payload.content; + break; case 'oauth2': item.draft.request.auth.mode = 'oauth2'; item.draft.request.auth.oauth2 = action.payload.content; @@ -1142,6 +1146,9 @@ export const collectionsSlice = createSlice({ case 'digest': set(collection, 'root.request.auth.digest', action.payload.content); break; + case 'ntlm': + set(collection, 'root.request.auth.ntlm', action.payload.content); + break; case 'oauth2': set(collection, 'root.request.auth.oauth2', action.payload.content); break; diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index cd925054d4..d206981c0b 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -339,6 +339,13 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} password: get(si.request, 'auth.digest.password', '') }; break; + case 'ntlm': + di.request.auth.ntlm = { + username: get(si.request, 'auth.ntlm.username', ''), + password: get(si.request, 'auth.ntlm.password', ''), + domain: get(si.request, 'auth.ntlm.domain', '') + }; + break; case 'oauth2': let grantType = get(si.request, 'auth.oauth2.grantType', ''); switch (grantType) { @@ -674,6 +681,10 @@ export const humanizeRequestAuthMode = (mode) => { label = 'Digest Auth'; break; } + case 'ntlm': { + label = 'NTLM'; + break; + } case 'oauth2': { label = 'OAuth 2.0'; break; diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 5020f8475a..167706f618 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -28,8 +28,10 @@ "@usebruno/common": "0.1.0", "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", + "@usebruno/vm2": "^3.9.13", "aws4-axios": "^3.3.0", "axios": "1.7.5", + "axios-ntlm": "^1.4.2", "chai": "^4.3.7", "chalk": "^3.0.0", "decomment": "^0.9.5", @@ -41,7 +43,6 @@ "lodash": "^4.17.21", "qs": "^6.11.0", "socks-proxy-agent": "^8.0.2", - "@usebruno/vm2": "^3.9.13", "xmlbuilder": "^15.1.1", "yargs": "^17.6.2" } diff --git a/packages/bruno-cli/src/runner/interpolate-vars.js b/packages/bruno-cli/src/runner/interpolate-vars.js index 2b727e671f..e41e489e85 100644 --- a/packages/bruno-cli/src/runner/interpolate-vars.js +++ b/packages/bruno-cli/src/runner/interpolate-vars.js @@ -158,6 +158,13 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn request.awsv4config.profileName = _interpolate(request.awsv4config.profileName) || ''; } + // interpolate vars for ntlmConfig auth + if (request.ntlmConfig) { + request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || ''; + request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || ''; + request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || ''; + } + if (request) return request; }; diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index bc2b228866..69c98dd57d 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -67,6 +67,14 @@ const prepareRequest = (request, collectionRoot) => { }; } + if (request.auth.mode === 'ntlm') { + axiosRequest.ntlmConfig = { + username: get(request, 'auth.ntlm.username'), + password: get(request, 'auth.ntlm.password'), + domain: get(request, 'auth.ntlm.domain') + }; + } + if (request.auth.mode === 'bearer') { axiosRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`; } diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index cb59c78ba5..330eac26b0 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -21,6 +21,8 @@ const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util' const path = require('path'); const { createFormData } = require('../utils/common'); const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; +const { NtlmClient } = require('axios-ntlm'); + const onConsoleLog = (type, args) => { console[type](...args); @@ -193,8 +195,13 @@ const runSingleRequest = async function ( let response, responseTime; try { - // run request - const axiosInstance = makeAxiosInstance(); + + let axiosInstance = makeAxiosInstance(); + if (request.ntlmConfig) { + axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance) + delete request.ntlmConfig; + } + if (request.awsv4config) { // todo: make this happen in prepare-request.js diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 9c4307a5df..26715d0132 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -19,7 +19,9 @@ "test": "node --experimental-vm-modules $(npx which jest)" }, "jest": { - "modulePaths": ["node_modules"] + "modulePaths": [ + "node_modules" + ] }, "dependencies": { "@aws-sdk/credential-providers": "3.658.1", @@ -28,9 +30,11 @@ "@usebruno/lang": "0.12.0", "@usebruno/node-machine-id": "^2.0.0", "@usebruno/schema": "0.7.0", + "@usebruno/vm2": "^3.9.13", "about-window": "^1.15.2", "aws4-axios": "^3.3.0", "axios": "1.7.5", + "axios-ntlm": "^1.4.2", "chai": "^4.3.7", "chokidar": "^3.5.3", "content-disposition": "^0.5.4", @@ -56,7 +60,6 @@ "socks-proxy-agent": "^8.0.2", "tough-cookie": "^4.1.3", "uuid": "^9.0.0", - "@usebruno/vm2": "^3.9.13", "yup": "^0.32.11" }, "optionalDependencies": { diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 97e3b8557a..53261aaa0a 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -39,6 +39,7 @@ const Oauth2Store = require('../../store/oauth2'); const iconv = require('iconv-lite'); const FormData = require('form-data'); const { createFormData } = prepareRequest; +const { NtlmClient } = require('axios-ntlm'); const safeStringifyJSON = (data) => { try { @@ -263,7 +264,15 @@ const configureRequest = async ( ...httpsAgentRequestFields }); } - const axiosInstance = makeAxiosInstance(); + + + let axiosInstance = makeAxiosInstance(); + + if (request.ntlmConfig) { + axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance) + delete request.ntlmConfig; + } + if (request.oauth2) { let requestCopy = cloneDeep(request); diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 59f4944169..1f2b0b2ff2 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -230,6 +230,14 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.wsse.password = _interpolate(request.wsse.password) || ''; } + + // interpolate vars for ntlmConfig auth + if (request.ntlmConfig) { + request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || ''; + request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || ''; + request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || ''; + } + return request; }; diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index c8b36bb89f..e920e2f15e 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -191,6 +191,7 @@ const createFormData = (datas, collectionPath) => { }; const setAuthHeaders = (axiosRequest, request, collectionRoot) => { + const collectionAuth = get(collectionRoot, 'request.auth'); if (collectionAuth && request.auth.mode === 'inherit') { switch (collectionAuth.mode) { @@ -219,6 +220,13 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { password: get(collectionAuth, 'digest.password') }; break; + case 'ntlm': + axiosRequest.ntlmConfig = { + username: get(collectionAuth, 'ntlm.username'), + password: get(collectionAuth, 'ntlm.password'), + domain: get(collectionAuth, 'ntlm.domain') + }; + break; case 'wsse': const username = get(request, 'auth.wsse.username', ''); const password = get(request, 'auth.wsse.password', ''); @@ -275,6 +283,13 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { password: get(request, 'auth.digest.password') }; break; + case 'ntlm': + axiosRequest.ntlmConfig = { + username: get(request, 'auth.ntlm.username'), + password: get(request, 'auth.ntlm.password'), + domain: get(request, 'auth.ntlm.domain') + }; + break; case 'oauth2': const grantType = get(request, 'auth.oauth2.grantType'); switch (grantType) { diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index c84d36d073..e0b0cf58ad 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils'); */ const grammar = ohm.grammar(`Bru { BruFile = (meta | http | query | params | headers | auths | bodies | varsandassert | script | tests | docs)* - auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authwsse | authapikey + auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM | authOAuth2 | authwsse | authapikey bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body bodyforms = bodyformurlencoded | bodymultipart params = paramspath | paramsquery @@ -87,6 +87,7 @@ const grammar = ohm.grammar(`Bru { authbasic = "auth:basic" dictionary authbearer = "auth:bearer" dictionary authdigest = "auth:digest" dictionary + authNTLM = "auth:ntlm" dictionary authOAuth2 = "auth:oauth2" dictionary authwsse = "auth:wsse" dictionary authapikey = "auth:apikey" dictionary @@ -435,6 +436,26 @@ const sem = grammar.createSemantics().addAttribute('ast', { } }; }, + authNTLM(_1, dictionary) { + const auth = mapPairListToKeyValPairs(dictionary.ast, false); + const usernameKey = _.find(auth, { name: 'username' }); + const passwordKey = _.find(auth, { name: 'password' }); + const domainKey = _.find(auth, { name: 'domain' }); + + const username = usernameKey ? usernameKey.value : ''; + const password = passwordKey ? passwordKey.value : ''; + const domain = passwordKey ? domainKey.value : ''; + + return { + auth: { + ntlm: { + username, + password, + domain + } + } + }; + }, authOAuth2(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const grantTypeKey = _.find(auth, { name: 'grant_type' }); diff --git a/packages/bruno-lang/v2/src/collectionBruToJson.js b/packages/bruno-lang/v2/src/collectionBruToJson.js index 5180f0193d..61d373d91e 100644 --- a/packages/bruno-lang/v2/src/collectionBruToJson.js +++ b/packages/bruno-lang/v2/src/collectionBruToJson.js @@ -4,7 +4,7 @@ const { outdentString } = require('../../v1/src/utils'); const grammar = ohm.grammar(`Bru { BruFile = (meta | query | headers | auth | auths | vars | script | tests | docs)* - auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authwsse | authapikey + auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM |authOAuth2 | authwsse | authapikey nl = "\\r"? "\\n" st = " " | "\\t" @@ -42,6 +42,7 @@ const grammar = ohm.grammar(`Bru { authbasic = "auth:basic" dictionary authbearer = "auth:bearer" dictionary authdigest = "auth:digest" dictionary + authNTLM = "auth:ntlm" dictionary authOAuth2 = "auth:oauth2" dictionary authwsse = "auth:wsse" dictionary authapikey = "auth:apikey" dictionary @@ -245,6 +246,26 @@ const sem = grammar.createSemantics().addAttribute('ast', { } }; }, + authNTLM(_1, dictionary) { + const auth = mapPairListToKeyValPairs(dictionary.ast, false); + const usernameKey = _.find(auth, { name: 'username' }); + const passwordKey = _.find(auth, { name: 'password' }); + const domainKey = _.find(auth, { name: 'domain' }); + + const username = usernameKey ? usernameKey.value : ''; + const password = passwordKey ? passwordKey.value : ''; + const domain = domainKey ? domainKey.value : ''; + + return { + auth: { + ntlm: { + username, + password, + domain + } + } + }; + }, authOAuth2(_1, dictionary) { const auth = mapPairListToKeyValPairs(dictionary.ast, false); const grantTypeKey = _.find(auth, { name: 'grant_type' }); diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index 8d3a5fdee8..8be05c50d0 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -165,6 +165,18 @@ ${indentString(`password: ${auth?.digest?.password || ''}`)} `; } + + if (auth && auth.ntlm) { + bru += `auth:ntlm { +${indentString(`username: ${auth?.ntlm?.username || ''}`)} +${indentString(`password: ${auth?.ntlm?.password || ''}`)} +${indentString(`domain: ${auth?.ntlm?.domain || ''}`)} + +} + +`; + } + if (auth && auth.oauth2) { switch (auth?.oauth2?.grantType) { case 'password': diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js index 8b162b7a6f..c2a843dc6e 100644 --- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js +++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js @@ -120,6 +120,17 @@ ${indentString(`username: ${auth.digest.username}`)} ${indentString(`password: ${auth.digest.password}`)} } +`; + } + +if (auth && auth.ntlm) { + bru += `auth:ntlm { +${indentString(`username: ${auth.ntlm.username}`)} +${indentString(`password: ${auth.ntlm.password}`)} +${indentString(`domain: ${auth.ntlm.domain}`)} + +} + `; } diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index 11561c5284..4a812e35a4 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -126,6 +126,17 @@ const authDigestSchema = Yup.object({ .noUnknown(true) .strict(); + + + const authNTLMSchema = Yup.object({ + username: Yup.string().nullable(), + password: Yup.string().nullable(), + domain: Yup.string().nullable() + + }) + .noUnknown(true) + .strict(); + const authApiKeySchema = Yup.object({ key: Yup.string().nullable(), value: Yup.string().nullable(), @@ -194,11 +205,12 @@ const oauth2Schema = Yup.object({ const authSchema = Yup.object({ mode: Yup.string() - .oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2', 'wsse', 'apikey']) + .oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'ntlm', 'oauth2', 'wsse', 'apikey']) .required('mode is required'), awsv4: authAwsV4Schema.nullable(), basic: authBasicSchema.nullable(), bearer: authBearerSchema.nullable(), + ntlm: authNTLMSchema.nullable(), digest: authDigestSchema.nullable(), oauth2: oauth2Schema.nullable(), wsse: authWsseSchema.nullable(),