Compare commits

..

5 Commits

6 changed files with 224 additions and 10 deletions

View File

@@ -23,6 +23,11 @@ class AutocompleteController extends Controller
{ {
$this->requireAccess($request, $server); $this->requireAccess($request, $server);
// Pterodactyl's route model binding resolves by UUID; ensure we have the integer PK
if ($server->id === null) {
$server = Server::where('uuid', $server->uuid)->firstOrFail();
}
$query = (string) $request->get('q', ''); $query = (string) $request->get('q', '');
$suggestions = $this->engine->suggest( $suggestions = $this->engine->suggest(
$server->id, $server->id,

View File

@@ -85,17 +85,62 @@ class ServerRoleController extends Controller
} }
/** GET /api/client/servers/{server}/addons/permissions/effective/{userId} */ /** GET /api/client/servers/{server}/addons/permissions/effective/{userId} */
public function effectivePermissions(Request $request, Server $server, int $targetUserId): JsonResponse public function effectivePermissions(Request $request, Server $server, string $targetUserId): JsonResponse
{ {
$this->authorizeServerAdmin($request, $server); // Allow callers to pass "me" to resolve to their own user ID
if ($targetUserId === 'me') {
$targetUserId = (string) $request->user()?->id;
}
$effective = $this->resolver->resolveAll( // Non-admins can only query their own permissions
$targetUserId, $user = $request->user();
$server->id, $isAdmin = $user && ($user->root_admin || $server->owner_id === $user->id);
RoleService::KNOWN_PERMISSIONS if (!$isAdmin && (string) $user?->id !== $targetUserId) {
); abort(403, 'You may only query your own permissions.');
}
return response()->json(['data' => $effective]); // If target user is the owner, return an Owner role item
if ((int) $targetUserId === $server->owner_id) {
$ownerRole = [
'role' => [
'id' => 0,
'name' => 'Owner',
'color' => '#f59e0b',
'description' => 'Full server access — assigned to the server owner',
'permissions' => array_keys(RoleService::KNOWN_PERMISSIONS),
'created_at' => now()->toIso8601String(),
],
'source' => 'Server Owner',
];
return response()->json(['data' => [$ownerRole]]);
}
// Get user's assigned roles on this server
$roles = $this->resolver->getUserRolesForServer((int) $targetUserId, $server->id);
$data = [];
foreach ($roles as $role) {
$allowedPermissions = [];
foreach ($role->permissions as $p) {
if ($p->value === 'allow') {
$allowedPermissions[] = $p->permission;
}
}
$data[] = [
'role' => [
'id' => $role->id,
'name' => $role->name,
'color' => $role->color,
'description' => $role->description,
'permissions' => $allowedPermissions,
'created_at' => $role->created_at?->toIso8601String(),
],
'source' => 'Role Assignment',
];
}
return response()->json(['data' => $data]);
} }
/** PUT /api/client/servers/{server}/addons/permissions/override */ /** PUT /api/client/servers/{server}/addons/permissions/override */

View File

@@ -0,0 +1,130 @@
<?php
namespace Pterodactyl\Addons\AdvancedAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class CommandDefinition extends Model
{
protected $table = 'addon_command_definitions';
protected $fillable = [
'server_id',
'node_id',
'egg_id',
'plugin_source',
'command_name',
'definition',
'is_active',
// Virtual/fillable fields for controller convenience
'command',
'description',
'syntax',
'dangerous',
'plugin',
];
protected $casts = [
'definition' => 'array',
'is_active' => 'boolean',
'dangerous' => 'boolean',
];
protected $appends = [
'command',
'description',
'syntax',
'dangerous',
'plugin',
];
protected static function booted()
{
// Add global scope to alias command_name as command for ordering
static::addGlobalScope('alias_command', function ($builder) {
$builder->select('addon_command_definitions.*')
->selectRaw('addon_command_definitions.command_name as command');
});
// Sync helper fields to database columns on saving
static::saving(function (CommandDefinition $model) {
if ($model->plugin) {
$model->plugin_source = $model->plugin;
}
if ($model->command) {
$model->command_name = $model->command;
}
$def = $model->definition ?? [];
$def['name'] = $model->command_name ?? $def['name'] ?? '';
$def['description'] = $model->description ?? $def['description'] ?? '';
$def['syntax'] = $model->syntax ?? $def['syntax'] ?? '';
$def['dangerous'] = (bool) ($model->dangerous ?? $def['dangerous'] ?? false);
$def['plugin'] = $model->plugin_source ?? $def['plugin'] ?? 'custom';
$def['args'] = $def['args'] ?? [];
$model->definition = $def;
});
}
// Accessors and Mutators for virtual fields
public function getCommandAttribute()
{
return $this->command_name;
}
public function setCommandAttribute($value)
{
$this->command_name = $value;
$this->attributes['command_name'] = $value;
}
public function getDescriptionAttribute()
{
return $this->definition['description'] ?? '';
}
public function setDescriptionAttribute($value)
{
$def = $this->definition ?? [];
$def['description'] = $value;
$this->definition = $def;
}
public function getSyntaxAttribute()
{
return $this->definition['syntax'] ?? '';
}
public function setSyntaxAttribute($value)
{
$def = $this->definition ?? [];
$def['syntax'] = $value;
$this->definition = $def;
}
public function getDangerousAttribute()
{
return (bool) ($this->definition['dangerous'] ?? false);
}
public function setDangerousAttribute($value)
{
$def = $this->definition ?? [];
$def['dangerous'] = (bool) $value;
$this->definition = $def;
}
public function getPluginAttribute()
{
return $this->plugin_source;
}
public function setPluginAttribute($value)
{
$this->plugin_source = $value;
$this->attributes['plugin_source'] = $value;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Pterodactyl\Addons\AdvancedAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class ConsolePlayerCache extends Model
{
protected $table = 'addon_console_player_cache';
protected $primaryKey = null;
public $incrementing = false;
public $timestamps = false;
protected $fillable = [
'server_id',
'player_name',
'last_seen_at',
];
protected $casts = [
'server_id' => 'integer',
'last_seen_at' => 'datetime',
];
}

View File

@@ -103,7 +103,11 @@ class DefaultRolesSeeder extends Seeder
]); ]);
} }
if ($this->command) {
$this->command->info("Created default role: {$roleData['name']}"); $this->command->info("Created default role: {$roleData['name']}");
} else {
echo "Created default role: {$roleData['name']}\n";
}
} }
} }
} }

View File

@@ -16,7 +16,12 @@ use Pterodactyl\Addons\AdvancedAdmin\Http\Controllers\Client\AutocompleteControl
Route::group([ Route::group([
'prefix' => 'api/client/servers/{server}', 'prefix' => 'api/client/servers/{server}',
'middleware' => ['api', 'auth:sanctum', 'addon.ratelimit'], 'middleware' => [
'api',
'auth:sanctum',
'addon.ratelimit',
\Pterodactyl\Http\Middleware\Api\Client\SubstituteClientBindings::class,
],
], function () { ], function () {
// ── Network Traffic ─────────────────────────────────────────────────────── // ── Network Traffic ───────────────────────────────────────────────────────