mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-28 19:03:15 +01:00
Add Digest Auth Support (MD5)
This commit is contained in:
parent
5274d77660
commit
3838848b03
@ -61,6 +61,15 @@ const AuthMode = ({ collection }) => {
|
|||||||
>
|
>
|
||||||
Bearer Token
|
Bearer Token
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="dropdown-item"
|
||||||
|
onClick={() => {
|
||||||
|
dropdownTippyRef.current.hide();
|
||||||
|
onModeChange('digest');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Digest Auth
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="dropdown-item"
|
className="dropdown-item"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
label {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-line-editor-wrapper {
|
||||||
|
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;
|
@ -0,0 +1,71 @@
|
|||||||
|
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 DigestAuth = ({ collection }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { storedTheme } = useTheme();
|
||||||
|
|
||||||
|
const digestAuth = get(collection, 'root.request.auth.digest', {});
|
||||||
|
|
||||||
|
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||||
|
|
||||||
|
const handleUsernameChange = (username) => {
|
||||||
|
dispatch(
|
||||||
|
updateCollectionAuth({
|
||||||
|
mode: 'digest',
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
content: {
|
||||||
|
username: username,
|
||||||
|
password: digestAuth.password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasswordChange = (password) => {
|
||||||
|
dispatch(
|
||||||
|
updateCollectionAuth({
|
||||||
|
mode: 'digest',
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
content: {
|
||||||
|
username: digestAuth.username,
|
||||||
|
password: password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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={digestAuth.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={digestAuth.password || ''}
|
||||||
|
theme={storedTheme}
|
||||||
|
onSave={handleSave}
|
||||||
|
onChange={(val) => handlePasswordChange(val)}
|
||||||
|
collection={collection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DigestAuth;
|
@ -5,6 +5,7 @@ import AuthMode from './AuthMode';
|
|||||||
import AwsV4Auth from './AwsV4Auth';
|
import AwsV4Auth from './AwsV4Auth';
|
||||||
import BearerAuth from './BearerAuth';
|
import BearerAuth from './BearerAuth';
|
||||||
import BasicAuth from './BasicAuth';
|
import BasicAuth from './BasicAuth';
|
||||||
|
import DigestAuth from './DigestAuth';
|
||||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
@ -25,6 +26,9 @@ const Auth = ({ collection }) => {
|
|||||||
case 'bearer': {
|
case 'bearer': {
|
||||||
return <BearerAuth collection={collection} />;
|
return <BearerAuth collection={collection} />;
|
||||||
}
|
}
|
||||||
|
case 'digest': {
|
||||||
|
return <DigestAuth collection={collection} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,6 +62,15 @@ const AuthMode = ({ item, collection }) => {
|
|||||||
>
|
>
|
||||||
Bearer Token
|
Bearer Token
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="dropdown-item"
|
||||||
|
onClick={() => {
|
||||||
|
dropdownTippyRef.current.hide();
|
||||||
|
onModeChange('digest');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Digest Auth
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="dropdown-item"
|
className="dropdown-item"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
label {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-line-editor-wrapper {
|
||||||
|
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;
|
@ -0,0 +1,76 @@
|
|||||||
|
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 DigestAuth = ({ item, collection }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { storedTheme } = useTheme();
|
||||||
|
|
||||||
|
const digestAuth = item.draft ? get(item, 'draft.request.auth.digest', {}) : get(item, 'request.auth.digest', {});
|
||||||
|
|
||||||
|
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||||
|
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||||
|
|
||||||
|
const handleUsernameChange = (username) => {
|
||||||
|
dispatch(
|
||||||
|
updateAuth({
|
||||||
|
mode: 'digest',
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
itemUid: item.uid,
|
||||||
|
content: {
|
||||||
|
username: username,
|
||||||
|
password: digestAuth.password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasswordChange = (password) => {
|
||||||
|
dispatch(
|
||||||
|
updateAuth({
|
||||||
|
mode: 'digest',
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
itemUid: item.uid,
|
||||||
|
content: {
|
||||||
|
username: digestAuth.username,
|
||||||
|
password: password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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={digestAuth.username || ''}
|
||||||
|
theme={storedTheme}
|
||||||
|
onSave={handleSave}
|
||||||
|
onChange={(val) => handleUsernameChange(val)}
|
||||||
|
onRun={handleRun}
|
||||||
|
collection={collection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label className="block font-medium mb-2">Password</label>
|
||||||
|
<div className="single-line-editor-wrapper">
|
||||||
|
<SingleLineEditor
|
||||||
|
value={digestAuth.password || ''}
|
||||||
|
theme={storedTheme}
|
||||||
|
onSave={handleSave}
|
||||||
|
onChange={(val) => handlePasswordChange(val)}
|
||||||
|
onRun={handleRun}
|
||||||
|
collection={collection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DigestAuth;
|
@ -4,6 +4,7 @@ import AuthMode from './AuthMode';
|
|||||||
import AwsV4Auth from './AwsV4Auth';
|
import AwsV4Auth from './AwsV4Auth';
|
||||||
import BearerAuth from './BearerAuth';
|
import BearerAuth from './BearerAuth';
|
||||||
import BasicAuth from './BasicAuth';
|
import BasicAuth from './BasicAuth';
|
||||||
|
import DigestAuth from './DigestAuth';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
const Auth = ({ item, collection }) => {
|
const Auth = ({ item, collection }) => {
|
||||||
@ -20,6 +21,9 @@ const Auth = ({ item, collection }) => {
|
|||||||
case 'bearer': {
|
case 'bearer': {
|
||||||
return <BearerAuth collection={collection} item={item} />;
|
return <BearerAuth collection={collection} item={item} />;
|
||||||
}
|
}
|
||||||
|
case 'digest': {
|
||||||
|
return <DigestAuth collection={collection} item={item} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -388,6 +388,10 @@ export const collectionsSlice = createSlice({
|
|||||||
item.draft.request.auth.mode = 'basic';
|
item.draft.request.auth.mode = 'basic';
|
||||||
item.draft.request.auth.basic = action.payload.content;
|
item.draft.request.auth.basic = action.payload.content;
|
||||||
break;
|
break;
|
||||||
|
case 'digest':
|
||||||
|
item.draft.request.auth.mode = 'digest';
|
||||||
|
item.draft.request.auth.digest = action.payload.content;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -976,6 +980,9 @@ export const collectionsSlice = createSlice({
|
|||||||
case 'basic':
|
case 'basic':
|
||||||
set(collection, 'root.request.auth.basic', action.payload.content);
|
set(collection, 'root.request.auth.basic', action.payload.content);
|
||||||
break;
|
break;
|
||||||
|
case 'digest':
|
||||||
|
set(collection, 'root.request.auth.digest', action.payload.content);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -494,6 +494,10 @@ export const humanizeRequestAuthMode = (mode) => {
|
|||||||
label = 'Bearer Token';
|
label = 'Bearer Token';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'digest': {
|
||||||
|
label = 'Digest Auth';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return label;
|
return label;
|
||||||
|
@ -80,7 +80,8 @@ const transformInsomniaRequestItem = (request, index, allRequests) => {
|
|||||||
auth: {
|
auth: {
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
basic: null,
|
basic: null,
|
||||||
bearer: null
|
bearer: null,
|
||||||
|
digest: null
|
||||||
},
|
},
|
||||||
headers: [],
|
headers: [],
|
||||||
params: [],
|
params: [],
|
||||||
|
@ -69,7 +69,8 @@ const transformOpenapiRequestItem = (request) => {
|
|||||||
auth: {
|
auth: {
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
basic: null,
|
basic: null,
|
||||||
bearer: null
|
bearer: null,
|
||||||
|
digest: null
|
||||||
},
|
},
|
||||||
headers: [],
|
headers: [],
|
||||||
params: [],
|
params: [],
|
||||||
|
79
packages/bruno-electron/src/ipc/network/digestauth-helper.js
Normal file
79
packages/bruno-electron/src/ipc/network/digestauth-helper.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
function isStrPresent(str) {
|
||||||
|
return str && str !== '' && str !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripQuotes(str) {
|
||||||
|
return str.replace(/"/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsDigestHeader(response) {
|
||||||
|
const authHeader = response?.headers?.['www-authenticate'];
|
||||||
|
return authHeader ? authHeader.trim().toLowerCase().startsWith('digest') : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsAuthorizationHeader(originalRequest) {
|
||||||
|
return Boolean(originalRequest.headers['Authorization']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function md5(input) {
|
||||||
|
return crypto.createHash('md5').update(input).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDigestInterceptor(axiosInstance, request) {
|
||||||
|
const { username, password } = request.digestConfig;
|
||||||
|
|
||||||
|
console.debug(request);
|
||||||
|
|
||||||
|
if (!isStrPresent(username) || !isStrPresent(password)) {
|
||||||
|
console.warn('Required Digest Auth fields are not present');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axiosInstance.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
const originalRequest = error.config;
|
||||||
|
|
||||||
|
if (
|
||||||
|
error.response?.status === 401 &&
|
||||||
|
containsDigestHeader(error.response) &&
|
||||||
|
!containsAuthorizationHeader(originalRequest)
|
||||||
|
) {
|
||||||
|
console.debug(error.response.headers['www-authenticate']);
|
||||||
|
|
||||||
|
const authDetails = error.response.headers['www-authenticate']
|
||||||
|
.split(', ')
|
||||||
|
.map((v) => v.split('=').map(stripQuotes))
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
|
||||||
|
console.debug(authDetails);
|
||||||
|
|
||||||
|
const nonceCount = '00000001';
|
||||||
|
const cnonce = crypto.randomBytes(24).toString('hex');
|
||||||
|
|
||||||
|
if (authDetails.algorithm.toUpperCase() !== 'MD5') {
|
||||||
|
console.warn(`Unsupported Digest algorithm: ${algo}`);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
const HA1 = md5(`${username}:${authDetails['Digest realm']}:${password}`);
|
||||||
|
const HA2 = md5(`${request.method}:${request.url}`);
|
||||||
|
const response = md5(`${HA1}:${authDetails.nonce}:${nonceCount}:${cnonce}:auth:${HA2}`);
|
||||||
|
|
||||||
|
const authorizationHeader =
|
||||||
|
`Digest username="${username}",realm="${authDetails['Digest realm']}",` +
|
||||||
|
`nonce="${authDetails.nonce}",uri="${request.url}",qop="auth",algorithm="${authDetails.algorithm}",` +
|
||||||
|
`response="${response}",nc="${nonceCount}",cnonce="${cnonce}"`;
|
||||||
|
originalRequest.headers['Authorization'] = authorizationHeader;
|
||||||
|
console.debug(`Authorization: ${originalRequest.headers['Authorization']}`);
|
||||||
|
|
||||||
|
delete originalRequest.digestConfig;
|
||||||
|
return axiosInstance(originalRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { addDigestInterceptor };
|
@ -23,6 +23,7 @@ const { HttpProxyAgent } = require('http-proxy-agent');
|
|||||||
const { SocksProxyAgent } = require('socks-proxy-agent');
|
const { SocksProxyAgent } = require('socks-proxy-agent');
|
||||||
const { makeAxiosInstance } = require('./axios-instance');
|
const { makeAxiosInstance } = require('./axios-instance');
|
||||||
const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper');
|
const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper');
|
||||||
|
const { addDigestInterceptor } = require('./digestauth-helper');
|
||||||
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
|
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
|
||||||
|
|
||||||
// override the default escape function to prevent escaping
|
// override the default escape function to prevent escaping
|
||||||
@ -168,6 +169,10 @@ const configureRequest = async (collectionUid, request, envVars, collectionVaria
|
|||||||
delete request.awsv4config;
|
delete request.awsv4config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.digestConfig) {
|
||||||
|
addDigestInterceptor(axiosInstance, request);
|
||||||
|
}
|
||||||
|
|
||||||
request.timeout = preferencesUtil.getRequestTimeout();
|
request.timeout = preferencesUtil.getRequestTimeout();
|
||||||
|
|
||||||
return axiosInstance;
|
return axiosInstance;
|
||||||
|
@ -131,6 +131,12 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
|
|||||||
request.awsv4config.profileName = interpolate(request.awsv4config.profileName) || '';
|
request.awsv4config.profileName = interpolate(request.awsv4config.profileName) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interpolate vars for digest auth
|
||||||
|
if (request.digestConfig) {
|
||||||
|
request.digestConfig.username = interpolate(request.digestConfig.username) || '';
|
||||||
|
request.digestConfig.password = interpolate(request.digestConfig.password) || '';
|
||||||
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,6 +28,12 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
|||||||
case 'bearer':
|
case 'bearer':
|
||||||
axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
|
axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
|
||||||
break;
|
break;
|
||||||
|
case 'digest':
|
||||||
|
axiosRequest.digestConfig = {
|
||||||
|
username: get(collectionAuth, 'digest.username'),
|
||||||
|
password: get(collectionAuth, 'digest.password')
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +58,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
|||||||
case 'bearer':
|
case 'bearer':
|
||||||
axiosRequest.headers['authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
|
axiosRequest.headers['authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
|
||||||
break;
|
break;
|
||||||
|
case 'digest':
|
||||||
|
axiosRequest.digestConfig = {
|
||||||
|
username: get(request, 'auth.digest.username'),
|
||||||
|
password: get(request, 'auth.digest.password')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils');
|
|||||||
*/
|
*/
|
||||||
const grammar = ohm.grammar(`Bru {
|
const grammar = ohm.grammar(`Bru {
|
||||||
BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)*
|
BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)*
|
||||||
auths = authawsv4 | authbasic | authbearer
|
auths = authawsv4 | authbasic | authbearer | authdigest
|
||||||
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
|
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
|
||||||
bodyforms = bodyformurlencoded | bodymultipart
|
bodyforms = bodyformurlencoded | bodymultipart
|
||||||
|
|
||||||
@ -79,6 +79,7 @@ const grammar = ohm.grammar(`Bru {
|
|||||||
authawsv4 = "auth:awsv4" dictionary
|
authawsv4 = "auth:awsv4" dictionary
|
||||||
authbasic = "auth:basic" dictionary
|
authbasic = "auth:basic" dictionary
|
||||||
authbearer = "auth:bearer" dictionary
|
authbearer = "auth:bearer" dictionary
|
||||||
|
authdigest = "auth:digest" dictionary
|
||||||
|
|
||||||
body = "body" st* "{" nl* textblock tagend
|
body = "body" st* "{" nl* textblock tagend
|
||||||
bodyjson = "body:json" st* "{" nl* textblock tagend
|
bodyjson = "body:json" st* "{" nl* textblock tagend
|
||||||
@ -350,6 +351,21 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
authdigest(_1, dictionary) {
|
||||||
|
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||||
|
const usernameKey = _.find(auth, { name: 'username' });
|
||||||
|
const passwordKey = _.find(auth, { name: 'password' });
|
||||||
|
const username = usernameKey ? usernameKey.value : '';
|
||||||
|
const password = passwordKey ? passwordKey.value : '';
|
||||||
|
return {
|
||||||
|
auth: {
|
||||||
|
digest: {
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
bodyformurlencoded(_1, dictionary) {
|
bodyformurlencoded(_1, dictionary) {
|
||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
|
@ -4,7 +4,7 @@ const { outdentString } = require('../../v1/src/utils');
|
|||||||
|
|
||||||
const grammar = ohm.grammar(`Bru {
|
const grammar = ohm.grammar(`Bru {
|
||||||
BruFile = (meta | query | headers | auth | auths | vars | script | tests | docs)*
|
BruFile = (meta | query | headers | auth | auths | vars | script | tests | docs)*
|
||||||
auths = authawsv4 | authbasic | authbearer
|
auths = authawsv4 | authbasic | authbearer | authdigest
|
||||||
|
|
||||||
nl = "\\r"? "\\n"
|
nl = "\\r"? "\\n"
|
||||||
st = " " | "\\t"
|
st = " " | "\\t"
|
||||||
@ -41,6 +41,7 @@ const grammar = ohm.grammar(`Bru {
|
|||||||
authawsv4 = "auth:awsv4" dictionary
|
authawsv4 = "auth:awsv4" dictionary
|
||||||
authbasic = "auth:basic" dictionary
|
authbasic = "auth:basic" dictionary
|
||||||
authbearer = "auth:bearer" dictionary
|
authbearer = "auth:bearer" dictionary
|
||||||
|
authdigest = "auth:digest" dictionary
|
||||||
|
|
||||||
script = scriptreq | scriptres
|
script = scriptreq | scriptres
|
||||||
scriptreq = "script:pre-request" st* "{" nl* textblock tagend
|
scriptreq = "script:pre-request" st* "{" nl* textblock tagend
|
||||||
@ -226,6 +227,21 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
authdigest(_1, dictionary) {
|
||||||
|
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||||
|
const usernameKey = _.find(auth, { name: 'username' });
|
||||||
|
const passwordKey = _.find(auth, { name: 'password' });
|
||||||
|
const username = usernameKey ? usernameKey.value : '';
|
||||||
|
const password = passwordKey ? passwordKey.value : '';
|
||||||
|
return {
|
||||||
|
auth: {
|
||||||
|
digest: {
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
varsreq(_1, dictionary) {
|
varsreq(_1, dictionary) {
|
||||||
const vars = mapPairListToKeyValPairs(dictionary.ast);
|
const vars = mapPairListToKeyValPairs(dictionary.ast);
|
||||||
_.each(vars, (v) => {
|
_.each(vars, (v) => {
|
||||||
|
@ -114,6 +114,15 @@ ${indentString(`password: ${auth.basic.password}`)}
|
|||||||
${indentString(`token: ${auth.bearer.token}`)}
|
${indentString(`token: ${auth.bearer.token}`)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth && auth.digest) {
|
||||||
|
bru += `auth:digest {
|
||||||
|
${indentString(`username: ${auth.digest.username}`)}
|
||||||
|
${indentString(`password: ${auth.digest.password}`)}
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +102,15 @@ ${indentString(`password: ${auth.basic.password}`)}
|
|||||||
${indentString(`token: ${auth.bearer.token}`)}
|
${indentString(`token: ${auth.bearer.token}`)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth && auth.digest) {
|
||||||
|
bru += `auth:digest {
|
||||||
|
${indentString(`username: ${auth.digest.username}`)}
|
||||||
|
${indentString(`password: ${auth.digest.password}`)}
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,11 @@ auth:bearer {
|
|||||||
token: 123
|
token: 123
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auth:digest {
|
||||||
|
username: john
|
||||||
|
password: secret
|
||||||
|
}
|
||||||
|
|
||||||
vars:pre-request {
|
vars:pre-request {
|
||||||
departingDate: 2020-01-01
|
departingDate: 2020-01-01
|
||||||
~returningDate: 2020-01-02
|
~returningDate: 2020-01-02
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
},
|
},
|
||||||
"bearer": {
|
"bearer": {
|
||||||
"token": "123"
|
"token": "123"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"username": "john",
|
||||||
|
"password": "secret"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vars": {
|
"vars": {
|
||||||
|
@ -40,6 +40,11 @@ auth:bearer {
|
|||||||
token: 123
|
token: 123
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auth:digest {
|
||||||
|
username: john
|
||||||
|
password: secret
|
||||||
|
}
|
||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"hello": "world"
|
"hello": "world"
|
||||||
|
@ -59,6 +59,10 @@
|
|||||||
},
|
},
|
||||||
"bearer": {
|
"bearer": {
|
||||||
"token": "123"
|
"token": "123"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"username": "john",
|
||||||
|
"password": "secret"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
|
@ -94,11 +94,19 @@ const authBearerSchema = Yup.object({
|
|||||||
.noUnknown(true)
|
.noUnknown(true)
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
const authDigestSchema = Yup.object({
|
||||||
|
username: Yup.string().nullable(),
|
||||||
|
password: Yup.string().nullable()
|
||||||
|
})
|
||||||
|
.noUnknown(true)
|
||||||
|
.strict();
|
||||||
|
|
||||||
const authSchema = Yup.object({
|
const authSchema = Yup.object({
|
||||||
mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer']).required('mode is required'),
|
mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer', 'digest']).required('mode is required'),
|
||||||
awsv4: authAwsV4Schema.nullable(),
|
awsv4: authAwsV4Schema.nullable(),
|
||||||
basic: authBasicSchema.nullable(),
|
basic: authBasicSchema.nullable(),
|
||||||
bearer: authBearerSchema.nullable()
|
bearer: authBearerSchema.nullable(),
|
||||||
|
digest: authDigestSchema.nullable()
|
||||||
})
|
})
|
||||||
.noUnknown(true)
|
.noUnknown(true)
|
||||||
.strict();
|
.strict();
|
||||||
|
Loading…
Reference in New Issue
Block a user