175 lines
5.0 KiB
PHP
175 lines
5.0 KiB
PHP
<?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;
|
|
}
|
|
}
|