Add activity logging to most of the endpoints

This commit is contained in:
DaneEveritt 2022-05-29 19:26:28 -04:00
parent 287fd60891
commit 9b7af02690
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
11 changed files with 170 additions and 30 deletions

View file

@ -16,6 +16,7 @@ use Pterodactyl\Services\Activity\ActivityLogService;
* @method static ActivityLogService property(string|array $key, mixed $value = null)
* @method static \Pterodactyl\Models\ActivityLog log(string $description = null)
* @method static ActivityLogService clone()
* @method static void reset()
* @method static mixed transaction(\Closure $callback)
*/
class Activity extends Facade

View file

@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Response;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Database;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Transformers\Api\Client\DatabaseTransformer;
@ -76,6 +77,11 @@ class DatabaseController extends ClientApiController
{
$database = $this->deployDatabaseService->handle($server, $request->validated());
Activity::event('server:database.create')
->subject($database)
->property('name', $database->database)
->log();
return $this->fractal->item($database)
->parseIncludes(['password'])
->transformWith($this->getTransformer(DatabaseTransformer::class))
@ -95,6 +101,8 @@ class DatabaseController extends ClientApiController
$this->passwordService->handle($database);
$database->refresh();
Activity::event('server:database.rotate-password')->subject($database)->log();
return $this->fractal->item($database)
->parseIncludes(['password'])
->transformWith($this->getTransformer(DatabaseTransformer::class))
@ -110,6 +118,11 @@ class DatabaseController extends ClientApiController
{
$this->managementService->delete($database);
Activity::event('server:database.delete')
->subject($database)
->property('name', $database->database)
->log();
return Response::create('', Response::HTTP_NO_CONTENT);
}
}

View file

@ -4,6 +4,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
@ -68,9 +69,16 @@ class NetworkAllocationController extends ClientApiController
*/
public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array
{
$allocation = $this->repository->update($allocation->id, [
'notes' => $request->input('notes'),
]);
$original = $allocation->notes;
$allocation->forceFill(['notes' => $request->input('notes')])->save();
if ($original !== $allocation->notes) {
Activity::event('server:allocation.notes')
->subject($allocation)
->property(['allocation' => $allocation->toString(), 'old' => $original, 'new' => $allocation->notes])
->log();
}
return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class))
@ -87,6 +95,11 @@ class NetworkAllocationController extends ClientApiController
{
$this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]);
Activity::event('server:allocation.primary')
->subject($allocation)
->property('allocation', $allocation->toString())
->log();
return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class))
->toArray();
@ -106,6 +119,11 @@ class NetworkAllocationController extends ClientApiController
$allocation = $this->assignableAllocationService->handle($server);
Activity::event('server:allocation.create')
->subject($allocation)
->property('allocation', $allocation->toString())
->log();
return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class))
->toArray();
@ -135,6 +153,11 @@ class NetworkAllocationController extends ClientApiController
'server_id' => null,
]);
Activity::event('server:allocation.delete')
->subject($allocation)
->property('allocation', $allocation->toString())
->log();
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}
}

View file

@ -9,6 +9,7 @@ use Illuminate\Http\Response;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Schedule;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Helpers\Utilities;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\Eloquent\ScheduleRepository;
@ -83,6 +84,11 @@ class ScheduleController extends ClientApiController
'next_run_at' => $this->getNextRunAt($request),
]);
Activity::event('server:schedule.create')
->subject($model)
->property('name', $model->name)
->log();
return $this->fractal->item($model)
->transformWith($this->getTransformer(ScheduleTransformer::class))
->toArray();
@ -141,6 +147,11 @@ class ScheduleController extends ClientApiController
$this->repository->update($schedule->id, $data);
Activity::event('server:schedule.update')
->subject($schedule)
->property(['name' => $schedule->name, 'active' => $active])
->log();
return $this->fractal->item($schedule->refresh())
->transformWith($this->getTransformer(ScheduleTransformer::class))
->toArray();
@ -158,6 +169,8 @@ class ScheduleController extends ClientApiController
{
$this->service->handle($schedule, true);
Activity::event('server:schedule.execute')->subject($schedule)->property('name', $schedule->name)->log();
return new JsonResponse([], JsonResponse::HTTP_ACCEPTED);
}
@ -170,6 +183,8 @@ class ScheduleController extends ClientApiController
{
$this->repository->delete($schedule->id);
Activity::event('server:schedule.delete')->subject($schedule)->property('name', $schedule->name)->log();
return new JsonResponse([], Response::HTTP_NO_CONTENT);
}

