Add basic auditing for filesystem actions
Specifically skipping read actions since there isn't much to say there, and it generally wouldn't be very helpful (plus, likely to generate lots of logs).
This commit is contained in:
parent
b15679d3bb
commit
ccecaa6694
5 changed files with 164 additions and 64 deletions
|
@ -4,6 +4,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||||
|
|
||||||
use Pterodactyl\Models\Backup;
|
use Pterodactyl\Models\Backup;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Models\AuditLog;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Pterodactyl\Services\Backups\DeleteBackupService;
|
use Pterodactyl\Services\Backups\DeleteBackupService;
|
||||||
use Pterodactyl\Repositories\Eloquent\BackupRepository;
|
use Pterodactyl\Repositories\Eloquent\BackupRepository;
|
||||||
|
@ -61,6 +62,7 @@ class BackupController extends ClientApiController
|
||||||
public function index(GetBackupsRequest $request, Server $server)
|
public function index(GetBackupsRequest $request, Server $server)
|
||||||
{
|
{
|
||||||
$limit = min($request->query('per_page') ?? 20, 50);
|
$limit = min($request->query('per_page') ?? 20, 50);
|
||||||
|
|
||||||
return $this->fractal->collection($server->backups()->paginate($limit))
|
return $this->fractal->collection($server->backups()->paginate($limit))
|
||||||
->transformWith($this->getTransformer(BackupTransformer::class))
|
->transformWith($this->getTransformer(BackupTransformer::class))
|
||||||
->toArray();
|
->toArray();
|
||||||
|
@ -77,12 +79,19 @@ class BackupController extends ClientApiController
|
||||||
*/
|
*/
|
||||||
public function store(StoreBackupRequest $request, Server $server)
|
public function store(StoreBackupRequest $request, Server $server)
|
||||||
{
|
{
|
||||||
|
/** @var \Pterodactyl\Models\Backup $backup */
|
||||||
|
$backup = $server->audit(AuditLog::ACTION_SERVER_BACKUP_STARTED, function (AuditLog $model, Server $server) use ($request) {
|
||||||
$backup = $this->initiateBackupService
|
$backup = $this->initiateBackupService
|
||||||
->setIgnoredFiles(
|
->setIgnoredFiles(
|
||||||
explode(PHP_EOL, $request->input('ignored') ?? '')
|
explode(PHP_EOL, $request->input('ignored') ?? '')
|
||||||
)
|
)
|
||||||
->handle($server, $request->input('name'));
|
->handle($server, $request->input('name'));
|
||||||
|
|
||||||
|
$model->metadata = ['backup_uuid' => $backup->uuid];
|
||||||
|
|
||||||
|
return $backup;
|
||||||
|
});
|
||||||
|
|
||||||
return $this->fractal->item($backup)
|
return $this->fractal->item($backup)
|
||||||
->transformWith($this->getTransformer(BackupTransformer::class))
|
->transformWith($this->getTransformer(BackupTransformer::class))
|
||||||
->toArray();
|
->toArray();
|
||||||
|
@ -116,8 +125,10 @@ class BackupController extends ClientApiController
|
||||||
*/
|
*/
|
||||||
public function delete(DeleteBackupRequest $request, Server $server, Backup $backup)
|
public function delete(DeleteBackupRequest $request, Server $server, Backup $backup)
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_BACKUP_DELETED, function () use ($backup) {
|
||||||
$this->deleteBackupService->handle($backup);
|
$this->deleteBackupService->handle($backup);
|
||||||
|
});
|
||||||
|
|
||||||
return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT);
|
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Models\AuditLog;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Pterodactyl\Services\Nodes\NodeJWTService;
|
use Pterodactyl\Services\Nodes\NodeJWTService;
|
||||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonFileRepository;
|
use Pterodactyl\Repositories\Wings\DaemonFileRepository;
|
||||||
|
@ -87,18 +87,15 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException
|
* @throws \Throwable
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
|
||||||
*/
|
*/
|
||||||
public function contents(GetFileContentsRequest $request, Server $server): Response
|
public function contents(GetFileContentsRequest $request, Server $server): Response
|
||||||
{
|
{
|
||||||
return new Response(
|
$response = $this->fileRepository->setServer($server)->getContent(
|
||||||
$this->fileRepository->setServer($server)->getContent(
|
|
||||||
$request->get('file'), config('pterodactyl.files.max_edit_size')
|
$request->get('file'), config('pterodactyl.files.max_edit_size')
|
||||||
),
|
|
||||||
Response::HTTP_OK,
|
|
||||||
['Content-Type' => 'text/plain']
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return new Response($response, Response::HTTP_OK, ['Content-Type' => 'text/plain']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,17 +106,21 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return array
|
* @return array
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function download(GetFileContentsRequest $request, Server $server)
|
public function download(GetFileContentsRequest $request, Server $server)
|
||||||
{
|
{
|
||||||
$token = $this->jwtService
|
$token = $server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_DOWNLOAD, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = ['file' => $request->get('file')];
|
||||||
|
|
||||||
|
return $this->jwtService
|
||||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||||
->setClaims([
|
->setClaims([
|
||||||
'file_path' => rawurldecode($request->get('file')),
|
'file_path' => rawurldecode($request->get('file')),
|
||||||
'server_uuid' => $server->uuid,
|
'server_uuid' => $server->uuid,
|
||||||
])
|
])
|
||||||
->handle($server->node, $request->user()->id . $server->uuid);
|
->handle($server->node, $request->user()->id . $server->uuid);
|
||||||
|
});
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'object' => 'signed_url',
|
'object' => 'signed_url',
|
||||||
|
@ -140,11 +141,20 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function write(WriteFileContentRequest $request, Server $server): JsonResponse
|
public function write(WriteFileContentRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
$this->fileRepository->setServer($server)->putContent($request->get('file'), $request->getContent());
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = [
|
||||||
|
'file' => $request->get('file'),
|
||||||
|
'sub_action' => 'write_content',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->fileRepository
|
||||||
|
->setServer($server)
|
||||||
|
->putContent($request->get('file'), $request->getContent());
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -156,13 +166,20 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function create(CreateFolderRequest $request, Server $server): JsonResponse
|
public function create(CreateFolderRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = [
|
||||||
|
'file' => $request->input('root', '/') . $request->input('name'),
|
||||||
|
'sub_action' => 'create_folder',
|
||||||
|
];
|
||||||
|
|
||||||
$this->fileRepository
|
$this->fileRepository
|
||||||
->setServer($server)
|
->setServer($server)
|
||||||
->createDirectory($request->input('name'), $request->input('root', '/'));
|
->createDirectory($request->input('name'), $request->input('root', '/'));
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -174,13 +191,17 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function rename(RenameFileRequest $request, Server $server): JsonResponse
|
public function rename(RenameFileRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_RENAME, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
|
||||||
|
|
||||||
$this->fileRepository
|
$this->fileRepository
|
||||||
->setServer($server)
|
->setServer($server)
|
||||||
->renameFiles($request->input('root'), $request->input('files'));
|
->renameFiles($request->input('root'), $request->input('files'));
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -192,13 +213,19 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function copy(CopyFileRequest $request, Server $server): JsonResponse
|
public function copy(CopyFileRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = [
|
||||||
|
'file' => $request->input('location'),
|
||||||
|
'sub_action' => 'copy_file',
|
||||||
|
];
|
||||||
$this->fileRepository
|
$this->fileRepository
|
||||||
->setServer($server)
|
->setServer($server)
|
||||||
->copyFile($request->input('location'));
|
->copyFile($request->input('location'));
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -208,17 +235,21 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return array
|
* @return array
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function compress(CompressFilesRequest $request, Server $server): array
|
public function compress(CompressFilesRequest $request, Server $server): array
|
||||||
{
|
{
|
||||||
|
$file = $server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_COMPRESS, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
// Allow up to five minutes for this request to process before timing out.
|
// Allow up to five minutes for this request to process before timing out.
|
||||||
set_time_limit(300);
|
set_time_limit(300);
|
||||||
|
|
||||||
$file = $this->fileRepository->setServer($server)
|
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
|
||||||
|
|
||||||
|
return $this->fileRepository->setServer($server)
|
||||||
->compressFiles(
|
->compressFiles(
|
||||||
$request->input('root'), $request->input('files')
|
$request->input('root'), $request->input('files')
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return $this->fractal->item($file)
|
return $this->fractal->item($file)
|
||||||
->transformWith($this->getTransformer(FileObjectTransformer::class))
|
->transformWith($this->getTransformer(FileObjectTransformer::class))
|
||||||
|
@ -230,15 +261,19 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse
|
public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$file = $server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_DECOMPRESS, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
// Allow up to five minutes for this request to process before timing out.
|
// Allow up to five minutes for this request to process before timing out.
|
||||||
set_time_limit(300);
|
set_time_limit(300);
|
||||||
|
|
||||||
|
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('file')];
|
||||||
|
|
||||||
$this->fileRepository->setServer($server)
|
$this->fileRepository->setServer($server)
|
||||||
->decompressFile($request->input('root'), $request->input('file'));
|
->decompressFile($request->input('root'), $request->input('file'));
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -250,14 +285,18 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function delete(DeleteFileRequest $request, Server $server): JsonResponse
|
public function delete(DeleteFileRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_DELETE, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
|
||||||
|
|
||||||
$this->fileRepository->setServer($server)
|
$this->fileRepository->setServer($server)
|
||||||
->deleteFiles(
|
->deleteFiles(
|
||||||
$request->input('root'), $request->input('files')
|
$request->input('root'), $request->input('files')
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@ -288,11 +327,15 @@ class FileController extends ClientApiController
|
||||||
* @param \Pterodactyl\Models\Server $server
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function pull(PullFileRequest $request, Server $server): JsonResponse
|
public function pull(PullFileRequest $request, Server $server): JsonResponse
|
||||||
{
|
{
|
||||||
|
$server->audit(AuditLog::ACTION_SERVER_FILESYSTEM_PULL, function (AuditLog $audit, Server $server) use ($request) {
|
||||||
|
$audit->metadata = ['directory' => $request->input('directory'), 'url' => $request->input('url')];
|
||||||
|
|
||||||
$this->fileRepository->setServer($server)->pull($request->input('url'), $request->input('directory'));
|
$this->fileRepository->setServer($server)->pull($request->input('url'), $request->input('directory'));
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,18 @@ class AuditLog extends Model
|
||||||
const ACTION_USER_AUTH_FAILED = 'user:auth.failed';
|
const ACTION_USER_AUTH_FAILED = 'user:auth.failed';
|
||||||
const ACTION_USER_AUTH_PASSWORD_CHANGED = 'user:auth.password-changed';
|
const ACTION_USER_AUTH_PASSWORD_CHANGED = 'user:auth.password-changed';
|
||||||
|
|
||||||
|
const ACTION_SERVER_FILESYSTEM_DOWNLOAD = 'server:filesystem.download';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_WRITE = 'server:filesystem.write';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_DELETE = 'server:filesystem.delete';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_RENAME = 'server:filesystem.rename';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_COMPRESS = 'server:filesystem.compress';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_DECOMPRESS = 'server:filesystem.decompress';
|
||||||
|
const ACTION_SERVER_FILESYSTEM_PULL = 'server:filesystem.pull';
|
||||||
|
|
||||||
|
const ACTION_SERVER_BACKUP_STARTED = 'server:backup.started';
|
||||||
|
const ACTION_SERVER_BACKUP_FAILED = 'server:backup.failed';
|
||||||
|
const ACTION_SERVER_BACKUP_COMPELTED = 'server:backup.completed';
|
||||||
|
const ACTION_SERVER_BACKUP_DELETED = 'server:backup.deleted';
|
||||||
const ACTION_SERVER_BACKUP_RESTORE_STARTED = 'server:backup.restore.started';
|
const ACTION_SERVER_BACKUP_RESTORE_STARTED = 'server:backup.restore.started';
|
||||||
const ACTION_SERVER_BACKUP_RESTORE_COMPLETED = 'server:backup.restore.completed';
|
const ACTION_SERVER_BACKUP_RESTORE_COMPLETED = 'server:backup.restore.completed';
|
||||||
const ACTION_SERVER_BACKUP_RESTORE_FAILED = 'server:backup.restore.failed';
|
const ACTION_SERVER_BACKUP_RESTORE_FAILED = 'server:backup.restore.failed';
|
||||||
|
@ -38,7 +50,7 @@ class AuditLog extends Model
|
||||||
public static $validationRules = [
|
public static $validationRules = [
|
||||||
'uuid' => 'required|uuid',
|
'uuid' => 'required|uuid',
|
||||||
'action' => 'required|string',
|
'action' => 'required|string',
|
||||||
'device' => 'required|array',
|
'device' => 'array',
|
||||||
'device.ip_address' => 'ip',
|
'device.ip_address' => 'ip',
|
||||||
'device.user_agent' => 'string',
|
'device.user_agent' => 'string',
|
||||||
'metadata' => 'array',
|
'metadata' => 'array',
|
||||||
|
@ -100,14 +112,14 @@ class AuditLog extends Model
|
||||||
{
|
{
|
||||||
/** @var \Illuminate\Http\Request $request */
|
/** @var \Illuminate\Http\Request $request */
|
||||||
$request = Container::getInstance()->make('request');
|
$request = Container::getInstance()->make('request');
|
||||||
if (! $isSystem || ! $request instanceof Request) {
|
if ($isSystem || ! $request instanceof Request) {
|
||||||
$request = null;
|
$request = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (new self())->fill([
|
return (new self())->fill([
|
||||||
'uuid' => Uuid::uuid4()->toString(),
|
'uuid' => Uuid::uuid4()->toString(),
|
||||||
'is_system' => $isSystem,
|
'is_system' => $isSystem,
|
||||||
'user_id' => $request->user() ? $request->user()->id : null,
|
'user_id' => ($request && $request->user()) ? $request->user()->id : null,
|
||||||
'server_id' => null,
|
'server_id' => null,
|
||||||
'action' => $action,
|
'action' => $action,
|
||||||
'device' => $request ? [
|
'device' => $request ? [
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Models;
|
namespace Pterodactyl\Models;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
use Znck\Eloquent\Traits\BelongsToThrough;
|
use Znck\Eloquent\Traits\BelongsToThrough;
|
||||||
|
@ -335,7 +336,7 @@ class Server extends Model
|
||||||
* @param array $metadata
|
* @param array $metadata
|
||||||
* @return \Pterodactyl\Models\AuditLog
|
* @return \Pterodactyl\Models\AuditLog
|
||||||
*/
|
*/
|
||||||
public function audit(string $action, array $metadata): AuditLog
|
public function newAuditEvent(string $action, array $metadata): AuditLog
|
||||||
{
|
{
|
||||||
$model = AuditLog::factory($action, $metadata)->fill([
|
$model = AuditLog::factory($action, $metadata)->fill([
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
|
@ -345,6 +346,32 @@ class Server extends Model
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a new audit event for a server by using a transaction. If the transaction
|
||||||
|
* fails for any reason everything executed within will be rolled back. The callback
|
||||||
|
* passed in will receive the AuditLog model before it is saved and the second argument
|
||||||
|
* will be the current server instance. The callback should modify the audit entry as
|
||||||
|
* needed before finishing, any changes will be persisted.
|
||||||
|
*
|
||||||
|
* The response from the callback is returned to the caller.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param \Closure $callback
|
||||||
|
* @return mixed
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function audit(string $action, Closure $callback)
|
||||||
|
{
|
||||||
|
$model = $this->newAuditEvent($action, []);
|
||||||
|
|
||||||
|
return $this->getConnection()->transaction(function () use ($callback, &$model) {
|
||||||
|
$response = $callback($model, $this);
|
||||||
|
$model->save();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
class CreateAuditLogsTable extends Migration
|
class CreateAuditLogsTable extends Migration
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,14 @@ class CreateAuditLogsTable extends Migration
|
||||||
{
|
{
|
||||||
Schema::create('audit_logs', function (Blueprint $table) {
|
Schema::create('audit_logs', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->timestamps();
|
$table->char('uuid', 36);
|
||||||
|
$table->boolean('is_system')->default(false);
|
||||||
|
$table->bigInteger('user_id')->nullable();
|
||||||
|
$table->bigInteger('server_id')->nullable();
|
||||||
|
$table->string('action');
|
||||||
|
$table->json('device');
|
||||||
|
$table->json('metadata');
|
||||||
|
$table->timestamp('created_at', 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue