A snap-in-owned access mechanism. Adds:
- unifi_page_grants table (nav_item_id, grantee_type, grantee_id)
with cascadeOnDelete from nav_items so uninstalling the snap-in
wipes its grant rows automatically
- UnifiPageGrant model + ::userCanAccess(user, navItem) helper
- UnifiPagesAccessController (index + update), super-admin only
- RouteMatched listener in UnifiServiceProvider that 403s any
unifi.* route if the matched nav_item has grants and the user
isn't a super-admin / granted user / member of a granted group
Semantics: a page with NO grants stays open per the existing
permission middleware (no behaviour change). The moment grants are
added, ONLY super-admins and listed users/groups can see/open the
page. Super-admins always pass; their access can't be removed.
v1.4.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
1.6 KiB
PHP
57 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace Dashboard\Unifi\Models;
|
|
|
|
use App\Models\NavItem;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
class UnifiPageGrant extends Model
|
|
{
|
|
protected $table = 'unifi_page_grants';
|
|
|
|
protected $fillable = [
|
|
'nav_item_id',
|
|
'grantee_type',
|
|
'grantee_id',
|
|
'granted_by_user_id',
|
|
];
|
|
|
|
public function navItem(): BelongsTo
|
|
{
|
|
return $this->belongsTo(NavItem::class);
|
|
}
|
|
|
|
public function grantedBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'granted_by_user_id');
|
|
}
|
|
|
|
/**
|
|
* True iff $user is allowed to access $navItem under this grant model.
|
|
* Super-admins always pass.
|
|
* If there are NO grants for the page, falls back to "open" (anyone
|
|
* who can reach the route can access — same as before grants existed).
|
|
*/
|
|
public static function userCanAccess(User $user, NavItem $navItem): bool
|
|
{
|
|
if ($user->is_super_admin) return true;
|
|
|
|
$hasGrants = static::where('nav_item_id', $navItem->id)->exists();
|
|
if (! $hasGrants) return true;
|
|
|
|
$groupIds = $user->groups()->pluck('groups.id');
|
|
|
|
return static::where('nav_item_id', $navItem->id)
|
|
->where(function ($q) use ($user, $groupIds) {
|
|
$q->where(function ($u) use ($user) {
|
|
$u->where('grantee_type', 'user')->where('grantee_id', $user->id);
|
|
})->orWhere(function ($g) use ($groupIds) {
|
|
$g->where('grantee_type', 'group')->whereIn('grantee_id', $groupIds);
|
|
});
|
|
})
|
|
->exists();
|
|
}
|
|
}
|