mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
console: add time limits setting to security settings
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2420 GitOrigin-RevId: 9c2752c4939a8d22474277f6894bf50f02486899
This commit is contained in:
parent
0a829f8762
commit
8ca962ab91
@ -32,6 +32,7 @@
|
||||
- console: fix cross-schema array relationship suggestions
|
||||
- console: add performance fixes for handle large db schemas
|
||||
- console: fix missing cross-schema computed fields in permission builder
|
||||
- console: add time limits setting to security settings
|
||||
- cli: add support for `network` metadata object
|
||||
- cli: `hasura migrate apply --all-databases` will return a non zero exit code if operation failed on atleast one database (#7499)
|
||||
- cli: `migrate create --from-server` creates the migration and marks it as applied on the server
|
||||
|
@ -197,7 +197,7 @@ export function TableRow<T>({
|
||||
: styles.justify_center
|
||||
} ${singleColumn ? styles.single_column : styles.justify_center}`}
|
||||
onClick={onClick(i)}
|
||||
key={i}
|
||||
key={`${index}${i}`}
|
||||
>
|
||||
{renderCol({
|
||||
data,
|
||||
|
@ -15,23 +15,19 @@ const ApiLimitsComponent: React.FC<securitySettingsComponentProps> = ({
|
||||
allRoles,
|
||||
dispatch,
|
||||
}) => {
|
||||
// useEffect(() => {
|
||||
// dispatch(exportMetadata());
|
||||
// }, [dispatch]);
|
||||
|
||||
const headers = [
|
||||
'Role',
|
||||
'Depth Limit',
|
||||
'Node Limit',
|
||||
'Rate Limit (RPM)',
|
||||
// 'Operation Timeout (Seconds)',
|
||||
'Timeout (Seconds)',
|
||||
];
|
||||
const keys = [
|
||||
'role',
|
||||
'depth_limit',
|
||||
'node_limit',
|
||||
'rate_limit',
|
||||
// 'operation_timeout',
|
||||
'time_limit',
|
||||
];
|
||||
const roles = allRoles;
|
||||
return (
|
||||
|
@ -28,6 +28,10 @@ export const labels: Record<
|
||||
info:
|
||||
'Set a request rate limit for this role. You can also combine additional unique parameters for more granularity.',
|
||||
},
|
||||
time_limit: {
|
||||
title: 'Timeout',
|
||||
info: 'Global timeout for GraphQL operations.',
|
||||
},
|
||||
};
|
||||
|
||||
interface LimitsFormWrapperProps extends TableFormProps<RoleLimits> {
|
||||
@ -47,6 +51,7 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
const api_limits = useSelector((state: ApiLimitsFormSate) => state);
|
||||
const rateLimit = useSelector((state: ApiLimitsFormSate) => state.rate_limit);
|
||||
const nodeLimit = useSelector((state: ApiLimitsFormSate) => state.node_limit);
|
||||
const timeLimit = useSelector((state: ApiLimitsFormSate) => state.time_limit);
|
||||
const depthLimit = useSelector(
|
||||
(state: ApiLimitsFormSate) => state.depth_limit
|
||||
);
|
||||
@ -81,6 +86,7 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
const disabled_for_role =
|
||||
isEmpty(depthLimit?.global) &&
|
||||
isEmpty(nodeLimit?.global) &&
|
||||
isEmpty(timeLimit?.global) &&
|
||||
isEmpty(rateLimit?.global);
|
||||
return currentRole !== 'global' && disabled_for_role;
|
||||
};
|
||||
@ -99,6 +105,8 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
return dispatch(apiLimitActions.updateDepthLimitState(state));
|
||||
case 'node_limit':
|
||||
return dispatch(apiLimitActions.updateNodeLimitState(state));
|
||||
case 'time_limit':
|
||||
return dispatch(apiLimitActions.updateTimeLimitState(state));
|
||||
default:
|
||||
return dispatch(apiLimitActions.updateRateLimitState(state));
|
||||
}
|
||||
@ -118,6 +126,10 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
return dispatch(
|
||||
apiLimitActions.updateNodeLimitRole({ role, limit: value })
|
||||
);
|
||||
case 'time_limit':
|
||||
return dispatch(
|
||||
apiLimitActions.updateTimeLimitRole({ role, limit: value })
|
||||
);
|
||||
default:
|
||||
return dispatch(
|
||||
apiLimitActions.updateMaxReqPerMin({ role, limit: value })
|
||||
@ -129,6 +141,8 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
return dispatch(apiLimitActions.updateGlobalDepthLimit(value));
|
||||
case 'node_limit':
|
||||
return dispatch(apiLimitActions.updateGlobalNodeLimit(value));
|
||||
case 'time_limit':
|
||||
return dispatch(apiLimitActions.updateGlobalTimeLimit(value));
|
||||
default:
|
||||
return dispatch(apiLimitActions.updateGlobalMaxReqPerMin(value));
|
||||
}
|
||||
@ -162,6 +176,7 @@ const LimitsFormWrapper: React.FC<LimitsFormWrapperProps> = ({
|
||||
return (
|
||||
<LimitsForm
|
||||
limit={limit}
|
||||
key={key}
|
||||
label={label}
|
||||
role={role}
|
||||
state={api_limits[limit]?.state ?? RoleState.global}
|
||||
|
@ -141,6 +141,7 @@ const LimitsTable: React.FC<Props> = ({
|
||||
{roles.map(role => (
|
||||
<TableRow
|
||||
index={role}
|
||||
key={role}
|
||||
entries={getRowData(role)}
|
||||
renderCol={({ data: { per_role, state } }) => {
|
||||
const roleLimit = per_role?.[role];
|
||||
|
@ -23,6 +23,13 @@ Array [
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
Object {
|
||||
"global": -1,
|
||||
"per_role": Object {
|
||||
"user": undefined,
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@ -49,6 +56,13 @@ Array [
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
Object {
|
||||
"global": -1,
|
||||
"per_role": Object {
|
||||
"new_role": undefined,
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@ -75,6 +89,13 @@ Array [
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
Object {
|
||||
"global": -1,
|
||||
"per_role": Object {
|
||||
"test_role": undefined,
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@ -101,6 +122,13 @@ Array [
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
Object {
|
||||
"global": -1,
|
||||
"per_role": Object {
|
||||
"editor": undefined,
|
||||
},
|
||||
"state": "disabled",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@ -121,5 +149,10 @@ Array [
|
||||
"per_role": Object {},
|
||||
"state": "disabled",
|
||||
},
|
||||
Object {
|
||||
"global": -1,
|
||||
"per_role": Object {},
|
||||
"state": "disabled",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -14,6 +14,7 @@ export type updateSecurityFeaturesActionType = {
|
||||
disabled: boolean;
|
||||
depth_limit?: APILimitInputType<number>;
|
||||
node_limit?: APILimitInputType<number>;
|
||||
time_limit?: APILimitInputType<number>;
|
||||
rate_limit?: APILimitInputType<{
|
||||
unique_params: Nullable<'IP' | string[]>;
|
||||
max_reqs_per_min: number;
|
||||
|
@ -25,6 +25,11 @@ const initialState = {
|
||||
{ unique_params: Nullable<'IP' | string[]>; max_reqs_per_min: number }
|
||||
>,
|
||||
},
|
||||
time_limit: {
|
||||
global: -1,
|
||||
state: RoleState.disabled,
|
||||
per_role: {} as Record<string, number>,
|
||||
},
|
||||
};
|
||||
|
||||
type LimitPayload<T = number> = {
|
||||
@ -51,9 +56,18 @@ const formStateSLice = createSlice({
|
||||
updateNodeLimitRole(state, action: PayloadAction<LimitPayload>) {
|
||||
state.node_limit.per_role[action.payload.role] = action.payload.limit;
|
||||
},
|
||||
updateTimeLimitRole(state, action: PayloadAction<LimitPayload>) {
|
||||
state.time_limit.per_role[action.payload.role] = action.payload.limit;
|
||||
},
|
||||
updateNodeLimitState(state, action: PayloadAction<RoleState>) {
|
||||
state.node_limit.state = action.payload;
|
||||
},
|
||||
updateTimeLimitState(state, action: PayloadAction<RoleState>) {
|
||||
state.time_limit.state = action.payload;
|
||||
},
|
||||
updateGlobalTimeLimit(state, action: PayloadAction<number>) {
|
||||
state.time_limit.global = action.payload;
|
||||
},
|
||||
updateUniqueParams(
|
||||
state,
|
||||
action: PayloadAction<LimitPayload<'IP' | string[]>>
|
||||
@ -95,6 +109,7 @@ const formStateSLice = createSlice({
|
||||
state.depth_limit = action.payload.depth_limit;
|
||||
state.node_limit = action.payload.node_limit;
|
||||
state.rate_limit = action.payload.rate_limit;
|
||||
state.time_limit = action.payload.time_limit;
|
||||
},
|
||||
setDisable(state, action: PayloadAction<boolean>) {
|
||||
state.disabled = action.payload;
|
||||
|
@ -4,8 +4,9 @@ import { isEmpty } from '../../../Common/utils/jsUtils';
|
||||
import { ApiLimitsFormSate } from './state';
|
||||
|
||||
const getApiLimits = (metadata: HasuraMetadataV3) => {
|
||||
const { node_limit, depth_limit, rate_limit } = metadata.api_limits ?? {};
|
||||
return { depth_limit, node_limit, rate_limit };
|
||||
const { node_limit, depth_limit, rate_limit, time_limit } =
|
||||
metadata.api_limits ?? {};
|
||||
return { depth_limit, node_limit, rate_limit, time_limit };
|
||||
};
|
||||
|
||||
export enum RoleState {
|
||||
@ -44,6 +45,12 @@ const prepareApiLimits = (
|
||||
state: RoleState.disabled,
|
||||
};
|
||||
|
||||
res.time_limit = {
|
||||
global: apiLimits?.time_limit?.global ?? -1,
|
||||
state: RoleState.disabled,
|
||||
per_role: apiLimits?.time_limit?.per_role ?? {},
|
||||
};
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
|
@ -1001,6 +1001,7 @@ export interface HasuraMetadataV3 {
|
||||
disabled?: boolean;
|
||||
depth_limit?: APILimit<number>;
|
||||
node_limit?: APILimit<number>;
|
||||
time_limit?: APILimit<number>;
|
||||
rate_limit?: APILimit<{
|
||||
unique_params: Nullable<'IP' | string[]>;
|
||||
max_reqs_per_min: number;
|
||||
|
@ -193,6 +193,7 @@ export const updateAPILimitsQuery = ({
|
||||
disabled: boolean;
|
||||
depth_limit?: APILimitInputType<number>;
|
||||
node_limit?: APILimitInputType<number>;
|
||||
time_limit?: APILimitInputType<number>;
|
||||
rate_limit?: APILimitInputType<{
|
||||
unique_params: Nullable<'IP' | string[]>;
|
||||
max_reqs_per_min: number;
|
||||
@ -204,7 +205,12 @@ export const updateAPILimitsQuery = ({
|
||||
disabled: newAPILimits.disabled,
|
||||
};
|
||||
|
||||
const api_limits = ['depth_limit', 'node_limit', 'rate_limit'] as const;
|
||||
const api_limits = [
|
||||
'depth_limit',
|
||||
'node_limit',
|
||||
'rate_limit',
|
||||
'time_limit',
|
||||
] as const;
|
||||
|
||||
api_limits.forEach(key => {
|
||||
const role = newAPILimits[key]?.per_role
|
||||
@ -281,7 +287,12 @@ export const removeAPILimitsQuery = ({
|
||||
};
|
||||
}
|
||||
|
||||
const api_limits = ['depth_limit', 'node_limit', 'rate_limit'] as const;
|
||||
const api_limits = [
|
||||
'depth_limit',
|
||||
'node_limit',
|
||||
'rate_limit',
|
||||
'time_limit',
|
||||
] as const;
|
||||
|
||||
api_limits.forEach(key => {
|
||||
delete existingAPILimits?.[key]?.per_role?.[role];
|
||||
|
Loading…
Reference in New Issue
Block a user