Don't allow backups to be made via schedules if limit = 0 (#3323)

This commit is contained in:
Charles Morgan 2021-05-16 12:47:36 -04:00 committed by GitHub
parent 5a82dd6a18
commit 76ac1998cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 21 deletions

View file

@ -39,6 +39,7 @@ class ScheduleTaskController extends ClientApiController
* *
* @return array * @return array
* *
* @throws \Pterodactyl\Exceptions\Model\HttpForbiddenException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\ServiceLimitExceededException * @throws \Pterodactyl\Exceptions\Service\ServiceLimitExceededException
*/ */
@ -49,6 +50,10 @@ class ScheduleTaskController extends ClientApiController
throw new ServiceLimitExceededException("Schedules may not have more than {$limit} tasks associated with them. Creating this task would put this schedule over the limit."); throw new ServiceLimitExceededException("Schedules may not have more than {$limit} tasks associated with them. Creating this task would put this schedule over the limit.");
} }
if ($server->backup_limit === 0 && $request->action === 'backup') {
throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0.");
}
/** @var \Pterodactyl\Models\Task|null $lastTask */ /** @var \Pterodactyl\Models\Task|null $lastTask */
$lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first(); $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first();
@ -72,6 +77,7 @@ class ScheduleTaskController extends ClientApiController
* *
* @return array * @return array
* *
* @throws \Pterodactyl\Exceptions\Model\HttpForbiddenException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
@ -81,6 +87,10 @@ class ScheduleTaskController extends ClientApiController
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
if ($server->backup_limit === 0 && $request->action === 'backup') {
throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0.");
}
$this->repository->update($task->id, [ $this->repository->update($task->id, [
'action' => $request->input('action'), 'action' => $request->input('action'),
'payload' => $request->input('payload') ?? '', 'payload' => $request->input('payload') ?? '',

View file

@ -3,6 +3,7 @@ import tw from 'twin.macro';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import asModal from '@/hoc/asModal'; import asModal from '@/hoc/asModal';
import ModalContext from '@/context/ModalContext'; import ModalContext from '@/context/ModalContext';
import CopyOnClick from '@/components/elements/CopyOnClick';
interface Props { interface Props {
apiKey: string; apiKey: string;
@ -19,7 +20,7 @@ const ApiKeyModal = ({ apiKey }: Props) => {
shown again. shown again.
</p> </p>
<pre css={tw`text-sm bg-neutral-900 rounded py-2 px-4 font-mono`}> <pre css={tw`text-sm bg-neutral-900 rounded py-2 px-4 font-mono`}>
<code css={tw`font-mono`}>{apiKey}</code> <CopyOnClick text={apiKey}><code css={tw`font-mono`}>{apiKey}</code></CopyOnClick>
</pre> </pre>
<div css={tw`flex justify-end mt-6`}> <div css={tw`flex justify-end mt-6`}>
<Button type={'button'} onClick={() => dismiss()}> <Button type={'button'} onClick={() => dismiss()}>

View file

@ -53,7 +53,7 @@ export default () => {
/> />
)) ))
: :
<p css={tw`text-center text-sm text-neutral-400`}> <p css={tw`text-center text-sm text-neutral-300`}>
{databaseLimit > 0 ? {databaseLimit > 0 ?
'It looks like you have no databases.' 'It looks like you have no databases.'
: :

View file

@ -81,7 +81,7 @@ export default () => {
}, []); }, []);
return ( return (
<PageContentBlock> <PageContentBlock title={'Schedules'}>
<FlashMessageRender byKey={'schedules'} css={tw`mb-4`}/> <FlashMessageRender byKey={'schedules'} css={tw`mb-4`}/>
{!schedule || isLoading ? {!schedule || isLoading ?
<Spinner size={'large'} centered/> <Spinner size={'large'} centered/>

View file

@ -69,6 +69,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => {
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule); const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
const backupLimit = ServerContext.useStoreState(state => state.server.data!.featureLimits.backups);
useEffect(() => { useEffect(() => {
return () => { return () => {
@ -78,21 +79,26 @@ const TaskDetailsModal = ({ schedule, task }: Props) => {
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => { const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
clearFlashes('schedule:task'); clearFlashes('schedule:task');
createOrUpdateScheduleTask(uuid, schedule.id, task?.id, values) if (backupLimit === 0 && values.action === 'backup') {
.then(task => { setSubmitting(false);
let tasks = schedule.tasks.map(t => t.id === task.id ? task : t); addError({ message: 'A backup task cannot be created when the server\'s backup limit is set to 0.', key: 'schedule:task' });
if (!schedule.tasks.find(t => t.id === task.id)) { } else {
tasks = [ ...tasks, task ]; createOrUpdateScheduleTask(uuid, schedule.id, task?.id, values)
} .then(task => {
let tasks = schedule.tasks.map(t => t.id === task.id ? task : t);
if (!schedule.tasks.find(t => t.id === task.id)) {
tasks = [ ...tasks, task ];
}
appendSchedule({ ...schedule, tasks }); appendSchedule({ ...schedule, tasks });
dismiss(); dismiss();
}) })
.catch(error => { .catch(error => {
console.error(error); console.error(error);
setSubmitting(false); setSubmitting(false);
addError({ message: httpErrorToHuman(error), key: 'schedule:task' }); addError({ message: httpErrorToHuman(error), key: 'schedule:task' });
}); });
}
}; };
return ( return (

View file

@ -89,9 +89,9 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
} }
/** /**
* Test that backups can be tasked out correctly since they do not require a payload. * Test that backups can not be tasked when the backup limit is 0
*/ */
public function testBackupsCanBeTaskedCorrectly() public function testBackupsCanNotBeTaskedIfLimit0()
{ {
[$user, $server] = $this->generateTestAccount(); [$user, $server] = $this->generateTestAccount();
@ -101,13 +101,17 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
$this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [ $this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [
'action' => 'backup', 'action' => 'backup',
'time_offset' => 0, 'time_offset' => 0,
])->assertOk(); ])
->assertStatus(Response::HTTP_FORBIDDEN)
->assertJsonPath('errors.0.detail', 'A backup task cannot be created when the server\'s backup limit is set to 0.');
$this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [ $this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [
'action' => 'backup', 'action' => 'backup',
'payload' => "file.txt\nfile2.log", 'payload' => "file.txt\nfile2.log",
'time_offset' => 0, 'time_offset' => 0,
])->assertOk(); ])
->assertStatus(Response::HTTP_FORBIDDEN)
->assertJsonPath('errors.0.detail', 'A backup task cannot be created when the server\'s backup limit is set to 0.');
} }
/** /**