feat(access): per-page user/group grants, snap-in-local
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>
This commit is contained in:
56
src/Models/UnifiPageGrant.php
Normal file
56
src/Models/UnifiPageGrant.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user