mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 08:44:46 +02:00
Allow to assign nodes to roles (node ownership) (#1231)
* allow to assign nodes to roles * fix typo * fix node policy * small ui improvements * add missing translation * make phpstan happy * fix migration on mysql * also restrict mounts & database hosts to allowed nodes * fix migration on mysql v2 * changes from review * fix hasManyThrough * change `accessibleNodes` to builder Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com> --------- Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
This commit is contained in:
parent
c0fda71e20
commit
03745eb4be
@ -16,6 +16,7 @@ use Filament\Tables\Actions\EditAction;
|
|||||||
use Filament\Tables\Actions\ViewAction;
|
use Filament\Tables\Actions\ViewAction;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class DatabaseHostResource extends Resource
|
class DatabaseHostResource extends Resource
|
||||||
{
|
{
|
||||||
@ -27,7 +28,7 @@ class DatabaseHostResource extends Resource
|
|||||||
|
|
||||||
public static function getNavigationBadge(): ?string
|
public static function getNavigationBadge(): ?string
|
||||||
{
|
{
|
||||||
return static::getModel()::count() ?: null;
|
return (string) static::getEloquentQuery()->count() ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getNavigationLabel(): string
|
public static function getNavigationLabel(): string
|
||||||
@ -144,7 +145,7 @@ class DatabaseHostResource extends Resource
|
|||||||
->preload()
|
->preload()
|
||||||
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
||||||
->label(trans('admin/databasehost.linked_nodes'))
|
->label(trans('admin/databasehost.linked_nodes'))
|
||||||
->relationship('nodes', 'name'),
|
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'))),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -158,4 +159,13 @@ class DatabaseHostResource extends Resource
|
|||||||
'edit' => Pages\EditDatabaseHost::route('/{record}/edit'),
|
'edit' => Pages\EditDatabaseHost::route('/{record}/edit'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
$query = parent::getEloquentQuery();
|
||||||
|
|
||||||
|
return $query->whereHas('nodes', function (Builder $query) {
|
||||||
|
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||||
|
})->orDoesntHave('nodes');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use Filament\Notifications\Notification;
|
|||||||
use Filament\Resources\Pages\CreateRecord;
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
use Filament\Resources\Pages\CreateRecord\Concerns\HasWizard;
|
use Filament\Resources\Pages\CreateRecord\Concerns\HasWizard;
|
||||||
use Filament\Support\Exceptions\Halt;
|
use Filament\Support\Exceptions\Halt;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\HtmlString;
|
use Illuminate\Support\HtmlString;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -145,7 +146,7 @@ class CreateDatabaseHost extends CreateRecord
|
|||||||
->preload()
|
->preload()
|
||||||
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
||||||
->label(trans('admin/databasehost.linked_nodes'))
|
->label(trans('admin/databasehost.linked_nodes'))
|
||||||
->relationship('nodes', 'name'),
|
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'))),
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use Filament\Tables\Actions\EditAction;
|
|||||||
use Filament\Tables\Actions\ViewAction;
|
use Filament\Tables\Actions\ViewAction;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class MountResource extends Resource
|
class MountResource extends Resource
|
||||||
{
|
{
|
||||||
@ -44,7 +45,7 @@ class MountResource extends Resource
|
|||||||
|
|
||||||
public static function getNavigationBadge(): ?string
|
public static function getNavigationBadge(): ?string
|
||||||
{
|
{
|
||||||
return static::getModel()::count() ?: null;
|
return (string) static::getEloquentQuery()->count() ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getNavigationGroup(): ?string
|
public static function getNavigationGroup(): ?string
|
||||||
@ -147,7 +148,7 @@ class MountResource extends Resource
|
|||||||
->preload(),
|
->preload(),
|
||||||
Select::make('nodes')->multiple()
|
Select::make('nodes')->multiple()
|
||||||
->label(trans('admin/mount.nodes'))
|
->label(trans('admin/mount.nodes'))
|
||||||
->relationship('nodes', 'name')
|
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||||
->searchable(['name', 'fqdn'])
|
->searchable(['name', 'fqdn'])
|
||||||
->preload(),
|
->preload(),
|
||||||
]),
|
]),
|
||||||
@ -170,4 +171,13 @@ class MountResource extends Resource
|
|||||||
'edit' => Pages\EditMount::route('/{record}/edit'),
|
'edit' => Pages\EditMount::route('/{record}/edit'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
$query = parent::getEloquentQuery();
|
||||||
|
|
||||||
|
return $query->whereHas('nodes', function (Builder $query) {
|
||||||
|
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||||
|
})->orDoesntHave('nodes');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use App\Filament\Admin\Resources\NodeResource\Pages;
|
|||||||
use App\Filament\Admin\Resources\NodeResource\RelationManagers;
|
use App\Filament\Admin\Resources\NodeResource\RelationManagers;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class NodeResource extends Resource
|
class NodeResource extends Resource
|
||||||
{
|
{
|
||||||
@ -37,7 +38,7 @@ class NodeResource extends Resource
|
|||||||
|
|
||||||
public static function getNavigationBadge(): ?string
|
public static function getNavigationBadge(): ?string
|
||||||
{
|
{
|
||||||
return static::getModel()::count() ?: null;
|
return (string) static::getEloquentQuery()->count() ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRelations(): array
|
public static function getRelations(): array
|
||||||
@ -56,4 +57,11 @@ class NodeResource extends Resource
|
|||||||
'edit' => Pages\EditNode::route('/{record}/edit'),
|
'edit' => Pages\EditNode::route('/{record}/edit'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
$query = parent::getEloquentQuery();
|
||||||
|
|
||||||
|
return $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use Filament\Forms\Components\Component;
|
|||||||
use Filament\Forms\Components\Fieldset;
|
use Filament\Forms\Components\Fieldset;
|
||||||
use Filament\Forms\Components\Placeholder;
|
use Filament\Forms\Components\Placeholder;
|
||||||
use Filament\Forms\Components\Section;
|
use Filament\Forms\Components\Section;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
use Filament\Forms\Get;
|
use Filament\Forms\Get;
|
||||||
@ -69,6 +70,11 @@ class RoleResource extends Resource
|
|||||||
->badge()
|
->badge()
|
||||||
->counts('permissions')
|
->counts('permissions')
|
||||||
->formatStateUsing(fn (Role $role, $state) => $role->isRootAdmin() ? trans('admin/role.all') : $state),
|
->formatStateUsing(fn (Role $role, $state) => $role->isRootAdmin() ? trans('admin/role.all') : $state),
|
||||||
|
TextColumn::make('nodes.name')
|
||||||
|
->icon('tabler-server-2')
|
||||||
|
->label(trans('admin/role.nodes'))
|
||||||
|
->badge()
|
||||||
|
->placeholder(trans('admin/role.all')),
|
||||||
TextColumn::make('users_count')
|
TextColumn::make('users_count')
|
||||||
->label(trans('admin/role.users'))
|
->label(trans('admin/role.users'))
|
||||||
->counts('users')
|
->counts('users')
|
||||||
@ -125,6 +131,14 @@ class RoleResource extends Resource
|
|||||||
->label(trans('admin/role.permissions'))
|
->label(trans('admin/role.permissions'))
|
||||||
->content(trans('admin/role.root_admin', ['role' => Role::ROOT_ADMIN]))
|
->content(trans('admin/role.root_admin', ['role' => Role::ROOT_ADMIN]))
|
||||||
->visible(fn (Get $get) => $get('name') === Role::ROOT_ADMIN),
|
->visible(fn (Get $get) => $get('name') === Role::ROOT_ADMIN),
|
||||||
|
Select::make('nodes')
|
||||||
|
->label(trans('admin/role.nodes'))
|
||||||
|
->multiple()
|
||||||
|
->relationship('nodes', 'name')
|
||||||
|
->searchable(['name', 'fqdn'])
|
||||||
|
->preload()
|
||||||
|
->hint(trans('admin/role.nodes_hint'))
|
||||||
|
->hidden(fn (Get $get) => $get('name') === Role::ROOT_ADMIN),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Filament\Admin\Resources;
|
|||||||
use App\Filament\Admin\Resources\ServerResource\Pages;
|
use App\Filament\Admin\Resources\ServerResource\Pages;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class ServerResource extends Resource
|
class ServerResource extends Resource
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ class ServerResource extends Resource
|
|||||||
|
|
||||||
public static function getNavigationBadge(): ?string
|
public static function getNavigationBadge(): ?string
|
||||||
{
|
{
|
||||||
return static::getModel()::count() ?: null;
|
return (string) static::getEloquentQuery()->count() ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
@ -47,4 +48,13 @@ class ServerResource extends Resource
|
|||||||
'edit' => Pages\EditServer::route('/{record}/edit'),
|
'edit' => Pages\EditServer::route('/{record}/edit'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
$query = parent::getEloquentQuery();
|
||||||
|
|
||||||
|
return $query->whereHas('node', function (Builder $query) {
|
||||||
|
$query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +109,20 @@ class CreateServer extends CreateRecord
|
|||||||
->disabledOn('edit')
|
->disabledOn('edit')
|
||||||
->prefixIcon('tabler-server-2')
|
->prefixIcon('tabler-server-2')
|
||||||
->selectablePlaceholder(false)
|
->selectablePlaceholder(false)
|
||||||
->default(fn () => ($this->node = Node::query()->latest()->first())?->id)
|
->default(function () {
|
||||||
|
/** @var ?Node $latestNode */
|
||||||
|
$latestNode = auth()->user()->accessibleNodes()->latest()->first();
|
||||||
|
$this->node = $latestNode;
|
||||||
|
|
||||||
|
return $this->node?->id;
|
||||||
|
})
|
||||||
->columnSpan([
|
->columnSpan([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'sm' => 2,
|
'sm' => 2,
|
||||||
'md' => 2,
|
'md' => 2,
|
||||||
])
|
])
|
||||||
->live()
|
->live()
|
||||||
->relationship('node', 'name')
|
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||||
->searchable()
|
->searchable()
|
||||||
->preload()
|
->preload()
|
||||||
->afterStateUpdated(function (Set $set, $state) {
|
->afterStateUpdated(function (Set $set, $state) {
|
||||||
|
@ -177,7 +177,7 @@ class EditServer extends EditRecord
|
|||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
Select::make('node_id')
|
Select::make('node_id')
|
||||||
->label(trans('admin/server.node'))
|
->label(trans('admin/server.node'))
|
||||||
->relationship('node', 'name')
|
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||||
->columnSpan([
|
->columnSpan([
|
||||||
'default' => 2,
|
'default' => 2,
|
||||||
'sm' => 1,
|
'sm' => 1,
|
||||||
|
@ -49,6 +49,8 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
* @property int|null $servers_count
|
* @property int|null $servers_count
|
||||||
* @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
|
* @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
|
||||||
* @property int|null $allocations_count
|
* @property int|null $allocations_count
|
||||||
|
* @property \App\Models\Role[]|\Illuminate\Database\Eloquent\Collection $roles
|
||||||
|
* @property int|null $roles_count
|
||||||
*/
|
*/
|
||||||
class Node extends Model implements Validatable
|
class Node extends Model implements Validatable
|
||||||
{
|
{
|
||||||
@ -268,6 +270,11 @@ class Node extends Model implements Validatable
|
|||||||
return $this->belongsToMany(DatabaseHost::class);
|
return $this->belongsToMany(DatabaseHost::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function roles(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Role::class, NodeRole::class, 'node_id', 'id', 'id', 'role_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a boolean if the node is viable for an additional server to be placed on it.
|
* Returns a boolean if the node is viable for an additional server to be placed on it.
|
||||||
*/
|
*/
|
||||||
|
12
app/Models/NodeRole.php
Normal file
12
app/Models/NodeRole.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||||
|
|
||||||
|
class NodeRole extends Pivot
|
||||||
|
{
|
||||||
|
protected $table = 'node_role';
|
||||||
|
|
||||||
|
protected $primaryKey = null;
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use App\Enums\RolePermissionModels;
|
use App\Enums\RolePermissionModels;
|
||||||
use App\Enums\RolePermissionPrefixes;
|
use App\Enums\RolePermissionPrefixes;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Spatie\Permission\Models\Role as BaseRole;
|
use Spatie\Permission\Models\Role as BaseRole;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,6 +16,8 @@ use Spatie\Permission\Models\Role as BaseRole;
|
|||||||
* @property int|null $permissions_count
|
* @property int|null $permissions_count
|
||||||
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\User[] $users
|
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\User[] $users
|
||||||
* @property int|null $users_count
|
* @property int|null $users_count
|
||||||
|
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\Node[] $nodes
|
||||||
|
* @property int|null $nodes_count
|
||||||
*/
|
*/
|
||||||
class Role extends BaseRole
|
class Role extends BaseRole
|
||||||
{
|
{
|
||||||
@ -128,4 +131,9 @@ class Role extends BaseRole
|
|||||||
|
|
||||||
return $role;
|
return $role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function nodes(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Node::class, NodeRole::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,22 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function accessibleNodes(): Builder
|
||||||
|
{
|
||||||
|
// Root admins can access all nodes
|
||||||
|
if ($this->isRootAdmin()) {
|
||||||
|
return Node::query();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are no restrictions from any role
|
||||||
|
$roleIds = $this->roles()->pluck('id');
|
||||||
|
if (!NodeRole::whereIn('role_id', $roleIds)->exists()) {
|
||||||
|
return Node::query();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Node::whereHas('roles', fn (Builder $builder) => $builder->whereIn('roles.id', $roleIds));
|
||||||
|
}
|
||||||
|
|
||||||
public function subusers(): HasMany
|
public function subusers(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Subuser::class);
|
return $this->hasMany(Subuser::class);
|
||||||
@ -390,13 +406,24 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
return $provider?->get($this);
|
return $provider?->get($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canTarget(Model $user): bool
|
public function canTarget(Model $model): bool
|
||||||
{
|
{
|
||||||
|
// Root admins can target everyone and everything
|
||||||
if ($this->isRootAdmin()) {
|
if ($this->isRootAdmin()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $user instanceof User && !$user->isRootAdmin();
|
// Make sure normal admins can't target root admins
|
||||||
|
if ($model instanceof User) {
|
||||||
|
return !$model->isRootAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the user can only target accessible nodes
|
||||||
|
if ($model instanceof Node) {
|
||||||
|
return $this->accessibleNodes()->where('id', $model->id)->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTenants(Panel $panel): array|Collection
|
public function getTenants(Panel $panel): array|Collection
|
||||||
|
@ -2,9 +2,28 @@
|
|||||||
|
|
||||||
namespace App\Policies;
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\DatabaseHost;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class DatabaseHostPolicy
|
class DatabaseHostPolicy
|
||||||
{
|
{
|
||||||
use DefaultPolicies;
|
use DefaultPolicies;
|
||||||
|
|
||||||
protected string $modelName = 'databasehost';
|
protected string $modelName = 'databasehost';
|
||||||
|
|
||||||
|
public function before(User $user, string $ability, string|DatabaseHost $databaseHost): ?bool
|
||||||
|
{
|
||||||
|
// For "viewAny" the $databaseHost param is the class name
|
||||||
|
if (is_string($databaseHost)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($databaseHost->nodes as $node) {
|
||||||
|
if (!$user->canTarget($node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,28 @@
|
|||||||
|
|
||||||
namespace App\Policies;
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Mount;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class MountPolicy
|
class MountPolicy
|
||||||
{
|
{
|
||||||
use DefaultPolicies;
|
use DefaultPolicies;
|
||||||
|
|
||||||
protected string $modelName = 'mount';
|
protected string $modelName = 'mount';
|
||||||
|
|
||||||
|
public function before(User $user, string $ability, string|Mount $mount): ?bool
|
||||||
|
{
|
||||||
|
// For "viewAny" the $mount param is the class name
|
||||||
|
if (is_string($mount)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($mount->nodes as $node) {
|
||||||
|
if (!$user->canTarget($node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Policies;
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Node;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class NodePolicy
|
class NodePolicy
|
||||||
{
|
{
|
||||||
use DefaultPolicies;
|
use DefaultPolicies;
|
||||||
|
|
||||||
protected string $modelName = 'node';
|
protected string $modelName = 'node';
|
||||||
|
|
||||||
|
public function before(User $user, string $ability, string|Node $node): ?bool
|
||||||
|
{
|
||||||
|
// For "viewAny" the $node param is the class name
|
||||||
|
if (is_string($node)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user->canTarget($node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,11 @@ class ServerPolicy
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure user can target node of the server
|
||||||
|
if (!$user->canTarget($server->node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Owner has full server permissions
|
// Owner has full server permissions
|
||||||
if ($server->owner_id === $user->id) {
|
if ($server->owner_id === $user->id) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('node_role', function (Blueprint $table) {
|
||||||
|
$table->unsignedInteger('node_id');
|
||||||
|
$table->unsignedBigInteger('role_id');
|
||||||
|
|
||||||
|
$table->unique(['node_id', 'role_id']);
|
||||||
|
|
||||||
|
$table->foreign('node_id')->references('id')->on('nodes')->cascadeOnDelete();
|
||||||
|
$table->foreign('role_id')->references('id')->on('roles')->cascadeOnDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('node_role');
|
||||||
|
}
|
||||||
|
};
|
@ -12,4 +12,6 @@ return [
|
|||||||
'root_admin' => 'The :role has all permissions.',
|
'root_admin' => 'The :role has all permissions.',
|
||||||
'root_admin_delete' => 'Can\'t delete Root Admin',
|
'root_admin_delete' => 'Can\'t delete Root Admin',
|
||||||
'users' => 'Users',
|
'users' => 'Users',
|
||||||
|
'nodes' => 'Nodes',
|
||||||
|
'nodes_hint' => 'Leave empty to allow access to all nodes.',
|
||||||
];
|
];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user