Skip to content

Commit

Permalink
Improved Feat/wsse auth (usebruno#3172)
Browse files Browse the repository at this point in the history
* adding wsse auth logic

* adding wsse auth logic to electron

* adding wsse auth formatting

* Refactoring WSSE 'secret' to 'password'

* Incorporating PR feedback

* Removed unused packages from package.json

* Fixed issue caused when resolving merge conflicts and added new route to test wsse

* Removed deprecated package usages from bruno-cli

* Fixed tests

---------

Co-authored-by: dwolter-emarsys <dylan.wolter@emarsys.com>
  • Loading branch information
sanjai0py and dwolter-emarsys authored Sep 23, 2024
1 parent bebb18f commit 4d820af
Show file tree
Hide file tree
Showing 26 changed files with 447 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ const AuthMode = ({ collection }) => {
>
Basic Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('wsse');
}}
>
WSSE Auth
</div>
<div
className="dropdown-item"
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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 WsseAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();

const wsseAuth = get(collection, 'root.request.auth.wsse', {});

const handleSave = () => dispatch(saveCollectionRoot(collection.uid));

const handleUserChange = (username) => {
dispatch(
updateCollectionAuth({
mode: 'wsse',
collectionUid: collection.uid,
content: {
username,
password: wsseAuth.password
}
})
);
};

const handlePasswordChange = (password) => {
dispatch(
updateCollectionAuth({
mode: 'wsse',
collectionUid: collection.uid,
content: {
username: wsseAuth.username,
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={wsseAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUserChange(val)}
collection={collection}
/>
</div>

<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={wsseAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
collection={collection}
/>
</div>
</StyledWrapper>
);
};

export default WsseAuth;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import AwsV4Auth from './AwsV4Auth';
import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth';
import WsseAuth from './WsseAuth';
import ApiKeyAuth from './ApiKeyAuth/';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
Expand Down Expand Up @@ -34,6 +35,9 @@ const Auth = ({ collection }) => {
case 'oauth2': {
return <OAuth2 collection={collection} />;
}
case 'wsse': {
return <WsseAuth collection={collection} />;
}
case 'apikey': {
return <ApiKeyAuth collection={collection} />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const AuthMode = ({ item, collection }) => {
})
);
};

return (
<StyledWrapper>
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
Expand Down Expand Up @@ -80,6 +79,15 @@ const AuthMode = ({ item, collection }) => {
>
OAuth 2.0
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef?.current?.hide();
onModeChange('wsse');
}}
>
WSSE Auth
</div>
<div
className="dropdown-item"
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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 WsseAuth = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();

const wsseAuth = item.draft ? get(item, 'draft.request.auth.wsse', {}) : get(item, 'request.auth.wsse', {});

const handleRun = () => dispatch(sendRequest(item, collection.uid));
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));

const handleUserChange = (username) => {
dispatch(
updateAuth({
mode: 'wsse',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
username,
password: wsseAuth.password
}
})
);
};

const handlePasswordChange = (password) => {
dispatch(
updateAuth({
mode: 'wsse',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
username: wsseAuth.username,
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={wsseAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUserChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>

<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={wsseAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
</StyledWrapper>
);
};

export default WsseAuth;
4 changes: 4 additions & 0 deletions packages/bruno-app/src/components/RequestPane/Auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AwsV4Auth from './AwsV4Auth';
import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth';
import WsseAuth from './WsseAuth';
import ApiKeyAuth from './ApiKeyAuth';
import StyledWrapper from './StyledWrapper';
import { humanizeRequestAuthMode } from 'utils/collections/index';
Expand Down Expand Up @@ -33,6 +34,9 @@ const Auth = ({ item, collection }) => {
case 'oauth2': {
return <OAuth2 collection={collection} item={item} />;
}
case 'wsse': {
return <WsseAuth collection={collection} item={item} />;
}
case 'apikey': {
return <ApiKeyAuth collection={collection} item={item} />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,10 @@ export const collectionsSlice = createSlice({
item.draft.request.auth.mode = 'oauth2';
item.draft.request.auth.oauth2 = action.payload.content;
break;
case 'wsse':
item.draft.request.auth.mode = 'wsse';
item.draft.request.auth.wsse = action.payload.content;
break;
case 'apikey':
item.draft.request.auth.mode = 'apikey';
item.draft.request.auth.apikey = action.payload.content;
Expand Down Expand Up @@ -1141,6 +1145,9 @@ export const collectionsSlice = createSlice({
case 'oauth2':
set(collection, 'root.request.auth.oauth2', action.payload.content);
break;
case 'wsse':
set(collection, 'root.request.auth.wsse', action.payload.content);
break;
case 'apikey':
set(collection, 'root.request.auth.apikey', action.payload.content);
break;
Expand Down
11 changes: 10 additions & 1 deletion packages/bruno-app/src/utils/collections/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,12 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
placement: get(si.request, 'auth.apikey.placement', 'header')
};
break;

case 'wsse':
di.request.auth.wsse = {
username: get(si.request, 'auth.wsse.username', ''),
password: get(si.request, 'auth.wsse.password', '')
};
break;
default:
break;
}
Expand Down Expand Up @@ -669,6 +674,10 @@ export const humanizeRequestAuthMode = (mode) => {
label = 'OAuth 2.0';
break;
}
case 'wsse': {
label = 'WSSE Auth';
break;
}
case 'apikey': {
label = 'API Key';
break;
Expand Down
19 changes: 19 additions & 0 deletions packages/bruno-cli/src/runner/prepare-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { get, each, filter } = require('lodash');
const fs = require('fs');
var JSONbig = require('json-bigint');
const decomment = require('decomment');
const crypto = require('node:crypto');

const prepareRequest = (request, collectionRoot) => {
const headers = {};
Expand Down Expand Up @@ -69,6 +70,24 @@ const prepareRequest = (request, collectionRoot) => {
if (request.auth.mode === 'bearer') {
axiosRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
}

if (request.auth.mode === 'wsse') {
const username = get(request, 'auth.wsse.username', '');
const password = get(request, 'auth.wsse.password', '');

const ts = new Date().toISOString();
const nonce = crypto.randomBytes(16).toString('base64');

// Create the password digest using SHA-256
const hash = crypto.createHash('sha256');
hash.update(nonce + ts + password);
const digest = hash.digest('base64');

// Construct the WSSE header
axiosRequest.headers[
'X-WSSE'
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Created="${ts}", Nonce="${nonce}"`;
}
}

request.body = request.body || {};
Expand Down
6 changes: 6 additions & 0 deletions packages/bruno-electron/src/ipc/network/interpolate-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
request.digestConfig.password = _interpolate(request.digestConfig.password) || '';
}

// interpolate vars for wsse auth
if (request.wsse) {
request.wsse.username = _interpolate(request.wsse.username) || '';
request.wsse.password = _interpolate(request.wsse.password) || '';
}

return request;
};

Expand Down
Loading

0 comments on commit 4d820af

Please sign in to comment.