152 lines
4.9 KiB
PHP
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);
|
|
}
|
|
}
|