ntlm auth

This commit is contained in:
tlaloc911 2024-10-21 17:53:15 -06:00
parent ac67c4c0d8
commit 6a3660947a
25 changed files with 486 additions and 9 deletions

41
package-lock.json generated
View File

@ -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",

View File

@ -54,6 +54,7 @@
"rollup":"3.29.5"
},
"dependencies": {
"axios-ntlm": "^1.4.2",
"json-bigint": "^1.0.0",
"lossless-json": "^4.0.1"
}

View File

@ -79,6 +79,15 @@ const AuthMode = ({ collection }) => {
>
Digest Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('ntlm');
}}
>
NTLM Auth
</div>
<div
className="dropdown-item"
onClick={() => {

View File

@ -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;

View File

@ -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 (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={ntlmAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
/>
</div>
<label className="block font-medium mb-2">Domain</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.domain || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleDomainChange(val)}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default NTLMAuth;

View File

@ -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 <DigestAuth collection={collection} />;
}
case 'ntlm': {
return <NTLMAuth collection={collection} />;
}
case 'oauth2': {
return <OAuth2 collection={collection} />;
}

View File

@ -70,6 +70,15 @@ const AuthMode = ({ item, collection }) => {
>
Digest Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef?.current?.hide();
onModeChange('ntlm');
}}
>
NTLM Auth
</div>
<div
className="dropdown-item"
onClick={() => {

View File

@ -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;

View File

@ -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 (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={ntlmAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
onRun={handleRun}
collection={collection}
item={item}
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
onRun={handleRun}
collection={collection}
item={item}
isSecret={true}
/>
</div>
<label className="block font-medium mb-2">Domain</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.domain || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleDomainChange(val)}
onRun={handleRun}
collection={collection}
item={item}
/>
</div>
</StyledWrapper>
);
};
export default NTLMAuth;

View File

@ -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 <DigestAuth collection={collection} item={item} />;
}
case 'ntlm': {
return <NTLMAuth collection={collection} item={item} />;
}
case 'oauth2': {
return <OAuth2 collection={collection} item={item} />;
}

View File

@ -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;

View File

@ -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;

View File

@ -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"
}

View File

@ -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;
};

View File

@ -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')}`;
}

View File

@ -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

View File

@ -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": {

View File

@ -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);

View File

@ -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;
};

View File

@ -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) {

View File

@ -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' });

View File

@ -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' });

View File

@ -162,6 +162,18 @@ ${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 || ''}`)}
}
`;
}

View File

@ -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}`)}
}
`;
}

View File

@ -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(),