diff --git a/src/main/java/com/aolda/itda/config/AuthInterceptor.java b/src/main/java/com/aolda/itda/config/AuthInterceptor.java index 63f2320e18fa92cd2edb275561be760518b19cc1..83c38212a4b0692ddd12039b3b027f3708d85a13 100644 --- a/src/main/java/com/aolda/itda/config/AuthInterceptor.java +++ b/src/main/java/com/aolda/itda/config/AuthInterceptor.java @@ -1,8 +1,10 @@ package com.aolda.itda.config; +import com.aolda.itda.dto.auth.ProjectIdAndNameDTO; import com.aolda.itda.exception.CustomException; import com.aolda.itda.exception.ErrorCode; import com.aolda.itda.service.AuthService; +import com.fasterxml.jackson.core.JsonProcessingException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -10,6 +12,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Component @Slf4j @@ -20,18 +26,43 @@ public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("X-Subject-Token"); + + /* 토큰 헤더 검증 */ if (token == null || token.isEmpty()) { throw new CustomException(ErrorCode.INVALID_TOKEN, request.getRequestURI()); } - // 어드민과 일반 유저 구분 필요 - try { - if (authService.validateTokenAndGetUserId(token) != null) { - return true; - } - } catch (Exception e) { - log.error("Token validation failed for URI {}: {}", request.getRequestURI(), e.getMessage(), e); + + /* 유효 토큰 검증 */ + String userId = authService.validateTokenAndGetUserId(token); + if (userId == null) { + log.error("Token validation failed for URI {}: {}", request.getRequestURI(), request.getRemoteAddr()); throw new CustomException(ErrorCode.INVALID_TOKEN, request.getRequestURI()); } - throw new CustomException(ErrorCode.INVALID_TOKEN, request.getRequestURI()); + + /* 프로젝트 권한 검증 */ + String projectId = request.getParameter("projectId"); + if (projectId != null) { + + try { + String role = authService.getBestRoleWithinProject(token, projectId).get("role"); + if (!role.equals("admin")) { + log.error("Unauthorized Token for URI {}: {}", request.getRequestURI(), request.getRemoteAddr()); + throw new CustomException(ErrorCode.UNAUTHORIZED_USER, request.getRequestURI()); + } + } catch (Exception e) { + throw new CustomException(ErrorCode.UNAUTHORIZED_USER, request.getRequestURI()); + } + + + + } + + /* 프로젝트 리스트 조회 */ + List<String> projects = authService.getProjectsWithUser(Map.of("id", userId, "token", token)) + .stream().map(ProjectIdAndNameDTO::getId) + .toList(); + request.setAttribute("projects", projects); + return true; + } } diff --git a/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java b/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java index a6b67ecbd0090ce8f8460d3fd8b08def88d520a8..36e6f9a8138da9fee0f76d6028770260d9fb5679 100644 --- a/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java +++ b/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java @@ -2,11 +2,14 @@ package com.aolda.itda.controller.forwarding; import com.aolda.itda.dto.forwarding.ForwardingDTO; import com.aolda.itda.service.forwarding.ForwardingService; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api") @RequiredArgsConstructor @@ -22,8 +25,8 @@ public class ForwardingController { } @GetMapping("/forwarding") - public ResponseEntity<Object> view(@RequestParam Long forwardingId) { - return ResponseEntity.ok(forwardingService.getForwarding(forwardingId)); + public ResponseEntity<Object> view(@RequestParam Long forwardingId, HttpServletRequest request) { + return ResponseEntity.ok(forwardingService.getForwarding(forwardingId, (List<String>) request.getAttribute("projects"))); } @GetMapping("/forwardings") @@ -33,14 +36,16 @@ public class ForwardingController { @PatchMapping("/forwarding") public ResponseEntity<Object> edit(@RequestParam Long forwardingId, - @RequestBody ForwardingDTO dto) { - forwardingService.editForwarding(forwardingId, dto); + @RequestBody ForwardingDTO dto, + HttpServletRequest request) { + forwardingService.editForwarding(forwardingId, dto, (List<String>) request.getAttribute("projects") ); return ResponseEntity.ok().build(); } @DeleteMapping("/forwarding") - public ResponseEntity<Object> delete(@RequestParam Long forwardingId) { - forwardingService.deleteForwarding(forwardingId); + public ResponseEntity<Object> delete(@RequestParam Long forwardingId, + HttpServletRequest request) { + forwardingService.deleteForwarding(forwardingId, (List<String>) request.getAttribute("projects")); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/aolda/itda/controller/routing/RoutingController.java b/src/main/java/com/aolda/itda/controller/routing/RoutingController.java index dc4d0db66032ef46861b7bc4d5e6be3b4e671b28..4b9d551ada34e56464d8b95e1577adff3f456d36 100644 --- a/src/main/java/com/aolda/itda/controller/routing/RoutingController.java +++ b/src/main/java/com/aolda/itda/controller/routing/RoutingController.java @@ -3,10 +3,13 @@ package com.aolda.itda.controller.routing; import com.aolda.itda.dto.forwarding.ForwardingDTO; import com.aolda.itda.dto.routing.RoutingDTO; import com.aolda.itda.service.routing.RoutingService; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api") @RequiredArgsConstructor @@ -22,8 +25,9 @@ public class RoutingController { } @GetMapping("/routing") - public ResponseEntity<Object> view(@RequestParam Long routingId) { - return ResponseEntity.ok(routingService.getRouting(routingId)); + public ResponseEntity<Object> view(@RequestParam Long routingId, + HttpServletRequest request) { + return ResponseEntity.ok(routingService.getRouting(routingId, (List<String>) request.getAttribute("projects"))); } @GetMapping("/routings") @@ -33,14 +37,16 @@ public class RoutingController { @PatchMapping("/routing") public ResponseEntity<Object> edit(@RequestParam Long routingId, - @RequestBody RoutingDTO dto) { - routingService.editRouting(routingId, dto); + @RequestBody RoutingDTO dto, + HttpServletRequest request) { + routingService.editRouting(routingId, dto, (List<String>) request.getAttribute("projects")); return ResponseEntity.ok().build(); } @DeleteMapping("/routing") - public ResponseEntity<Object> delete(@RequestParam Long routingId) { - routingService.deleteRouting(routingId); + public ResponseEntity<Object> delete(@RequestParam Long routingId, + HttpServletRequest request) { + routingService.deleteRouting(routingId, (List<String>) request.getAttribute("projects")); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/aolda/itda/exception/ErrorCode.java b/src/main/java/com/aolda/itda/exception/ErrorCode.java index 321bdaddd8b6eb5a3f523e8e56b5d738e3105319..6eea5fb80b24a23bab78c4c0ee5676feac05bae4 100644 --- a/src/main/java/com/aolda/itda/exception/ErrorCode.java +++ b/src/main/java/com/aolda/itda/exception/ErrorCode.java @@ -10,6 +10,7 @@ public enum ErrorCode { // User INVALID_USER_INFO(HttpStatus.BAD_REQUEST, "잘못된 회원 정보입니다"), + UNAUTHORIZED_USER(HttpStatus.BAD_REQUEST, "권한이 없는 사용자입니다"), // Token INVALID_TOKEN(HttpStatus.BAD_REQUEST, "잘못된 토큰입니다"), diff --git a/src/main/java/com/aolda/itda/service/AuthService.java b/src/main/java/com/aolda/itda/service/AuthService.java index cc523fa856bbb7101e995e049bb7b3bdfb196127..cbc157f6b4b7f5cf9398c9095330fd4ab40c6184 100644 --- a/src/main/java/com/aolda/itda/service/AuthService.java +++ b/src/main/java/com/aolda/itda/service/AuthService.java @@ -239,7 +239,7 @@ public class AuthService { } // 특정 사용자의 참여 프로젝트 반환 - private List<ProjectIdAndNameDTO> getProjectsWithUser(Map<String, String> user) throws JsonProcessingException { + public List<ProjectIdAndNameDTO> getProjectsWithUser(Map<String, String> user) throws JsonProcessingException { String userId = user.get("id"); String token = user.get("token"); if (userId == null || token == null) { @@ -283,6 +283,12 @@ public class AuthService { } + public void validateProjectAuth(List<String> projects, String projectId) { + if (!projects.contains(projectId)) { + throw new CustomException(ErrorCode.UNAUTHORIZED_USER); + } + } + private Boolean isAdmin(Map<String, String> user) throws JsonProcessingException { String url = keystone + "/role_assignments?user.id=" + user.get("id") + "&scope.system&include_names"; HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java index 9ec934398f5635dbb05e868888506f2140757f1c..441090bb334842d0a0142511f87b7992f5919a9f 100644 --- a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java +++ b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java @@ -1,13 +1,16 @@ package com.aolda.itda.service.forwarding; import com.aolda.itda.dto.PageResp; +import com.aolda.itda.dto.auth.ProjectIdAndNameDTO; import com.aolda.itda.dto.forwarding.ForwardingDTO; import com.aolda.itda.entity.forwarding.Forwarding; import com.aolda.itda.exception.CustomException; import com.aolda.itda.exception.ErrorCode; import com.aolda.itda.repository.forwarding.ForwardingRepository; +import com.aolda.itda.service.AuthService; import com.aolda.itda.template.ForwardingTemplate; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; import lombok.RequiredArgsConstructor; @@ -30,6 +33,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.List; import java.util.Map; @Service @@ -42,12 +46,17 @@ public class ForwardingService { private String serverBaseIp; private final ForwardingTemplate forwardingTemplate; private final ForwardingRepository forwardingRepository; + private final AuthService authService; private final RestTemplate restTemplate = new RestTemplate(); /* 포트포워딩 정보 조회 */ - public ForwardingDTO getForwarding(Long forwardingId) { + public ForwardingDTO getForwarding(Long forwardingId, List<String> projects) { Forwarding forwarding = forwardingRepository.findByForwardingIdAndIsDeleted(forwardingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING)); + + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, forwarding.getProjectId()); + return forwarding.toForwardingDTO(); } @@ -158,10 +167,12 @@ public class ForwardingService { } /* 포트포워딩 정보 수정 */ - public void editForwarding(Long forwardingId, ForwardingDTO dto) { + public void editForwarding(Long forwardingId, ForwardingDTO dto, List<String> projects) { Forwarding forwarding = forwardingRepository.findByForwardingIdAndIsDeleted(forwardingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING)); + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, forwarding.getProjectId()); /* 중복 검증 */ if (dto.getServerPort() != null && forwardingRepository.existsByServerPortAndIsDeleted(dto.getServerPort(), false)) { @@ -270,10 +281,13 @@ public class ForwardingService { } /* 포트포워딩 삭제 (소프트) */ - public void deleteForwarding(Long forwardingId) { + public void deleteForwarding(Long forwardingId, List<String> projects) { Forwarding forwarding = forwardingRepository.findByForwardingIdAndIsDeleted(forwardingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING)); + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, forwarding.getProjectId()); + /* 파일 삭제 */ String confPath = "/data/nginx/stream/" + forwarding.getForwardingId() + ".conf"; String deletePath = confPath + ".deleted"; diff --git a/src/main/java/com/aolda/itda/service/routing/RoutingService.java b/src/main/java/com/aolda/itda/service/routing/RoutingService.java index 9cfad7d4ec75d158fce65ced051f8036749ef5b2..eb3777b6fc26fbbd7c822894b5fed5f640202634 100644 --- a/src/main/java/com/aolda/itda/service/routing/RoutingService.java +++ b/src/main/java/com/aolda/itda/service/routing/RoutingService.java @@ -8,6 +8,7 @@ import com.aolda.itda.exception.CustomException; import com.aolda.itda.exception.ErrorCode; import com.aolda.itda.repository.certificate.CertificateRepository; import com.aolda.itda.repository.routing.RoutingRepository; +import com.aolda.itda.service.AuthService; import com.aolda.itda.template.RoutingTemplate; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; @@ -26,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.List; @Service @Transactional @@ -35,14 +37,18 @@ public class RoutingService { private final RoutingRepository routingRepository; private final CertificateRepository certificateRepository; + private final AuthService authService; private final RoutingTemplate routingTemplate; private final RestTemplate restTemplate = new RestTemplate(); /* Routing 조회 */ - public RoutingDTO getRouting(Long routingId) { - // project id 확인 필요 + public RoutingDTO getRouting(Long routingId, List<String> projects) { Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, routing.getProjectId()); + return routing.toRoutingDTO(); } @@ -154,10 +160,13 @@ public class RoutingService { } /* Routing 수정 */ - public void editRouting(Long routingId, RoutingDTO dto) { + public void editRouting(Long routingId, RoutingDTO dto, List<String> projects) { Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, routing.getProjectId()); + /* 입력 DTO 검증 */ validateDTO(dto); @@ -253,10 +262,13 @@ public class RoutingService { } /* Routing 삭제 */ - public void deleteRouting(Long routingId) { + public void deleteRouting(Long routingId, List<String> projects) { Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + /* 프로젝트 권한 검증 */ + authService.validateProjectAuth(projects, routing.getProjectId()); + /* 파일 삭제 */ String confPath = "/data/nginx/proxy_host/" + routing.getRoutingId() + ".conf"; String deletePath = confPath + ".deleted";