fix(security): [Developer] #267 AdminUserController GET 4종에 requireAdmin() 추가

CRITICAL: listUsers, userFavorites, userReviews, userMemos 4개 GET이 인증만 요구하고 admin 검사가 없어, 일반 사용자 토큰으로 전체 사용자 목록 및 타인 활동 조회 가능했음. 각 메서드 첫 줄에 AuthUtil.requireAdmin() 호출 추가 → non-admin은 403.

함께 커밋(이전 미커밋 작업):
- Logger 등록 (감사 로그용)
- AuthUtil/Logger/HttpStatus/ResponseStatusException import 정리
- updateAdmin: 자기 자신 admin 변경 차단 + 감사 로그
  (이미 동작 중이던 변경이나 git 미커밋 상태였음)

문서:
- 설계서 §3 인수조건에 권한 강제 항목 추가, 상태 Draft → Approved
- CHANGELOG.md 2026-06-15 핫픽스 항목 추가

검증:
- Anonymous GET /api/admin/users → 403 ✓
- Bad-token GET /api/admin/users → 403 ✓
- 백엔드 빌드 성공, tasteby-api 재시작 완료

Refs: #267
This commit is contained in:
joungmin
2026-06-15 11:17:48 +09:00
parent 80b553ec19
commit 4638f605aa
3 changed files with 32 additions and 2 deletions

View File

@@ -3,10 +3,15 @@ package com.tasteby.controller;
import com.tasteby.domain.Memo;
import com.tasteby.domain.Restaurant;
import com.tasteby.domain.Review;
import com.tasteby.security.AuthUtil;
import com.tasteby.service.MemoService;
import com.tasteby.service.ReviewService;
import com.tasteby.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import java.util.Map;
@@ -15,6 +20,7 @@ import java.util.Map;
@RequestMapping("/api/admin/users")
public class AdminUserController {
private static final Logger log = LoggerFactory.getLogger(AdminUserController.class);
private final UserService userService;
private final ReviewService reviewService;
private final MemoService memoService;
@@ -29,6 +35,7 @@ public class AdminUserController {
public Map<String, Object> listUsers(
@RequestParam(defaultValue = "50") int limit,
@RequestParam(defaultValue = "0") int offset) {
AuthUtil.requireAdmin();
var users = userService.findAllWithCounts(limit, offset);
int total = userService.countAll();
return Map.of("users", users, "total", total);
@@ -36,16 +43,32 @@ public class AdminUserController {
@GetMapping("/{userId}/favorites")
public List<Restaurant> userFavorites(@PathVariable String userId) {
AuthUtil.requireAdmin();
return reviewService.getUserFavorites(userId);
}
@GetMapping("/{userId}/reviews")
public List<Review> userReviews(@PathVariable String userId) {
AuthUtil.requireAdmin();
return reviewService.findByUser(userId, 100, 0);
}
@GetMapping("/{userId}/memos")
public List<Memo> userMemos(@PathVariable String userId) {
AuthUtil.requireAdmin();
return memoService.findByUser(userId);
}
@PatchMapping("/{userId}/admin")
public Map<String, Object> updateAdmin(@PathVariable String userId, @RequestBody Map<String, Boolean> body) {
var currentUser = AuthUtil.requireAdmin();
if (userId.equals(currentUser.getSubject())) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "자기 자신의 관리자 권한은 변경할 수 없습니다");
}
boolean admin = Boolean.TRUE.equals(body.get("admin"));
userService.updateAdmin(userId, admin);
log.info("[ADMIN] User {} set admin={} for user {}", currentUser.getSubject(), admin, userId);
return Map.of("success", true, "user_id", userId, "is_admin", admin);
}
}