Add support for tracking when an activity event is triggered from an API key

This commit is contained in:
DaneEveritt 2022-06-18 12:07:44 -04:00
parent 92c1c162af
commit 0520014c0f
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
7 changed files with 87 additions and 0 deletions

View file

@ -16,6 +16,7 @@ use Illuminate\Routing\Middleware\ThrottleRequests;
use Pterodactyl\Http\Middleware\LanguageMiddleware; use Pterodactyl\Http\Middleware\LanguageMiddleware;
use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Routing\Middleware\SubstituteBindings;
use Pterodactyl\Http\Middleware\Activity\TrackAPIKey;
use Illuminate\Session\Middleware\AuthenticateSession; use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\View\Middleware\ShareErrorsFromSession; use Illuminate\View\Middleware\ShareErrorsFromSession;
use Pterodactyl\Http\Middleware\MaintenanceMiddleware; use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
@ -68,6 +69,7 @@ class Kernel extends HttpKernel
EnsureStatefulRequests::class, EnsureStatefulRequests::class,
'auth:sanctum', 'auth:sanctum',
IsValidJson::class, IsValidJson::class,
TrackAPIKey::class,
RequireTwoFactorAuthentication::class, RequireTwoFactorAuthentication::class,
AuthenticateIPAccess::class, AuthenticateIPAccess::class,
], ],

View file

@ -0,0 +1,30 @@
<?php
namespace Pterodactyl\Http\Middleware\Activity;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Models\ApiKey;
use Pterodactyl\Facades\LogTarget;
class TrackAPIKey
{
/**
* Determines if the authenticated user making this request is using an actual
* API key, or it is just a cookie authenticated session. This data is set in a
* request singleton so that all tracked activity log events are properly associated
* with the given API key.
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($request->user()) {
$token = $request->user()->currentAccessToken();
LogTarget::setApiKeyId($token instanceof ApiKey ? $token->id : null);
}
return $next($request);
}
}

View file

@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Event;
use Pterodactyl\Events\ActivityLogged; use Pterodactyl\Events\ActivityLogged;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\MassPrunable; use Illuminate\Database\Eloquent\MassPrunable;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Model as IlluminateModel; use Illuminate\Database\Eloquent\Model as IlluminateModel;
@ -21,11 +22,13 @@ use Illuminate\Database\Eloquent\Model as IlluminateModel;
* @property string|null $description * @property string|null $description
* @property string|null $actor_type * @property string|null $actor_type
* @property int|null $actor_id * @property int|null $actor_id
* @property int|null $api_key_id
* @property \Illuminate\Support\Collection|null $properties * @property \Illuminate\Support\Collection|null $properties
* @property \Carbon\Carbon $timestamp * @property \Carbon\Carbon $timestamp
* @property IlluminateModel|\Eloquent $actor * @property IlluminateModel|\Eloquent $actor
* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ActivityLogSubject[] $subjects * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ActivityLogSubject[] $subjects
* @property int|null $subjects_count * @property int|null $subjects_count
* @property \Pterodactyl\Models\ApiKey|null $apiKey
* *
* @method static Builder|ActivityLog forActor(\Illuminate\Database\Eloquent\Model $actor) * @method static Builder|ActivityLog forActor(\Illuminate\Database\Eloquent\Model $actor)
* @method static Builder|ActivityLog forEvent(string $action) * @method static Builder|ActivityLog forEvent(string $action)
@ -34,6 +37,7 @@ use Illuminate\Database\Eloquent\Model as IlluminateModel;
* @method static Builder|ActivityLog query() * @method static Builder|ActivityLog query()
* @method static Builder|ActivityLog whereActorId($value) * @method static Builder|ActivityLog whereActorId($value)
* @method static Builder|ActivityLog whereActorType($value) * @method static Builder|ActivityLog whereActorType($value)
* @method static Builder|ActivityLog whereApiKeyId($value)
* @method static Builder|ActivityLog whereBatch($value) * @method static Builder|ActivityLog whereBatch($value)
* @method static Builder|ActivityLog whereDescription($value) * @method static Builder|ActivityLog whereDescription($value)
* @method static Builder|ActivityLog whereEvent($value) * @method static Builder|ActivityLog whereEvent($value)
@ -86,6 +90,11 @@ class ActivityLog extends Model
return $this->hasMany(ActivityLogSubject::class); return $this->hasMany(ActivityLogSubject::class);
} }
public function apiKey(): HasOne
{
return $this->hasOne(ApiKey::class, 'id', 'api_key_id');
}
public function scopeForEvent(Builder $builder, string $action): Builder public function scopeForEvent(Builder $builder, string $action): Builder
{ {
return $builder->where('event', $action); return $builder->where('event', $action);

View file

@ -210,6 +210,7 @@ class ActivityLogService
'ip' => Request::ip(), 'ip' => Request::ip(),
'batch_uuid' => $this->batch->uuid(), 'batch_uuid' => $this->batch->uuid(),
'properties' => Collection::make([]), 'properties' => Collection::make([]),
'api_key_id' => $this->targetable->apiKeyId(),
]); ]);
if ($subject = $this->targetable->subject()) { if ($subject = $this->targetable->subject()) {

View file

@ -10,6 +10,8 @@ class ActivityLogTargetableService
protected ?Model $subject = null; protected ?Model $subject = null;
protected ?int $apiKeyId = null;
public function setActor(Model $actor): void public function setActor(Model $actor): void
{ {
$this->actor = $actor; $this->actor = $actor;
@ -20,6 +22,11 @@ class ActivityLogTargetableService
$this->subject = $subject; $this->subject = $subject;
} }
public function setApiKeyId(?int $apiKeyId): void
{
$this->apiKeyId = $apiKeyId;
}
public function actor(): ?Model public function actor(): ?Model
{ {
return $this->actor; return $this->actor;
@ -30,9 +37,15 @@ class ActivityLogTargetableService
return $this->subject; return $this->subject;
} }
public function apiKeyId(): ?int
{
return $this->apiKeyId;
}
public function reset(): void public function reset(): void
{ {
$this->actor = null; $this->actor = null;
$this->subject = null; $this->subject = null;
$this->apiKeyId = null;
} }
} }

View file

@ -19,6 +19,7 @@ class ActivityLogTransformer extends BaseClientTransformer
return [ return [
'batch' => $model->batch, 'batch' => $model->batch,
'event' => $model->event, 'event' => $model->event,
'is_api' => !is_null($model->api_key_id),
'ip' => $model->ip, 'ip' => $model->ip,
'description' => $model->description, 'description' => $model->description,
'properties' => $model->properties ? $model->properties->toArray() : [], 'properties' => $model->properties ? $model->properties->toArray() : [],

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('activity_logs', function (Blueprint $table) {
$table->unsignedInteger('api_key_id')->nullable()->after('actor_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('activity_logs', function (Blueprint $table) {
$table->dropColumn('api_key_id');
});
}
};