mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 23:43:15 +01:00
feat: Implement OAuth 1.0 - collection level
This commit is contained in:
parent
9a3790b4cc
commit
87a4aeadbe
@ -13,7 +13,7 @@ const AuthMode = ({ collection }) => {
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
const authMode = get(collection, 'root.request.auth.mode');
|
||||
|
||||
const authModes = ['awsv4', 'basic', 'bearer', 'digest', 'oauth2', 'wsse', 'apikey', 'none'];
|
||||
const authModes = ['awsv4', 'basic', 'bearer', 'digest', 'oauth1', 'oauth2', 'wsse', 'apikey', 'none'];
|
||||
|
||||
const Icon = forwardRef((props, ref) => {
|
||||
return (
|
||||
|
@ -0,0 +1,20 @@
|
||||
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};
|
||||
}
|
||||
.file-picker-wrapper {
|
||||
max-width: 400px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,127 @@
|
||||
import React, { forwardRef, useCallback, useRef } 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';
|
||||
import { inputsConfig } from './inputsConfig';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import { IconCaretDown } from '@tabler/icons';
|
||||
import FilePickerEditor from 'components/FilePickerEditor';
|
||||
import path from 'path';
|
||||
import { isWindowsOS } from 'utils/common/platform';
|
||||
import slash from 'utils/common/slash';
|
||||
|
||||
const OAuth1 = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const oAuth1 = get(collection, 'root.request.auth.oauth1', {});
|
||||
|
||||
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||
|
||||
const refs = useRef(new Map());
|
||||
const setRef = useCallback((ref, key) => {
|
||||
if (ref) {
|
||||
refs.current.set(key, ref);
|
||||
} else {
|
||||
refs.current.delete(key);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const hideDropdown = (key) => {
|
||||
const dropdown = refs.current.get(key);
|
||||
if (dropdown?.hide) {
|
||||
dropdown.hide();
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (key, val) => {
|
||||
console.log(key, val);
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'oauth1',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
...oAuth1, [key]: val
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const relativeFile = (file) => {
|
||||
if (file) {
|
||||
if (isWindowsOS()) {
|
||||
return slash((path.win32.relative(collection.pathname, file)));
|
||||
} else {
|
||||
return path.posix.relative(collection.pathname, file);
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const optionDisplayName = (options, key) => {
|
||||
const option = (options.find(option => option.key === key));
|
||||
return option?.label ? t(option.label) : key;
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 flex w-full gap-4 flex-col">
|
||||
{inputsConfig.map((input) => {
|
||||
const { key, label, type, options } = input;
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-1" key={`input-${key}`}>
|
||||
<label className="block font-medium">{t(label)}</label>
|
||||
{type === 'Dropdown' ?
|
||||
<div className="inline-flex items-center cursor-pointer grant-type-mode-selector w-fit">
|
||||
<Dropdown icon={(<div
|
||||
className="flex items-center justify-end grant-type-label select-none">{optionDisplayName(options, oAuth1[key])}
|
||||
<IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} /></div>)}
|
||||
onCreate={(ref) => setRef(ref, key)}
|
||||
placement="bottom-end"
|
||||
children={options.map((option) => (
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
hideDropdown(key);
|
||||
handleChange(key, option.key ? option.key : option);
|
||||
}}>
|
||||
{option.label ? t(option.label) : option}
|
||||
</div>
|
||||
))}
|
||||
/>
|
||||
</div>
|
||||
: ''}
|
||||
{type === 'SingleLineEditor' ?
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={oAuth1[key] || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleChange(key, val)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
: ''}
|
||||
{type === 'FilePickerEditor' ?
|
||||
<div className="file-picker-wrapper">
|
||||
<FilePickerEditor
|
||||
value={[oAuth1[key]] || []}
|
||||
onChange={(val) => handleChange(key, relativeFile(val[0]))}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
: ''}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuth1;
|
@ -0,0 +1,79 @@
|
||||
const inputsConfig = [
|
||||
{
|
||||
key: 'consumerKey',
|
||||
label: 'AUTHORIZATION.OAUTH1.CONSUMER_KEY_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'consumerSecret',
|
||||
label: 'AUTHORIZATION.OAUTH1.CONSUMER_SECRET_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'requestTokenUrl',
|
||||
label: 'AUTHORIZATION.OAUTH1.REQUEST_TOKEN_URL_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'accessTokenUrl',
|
||||
label: 'AUTHORIZATION.OAUTH1.ACCESS_TOKEN_URL_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'authorizeUrl',
|
||||
label: 'AUTHORIZATION.OAUTH1.AUTHORIZE_URL_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'callbackUrl',
|
||||
label: 'AUTHORIZATION.OAUTH1.CALLBACK_TOKEN_URL_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'verifier',
|
||||
label: 'AUTHORIZATION.OAUTH1.OAUTH_VERIFIER_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'accessToken',
|
||||
label: 'AUTHORIZATION.OAUTH1.ACCESS_TOKEN_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'accessTokenSecret',
|
||||
label: 'AUTHORIZATION.OAUTH1.ACCESS_TOKEN_SECRET_FIELD',
|
||||
type: 'SingleLineEditor'
|
||||
},
|
||||
{
|
||||
key: 'rsaPrivateKey',
|
||||
label: 'AUTHORIZATION.OAUTH1.RSA_PRIVATE_KEY_FIELD',
|
||||
type: 'FilePickerEditor'
|
||||
},
|
||||
{
|
||||
key: 'signatureMethod',
|
||||
label: 'AUTHORIZATION.OAUTH1.SIGNATURE_METHOD_FIELD',
|
||||
type: 'Dropdown',
|
||||
options: ['HMAC-SHA1', 'HMAC-SHA256', 'HMAC-SHA512', 'RSA-SHA1', 'RSA-SHA256', 'RSA-SHA512', 'PLAINTEXT']
|
||||
},
|
||||
{
|
||||
key: 'parameterTransmissionMethod',
|
||||
label: 'AUTHORIZATION.OAUTH1.PARAM_TRANSMISSION_METHOD_FIELD',
|
||||
type: 'Dropdown',
|
||||
options: [
|
||||
{
|
||||
key: 'authorization_header',
|
||||
label: 'AUTHORIZATION.OAUTH1.PARAM_TRANSMISSION_METHOD.AUTHORIZATION_HEADER'
|
||||
},
|
||||
{
|
||||
key: 'request_body',
|
||||
label: 'AUTHORIZATION.OAUTH1.PARAM_TRANSMISSION_METHOD.REQUEST_BODY'
|
||||
},
|
||||
{
|
||||
key: 'query_param',
|
||||
label: 'AUTHORIZATION.OAUTH1.PARAM_TRANSMISSION_METHOD.QUERY_PARAM'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export { inputsConfig };
|
@ -11,6 +11,7 @@ import ApiKeyAuth from './ApiKeyAuth/';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import OAuth2 from './OAuth2';
|
||||
import OAuth1 from 'components/CollectionSettings/Auth/OAuth1';
|
||||
|
||||
const Auth = ({ collection }) => {
|
||||
const authMode = get(collection, 'root.request.auth.mode');
|
||||
@ -32,6 +33,9 @@ const Auth = ({ collection }) => {
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} />;
|
||||
}
|
||||
case 'oauth1': {
|
||||
return <OAuth1 collection={collection} />;
|
||||
}
|
||||
case 'oauth2': {
|
||||
return <OAuth2 collection={collection} />;
|
||||
}
|
||||
|
@ -1146,6 +1146,9 @@ export const collectionsSlice = createSlice({
|
||||
case 'digest':
|
||||
set(collection, 'root.request.auth.digest', action.payload.content);
|
||||
break;
|
||||
case 'oauth1':
|
||||
set(collection, 'root.request.auth.oauth1', action.payload.content);
|
||||
break;
|
||||
case 'oauth2':
|
||||
set(collection, 'root.request.auth.oauth2', action.payload.content);
|
||||
break;
|
||||
|
@ -245,6 +245,22 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
axiosRequest.apiKeyAuthValueForQueryParams = apiKeyAuth;
|
||||
}
|
||||
break;
|
||||
case 'oauth1':
|
||||
axiosRequest.oauth1 = {
|
||||
consumerKey: get(collectionAuth, 'oauth1.consumerKey'),
|
||||
consumerSecret: get(collectionAuth, 'oauth1.consumerSecret'),
|
||||
requestTokenUrl: get(collectionAuth, 'oauth1.requestTokenUrl'),
|
||||
accessTokenUrl: get(collectionAuth, 'oauth1.accessTokenUrl'),
|
||||
authorizeUrl: get(collectionAuth, 'oauth1.authorizeUrl'),
|
||||
callbackUrl: get(collectionAuth, 'oauth1.callbackUrl'),
|
||||
verifier: get(collectionAuth, 'oauth1.verifier'),
|
||||
accessToken: get(collectionAuth, 'oauth1.accessToken'),
|
||||
accessTokenSecret: get(collectionAuth, 'oauth1.accessTokenSecret'),
|
||||
rsaPrivateKey: get(collectionAuth, 'oauth1.rsaPrivateKey'),
|
||||
parameterTransmissionMethod: get(collectionAuth, 'oauth1.parameterTransmissionMethod'),
|
||||
signatureMethod: get(collectionAuth, 'oauth1.signatureMethod')
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 | authOAuth1 | 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
|
||||
authOAuth1 = "auth:oauth1" dictionary
|
||||
authOAuth2 = "auth:oauth2" dictionary
|
||||
authwsse = "auth:wsse" dictionary
|
||||
authapikey = "auth:apikey" dictionary
|
||||
@ -245,6 +246,39 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
}
|
||||
};
|
||||
},
|
||||
authOAuth1(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const consumerKey = _.find(auth, { name: 'consumerKey' });
|
||||
const consumerSecret = _.find(auth, { name: 'consumerSecret' });
|
||||
const requestTokenUrl = _.find(auth, { name: 'requestTokenUrl' });
|
||||
const accessTokenUrl = _.find(auth, { name: 'accessTokenUrl' });
|
||||
const authorizeUrl = _.find(auth, { name: 'authorizeUrl' });
|
||||
const verifier = _.find(auth, { name: 'verifier' });
|
||||
const callbackUrl = _.find(auth, { name: 'callbackUrl' });
|
||||
const accessToken = _.find(auth, { name: 'accessToken' });
|
||||
const accessTokenSecret = _.find(auth, { name: 'accessTokenSecret' });
|
||||
const rsaPrivateKey = _.find(auth, { name: 'rsaPrivateKey' });
|
||||
const signatureMethod = _.find(auth, { name: 'signatureMethod' });
|
||||
const parameterTransmissionMethod = _.find(auth, { name: 'parameterTransmissionMethod' });
|
||||
return {
|
||||
auth: {
|
||||
oauth1: {
|
||||
consumerKey: consumerKey ? consumerKey.value : '',
|
||||
consumerSecret: consumerSecret ? consumerSecret.value : '',
|
||||
requestTokenUrl: requestTokenUrl ? requestTokenUrl.value : '',
|
||||
accessTokenUrl: accessTokenUrl ? accessTokenUrl.value : '',
|
||||
authorizeUrl: authorizeUrl ? authorizeUrl.value : '',
|
||||
callbackUrl: callbackUrl ? callbackUrl.value : '',
|
||||
verifier: verifier ? verifier.value : '',
|
||||
accessToken: accessToken ? accessToken.value : '',
|
||||
accessTokenSecret: accessTokenSecret ? accessTokenSecret.value : '',
|
||||
rsaPrivateKey: rsaPrivateKey ? rsaPrivateKey.value : '',
|
||||
parameterTransmissionMethod: parameterTransmissionMethod ? parameterTransmissionMethod.value : '',
|
||||
signatureMethod: signatureMethod ? signatureMethod.value : ''
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
authOAuth2(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const grantTypeKey = _.find(auth, { name: 'grant_type' });
|
||||
@ -309,7 +343,7 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
authapikey(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
|
||||
|
@ -129,6 +129,25 @@ ${indentString(`key: ${auth?.apikey?.key || ''}`)}
|
||||
${indentString(`value: ${auth?.apikey?.value || ''}`)}
|
||||
${indentString(`placement: ${auth?.apikey?.placement || ''}`)}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
if (auth && auth.oauth1) {
|
||||
bru += `auth:oauth1 {
|
||||
${indentString(`consumerKey: ${auth?.oauth1?.consumerKey || ''}`)}
|
||||
${indentString(`consumerSecret: ${auth?.oauth1?.consumerSecret || ''}`)}
|
||||
${indentString(`requestTokenUrl: ${auth?.oauth1?.requestTokenUrl || ''}`)}
|
||||
${indentString(`accessTokenUrl: ${auth?.oauth1?.accessTokenUrl || ''}`)}
|
||||
${indentString(`authorizeUrl: ${auth?.oauth1?.authorizeUrl || ''}`)}
|
||||
${indentString(`callbackUrl: ${auth?.oauth1?.callbackUrl || ''}`)}
|
||||
${indentString(`verifier: ${auth?.oauth1?.verifier || ''}`)}
|
||||
${indentString(`accessToken: ${auth?.oauth1?.accessToken || ''}`)}
|
||||
${indentString(`accessTokenSecret: ${auth?.oauth1?.accessTokenSecret || ''}`)}
|
||||
${indentString(`rsaPrivateKey: ${auth?.oauth1?.rsaPrivateKey || ''}`)}
|
||||
${indentString(`parameterTransmissionMethod: ${auth?.oauth1?.parameterTransmissionMethod || ''}`)}
|
||||
${indentString(`signatureMethod: ${auth?.oauth1?.signatureMethod || ''}`)}
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user