mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 14:35:03 +00:00
feat(auth): akamai auth integration (#8199)
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useMemo, useCallback } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { IconCaretDown } from '@tabler/icons';
|
||||
import MenuDropdown from 'ui/MenuDropdown';
|
||||
import StatusBadge from 'ui/StatusBadge/index';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { updateCollectionAuthMode } from 'providers/ReduxStore/slices/collections';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
@@ -66,6 +67,17 @@ const AuthMode = ({ collection }) => {
|
||||
label: 'API Key',
|
||||
onClick: () => onModeChange('apikey')
|
||||
},
|
||||
{
|
||||
id: 'akamai-edgegrid',
|
||||
label: (
|
||||
<span className="flex items-center gap-2">
|
||||
Akamai EdgeGrid
|
||||
<StatusBadge status="info" size="xs">Beta</StatusBadge>
|
||||
</span>
|
||||
),
|
||||
ariaLabel: 'Akamai EdgeGrid (Beta)',
|
||||
onClick: () => onModeChange('akamai-edgegrid')
|
||||
},
|
||||
{
|
||||
id: 'none',
|
||||
label: 'No Auth',
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 400px;
|
||||
margin-bottom: 0.5rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
|
||||
&:focus-within {
|
||||
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: fit-content;
|
||||
margin: 1rem 0 0.75rem;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
user-select: none;
|
||||
|
||||
.advanced-settings-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.375rem 0.625rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: ${(props) => rgba(props.theme.primary.solid, 0.1)};
|
||||
|
||||
svg {
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings-hint {
|
||||
margin: -0.25rem 0 0.75rem;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
.field-info {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
.field-tooltip {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
z-index: 10;
|
||||
width: max-content;
|
||||
max-width: 15rem;
|
||||
margin-bottom: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: #374151;
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
font-weight: 400;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover .field-tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,165 @@
|
||||
import { IconAdjustmentsHorizontal, IconInfoCircle } from '@tabler/icons';
|
||||
import get from 'lodash/get';
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import SensitiveFieldWarning from 'components/SensitiveFieldWarning';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField';
|
||||
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
interface AkamaiEdgeGridAuthValues {
|
||||
accessToken?: string;
|
||||
clientToken?: string;
|
||||
clientSecret?: string;
|
||||
nonce?: string;
|
||||
timestamp?: string;
|
||||
baseURL?: string;
|
||||
headersToSign?: string;
|
||||
maxBodySize?: number | null;
|
||||
}
|
||||
|
||||
type EdgeGridField = keyof AkamaiEdgeGridAuthValues;
|
||||
|
||||
const toMaxBodySize = (value: string): number | null => {
|
||||
if (value === '' || value == null) return null;
|
||||
const num = Number(value);
|
||||
return Number.isNaN(num) ? null : num;
|
||||
};
|
||||
|
||||
interface AkamaiEdgeGridAuthProps {
|
||||
collection: any;
|
||||
}
|
||||
|
||||
const FIELDS: Array<{ key: EdgeGridField; label: string; tooltip?: string; isSecret?: boolean }> = [
|
||||
{ key: 'accessToken', label: 'Access Token' },
|
||||
{ key: 'clientToken', label: 'Client Token' },
|
||||
{ key: 'clientSecret', label: 'Client Secret', isSecret: true },
|
||||
{ key: 'baseURL', label: 'Base URL', tooltip: 'Defaults to the request URL if not specified.' },
|
||||
{
|
||||
key: 'nonce',
|
||||
label: 'Nonce',
|
||||
tooltip: 'A unique nonce is required per request. Defaults to an auto-generated UUID v4 if not provided.'
|
||||
},
|
||||
{
|
||||
key: 'timestamp',
|
||||
label: 'Timestamp',
|
||||
tooltip:
|
||||
'UTC timestamp of when the request is signed (yyyyMMddTHH:mm:ss+0000). Defaults to current time if not provided.'
|
||||
},
|
||||
{
|
||||
key: 'headersToSign',
|
||||
label: 'Headers to Sign',
|
||||
tooltip: 'Comma-separated list of headers to include in the signature.'
|
||||
},
|
||||
{
|
||||
key: 'maxBodySize',
|
||||
label: 'Max Body Size',
|
||||
tooltip: 'Maximum message body size to include in the signature, in bytes. Defaults to 131072.'
|
||||
}
|
||||
];
|
||||
|
||||
type EdgeGridFieldConfig = (typeof FIELDS)[number];
|
||||
|
||||
// Fields shown up front vs. those grouped under the "Advanced Settings" section
|
||||
const BASIC_FIELDS = FIELDS.slice(0, 3);
|
||||
const ADVANCED_FIELDS = FIELDS.slice(3);
|
||||
|
||||
const EdgeGridAuth: React.FC<AkamaiEdgeGridAuthProps> = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const edgeGridAuth: AkamaiEdgeGridAuthValues =
|
||||
(collection.draft?.root
|
||||
? get(collection, 'draft.root.request.auth.akamaiEdgegrid')
|
||||
: get(collection, 'root.request.auth.akamaiEdgegrid')) || {};
|
||||
const { isSensitive } = useDetectSensitiveField(collection);
|
||||
const { showWarning: showClientSecretWarning, warningMessage: clientSecretWarningMessage } = isSensitive(
|
||||
edgeGridAuth?.clientSecret
|
||||
);
|
||||
|
||||
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||
|
||||
const handleFieldChange = (field: EdgeGridField, value: string) => {
|
||||
const content: AkamaiEdgeGridAuthValues = {
|
||||
accessToken: edgeGridAuth.accessToken || '',
|
||||
clientToken: edgeGridAuth.clientToken || '',
|
||||
clientSecret: edgeGridAuth.clientSecret || '',
|
||||
nonce: edgeGridAuth.nonce || '',
|
||||
timestamp: edgeGridAuth.timestamp || '',
|
||||
baseURL: edgeGridAuth.baseURL || '',
|
||||
headersToSign: edgeGridAuth.headersToSign || '',
|
||||
maxBodySize: edgeGridAuth.maxBodySize ?? null
|
||||
};
|
||||
|
||||
if (field === 'maxBodySize') {
|
||||
content.maxBodySize = toMaxBodySize(value);
|
||||
} else {
|
||||
(content as Record<string, unknown>)[field] = value || '';
|
||||
}
|
||||
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'akamai-edgegrid',
|
||||
collectionUid: collection.uid,
|
||||
content
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const renderField = ({ key, label, tooltip, isSecret }: EdgeGridFieldConfig) => {
|
||||
const showWarning = isSecret && showClientSecretWarning;
|
||||
const rawValue = edgeGridAuth[key];
|
||||
const fieldValue = rawValue === null || rawValue === undefined ? '' : String(rawValue);
|
||||
return (
|
||||
<div key={key}>
|
||||
<label>
|
||||
{label}
|
||||
{tooltip && (
|
||||
<span className="field-info">
|
||||
<IconInfoCircle size={16} />
|
||||
<span className="field-tooltip">{tooltip}</span>
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={fieldValue}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val: string) => handleFieldChange(key, val)}
|
||||
collection={collection}
|
||||
isSecret={isSecret}
|
||||
isCompact
|
||||
/>
|
||||
{showWarning && (
|
||||
<SensitiveFieldWarning fieldName="edgegrid-client-secret" warningMessage={clientSecretWarningMessage} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
{BASIC_FIELDS.map(renderField)}
|
||||
|
||||
<div className="advanced-settings-header">
|
||||
<span className="advanced-settings-icon">
|
||||
<IconAdjustmentsHorizontal size={16} />
|
||||
</span>
|
||||
<span>Advanced Settings</span>
|
||||
</div>
|
||||
|
||||
<>
|
||||
{ADVANCED_FIELDS.map(renderField)}
|
||||
</>
|
||||
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default EdgeGridAuth;
|
||||
@@ -8,6 +8,7 @@ import BasicAuth from './BasicAuth';
|
||||
import DigestAuth from './DigestAuth';
|
||||
import WsseAuth from './WsseAuth';
|
||||
import ApiKeyAuth from './ApiKeyAuth/';
|
||||
import EdgeGridAuth from './EdgeGridAuth';
|
||||
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import OAuth2 from './OAuth2';
|
||||
@@ -50,6 +51,9 @@ const Auth = ({ collection }) => {
|
||||
case 'apikey': {
|
||||
return <ApiKeyAuth collection={collection} />;
|
||||
}
|
||||
case 'akamai-edgegrid': {
|
||||
return <EdgeGridAuth collection={collection} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import OAuth2AuthorizationCode from 'components/RequestPane/Auth/OAuth2/AuthorizationCode/index';
|
||||
import { updateFolderAuth as _updateFolderAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import OAuth2PasswordCredentials from 'components/RequestPane/Auth/OAuth2/PasswordCredentials/index';
|
||||
import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCredentials/index';
|
||||
import OAuth2Implicit from 'components/RequestPane/Auth/OAuth2/Implicit/index';
|
||||
import GrantTypeSelector from 'components/RequestPane/Auth/OAuth2/GrantTypeSelector/index';
|
||||
import AuthMode from '../AuthMode';
|
||||
import ApiKeyAuth from 'components/RequestPane/Auth/ApiKeyAuth';
|
||||
import AwsV4Auth from 'components/RequestPane/Auth/AwsV4Auth';
|
||||
import BasicAuth from 'components/RequestPane/Auth/BasicAuth';
|
||||
import BearerAuth from 'components/RequestPane/Auth/BearerAuth';
|
||||
import DigestAuth from 'components/RequestPane/Auth/DigestAuth';
|
||||
import EdgeGridAuth from 'components/RequestPane/Auth/EdgeGridAuth';
|
||||
import NTLMAuth from 'components/RequestPane/Auth/NTLMAuth';
|
||||
import OAuth1 from 'components/RequestPane/Auth/OAuth1';
|
||||
import OAuth2AuthorizationCode from 'components/RequestPane/Auth/OAuth2/AuthorizationCode/index';
|
||||
import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCredentials/index';
|
||||
import GrantTypeSelector from 'components/RequestPane/Auth/OAuth2/GrantTypeSelector/index';
|
||||
import OAuth2Implicit from 'components/RequestPane/Auth/OAuth2/Implicit/index';
|
||||
import OAuth2PasswordCredentials from 'components/RequestPane/Auth/OAuth2/PasswordCredentials/index';
|
||||
import WsseAuth from 'components/RequestPane/Auth/WsseAuth';
|
||||
import ApiKeyAuth from 'components/RequestPane/Auth/ApiKeyAuth';
|
||||
import AwsV4Auth from 'components/RequestPane/Auth/AwsV4Auth';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
import get from 'lodash/get';
|
||||
import { updateFolderAuth as _updateFolderAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Button from 'ui/Button';
|
||||
import { getEffectiveAuthSource } from 'utils/auth';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
import AuthMode from '../AuthMode';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const GrantTypeComponentMap = ({ collection, folder, updateFolderAuth }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -182,6 +183,19 @@ const Auth = ({ collection, folder }) => {
|
||||
</>
|
||||
);
|
||||
}
|
||||
case 'akamai-edgegrid': {
|
||||
return (
|
||||
<>
|
||||
<EdgeGridAuth
|
||||
collection={collection}
|
||||
item={folder}
|
||||
updateAuth={updateFolderAuth}
|
||||
request={request}
|
||||
save={() => handleSave()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
case 'none': {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useMemo, useCallback } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { IconCaretDown } from '@tabler/icons';
|
||||
import MenuDropdown from 'ui/MenuDropdown';
|
||||
import StatusBadge from 'ui/StatusBadge/index';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { updateFolderAuthMode } from 'providers/ReduxStore/slices/collections';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
@@ -67,6 +68,17 @@ const AuthMode = ({ collection, folder }) => {
|
||||
label: 'API Key',
|
||||
onClick: () => onModeChange('apikey')
|
||||
},
|
||||
{
|
||||
id: 'akamai-edgegrid',
|
||||
label: (
|
||||
<span className="flex items-center gap-2">
|
||||
Akamai EdgeGrid
|
||||
<StatusBadge status="info" size="xs">Beta</StatusBadge>
|
||||
</span>
|
||||
),
|
||||
ariaLabel: 'Akamai EdgeGrid (Beta)',
|
||||
onClick: () => onModeChange('akamai-edgegrid')
|
||||
},
|
||||
{
|
||||
id: 'inherit',
|
||||
label: 'Inherit',
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useMemo, useCallback } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { IconCaretDown } from '@tabler/icons';
|
||||
import MenuDropdown from 'ui/MenuDropdown';
|
||||
import StatusBadge from 'ui/StatusBadge/index';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { updateRequestAuthMode } from 'providers/ReduxStore/slices/collections';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
@@ -67,6 +68,17 @@ const AuthMode = ({ item, collection }) => {
|
||||
label: 'API Key',
|
||||
onClick: () => onModeChange('apikey')
|
||||
},
|
||||
{
|
||||
id: 'akamai-edgegrid',
|
||||
label: (
|
||||
<span className="flex items-center gap-2">
|
||||
Akamai EdgeGrid
|
||||
<StatusBadge status="info" size="xs">Beta</StatusBadge>
|
||||
</span>
|
||||
),
|
||||
ariaLabel: 'Akamai EdgeGrid (Beta)',
|
||||
onClick: () => onModeChange('akamai-edgegrid')
|
||||
},
|
||||
{
|
||||
id: 'inherit',
|
||||
label: 'Inherit',
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 400px;
|
||||
margin-bottom: 0.5rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
|
||||
&:focus-within {
|
||||
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: fit-content;
|
||||
margin: 1rem 0 0.75rem;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
user-select: none;
|
||||
|
||||
.advanced-settings-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.375rem 0.625rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: ${(props) => rgba(props.theme.primary.solid, 0.1)};
|
||||
|
||||
svg {
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings-hint {
|
||||
margin: -0.25rem 0 0.75rem;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
.field-info {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
.field-tooltip {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
z-index: 10;
|
||||
width: max-content;
|
||||
max-width: 15rem;
|
||||
margin-bottom: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: #374151;
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
font-weight: 400;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover .field-tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,174 @@
|
||||
import { IconAdjustmentsHorizontal, IconInfoCircle } from '@tabler/icons';
|
||||
import get from 'lodash/get';
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import SensitiveFieldWarning from 'components/SensitiveFieldWarning';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField';
|
||||
import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
interface AkamaiEdgeGridAuthValues {
|
||||
accessToken?: string;
|
||||
clientToken?: string;
|
||||
clientSecret?: string;
|
||||
nonce?: string;
|
||||
timestamp?: string;
|
||||
baseURL?: string;
|
||||
headersToSign?: string;
|
||||
maxBodySize?: number | null;
|
||||
}
|
||||
|
||||
type EdgeGridField = keyof AkamaiEdgeGridAuthValues;
|
||||
|
||||
// Coerce the Max Body Size editor string into the numeric value the model stores (empty/invalid -> null).
|
||||
const toMaxBodySize = (value: string): number | null => {
|
||||
if (value === '' || value == null) return null;
|
||||
const num = Number(value);
|
||||
return Number.isNaN(num) ? null : num;
|
||||
};
|
||||
|
||||
interface AkamaiEdgeGridAuthProps {
|
||||
item: any;
|
||||
collection: any;
|
||||
request: any;
|
||||
updateAuth: (payload: any) => any;
|
||||
save: () => void;
|
||||
}
|
||||
|
||||
const FIELDS: Array<{ key: EdgeGridField; label: string; tooltip?: string; isSecret?: boolean }> = [
|
||||
{ key: 'accessToken', label: 'Access Token' },
|
||||
{ key: 'clientToken', label: 'Client Token' },
|
||||
{ key: 'clientSecret', label: 'Client Secret', isSecret: true },
|
||||
{ key: 'baseURL', label: 'Base URL', tooltip: 'Defaults to the request URL if not specified.' },
|
||||
{
|
||||
key: 'nonce',
|
||||
label: 'Nonce',
|
||||
tooltip: 'A unique nonce is required per request. Defaults to an auto-generated UUID v4 if not provided.'
|
||||
},
|
||||
{
|
||||
key: 'timestamp',
|
||||
label: 'Timestamp',
|
||||
tooltip:
|
||||
'UTC timestamp of when the request is signed (yyyyMMddTHH:mm:ss+0000). Defaults to current time if not provided.'
|
||||
},
|
||||
{
|
||||
key: 'headersToSign',
|
||||
label: 'Headers to Sign',
|
||||
tooltip: 'Comma-separated list of headers to include in the signature.'
|
||||
},
|
||||
{
|
||||
key: 'maxBodySize',
|
||||
label: 'Max Body Size',
|
||||
tooltip: 'Maximum message body size to include in the signature, in bytes. Defaults to 131072.'
|
||||
}
|
||||
];
|
||||
|
||||
type EdgeGridFieldConfig = (typeof FIELDS)[number];
|
||||
|
||||
// Fields shown up front vs. those grouped under the "Advanced Settings" section
|
||||
const BASIC_FIELDS = FIELDS.slice(0, 3);
|
||||
const ADVANCED_FIELDS = FIELDS.slice(3);
|
||||
|
||||
const EdgeGridAuth: React.FC<AkamaiEdgeGridAuthProps> = ({ item, collection, updateAuth, request, save }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const edgeGridAuth: AkamaiEdgeGridAuthValues = get(request, 'auth.akamaiEdgegrid') || {};
|
||||
const requestUrl = get(request, 'url', '');
|
||||
const { isSensitive } = useDetectSensitiveField(collection);
|
||||
const { showWarning: showClientSecretWarning, warningMessage: clientSecretWarningMessage } = isSensitive(
|
||||
edgeGridAuth?.clientSecret
|
||||
);
|
||||
|
||||
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||
|
||||
const handleSave = () => {
|
||||
save();
|
||||
};
|
||||
|
||||
const handleFieldChange = (field: EdgeGridField, value: string) => {
|
||||
const content: AkamaiEdgeGridAuthValues = {
|
||||
accessToken: edgeGridAuth.accessToken || '',
|
||||
clientToken: edgeGridAuth.clientToken || '',
|
||||
clientSecret: edgeGridAuth.clientSecret || '',
|
||||
nonce: edgeGridAuth.nonce || '',
|
||||
timestamp: edgeGridAuth.timestamp || '',
|
||||
baseURL: edgeGridAuth.baseURL || '',
|
||||
headersToSign: edgeGridAuth.headersToSign || '',
|
||||
maxBodySize: edgeGridAuth.maxBodySize ?? null
|
||||
};
|
||||
|
||||
if (field === 'maxBodySize') {
|
||||
content.maxBodySize = toMaxBodySize(value);
|
||||
} else {
|
||||
(content as Record<string, unknown>)[field] = value || '';
|
||||
}
|
||||
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'akamai-edgegrid',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const renderField = ({ key, label, tooltip, isSecret }: EdgeGridFieldConfig) => {
|
||||
const showWarning = isSecret && showClientSecretWarning;
|
||||
const rawValue = key === 'baseURL' ? edgeGridAuth.baseURL || requestUrl : edgeGridAuth[key];
|
||||
const fieldValue = rawValue === null || rawValue === undefined ? '' : String(rawValue);
|
||||
return (
|
||||
<div key={key}>
|
||||
<label>
|
||||
{label}
|
||||
{tooltip && (
|
||||
<span className="field-info">
|
||||
<IconInfoCircle size={16} />
|
||||
<span className="field-tooltip">{tooltip}</span>
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={fieldValue}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val: string) => handleFieldChange(key, val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
isSecret={isSecret}
|
||||
isCompact
|
||||
/>
|
||||
{showWarning && (
|
||||
<SensitiveFieldWarning fieldName="edgegrid-client-secret" warningMessage={clientSecretWarningMessage} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
{BASIC_FIELDS.map(renderField)}
|
||||
|
||||
<div className="advanced-settings-header">
|
||||
<span className="advanced-settings-icon">
|
||||
<IconAdjustmentsHorizontal size={16} />
|
||||
</span>
|
||||
<span>Advanced Settings</span>
|
||||
</div>
|
||||
|
||||
<>
|
||||
{ADVANCED_FIELDS.map(renderField)}
|
||||
</>
|
||||
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default EdgeGridAuth;
|
||||
@@ -12,6 +12,7 @@ import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import ApiKeyAuth from './ApiKeyAuth';
|
||||
import EdgeGridAuth from './EdgeGridAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
import OAuth2 from './OAuth2/index';
|
||||
@@ -68,6 +69,9 @@ const Auth = ({ item, collection }) => {
|
||||
case 'apikey': {
|
||||
return <ApiKeyAuth collection={collection} item={item} request={request} save={save} updateAuth={updateAuth} />;
|
||||
}
|
||||
case 'akamai-edgegrid': {
|
||||
return <EdgeGridAuth collection={collection} item={item} request={request} save={save} updateAuth={updateAuth} />;
|
||||
}
|
||||
case 'inherit': {
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user