'Method not allowed', 'allowed_methods' => ['PUT', 'POST'] ]); exit; } // Start performance monitoring $startTime = microtime(true); $startMemory = memory_get_usage(true); // Debug: Log API access // error_log("Change Password API accessed"); // error_log("Request method: " . $_SERVER['REQUEST_METHOD']); try { // Require authentication $user = authRequired(); // Debug: Log authenticated user // error_log("Change Password API - Authenticated user: " . $user); // Get request body $input = json_decode(file_get_contents('php://input'), true); if (!$input) { http_response_code(400); echo json_encode([ 'error' => 'Invalid JSON input', 'details' => 'Request body must be valid JSON' ]); exit; } // Debug: Log request data (without passwords) // error_log("Change Password API - Request received for user: " . $user); // Validasi required fields $requiredFields = ['current_password', 'new_password', 'confirm_password']; $missingFields = []; foreach ($requiredFields as $field) { if (!isset($input[$field]) || empty(trim($input[$field]))) { $missingFields[] = $field; } } if (!empty($missingFields)) { http_response_code(400); echo json_encode([ 'error' => 'Missing required fields', 'missing_fields' => $missingFields, 'required_fields' => $requiredFields ]); exit; } // Sanitize input $currentPassword = trim($input['current_password']); $newPassword = trim($input['new_password']); $confirmPassword = trim($input['confirm_password']); // Validasi new password dan confirm password match if ($newPassword !== $confirmPassword) { http_response_code(400); echo json_encode([ 'error' => 'Password confirmation does not match', 'details' => 'new_password and confirm_password must be identical' ]); exit; } // Validasi panjang password (minimum 6 karakter) if (strlen($newPassword) < 6) { http_response_code(400); echo json_encode([ 'error' => 'Password too short', 'details' => 'Password must be at least 6 characters long', 'current_length' => strlen($newPassword) ]); exit; } // Validasi current password tidak sama dengan new password if ($currentPassword === $newPassword) { http_response_code(400); echo json_encode([ 'error' => 'New password must be different from current password', 'details' => 'Please choose a different password' ]); exit; } // Get database connection $pdo = Database::getConnection(); // Validasi current password dari database $sql = "SELECT newpass FROM tbUsers WHERE userName = ?"; $stmt = $pdo->prepare($sql); $stmt->execute([$user]); $userRecord = $stmt->fetch(PDO::FETCH_ASSOC); if (!$userRecord) { http_response_code(404); echo json_encode([ 'error' => 'User not found', 'user' => $user ]); exit; } // Hash current password dan compare dengan database $currentPasswordHashed = SessionHelper::hash_password($currentPassword); // Debug: Log password validation (without actual passwords) // error_log("Change Password API - Validating current password for user: " . $user); if ($currentPasswordHashed !== $userRecord['newpass']) { // Log failed password attempt untuk security error_log("Change Password API - Invalid current password attempt for user: " . $user . " from IP: " . SessionHelper::get_ip()); http_response_code(401); echo json_encode([ 'error' => 'Current password is incorrect', 'details' => 'Please verify your current password and try again' ]); exit; } // Hash new password dengan sha1(md5()) $newPasswordHashed = SessionHelper::hash_password($newPassword); // Debug: Log password update attempt // error_log("Change Password API - Updating password for user: " . $user); // Update password di database $updateSql = "UPDATE tbUsers SET newpass = ? WHERE userName = ?"; $updateStmt = $pdo->prepare($updateSql); $updateResult = $updateStmt->execute([$newPasswordHashed, $user]); if (!$updateResult) { error_log("Change Password API - Database update failed for user: " . $user); http_response_code(500); echo json_encode([ 'error' => 'Failed to update password', 'details' => 'Database update operation failed' ]); exit; } // Verify update berhasil $rowsAffected = $updateStmt->rowCount(); if ($rowsAffected === 0) { error_log("Change Password API - No rows affected for user: " . $user); http_response_code(500); echo json_encode([ 'error' => 'Password update failed', 'details' => 'No database records were updated' ]); exit; } // Log successful password change untuk audit error_log("Change Password API - Password successfully updated for user: " . $user . " from IP: " . SessionHelper::get_ip()); // Performance monitoring $endTime = microtime(true); $endMemory = memory_get_usage(true); $processingTime = round($endTime - $startTime, 2); $memoryUsed = round(($endMemory - $startMemory) / 1024 / 1024, 2); // Debug: Log performance metrics // error_log("Change Password API - Processing completed in {$processingTime}s, Memory: {$memoryUsed}MB"); // Return success response http_response_code(200); echo json_encode([ 'success' => true, 'message' => 'Password updated successfully', 'user' => $user, 'timestamp' => date('Y-m-d H:i:s'), 'processing_info' => [ 'processing_time' => $processingTime . " seconds", 'memory_usage' => $memoryUsed . " MB" ] ]); } catch (Exception $e) { // Log error dengan detail $errorTime = microtime(true); $errorMemory = memory_get_usage(true); error_log("Change Password API Error: " . $e->getMessage()); error_log("Error occurred at: " . round($errorTime - $startTime, 2) . " seconds"); error_log("Memory at error: " . number_format($errorMemory / 1024 / 1024, 2) . " MB"); error_log("Error trace: " . $e->getTraceAsString()); http_response_code(500); echo json_encode([ 'error' => 'Internal server error during password change', 'message' => $e->getMessage(), 'processing_time' => round($errorTime - $startTime, 2) . " seconds", 'memory_usage' => number_format(($errorMemory - $startMemory) / 1024 / 1024, 2) . " MB" // Debug version dengan detail error: // 'error_details' => [ // 'message' => $e->getMessage(), // 'file' => $e->getFile(), // 'line' => $e->getLine(), // 'trace' => $e->getTraceAsString() // ] ]); } finally { // Cleanup memory gc_collect_cycles(); }