feat: improve admin security on dnshole controls (#1686)

This commit is contained in:
Tagaishi
2024-01-11 19:04:41 +01:00
committed by GitHub
parent bd280d171b
commit 60bca7412c
2 changed files with 27 additions and 50 deletions

View File

@@ -7,10 +7,10 @@ import { PiHoleClient } from '~/tools/server/sdk/pihole/piHole';
import { ConfigAppType } from '~/types/app'; import { ConfigAppType } from '~/types/app';
import { AdStatistics } from '~/widgets/dnshole/type'; import { AdStatistics } from '~/widgets/dnshole/type';
import { createTRPCRouter, publicProcedure } from '../../trpc'; import { adminProcedure, createTRPCRouter, publicProcedure } from '../../trpc';
export const dnsHoleRouter = createTRPCRouter({ export const dnsHoleRouter = createTRPCRouter({
control: publicProcedure control: adminProcedure
.input( .input(
z.object({ z.object({
action: z.enum(['enable', 'disable']), action: z.enum(['enable', 'disable']),

View File

@@ -68,16 +68,17 @@ const dnsLightStatus = (
}; };
function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) { function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
const utils = api.useContext();
const { data: sessionData } = useSession(); const { data: sessionData } = useSession();
const { isInitialLoading, data, isFetching: fetchingDnsSummary } = useDnsHoleSummeryQuery(); const { isInitialLoading, data, isFetching: fetchingDnsSummary } = useDnsHoleSummeryQuery();
const { mutateAsync, isLoading: changingStatus } = useDnsHoleControlMutation(); const { mutateAsync, isLoading: changingStatus } = useDnsHoleControlMutation();
const { width, ref } = useElementSize(); const { width, ref } = useElementSize();
const { t } = useTranslation(['common', 'modules/dns-hole-controls']); const { t } = useTranslation(['common', 'modules/dns-hole-controls']);
const enableControls = sessionData?.user.isAdmin ?? false;
const { name: configName, config } = useConfigContext(); const { name: configName, config } = useConfigContext();
const trpcUtils = api.useContext(); const trpcUtils = api.useUtils();
if (isInitialLoading || !data || !configName) { if (isInitialLoading || !data || !configName) {
return <WidgetLoading />; return <WidgetLoading />;
@@ -123,13 +124,24 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
return dnsList; return dnsList;
}; };
const reFetchSummaryDns = () => { const toggleDns = async (action: 'enable' | 'disable', appsToChange?: string[]) => {
trpcUtils.dnsHole.summary.invalidate(); await mutateAsync(
{
action,
configName,
appsToChange,
},
{
onSettled: () => {
trpcUtils.dnsHole.summary.invalidate();
},
}
);
}; };
return ( return (
<Stack justify="space-between" h={'100%'} spacing="0.25rem"> <Stack justify="space-between" h={'100%'} spacing="0.25rem">
{sessionData?.user?.isAdmin && widget.properties.showToggleAllButtons && ( {enableControls && widget.properties.showToggleAllButtons && (
<SimpleGrid <SimpleGrid
ref={ref} ref={ref}
cols={width > 275 ? 2 : 1} cols={width > 275 ? 2 : 1}
@@ -137,20 +149,7 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
spacing="0.25rem" spacing="0.25rem"
> >
<Button <Button
onClick={async () => { onClick={() => toggleDns('enable', getDnsStatus()?.disabled)}
await mutateAsync(
{
action: 'enable',
configName,
appsToChange: getDnsStatus()?.disabled,
},
{
onSettled: () => {
reFetchSummaryDns();
},
}
);
}}
disabled={getDnsStatus()?.disabled.length === 0 || fetchingDnsSummary || changingStatus} disabled={getDnsStatus()?.disabled.length === 0 || fetchingDnsSummary || changingStatus}
leftIcon={<IconPlayerPlay size={20} />} leftIcon={<IconPlayerPlay size={20} />}
variant="light" variant="light"
@@ -160,20 +159,7 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
{t('enableAll')} {t('enableAll')}
</Button> </Button>
<Button <Button
onClick={async () => { onClick={() => toggleDns('disable', getDnsStatus()?.enabled)}
await mutateAsync(
{
action: 'disable',
configName,
appsToChange: getDnsStatus()?.enabled,
},
{
onSettled: () => {
reFetchSummaryDns();
},
}
);
}}
disabled={getDnsStatus()?.enabled.length === 0 || fetchingDnsSummary || changingStatus} disabled={getDnsStatus()?.enabled.length === 0 || fetchingDnsSummary || changingStatus}
leftIcon={<IconPlayerStop size={20} />} leftIcon={<IconPlayerStop size={20} />}
variant="light" variant="light"
@@ -190,7 +176,8 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
display="flex" display="flex"
style={{ style={{
flex: '1', flex: '1',
justifyContent: widget.properties.showToggleAllButtons ? 'flex-end' : 'space-evenly', justifyContent:
enableControls && widget.properties.showToggleAllButtons ? 'flex-end' : 'space-evenly',
}} }}
> >
{data.status.map((dnsHole, index) => { {data.status.map((dnsHole, index) => {
@@ -217,21 +204,11 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
<Stack spacing="0rem"> <Stack spacing="0rem">
<Text>{app.name}</Text> <Text>{app.name}</Text>
<UnstyledButton <UnstyledButton
onClick={async () => { onClick={() =>
await mutateAsync( toggleDns(dnsHole.status === 'enabled' ? 'disable' : 'enable', [app.id])
{ }
action: dnsHole.status === 'enabled' ? 'disable' : 'enable',
configName,
appsToChange: [app.id],
},
{
onSettled: () => {
reFetchSummaryDns();
},
}
);
}}
disabled={fetchingDnsSummary || changingStatus} disabled={fetchingDnsSummary || changingStatus}
style={{ pointerEvents: enableControls ? 'auto' : 'none' }}
> >
<Badge <Badge
variant="dot" variant="dot"