View file

@ -7,6 +7,7 @@ use Illuminate\Http\Response;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Schedule;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Models\Permission;
use Pterodactyl\Repositories\Eloquent\TaskRepository;
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
@ -67,6 +68,11 @@ class ScheduleTaskController extends ClientApiController
'continue_on_failure' => (bool) $request->input('continue_on_failure'),
]);
Activity::event('server:task.create')
->subject($schedule, $task)
->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload])
->log();
return $this->fractal->item($task)
->transformWith($this->getTransformer(TaskTransformer::class))
->toArray();
@ -98,6 +104,11 @@ class ScheduleTaskController extends ClientApiController
'continue_on_failure' => (bool) $request->input('continue_on_failure'),
]);
Activity::event('server:task.update')
->subject($schedule, $task)
->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload])
->log();
return $this->fractal->item($task->refresh())
->transformWith($this->getTransformer(TaskTransformer::class))
->toArray();
@ -127,6 +138,8 @@ class ScheduleTaskController extends ClientApiController
$task->delete();
Activity::event('server:task.delete')->subject($schedule, $task)->property('name', $schedule->name)->log();
return new JsonResponse(null, Response::HTTP_NO_CONTENT);
}
}

View file

@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Response;
use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Services\Servers\ReinstallServerService;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
@ -52,6 +53,12 @@ class SettingsController extends ClientApiController
'name' => $request->input('name'),
]);
if ($server->name !== $request->input('name')) {
Activity::event('server:settings.rename')
->property(['old' => $server->name, 'new' => $request->input('name')])
->log();
}
return new JsonResponse([], Response::HTTP_NO_CONTENT);
}
@ -66,6 +73,8 @@ class SettingsController extends ClientApiController
{
$this->reinstallServerService->handle($server);
Activity::event('server:reinstall')->log();
return new JsonResponse([], Response::HTTP_ACCEPTED);
}
@ -82,8 +91,15 @@ class SettingsController extends ClientApiController
throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.');
}
$original = $server->image;
$server->forceFill(['image' => $request->input('docker_image')])->saveOrFail();
if ($original !== $server->image) {
Activity::event('server:startup.image')
->property(['old' => $original, 'new' => $request->input('docker_image')])
->log();
}
return new JsonResponse([], Response::HTTP_NO_CONTENT);
}
}

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Pterodactyl\Models\Server;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Services\Servers\StartupCommandService;
use Pterodactyl\Services\Servers\VariableValidatorService;
use Pterodactyl\Repositories\Eloquent\ServerVariableRepository;
@ -75,6 +76,7 @@ class StartupController extends ClientApiController
{
/** @var \Pterodactyl\Models\EggVariable $variable */
$variable = $server->variables()->where('env_variable', $request->input('key'))->first();
$original = $variable->server_value;
if (is_null($variable) || !$variable->user_viewable) {
throw new BadRequestHttpException('The environment variable you are trying to edit does not exist.');
@ -97,6 +99,17 @@ class StartupController extends ClientApiController
$startup = $this->startupCommandService->handle($server, false);
if ($variable->env_variable !== $request->input('value')) {
Activity::event('server:startup.edit')
->subject($variable)
->property([
'variable' => $variable->env_variable,
'old' => $original,
'new' => $request->input('value'),
])
->log();
}
return $this->fractal->item($variable)
->transformWith($this->getTransformer(EggVariableTransformer::class))
->addMeta([

View file

@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Models\Permission;
use Illuminate\Support\Facades\Log;
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
@ -94,6 +95,11 @@ class SubuserController extends ClientApiController
$this->getDefaultPermissions($request)
);
Activity::event('server:subuser.create')
->subject($response->user)
->property(['email' => $request->input('email'), 'permissions' => $this->getDefaultPermissions($request)])
->log();
return $this->fractal->item($response)
->transformWith($this->getTransformer(SubuserTransformer::class))
->toArray();
@ -116,9 +122,19 @@ class SubuserController extends ClientApiController
sort($permissions);
sort($current);
$log = Activity::event('server:subuser.update')
->subject($subuser->user)
->property([
'email' => $subuser->user->email,
'old' => $current,
'new' => $permissions,
'revoked' => true,
]);
// Only update the database and hit up the Wings instance to invalidate JTI's if the permissions
// have actually changed for the user.
if ($permissions !== $current) {
$log->transaction(function ($instance) use ($request, $subuser, $server) {
$this->repository->update($subuser->id, [
'permissions' => $this->getDefaultPermissions($request),
]);
@ -129,9 +145,14 @@ class SubuserController extends ClientApiController
// Don't block this request if we can't connect to the Wings instance. Chances are it is
// offline in this event and the token will be invalid anyways once Wings boots back.
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
$instance->property('revoked', false);
}
});
}
$log->reset();
return $this->fractal->item($subuser->refresh())
->transformWith($this->getTransformer(SubuserTransformer::class))
->toArray();
@ -147,14 +168,23 @@ class SubuserController extends ClientApiController
/** @var \Pterodactyl\Models\Subuser $subuser */
$subuser = $request->attributes->get('subuser');
$this->repository->delete($subuser->id);
$log = Activity::event('server:subuser.delete')
->subject($subuser->user)
->property('email', $subuser->user->email)
->property('revoked', true);
$log->transaction(function ($instance) use ($server, $subuser) {
$subuser->delete();
try {
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
} catch (DaemonConnectionException $exception) {
// Don't block this request if we can't connect to the Wings instance.
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
$instance->property('revoked', false);
}
});
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}

View file

@ -122,6 +122,11 @@ class Allocation extends Model
return !is_null($this->ip_alias);
}
public function toString(): string
{
return sprintf('%s:%s', $this->ip, $this->port);
}
/**
* Gets information for the server associated with this allocation.
*

View file

@ -4,12 +4,8 @@ namespace Pterodactyl\Providers;
use View;
use Cache;
use Pterodactyl\Models;
use Illuminate\Support\Str;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Backup;
use Pterodactyl\Models\ApiKey;
use Pterodactyl\Models\UserSSHKey;
use Illuminate\Support\Facades\URL;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Schema;
@ -41,11 +37,17 @@ class AppServiceProvider extends ServiceProvider
}
Relation::enforceMorphMap([
'api_key' => ApiKey::class,
'backup' => Backup::class,
'server' => Server::class,
'ssh_key' => UserSSHKey::class,
'user' => User::class,
'allocation' => Models\Allocation::class,
'api_key' => Models\ApiKey::class,
'backup' => Models\Backup::class,
'database' => Models\Database::class,
'egg' => Models\Egg::class,
'egg_variable' => Models\EggVariable::class,
'schedule' => Models\Schedule::class,
'server' => Models\Server::class,
'ssh_key' => Models\UserSSHKey::class,
'task' => Models\Task::class,
'user' => Models\User::class,
]);
}

View file

@ -188,6 +188,15 @@ class ActivityLogService
});
}
/**
* Resets the instance and clears out the log.
*/
public function reset(): void
{
$this->activity = null;
$this->subjects = [];
}
/**
* Returns the current activity log instance.
*/