diff --git a/app/Http/Controllers/AlertRuleController.php b/app/Http/Controllers/AlertRuleController.php index ed52113c7ac8..036fe3cfd848 100644 --- a/app/Http/Controllers/AlertRuleController.php +++ b/app/Http/Controllers/AlertRuleController.php @@ -6,6 +6,7 @@ use App\Models\AlertRule; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Str; use LibreNMS\Alerting\QueryBuilderParser; use LibreNMS\Util\Time; @@ -17,6 +18,12 @@ class AlertRuleController extends Controller */ public function store(AlertRuleRequest $request): JsonResponse { + if (Gate::denies('create', AlertRule::class)) { + return response()->json([ + 'status' => 'error', + 'message' => 'You are not authorized to create alert rules', + ], 403); + } try { $alertRule = new AlertRule; $this->fillAlertRule($alertRule, $request); @@ -69,6 +76,12 @@ public function show(AlertRule $alertRule): JsonResponse */ public function update(AlertRuleRequest $request, AlertRule $alertRule): JsonResponse { + if (Gate::denies('update', AlertRule::class)) { + return response()->json([ + 'status' => 'error', + 'message' => 'You are not authorized to update alert rules', + ], 403); + } try { $this->fillAlertRule($alertRule, $request); diff --git a/app/Policies/AlertPolicy.php b/app/Policies/AlertPolicy.php index ff0464f63d28..2c1785bc05cf 100644 --- a/app/Policies/AlertPolicy.php +++ b/app/Policies/AlertPolicy.php @@ -23,28 +23,35 @@ public function viewAny(User $user): bool */ public function view(User $user, Alert $alert): bool { + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + return $this->hasGlobalPermission($user, 'view') - || Permissions::canAccessDevice($alert->device_id, $user); + && Permissions::canAccessDevice($alert->device_id, $user); } - public function detail(User $user): bool + public function detail(User $user, Alert $alert): bool { - return $this->hasGlobalPermission($user, 'detail'); + return $this->hasGlobalPermission($user, 'detail') && + Permissions::canAccessDevice($alert->device_id, $user); } /** * Determine whether the user can update the model. */ - public function update(User $user): bool + public function update(User $user, Alert $alert): bool { - return $this->hasGlobalPermission($user, 'update'); + return $this->hasGlobalPermission($user, 'update') && + Permissions::canAccessDevice($alert->device_id, $user); } /** * Determine whether the user can delete the model. */ - public function delete(User $user): bool + public function delete(User $user, Alert $alert): bool { - return $this->hasGlobalPermission($user, 'delete'); + return $this->hasGlobalPermission($user, 'delete') && + Permissions::canAccessDevice($alert->device_id, $user); } } diff --git a/app/Policies/AlertRulePolicy.php b/app/Policies/AlertRulePolicy.php index 82bf3a645d61..529a8a1243c2 100644 --- a/app/Policies/AlertRulePolicy.php +++ b/app/Policies/AlertRulePolicy.php @@ -2,8 +2,11 @@ namespace App\Policies; +use App\Facades\Permissions; +use App\Models\AlertRule; use App\Models\User; + class AlertRulePolicy { use ChecksGlobalPermissions; @@ -19,32 +22,74 @@ public function viewAny(User $user): bool /** * Determine whether the user can view the model. */ - public function view(User $user): bool + public function view(User $user, AlertRule $alertRule): bool { - return $this->hasGlobalPermission($user, 'view'); + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + + if (! $this->hasGlobalPermission($user, 'view')) { + return false; + } + + foreach ($alertRule->devices()->pluck('device_id') as $deviceId) { + if (Permissions::canAccessDevice($deviceId, $user)) { + return true; + } + } + + return false; } /** * Determine whether the user can create models. */ - public function create(User $user): bool + public function create(User $user, AlertRule $alertRule): bool { - return $this->hasGlobalPermission($user, 'create'); + if (! $this->hasGlobalPermission($user, 'create')) { + return false; + } + + foreach ($alertRule->devices()->pluck('device_id') as $deviceId) { + if (Permissions::canAccessDevice($deviceId, $user)) { + return true; + } + } + + return false; } /** * Determine whether the user can update the model. */ - public function update(User $user): bool + public function update(User $user, AlertRule $alertRule): bool { - return $this->hasGlobalPermission($user, 'update'); + if (! $this->hasGlobalPermission($user, 'update')) { + return false; + } + + foreach ($alertRule->devices()->pluck('device_id') as $deviceId) { + if (Permissions::canAccessDevice($deviceId, $user)) { + return true; + } + } + + return false; } /** * Determine whether the user can delete the model. */ - public function delete(User $user): bool + public function delete(User $user, AlertRule $alertRule): bool { - return $this->hasGlobalPermission($user, 'delete'); + if (! $this->hasGlobalPermission($user, 'delete')) { + return false; + } + + foreach ($alertRule->devices()->pluck('device_id') as $deviceId) { + if (Permissions::canAccessDevice($deviceId, $user)) { + return true; + } + } } } diff --git a/app/Policies/AlertSchedulePolicy.php b/app/Policies/AlertSchedulePolicy.php index f0a58e39a9a0..8ac445ac57c3 100644 --- a/app/Policies/AlertSchedulePolicy.php +++ b/app/Policies/AlertSchedulePolicy.php @@ -21,6 +21,10 @@ public function viewAny(User $user): bool */ public function view(User $user): bool { + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + return $this->hasGlobalPermission($user, 'view'); } diff --git a/app/Policies/AlertTemplatePolicy.php b/app/Policies/AlertTemplatePolicy.php index 48342d01bc10..7afdca95ac8a 100644 --- a/app/Policies/AlertTemplatePolicy.php +++ b/app/Policies/AlertTemplatePolicy.php @@ -21,6 +21,10 @@ public function viewAny(User $user): bool */ public function view(User $user): bool { + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + return $this->hasGlobalPermission($user, 'view'); } diff --git a/app/Policies/AlertTransportPolicy.php b/app/Policies/AlertTransportPolicy.php index 7bcfa4f26ee5..ab4c38ddde6d 100644 --- a/app/Policies/AlertTransportPolicy.php +++ b/app/Policies/AlertTransportPolicy.php @@ -21,6 +21,10 @@ public function viewAny(User $user): bool */ public function view(User $user): bool { + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + return $this->hasGlobalPermission($user, 'view'); } diff --git a/app/Policies/ApplicationPolicy.php b/app/Policies/ApplicationPolicy.php index ee50cc4a9a22..2fa021d440c1 100644 --- a/app/Policies/ApplicationPolicy.php +++ b/app/Policies/ApplicationPolicy.php @@ -2,6 +2,8 @@ namespace App\Policies; +use App\Facades\Permissions; +use App\Models\Application; use App\Models\User; class ApplicationPolicy @@ -19,16 +21,21 @@ public function viewAny(User $user): bool /** * Determine whether the user can view the model. */ - public function view(User $user): bool + public function view(User $user, Application $application): bool { - return $this->hasGlobalPermission($user, 'view'); + if ($this->hasGlobalPermission($user, 'viewAny')) { + return true; + } + + return $this->hasGlobalPermission($user, 'view') && Permissions::canAccessDevice($application->device_id, $user); } /** * Determine whether the user can update the model. */ - public function update(User $user): bool + public function update(User $user, Application $application): bool { - return $this->hasGlobalPermission($user, 'update'); + return $this->hasGlobalPermission($user, 'update') && + Permissions::canAccessDevice($application->device_id, $user); } } diff --git a/database/migrations/2026_03_18_000001_add_alert_template_viewany_permission.php b/database/migrations/2026_03_18_000001_add_alert_template_viewany_permission.php new file mode 100644 index 000000000000..69c3255b620a --- /dev/null +++ b/database/migrations/2026_03_18_000001_add_alert_template_viewany_permission.php @@ -0,0 +1,42 @@ + [ + 'name' => $name, + 'guard_name' => 'web', + 'created_at' => $now, + 'updated_at' => $now, + ], self::PERMISSIONS); + + DB::table('permissions')->insertOrIgnore($insertData); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('permissions') + ->whereIn('name', self::PERMISSIONS) + ->where('guard_name', 'web') + ->delete(); + } +}; + diff --git a/includes/html/forms/schedule-maintenance.inc.php b/includes/html/forms/schedule-maintenance.inc.php index 29b3fdb9abdb..74bd8bbce596 100644 --- a/includes/html/forms/schedule-maintenance.inc.php +++ b/includes/html/forms/schedule-maintenance.inc.php @@ -15,6 +15,7 @@ use App\Facades\LibrenmsConfig; use App\Models\AlertSchedule; use App\Models\UserPref; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Str; use LibreNMS\Enum\MaintenanceBehavior; @@ -196,8 +197,8 @@ 'schedule_id' => $alert_schedule->schedule_id ?? null, ]; } elseif ($sub_type == 'parse-maintenance') { - Gate::authorize('view', AlertSchedule::class); $alert_schedule = AlertSchedule::findOrFail($_POST['schedule_id']); + Gate::authorize('view', $alert_schedule); $items = []; foreach (dbFetchRows('SELECT `alert_schedulable_type`, `alert_schedulable_id` FROM `alert_schedulables` WHERE `schedule_id`=?', [$alert_schedule->schedule_id]) as $target) { diff --git a/includes/html/forms/show-alert-transport.inc.php b/includes/html/forms/show-alert-transport.inc.php index b7f24d287402..16e9d4ae81bd 100644 --- a/includes/html/forms/show-alert-transport.inc.php +++ b/includes/html/forms/show-alert-transport.inc.php @@ -15,7 +15,7 @@ header('Content-type: application/json'); -if (Gate::denies('view', AlertTransport::class)) { +if (Gate::denies('viewAny', AlertTransport::class)) { exit(json_encode([ 'status' => 'error', 'message' => 'You need permission', diff --git a/includes/html/pages/alert-schedule.inc.php b/includes/html/pages/alert-schedule.inc.php index d41085e6812c..ebb69a01821b 100644 --- a/includes/html/pages/alert-schedule.inc.php +++ b/includes/html/pages/alert-schedule.inc.php @@ -13,6 +13,7 @@ */ use App\Models\AlertSchedule; +use Illuminate\Support\Facades\Gate; $pagetitle[] = 'Alert Schedule'; $no_refresh = true; diff --git a/includes/html/pages/alert-transports.inc.php b/includes/html/pages/alert-transports.inc.php index d9ca23322477..3f8a4b04f032 100644 --- a/includes/html/pages/alert-transports.inc.php +++ b/includes/html/pages/alert-transports.inc.php @@ -3,7 +3,7 @@ use App\Models\AlertTransport; use Illuminate\Support\Facades\Gate; -if (Gate::allows('create', AlertTransport::class)) { +if (Gate::allows('viewAny', AlertTransport::class)) { // handle OAuth requests $request = request(); // grab the Request object diff --git a/includes/html/print-alert-rules.php b/includes/html/print-alert-rules.php index bccda8ed88de..cce23acf9596 100644 --- a/includes/html/print-alert-rules.php +++ b/includes/html/print-alert-rules.php @@ -26,6 +26,7 @@ use App\Facades\DeviceCache; use App\Models\AlertRule; +use Illuminate\Support\Facades\Gate; use LibreNMS\Alerting\QueryBuilderParser; use LibreNMS\Enum\AlertState; @@ -35,14 +36,10 @@ $no_refresh = true; -?> -