Enforce tenant member permissions
This commit is contained in:
174
app/Support/TenantMemberPermissions.php
Normal file
174
app/Support/TenantMemberPermissions.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Models\Event;
|
||||
use App\Models\EventMember;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class TenantMemberPermissions
|
||||
{
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function resolveEventPermissions(Request $request, Event $event): array
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user instanceof User) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (self::isTenantAdmin($user)) {
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
$member = self::resolveEventMember($user, $event);
|
||||
|
||||
if (! $member) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return self::normalizePermissions($member->permissions);
|
||||
}
|
||||
|
||||
public static function ensureEventPermission(Request $request, Event $event, string $permission): void
|
||||
{
|
||||
if (self::allowsPermission(self::resolveEventPermissions($request, $event), $permission)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new HttpResponseException(ApiError::response(
|
||||
'insufficient_permission',
|
||||
'Insufficient permission',
|
||||
'You are not allowed to perform this action.',
|
||||
Response::HTTP_FORBIDDEN,
|
||||
['required_permission' => $permission]
|
||||
));
|
||||
}
|
||||
|
||||
public static function allowsEventPermission(Request $request, Event $event, string $permission): bool
|
||||
{
|
||||
return self::allowsPermission(self::resolveEventPermissions($request, $event), $permission);
|
||||
}
|
||||
|
||||
public static function ensureTenantPermission(Request $request, string $permission): void
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user instanceof User) {
|
||||
throw new HttpResponseException(ApiError::response(
|
||||
'unauthenticated',
|
||||
'Unauthenticated',
|
||||
'You must be authenticated to perform this action.',
|
||||
Response::HTTP_UNAUTHORIZED
|
||||
));
|
||||
}
|
||||
|
||||
if (self::isTenantAdmin($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$permissions = self::resolveTenantMemberPermissions($user);
|
||||
|
||||
if (self::allowsPermission($permissions, $permission)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new HttpResponseException(ApiError::response(
|
||||
'insufficient_permission',
|
||||
'Insufficient permission',
|
||||
'You are not allowed to perform this action.',
|
||||
Response::HTTP_FORBIDDEN,
|
||||
['required_permission' => $permission]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private static function resolveTenantMemberPermissions(User $user): array
|
||||
{
|
||||
if (! $user->tenant_id) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$memberships = EventMember::query()
|
||||
->where('tenant_id', $user->tenant_id)
|
||||
->whereIn('status', ['active', 'invited'])
|
||||
->where(function ($query) use ($user) {
|
||||
$query->where('user_id', $user->id)
|
||||
->orWhere('email', $user->email);
|
||||
})
|
||||
->get(['permissions']);
|
||||
|
||||
$permissions = [];
|
||||
|
||||
foreach ($memberships as $member) {
|
||||
$permissions = array_merge($permissions, self::normalizePermissions($member->permissions));
|
||||
}
|
||||
|
||||
return array_values(array_unique($permissions));
|
||||
}
|
||||
|
||||
private static function isTenantAdmin(User $user): bool
|
||||
{
|
||||
return in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'superadmin'], true);
|
||||
}
|
||||
|
||||
private static function resolveEventMember(User $user, Event $event): ?EventMember
|
||||
{
|
||||
return EventMember::query()
|
||||
->where('tenant_id', $event->tenant_id)
|
||||
->where('event_id', $event->id)
|
||||
->whereIn('status', ['active', 'invited'])
|
||||
->where(function ($query) use ($user) {
|
||||
$query->where('user_id', $user->id)
|
||||
->orWhere('email', $user->email);
|
||||
})
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string>|string|null $permissions
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private static function normalizePermissions(mixed $permissions): array
|
||||
{
|
||||
if (is_array($permissions)) {
|
||||
return array_values(array_filter(array_map('strval', $permissions)));
|
||||
}
|
||||
|
||||
if (is_string($permissions) && $permissions !== '') {
|
||||
return array_values(array_filter(array_map('trim', explode(',', $permissions))));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $permissions
|
||||
*/
|
||||
private static function allowsPermission(array $permissions, string $permission): bool
|
||||
{
|
||||
foreach ($permissions as $entry) {
|
||||
if ($entry === '*' || $entry === $permission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Str::endsWith($entry, ':*')) {
|
||||
$prefix = Str::beforeLast($entry, '*');
|
||||
|
||||
if (Str::startsWith($permission, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user