AuthController.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. namespace App\Controllers\Api;
  3. use CodeIgniter\HTTP\ResponseInterface;
  4. class AuthController extends BaseApiController
  5. {
  6. /**
  7. * Login
  8. */
  9. public function login()
  10. {
  11. $json = $this->request->getJSON();
  12. $username = $json->username ?? '';
  13. $password = $json->password ?? '';
  14. if (empty($username) || empty($password)) {
  15. return $this->respondError('아이디와 비밀번호를 입력하세요.');
  16. }
  17. // Find admin user (삭제된 계정 제외)
  18. $builder = $this->getDB()->table('admin_users');
  19. $admin = $builder
  20. ->where('username', $username)
  21. ->where('deleted_YN', 'N')
  22. ->get()->getRow();
  23. if (!$admin) {
  24. return $this->respondError('아이디 또는 비밀번호가 올바르지 않습니다.');
  25. }
  26. // Check if account is locked (5 failed attempts)
  27. $loginAttempts = $admin->login_attempts ?? 0;
  28. if ($loginAttempts >= 5) {
  29. return $this->respondError('계정이 잠겼습니다. 슈퍼 관리자에게 문의하여주세요.', ResponseInterface::HTTP_FORBIDDEN);
  30. }
  31. // Check if admin is active
  32. if ($admin->status !== 'active') {
  33. return $this->respondError('비활성화된 계정입니다. 관리자에게 문의하세요.');
  34. }
  35. // Verify password
  36. if (!password_verify($password, $admin->password)) {
  37. // Increment login attempts
  38. $newAttempts = $loginAttempts + 1;
  39. $updateBuilder = $this->getDB()->table('admin_users');
  40. $updateBuilder->where('id', $admin->id)->update([
  41. 'login_attempts' => $newAttempts,
  42. 'last_failed_login' => date('Y-m-d H:i:s')
  43. ]);
  44. // Check if account should be locked
  45. if ($newAttempts >= 5) {
  46. return $this->respondError('비밀번호를 5회 틀렸습니다. 계정이 잠겼습니다. 슈퍼 관리자에게 문의하여주세요.', ResponseInterface::HTTP_FORBIDDEN);
  47. }
  48. $remainingAttempts = 5 - $newAttempts;
  49. return $this->respondError("아이디 또는 비밀번호가 올바르지 않습니다. (남은 시도 횟수: {$remainingAttempts}회)");
  50. }
  51. // Reset login attempts + last_login 업데이트
  52. $resetBuilder = $this->getDB()->table('admin_users');
  53. $resetBuilder->where('id', $admin->id)->update([
  54. 'login_attempts' => 0,
  55. 'last_failed_login' => null,
  56. 'last_login' => date('Y-m-d H:i:s'),
  57. ]);
  58. // Generate token
  59. $token = bin2hex(random_bytes(32));
  60. $expiresAt = date('Y-m-d H:i:s', strtotime('+24 hours'));
  61. // Save token
  62. $tokenBuilder = $this->getDB()->table('admin_tokens');
  63. $tokenBuilder->insert([
  64. 'admin_id' => $admin->id,
  65. 'token' => $token,
  66. 'expires_at' => $expiresAt,
  67. 'created_at' => date('Y-m-d H:i:s')
  68. ]);
  69. // Check if password change is needed (6 months)
  70. $passwordChangeNeeded = false;
  71. if (!empty($admin->password_changed_at)) {
  72. $sixMonthsAgo = strtotime('-6 months');
  73. $passwordChangedTime = strtotime($admin->password_changed_at);
  74. $passwordChangeNeeded = $passwordChangedTime < $sixMonthsAgo;
  75. } else {
  76. // password_changed_at이 없으면 created_at 기준으로 체크
  77. if (!empty($admin->created_at)) {
  78. $sixMonthsAgo = strtotime('-6 months');
  79. $createdTime = strtotime($admin->created_at);
  80. $passwordChangeNeeded = $createdTime < $sixMonthsAgo;
  81. }
  82. }
  83. return $this->respondSuccess([
  84. 'token' => $token,
  85. 'expires_at' => $expiresAt,
  86. 'password_change_needed' => $passwordChangeNeeded,
  87. 'admin' => [
  88. 'id' => $admin->id,
  89. 'username' => $admin->username,
  90. 'name' => $admin->name,
  91. 'email' => $admin->email,
  92. 'role' => $admin->role,
  93. 'permissions' => $this->fetchPermissions((int) $admin->id, $admin->role),
  94. ]
  95. ], '로그인 성공');
  96. }
  97. /**
  98. * 관리자 권한 배열 반환 — super_admin은 'all', admin은 메뉴 id 배열
  99. */
  100. private function fetchPermissions(int $adminId, string $role)
  101. {
  102. if ($role === 'super_admin') return 'all';
  103. $rows = $this->getDB()->table('admin_permissions')
  104. ->select('permission')
  105. ->where('admin_id', $adminId)
  106. ->get()->getResult();
  107. return array_map(fn($r) => $r->permission, $rows);
  108. }
  109. /**
  110. * Logout
  111. */
  112. public function logout()
  113. {
  114. $tokenData = parent::validateToken();
  115. if ($tokenData) {
  116. $builder = $this->getDB()->table('admin_tokens');
  117. $builder->where('token', $tokenData->token)->delete();
  118. }
  119. return $this->respondSuccess(null, '로그아웃 성공');
  120. }
  121. /**
  122. * Check token (API endpoint)
  123. */
  124. public function check()
  125. {
  126. $tokenData = $this->requireAuth();
  127. if ($tokenData instanceof ResponseInterface) {
  128. return $tokenData;
  129. }
  130. // Get admin info
  131. $builder = $this->getDB()->table('admin_users');
  132. $admin = $builder->where('id', $tokenData->admin_id)->get()->getRow();
  133. return $this->respondSuccess([
  134. 'admin' => [
  135. 'id' => $admin->id,
  136. 'username' => $admin->username,
  137. 'name' => $admin->name,
  138. 'email' => $admin->email,
  139. 'role' => $admin->role,
  140. 'permissions' => $this->fetchPermissions((int) $admin->id, $admin->role),
  141. ]
  142. ]);
  143. }
  144. }