Files
My-KOPKB/be/app/Http/Controllers/ImpersonateController.php
ISMAIL MASSERAN 94ecbe5887 first init
2026-06-08 11:37:14 +08:00

152 lines
4.9 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\PersonalAccessToken;
use App\Services\ActiveRoleService;
use App\Support\AuthCookie;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Modules\Auth\Entities\User;
use Modules\Auth\Transformers\UserResource;
class ImpersonateController extends Controller
{
private const IMPERSONATION_TOKEN = 'impersonation-token';
private const AUTH_TOKEN_NAMES = ['authToken', 'auth-token'];
public function take(Request $request, string $id): JsonResponse
{
$target = User::findOrFail($id);
$admin = $request->user();
if (! $admin->can('impersonate.user', $target)) {
return $this->error('Anda tidak mempunyai keizinan untuk menyamar pengguna.', 403);
}
if ($request->hasCookie(AuthCookie::originalUserCookieName())) {
return $this->error('Anda sudah menyamar sebagai pengguna.', 400);
}
$issued = $this->issueToken($target, self::IMPERSONATION_TOKEN);
return AuthCookie::attachAuthToken(
response()->json([
'success' => true,
'message' => "Anda sekarang menyamar sebagai {$target->name}",
'impersonated_user' => $this->userSummary($target),
...$this->sessionPayload($target, $issued['accessToken']),
]),
$issued['plainTextToken']
)->withCookie(AuthCookie::makeOriginalUserId($admin->id));
}
public function leave(Request $request): JsonResponse
{
$originalUserId = $request->cookie(AuthCookie::originalUserCookieName());
if (! $originalUserId) {
return $this->error('Maklumat pengguna asal tidak ditemui.', 400);
}
$original = User::findOrFail($originalUserId);
$request->user()?->tokens()->where('name', self::IMPERSONATION_TOKEN)->delete();
$original->tokens()->whereIn('name', [self::IMPERSONATION_TOKEN, ...self::AUTH_TOKEN_NAMES])->delete();
$issued = $this->issueToken($original, 'auth-token', expires: true);
$response = response()->json([
'success' => true,
'message' => 'Anda telah kembali ke akaun anda',
'original_user' => $this->userSummary($original),
...$this->sessionPayload($original, $issued['accessToken']),
]);
if (AuthCookie::shouldExposeTokenInResponse()) {
$response->setData(array_merge($response->getData(true), [
'original_token' => $issued['plainTextToken'],
]));
}
return AuthCookie::attachAuthToken($response, $issued['plainTextToken'])
->withCookie(AuthCookie::forgetOriginalUserId());
}
public function status(Request $request): JsonResponse
{
if (! $request->hasCookie(AuthCookie::originalUserCookieName())) {
return response()->json(['is_impersonating' => false]);
}
$user = $request->user();
return response()->json([
'is_impersonating' => true,
'impersonated_user' => $user ? $this->userSummary($user) : null,
]);
}
/**
* @return array{plainTextToken: string, accessToken: PersonalAccessToken}
*/
private function issueToken(User $user, string $name, bool $expires = false): array
{
$user->loadMissing(['roles.permissions']);
$result = $user->createToken(
$name,
['*'],
$expires ? now()->addMinutes((int) config('auth_cookie.lifetime_minutes', 720)) : null
);
ActiveRoleService::assignDefaultToToken($user, $result->accessToken);
return [
'plainTextToken' => $result->plainTextToken,
'accessToken' => $result->accessToken,
];
}
/**
* @return array<string, mixed>
*/
private function sessionPayload(User $user, PersonalAccessToken $accessToken): array
{
$user->loadMissing(['roles.permissions']);
$activeRole = $user->roles->firstWhere('id', $accessToken->active_role_id)
?? ActiveRoleService::resolveDefaultRole($user);
return [
'data' => new UserResource($user),
'active_role' => ActiveRoleService::formatRole($activeRole),
'can_switch_role' => $user->roles->count() > 1,
'redirect_path' => $activeRole
? ActiveRoleService::redirectPathForRole($activeRole)
: config('active_role.member_redirect', '/profile'),
];
}
/**
* @return array{id: mixed, name: string, email: string}
*/
private function userSummary(User $user): array
{
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
];
}
private function error(string $message, int $status): JsonResponse
{
return response()->json([
'success' => false,
'message' => $message,
], $status);
}
}