From 32cc8443063a86fa52bbf1f989f94767afad01f6 Mon Sep 17 00:00:00 2001 From: memesalot <10216384+memesalot@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:41:09 -0800 Subject: [PATCH 1/3] Refactor Node and User models for role management and permission checks - Changed the `roles` method in the Node model from `HasManyThrough` to `BelongsToMany` for better relationship handling. - Introduced a new `canViewServers` method in the User model to encapsulate permission logic for viewing servers. - Updated the `canAccessTenant` method to streamline permission checks and incorporate role-based access. - Modified the ServerPolicy to improve permission handling for subusers and added role-based permission checks. --- app/Models/Node.php | 5 ++--- app/Models/User.php | 30 +++++++++++++++++++++++++++--- app/Policies/ServerPolicy.php | 20 +++++++++++++++----- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/app/Models/Node.php b/app/Models/Node.php index 69b1072d51..bef6214723 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -13,7 +13,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\Http; @@ -277,9 +276,9 @@ public function databaseHosts(): BelongsToMany return $this->belongsToMany(DatabaseHost::class); } - public function roles(): HasManyThrough + public function roles(): BelongsToMany { - return $this->hasManyThrough(Role::class, NodeRole::class, 'node_id', 'id', 'id', 'role_id'); + return $this->belongsToMany(Role::class, 'node_role', 'node_id', 'role_id'); } /** diff --git a/app/Models/User.php b/app/Models/User.php index 7a969ec915..56baed314a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -266,7 +266,7 @@ public function activity(): MorphToMany */ public function accessibleServers(): Builder { - if ($this->canned('viewAny', Server::class)) { + if ($this->canViewServers()) { return Server::select('servers.*') ->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id') ->where(function (Builder $builder) { @@ -278,6 +278,22 @@ public function accessibleServers(): Builder return $this->directAccessibleServers(); } + /** + * Check if the user has permission to view servers via role permissions. + */ + public function canViewServers(): bool + { + if ($this->isRootAdmin()) { + return true; + } + + try { + return $this->hasPermissionTo('viewList server'); + } catch (\Spatie\Permission\Exceptions\PermissionDoesNotExist) { + return false; + } + } + /** * Returns all the servers that a user can access "directly". * This means either because they are the owner or a subuser of the server. @@ -438,13 +454,21 @@ public function getTenants(Panel $panel): array|Collection public function canAccessTenant(Model $tenant): bool { if ($tenant instanceof Server) { - if ($this->canned('view', $tenant) || $tenant->owner_id === $this->id) { + if ($tenant->owner_id === $this->id) { return true; } $subuser = $tenant->subusers->where('user_id', $this->id)->first(); + if ($subuser !== null) { + return true; + } - return $subuser !== null; + // Check if user has role-based access to this server's node + if ($this->canViewServers() && $this->canTarget($tenant->node)) { + return true; + } + + return false; } return false; diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 6f58b28fbe..fe955c59e6 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -22,12 +22,12 @@ public function before(User $user, string $ability, string|Server $server): ?boo return null; } - if (Subuser::doesPermissionExist($ability)) { - // Owner has full server permissions - if ($server->owner_id === $user->id) { - return true; - } + // Owner has full server permissions + if ($server->owner_id === $user->id) { + return true; + } + if (Subuser::doesPermissionExist($ability)) { $subuser = $server->subusers->where('user_id', $user->id)->first(); // If the user is a subuser check their permissions if ($subuser && in_array($ability, $subuser->permissions)) { @@ -40,6 +40,16 @@ public function before(User $user, string $ability, string|Server $server): ?boo return false; } + // Check if user has role-based permission for this specific ability + $permissionName = $ability . ' ' . $this->modelName; + try { + if ($user->hasPermissionTo($permissionName)) { + return true; + } + } catch (\Spatie\Permission\Exceptions\PermissionDoesNotExist) { + // Permission doesn't exist, continue to default policies + } + // Return null to let default policies take over return null; } From d8446736e750e51974dda6c04082a65cd044c21d Mon Sep 17 00:00:00 2001 From: memesalot <10216384+memesalot@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:03:20 -0800 Subject: [PATCH 2/3] Update Node model to use NodeRole for role relationships - Refactored the `roles` method in the Node model to utilize the NodeRole model for defining the many-to-many relationship with roles, enhancing clarity and maintainability. --- app/Models/Node.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Node.php b/app/Models/Node.php index bef6214723..a1f176e057 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -4,6 +4,7 @@ use App\Contracts\Validatable; use App\Exceptions\Service\HasActiveServersException; +use App\Models\NodeRole; use App\Repositories\Daemon\DaemonSystemRepository; use App\Traits\HasValidation; use Carbon\Carbon; @@ -278,7 +279,7 @@ public function databaseHosts(): BelongsToMany public function roles(): BelongsToMany { - return $this->belongsToMany(Role::class, 'node_role', 'node_id', 'role_id'); + return $this->belongsToMany(Role::class, NodeRole::class); } /** From 028dba0ab39b24e21abcb63e2820f7671bac5b57 Mon Sep 17 00:00:00 2001 From: memesalot <10216384+memesalot@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:06:37 -0800 Subject: [PATCH 3/3] fix oops --- app/Models/Node.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Models/Node.php b/app/Models/Node.php index a1f176e057..f6828212a3 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -4,7 +4,6 @@ use App\Contracts\Validatable; use App\Exceptions\Service\HasActiveServersException; -use App\Models\NodeRole; use App\Repositories\Daemon\DaemonSystemRepository; use App\Traits\HasValidation; use Carbon\Carbon;