diff --git a/ontime-back/docs/logging-redaction-policy.md b/ontime-back/docs/logging-redaction-policy.md new file mode 100644 index 0000000..640bcc2 --- /dev/null +++ b/ontime-back/docs/logging-redaction-policy.md @@ -0,0 +1,40 @@ +# Logging Redaction Policy + +Production logs must only contain operational metadata needed to debug routing, +ownership, status, and latency. Request payloads are not safe log data. + +## Request Logs + +Controller request logging is centralized in `LoggingAspect` and +`RequestLogPolicy`. Request logs must include only: + +- request ID +- route +- method +- actor identifier +- client IP +- response status +- timing in milliseconds + +The request logger must not inspect or render `@RequestBody` arguments. + +## Sensitive Fields + +Never log values or raw key/value payloads for credentials, OAuth material, +profile text, notes, or request bodies. Sensitive key names include: + +- `authorization` +- `firebaseToken` +- `password` +- `secret` +- `token` + +When field-level logging is genuinely needed, add the field to +`RequestLogPolicy`'s allowlist and keep the log statement metadata-only. + +## Regression Guard + +`SensitiveLoggingPolicyTest` scans application source for sensitive key names in +logger calls, request-body logging in `LoggingAspect`, and DTO-generated +`toString` methods. Any future exception must update this policy and the +central allowlist in the same change. diff --git a/ontime-back/src/main/java/devkor/ontime_back/LoggingAspect.java b/ontime-back/src/main/java/devkor/ontime_back/LoggingAspect.java index a3873b7..ab72d19 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/LoggingAspect.java +++ b/ontime-back/src/main/java/devkor/ontime_back/LoggingAspect.java @@ -2,34 +2,23 @@ import devkor.ontime_back.dto.RequestInfoDto; import devkor.ontime_back.entity.ApiLog; -import devkor.ontime_back.repository.ApiLogRepository; +import devkor.ontime_back.logging.RequestLogPolicy; import devkor.ontime_back.response.GeneralException; import devkor.ontime_back.service.ApiLogService; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import java.lang.annotation.Annotation; -import java.util.Map; - - @Slf4j @Aspect @Component @@ -37,95 +26,47 @@ public class LoggingAspect { private final ApiLogService apiLogService; - private static final String NO_PARAMS = "No Params"; - private static final String NO_BODY = "No Body"; @Pointcut("bean(*Controller)") private void allRequest() {} @Around("allRequest()") public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable { - RequestInfoDto requestInfoDto = extractRequestInfo(); - - // requestTime + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + String requestId = RequestLogPolicy.resolveRequestId(request); + RequestLogPolicy.exposeRequestId(attributes, requestId); + RequestInfoDto requestInfoDto = extractRequestInfo(request); long beforeRequest = System.currentTimeMillis(); - // pathVariable, requestBody - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Object[] args = joinPoint.getArgs(); - Annotation[][] parameterAnnotations = signature.getMethod().getParameterAnnotations(); - - String pathVariable = null; - String requestBody = null; - - for (int i = 0; i < parameterAnnotations.length; i++) { - Annotation[] annotations = parameterAnnotations[i]; - for (Annotation annotation : annotations) { - if (annotation instanceof PathVariable) { - pathVariable = args[i].toString(); // @PathVariable 값 저장 - } else if (annotation instanceof RequestBody) { - requestBody = args[i].toString(); // @RequestBody 값 저장 - } - } - } - - // responseStatus - int responseStatus = 200; - Object result; try { - // 실제 메서드 실행 - result = joinPoint.proceed(); + Object result = joinPoint.proceed(); + int responseStatus = 200; if (result instanceof ResponseEntity) { ResponseEntity responseEntity = (ResponseEntity) result; - responseStatus = responseEntity.getStatusCodeValue(); // 상태 코드 추출 + responseStatus = responseEntity.getStatusCode().value(); } - // 정상 요청 로그 저장 long timeTaken = System.currentTimeMillis() - beforeRequest; - - ApiLog apiLog = buildApiLog(requestInfoDto, responseStatus, timeTaken); - apiLogService.saveLog(apiLog); - - log.info("[Request Log] requestUrl: {}, requestMethod: {}, userId: {}, clientIp: {}, pathVariable: {}, requestBody: {}, responseStatus: {}, timeTaken: {}", - requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), requestInfoDto.getClientIp(), - pathVariable != null ? pathVariable : NO_PARAMS, - requestBody != null ? requestBody : NO_BODY, - responseStatus, timeTaken); + saveApiLog(requestInfoDto, responseStatus, timeTaken); + log.info("[Request Log] requestId: {}, route: {}, method: {}, actor: {}, clientIp: {}, responseStatus: {}, timeTakenMs: {}", + requestId, requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), + requestInfoDto.getClientIp(), responseStatus, timeTaken); return result; - - } catch (Exception ex) { + } catch (Throwable ex) { + int responseStatus = mapExceptionToStatusCode(ex); + long timeTaken = System.currentTimeMillis() - beforeRequest; + saveApiLog(requestInfoDto, responseStatus, timeTaken); + log.error("[Error Log] requestId: {}, route: {}, method: {}, actor: {}, clientIp: {}, exception: {}, responseStatus: {}, timeTakenMs: {}", + requestId, requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), + requestInfoDto.getClientIp(), ex.getClass().getSimpleName(), responseStatus, timeTaken); throw ex; } } - @AfterThrowing(pointcut = "allRequest()", throwing = "ex") - public void logException(JoinPoint joinPoint, Exception ex) { - RequestInfoDto requestInfoDto = extractRequestInfo(); - - // exceptionName - String exceptionName; - if (ex instanceof GeneralException) { - exceptionName = ((GeneralException) ex).getErrorCode().name(); - } else { - exceptionName = ex.getClass().getSimpleName(); - }; - // exceptionMessage - String exceptionMessage = ex.getMessage(); - // responseStatus - int responseStatus = mapExceptionToStatusCode(ex); - - log.error("[Error Log] requestUrl: {}, requestMethod: {}, userId: {}, clientIp: {}, exception: {}, message: {}, responseStatus: {}", - requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), requestInfoDto.getClientIp(), exceptionName, exceptionMessage, responseStatus); - - ApiLog errorLog = buildApiLog(requestInfoDto, responseStatus, 0); - apiLogService.saveLog(errorLog); - } - // requestinfo 추출 - private RequestInfoDto extractRequestInfo() { - HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); - + private RequestInfoDto extractRequestInfo(HttpServletRequest request) { String requestUrl = request.getRequestURI(); String requestMethod = request.getMethod(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); @@ -137,6 +78,11 @@ private RequestInfoDto extractRequestInfo() { return new RequestInfoDto(requestUrl, requestMethod, userId, clientIp); } + private void saveApiLog(RequestInfoDto requestInfoDto, int responseStatus, long timeTaken) { + ApiLog apiLog = buildApiLog(requestInfoDto, responseStatus, timeTaken); + apiLogService.saveLog(apiLog); + } + // apilog 생성 private ApiLog buildApiLog(RequestInfoDto info, int responseStatus, long timeTaken) { return ApiLog.builder() @@ -149,7 +95,7 @@ private ApiLog buildApiLog(RequestInfoDto info, int responseStatus, long timeTak .build(); } - private int mapExceptionToStatusCode(Exception e) { + private int mapExceptionToStatusCode(Throwable e) { if (e instanceof GeneralException ge) { return ge.getErrorCode().getCode(); } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentRequestDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentRequestDto.java index 4d1884b..22604f0 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentRequestDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentRequestDto.java @@ -4,11 +4,9 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; @Getter @Builder -@ToString @NoArgsConstructor @AllArgsConstructor public class AlarmDeviceCurrentRequestDto { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentResponseDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentResponseDto.java index c5f1d95..25aaf71 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentResponseDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentResponseDto.java @@ -3,7 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - import java.time.Instant; @Getter diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceUnregisterRequestDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceUnregisterRequestDto.java index 90530f8..49bdb87 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceUnregisterRequestDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceUnregisterRequestDto.java @@ -4,11 +4,9 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; @Getter @Builder -@ToString @NoArgsConstructor @AllArgsConstructor public class AlarmDeviceUnregisterRequestDto { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmSettingsResponseDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmSettingsResponseDto.java index 8547054..57d158a 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmSettingsResponseDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmSettingsResponseDto.java @@ -3,7 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - import java.time.Instant; @Getter diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusCurrentResponseDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusCurrentResponseDto.java index 3274908..1ab130d 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusCurrentResponseDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusCurrentResponseDto.java @@ -3,7 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - import java.time.Instant; import java.time.LocalDateTime; import java.util.List; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusFailureDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusFailureDto.java index 86f4055..1c5fbd6 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusFailureDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusFailureDto.java @@ -4,11 +4,9 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; @Getter @Builder -@ToString @NoArgsConstructor @AllArgsConstructor public class AlarmStatusFailureDto { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusReportRequestDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusReportRequestDto.java index a8e7828..bb52992 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusReportRequestDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusReportRequestDto.java @@ -4,15 +4,12 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; - import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.List; @Getter @Builder -@ToString @NoArgsConstructor @AllArgsConstructor public class AlarmStatusReportRequestDto { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmWindowScheduleDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmWindowScheduleDto.java index f33da75..6dcd968 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmWindowScheduleDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AlarmWindowScheduleDto.java @@ -4,7 +4,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - import java.time.LocalDateTime; import java.util.List; import java.util.UUID; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/AppleTokenResponseDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/AppleTokenResponseDto.java index e24fcc0..8b291b3 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/AppleTokenResponseDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/AppleTokenResponseDto.java @@ -7,17 +7,12 @@ public class AppleTokenResponseDto { @JsonProperty("access_token") private String accessToken; - @JsonProperty("token_type") private String tokenType; - @JsonProperty("expires_in") private long expiresIn; - @JsonProperty("refresh_token") private String refreshToken; - @JsonProperty("id_token") private String idToken; - } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/ChangePasswordDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/ChangePasswordDto.java index f7fa3ea..92c28bd 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/ChangePasswordDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/ChangePasswordDto.java @@ -8,7 +8,6 @@ public class ChangePasswordDto { private String currentPassword; private String newPassword; - @Builder public ChangePasswordDto(String currentPassword, String newPassword) { this.currentPassword = currentPassword; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/FeedbackAddDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/FeedbackAddDto.java index b653711..54c658c 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/FeedbackAddDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/FeedbackAddDto.java @@ -1,10 +1,12 @@ package devkor.ontime_back.dto; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import java.util.UUID; -@ToString @Getter @Builder @NoArgsConstructor diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/FinishPreparationDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/FinishPreparationDto.java index 3623e03..b42e781 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/FinishPreparationDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/FinishPreparationDto.java @@ -2,16 +2,12 @@ import lombok.Builder; import lombok.Getter; -import lombok.ToString; - import java.util.UUID; -@ToString @Getter public class FinishPreparationDto { private UUID scheduleId; private Integer latenessTime; - @Builder public FinishPreparationDto(UUID scheduleId, Integer latenessTime) { this.scheduleId = scheduleId; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/FirebaseTokenAddDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/FirebaseTokenAddDto.java index e15f7d6..c468086 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/FirebaseTokenAddDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/FirebaseTokenAddDto.java @@ -1,9 +1,7 @@ package devkor.ontime_back.dto; import lombok.Getter; -import lombok.ToString; -@ToString @Getter public class FirebaseTokenAddDto { String firebaseToken; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/FriendDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/FriendDto.java index 8b7c043..723a645 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/FriendDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/FriendDto.java @@ -2,16 +2,13 @@ import lombok.Builder; import lombok.Getter; -import lombok.ToString; -@ToString @Getter @Builder public class FriendDto { private Long friendId; private String friendName; private String friendEmail; - public FriendDto(Long friendId, String friendName, String friendEmail) { this.friendId = friendId; this.friendName = friendName; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendListResponse.java b/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendListResponse.java index b092ee3..2b860e4 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendListResponse.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendListResponse.java @@ -2,13 +2,10 @@ import lombok.Builder; import lombok.Getter; - import java.util.List; @Getter @Builder public class GetFriendListResponse { - private List friendsList; - } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendshipRequesterResponse.java b/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendshipRequesterResponse.java index 0118bb0..8997e20 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendshipRequesterResponse.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/GetFriendshipRequesterResponse.java @@ -2,15 +2,12 @@ import lombok.Builder; import lombok.Getter; -import lombok.ToString; -@ToString @Getter public class GetFriendshipRequesterResponse { private Long requesterId; private String requesterName; private String requesterEmail; - @Builder public GetFriendshipRequesterResponse(Long requesterId, String requesterName, String requesterEmail) { this.requesterId = requesterId; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/LatenessHistoryResponse.java b/ontime-back/src/main/java/devkor/ontime_back/dto/LatenessHistoryResponse.java index 85f2039..40ecc0a 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/LatenessHistoryResponse.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/LatenessHistoryResponse.java @@ -1,19 +1,15 @@ package devkor.ontime_back.dto; import lombok.Getter; -import lombok.ToString; - import java.time.LocalDateTime; import java.util.UUID; -@ToString @Getter public class LatenessHistoryResponse { private UUID scheduleId; private String scheduleName; private LocalDateTime scheduleTime; private int latenessTime; - public LatenessHistoryResponse(UUID scheduleId, String scheduleName, LocalDateTime scheduleTime, int latenessTime) { this.scheduleId = scheduleId; this.scheduleName = scheduleName; @@ -21,4 +17,3 @@ public LatenessHistoryResponse(UUID scheduleId, String scheduleName, LocalDateTi this.latenessTime = latenessTime; } } - diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthAppleUserDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthAppleUserDto.java index 8392836..3ff579f 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthAppleUserDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthAppleUserDto.java @@ -7,7 +7,6 @@ public class OAuthAppleUserDto { private String appleUserId; private String email; private String fullName; - public OAuthAppleUserDto(String appleUserId, String email, String fullName) { this.appleUserId = appleUserId; this.email = email; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthGoogleUserDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthGoogleUserDto.java index 9fcd31b..d835e30 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthGoogleUserDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthGoogleUserDto.java @@ -4,12 +4,10 @@ @Getter public class OAuthGoogleUserDto { - private String id; // 고유 사용자 ID private String name; // 사용자 이름 private String picture; // 프로필 이미지 URL private String email; // 이메일 - public OAuthGoogleUserDto(String id, String name, String picture, String email) { this.id = id; this.name = name; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthKakaoUserDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthKakaoUserDto.java index b8d2dc8..e388734 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthKakaoUserDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/OAuthKakaoUserDto.java @@ -7,7 +7,6 @@ public class OAuthKakaoUserDto { private String id; private Profile profile; - @Data public static class Profile { private String nickname; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/PlaceDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/PlaceDto.java index 0a2e731..7eb2068 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/PlaceDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/PlaceDto.java @@ -3,7 +3,6 @@ import devkor.ontime_back.entity.Place; import lombok.AllArgsConstructor; import lombok.Getter; - import java.util.UUID; @Getter @@ -11,7 +10,6 @@ public class PlaceDto { private UUID placeId; private String placeName; - public static PlaceDto fromEntity(Place place) { return new PlaceDto(place.getPlaceId(), place.getPlaceName()); } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/PreparationDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/PreparationDto.java index 4fa774e..34676a0 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/PreparationDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/PreparationDto.java @@ -3,8 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - -import java.sql.Time; import java.util.UUID; @AllArgsConstructor @@ -12,20 +10,7 @@ @Builder public class PreparationDto { private UUID preparationId; - private String preparationName; - private Integer preparationTime; - private UUID nextPreparationId; - - @Override - public String toString() { - return "PreparationDto{" + - "preparationId=" + preparationId + - ", preparationName='" + preparationName + '\'' + - ", preparationTime=" + preparationTime + - ", nextPreparationId=" + nextPreparationId + - '}'; - } } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/PunctualityScoreResponse.java b/ontime-back/src/main/java/devkor/ontime_back/dto/PunctualityScoreResponse.java index bbf69e5..315c547 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/PunctualityScoreResponse.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/PunctualityScoreResponse.java @@ -2,9 +2,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.ToString; -@ToString @AllArgsConstructor @Getter public class PunctualityScoreResponse { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/RequestInfoDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/RequestInfoDto.java index 6d03ccb..495feb0 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/RequestInfoDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/RequestInfoDto.java @@ -1,6 +1,5 @@ package devkor.ontime_back.dto; - import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleAddDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleAddDto.java index 8f7458d..381fcb8 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleAddDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleAddDto.java @@ -7,12 +7,9 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.ToString; - import java.time.LocalDateTime; import java.util.UUID; -@ToString @Getter @Builder @AllArgsConstructor @@ -27,7 +24,6 @@ public class ScheduleAddDto { private Boolean isStarted; // 버튼누름여부 private Integer scheduleSpareTime; // 스케줄 별 여유시간 private String scheduleNote; // 스케줄 별 주의사항 - public Schedule toEntity(User user, Place place) { return Schedule.builder() .user(user) diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleDto.java index 118125a..260f71e 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleDto.java @@ -5,12 +5,10 @@ import devkor.ontime_back.entity.User; import jakarta.persistence.*; import lombok.*; - import java.sql.Time; import java.time.LocalDateTime; import java.util.UUID; -@ToString @Data @NoArgsConstructor // 기본 생성자 추가 @AllArgsConstructor // 모든 필드를 포함하는 생성자 추가 @@ -24,5 +22,4 @@ public class ScheduleDto { private String scheduleNote; private Integer latenessTime; private DoneStatus doneStatus; - } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleModDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleModDto.java index c549350..0243826 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleModDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/ScheduleModDto.java @@ -3,31 +3,19 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.ToString; - import java.time.LocalDateTime; import java.util.UUID; -@ToString @Getter @Builder @AllArgsConstructor public class ScheduleModDto { - private UUID placeId; - private String placeName; - private String scheduleName; - private Integer moveTime; // 이동시간 - private LocalDateTime scheduleTime; // 약속시각 - private Integer scheduleSpareTime; // 스케줄 별 여유시간 - private Integer latenessTime; - private String scheduleNote; // 스케줄 별 주의사항 - } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/SchedulePeriodDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/SchedulePeriodDto.java index 48179fa..81f4677 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/SchedulePeriodDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/SchedulePeriodDto.java @@ -1,11 +1,8 @@ package devkor.ontime_back.dto; import lombok.Getter; -import lombok.ToString; - import java.time.LocalDateTime; -@ToString @Getter public class SchedulePeriodDto { LocalDateTime startDate; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateAcceptStatusDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateAcceptStatusDto.java index ad9b527..c19dbe2 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateAcceptStatusDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateAcceptStatusDto.java @@ -1,9 +1,7 @@ package devkor.ontime_back.dto; import lombok.Getter; -import lombok.ToString; -@ToString @Getter public class UpdateAcceptStatusDto { private String acceptStatus; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdatePunctualityScoreDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdatePunctualityScoreDto.java index 0b90cfe..a7325ef 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdatePunctualityScoreDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdatePunctualityScoreDto.java @@ -1,9 +1,7 @@ package devkor.ontime_back.dto; import lombok.Getter; -import lombok.ToString; -@ToString @Getter public class UpdatePunctualityScoreDto { private Long userId; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateSpareTimeDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateSpareTimeDto.java index 9b364dc..792e067 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateSpareTimeDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UpdateSpareTimeDto.java @@ -3,14 +3,11 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; -@ToString @Getter @NoArgsConstructor public class UpdateSpareTimeDto { private Integer newSpareTime; - @Builder public UpdateSpareTimeDto(Integer newSpareTime) { this.newSpareTime = newSpareTime; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UserAdditionalInfoDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UserAdditionalInfoDto.java index b705151..8a6ac54 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UserAdditionalInfoDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UserAdditionalInfoDto.java @@ -1,15 +1,12 @@ package devkor.ontime_back.dto; import lombok.*; - import java.sql.Time; -@ToString @Getter public class UserAdditionalInfoDto { private Integer spareTime; // 여유시간 private String note; // 주의사항 - @Builder public UserAdditionalInfoDto(Integer spareTime, String note) { this.spareTime = spareTime; diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UserInfoResponse.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UserInfoResponse.java index c671f72..c75d50c 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UserInfoResponse.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UserInfoResponse.java @@ -5,9 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.ToString; -@ToString @AllArgsConstructor @Getter @Builder diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UserOnboardingDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UserOnboardingDto.java index 57a167a..5f8e7e7 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UserOnboardingDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UserOnboardingDto.java @@ -2,7 +2,6 @@ import lombok.Builder; import lombok.Getter; - import java.util.List; @Getter @@ -11,5 +10,4 @@ public class UserOnboardingDto { private Integer spareTime; private String note; private List preparationList; - } diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UserSettingUpdateDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UserSettingUpdateDto.java index 83666e1..1b87922 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UserSettingUpdateDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UserSettingUpdateDto.java @@ -2,9 +2,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; -@ToString @Getter @NoArgsConstructor public class UserSettingUpdateDto { diff --git a/ontime-back/src/main/java/devkor/ontime_back/dto/UserSignUpDto.java b/ontime-back/src/main/java/devkor/ontime_back/dto/UserSignUpDto.java index 55a364a..27ced8e 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/dto/UserSignUpDto.java +++ b/ontime-back/src/main/java/devkor/ontime_back/dto/UserSignUpDto.java @@ -4,7 +4,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; - import java.util.UUID; @NoArgsConstructor diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/generallogin/handler/LoginSuccessHandler.java b/ontime-back/src/main/java/devkor/ontime_back/global/generallogin/handler/LoginSuccessHandler.java index 7b19764..c8e523f 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/generallogin/handler/LoginSuccessHandler.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/generallogin/handler/LoginSuccessHandler.java @@ -4,8 +4,6 @@ import devkor.ontime_back.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.hibernate.persister.entity.EntityNameUse; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; @@ -22,9 +20,6 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final JwtTokenProvider jwtTokenProvider; private final UserRepository userRepository; - @Value("${jwt.access.expiration}") - private String accessTokenExpiration; - @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { @@ -43,9 +38,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo user.updateRefreshToken(refreshToken); userRepository.saveAndFlush(user); - log.info("로그인에 성공하였습니다. 이메일 : {}", email); - log.info("로그인에 성공하였습니다. AccessToken : {}", accessToken); - log.info("발급된 AccessToken 만료 기간 : {}", accessTokenExpiration); + log.info("Login succeeded for userId: {}", user.getId()); try { diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java b/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java index 25bc561..499247d 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java @@ -60,7 +60,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 리프레시토큰의 경우 토큰의 유효성 뿐만 아니라 DB에 등록되어 있는지도 확인해야 함 // reIssueAccessToken 메소드에서 DB를 확인해 등록된 리프레시 토큰이면 엑세스 토큰 재발급 // 이때 reIssueAccessToken 메소드에서 DB에 등록된 리프레시 토큰이 아니면 InvalidRefreshTokenException 발생 - log.info("리프레시 토큰이 있고 유효한데 DB에 있는지는 아직 모름"); + log.info("Refresh credential passed signature validation; checking stored credential"); reIssueAccessToken(response, refreshToken); return; } @@ -90,10 +90,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 리프레시 토큰이 DB에 있으면 엑세스 토큰을 재발급 // DB에 없으면 InvalidRefreshTokenException 발생 public void reIssueAccessToken(HttpServletResponse response, String refreshToken) throws IOException { - log.info("리프레시토큰이 유효하나 DB에 있는지는 모름. DB에서 찾아봐서 없으면 예외 발생할 것임."); + log.info("Checking stored refresh credential"); User user = userRepository.findByRefreshToken(refreshToken) .orElseThrow(() -> new InvalidRefreshTokenException("Invalid Refresh token!~!")); - log.info("리프레시토큰이 DB에도 있음"); + log.info("Stored refresh credential matched"); String accessToken = jwtTokenProvider.createAccessToken(user.getEmail(), user.getId()); @@ -105,11 +105,11 @@ public void reIssueAccessToken(HttpServletResponse response, String refreshToken // accessToken으로 유저의 권한정보만 저장하고 인증 허가(스프링 시큐리티 필터체인 中 인증체인 통과해 다음 체인으로 이동) public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - log.info("checkAccessTokenAndAuthentication() 호출"); + log.info("Checking access credential authentication"); jwtTokenProvider.extractAccessToken(request) .ifPresent(accessToken -> jwtTokenProvider.extractUserId(accessToken) .ifPresent(userId -> { - log.info("추출된 userId: {}", userId); + log.info("Authenticated userId: {}", userId); userRepository.findById(userId) .ifPresent(this::saveAuthentication); })); @@ -155,7 +155,7 @@ private void handleInvalidTokenException(HttpServletResponse response, InvalidTo response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - log.info("InvalidTokenException 발생"); + log.info("Credential validation exception occurred"); // ErrorCode에서 정보를 가져옴 ErrorCode errorCode = ErrorCode.UNAUTHORIZED; diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java b/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java index 92beb7a..2c27b8c 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java @@ -55,7 +55,6 @@ public class JwtTokenProvider { // accessToken 생성 public String createAccessToken(String email, Long userId) { Date now = new Date(); - log.info("expiresAt: {}", new Date(now.getTime() + accessTokenExpirationPeriod)); return JWT.create() .withSubject(ACCESS_TOKEN_SUBJECT) .withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod)) @@ -80,7 +79,6 @@ public void sendAccessToken(HttpServletResponse response, String accessToken) { response.setStatus(HttpServletResponse.SC_OK); response.setHeader(accessHeader, accessToken); - log.info("발급된 Access Token : {}", accessToken); } // accessToken + refreshToken header에 넣어서 전송 @@ -89,8 +87,7 @@ public void sendAccessAndRefreshToken(HttpServletResponse response, String acces setAccessTokenHeader(response, accessToken); setRefreshTokenHeader(response, refreshToken); - log.info("accesstoken: " + accessToken + "refreshtoken" + refreshToken); - log.info("Access Token, Refresh Token 헤더 설정 완료"); + log.info("Credential headers set"); } // header에서 refreshToken 추출 @@ -116,7 +113,7 @@ public Optional extractEmail(String accessToken) { .getClaim(EMAIL_CLAIM) .asString()); } catch (Exception e) { - log.error("액세스 토큰이 유효하지 않습니다.as"); + log.error("Access credential is invalid"); return Optional.empty(); } } @@ -130,7 +127,7 @@ public Optional extractUserId(String accessToken) { .getClaim(USER_ID_CLAIM) .asLong()); } catch (Exception e) { - log.error("유효하지 않은 accessToken입니다."); + log.error("Access credential is invalid"); return Optional.empty(); } } @@ -159,10 +156,10 @@ public void updateRefreshToken(String email, String refreshToken) { public boolean isTokenValid(String token) { try { JWT.require(Algorithm.HMAC512(secretKey)).build().verify(token); - log.info("유효한 토큰입니다."); + log.info("Credential is valid"); return true; } catch (Exception e) { - log.error("유효하지 않은 토큰입니다. {}", e.getMessage()); + log.error("Credential is invalid"); throw new InvalidTokenException("유효하지 않은 토큰입니다."); } } @@ -172,10 +169,10 @@ public boolean isAccessTokenValid(String token) { userRepository.findByAccessToken(token) .orElseThrow(() -> new InvalidAccessTokenException("유효하지 않은 엑세스 토큰입니다.")); JWT.require(Algorithm.HMAC512(secretKey)).build().verify(token); - log.info("유효한 엑세스 토큰입니다."); + log.info("Access credential is valid"); return true; } catch (Exception e) { - log.error("유효하지 않은 엑세스 토큰입니다. {}", e.getMessage()); + log.error("Access credential is invalid"); throw new InvalidAccessTokenException("유효하지 않은 엑세스 토큰입니다."); } } @@ -183,10 +180,10 @@ public boolean isAccessTokenValid(String token) { public boolean isRefreshTokenValid(String token) { try { JWT.require(Algorithm.HMAC512(secretKey)).build().verify(token); - log.info("유효한 리프레시 토큰입니다."); + log.info("Refresh credential is valid"); return true; } catch (Exception e) { - log.error("유효하지 않은 리프레시 토큰입니다. {}", e.getMessage()); + log.error("Refresh credential is invalid"); throw new InvalidRefreshTokenException("유효하지 않은 리프레시 토큰입니다."); } } diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginFilter.java b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginFilter.java index c28ec3d..fc2922b 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginFilter.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginFilter.java @@ -69,7 +69,6 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ } String appleUserId = tokenClaims.getSubject(); - log.info("appleUserId: {}", appleUserId); String email = tokenClaims.get("email", String.class); // socialRefreshtoken에 저장 @@ -86,10 +85,9 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ } } catch (Exception e) { - log.error("Apple 로그인 실패: {}", e.getMessage(), e); + log.error("Apple login failed: {}", e.getClass().getSimpleName()); throw new AuthenticationException("Apple 로그인 실패") {}; } } } - diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginService.java b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginService.java index 15a3147..637e2ba 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginService.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginService.java @@ -164,7 +164,7 @@ public Authentication handleRegister(String appleRefreshToken, OAuthAppleUserDto // identitytoken 검증 public Claims verifyIdentityToken(String identityToken) throws Exception { - log.info("verifyIdentityToken"); + log.info("Verify Apple identity credential"); Map headers = jwtUtils.parseHeaders(identityToken); // apple publickey ApplePublicKeyResponse applePublicKeyResponse = restTemplate.getForObject(APPLE_KEYS_URL, ApplePublicKeyResponse.class); @@ -192,9 +192,7 @@ public Claims verifyIdentityToken(String identityToken) throws public AppleTokenResponseDto getAppleAccessTokenAndRefreshToken(String authCode) throws Exception { // clientSecret String clientSecret = generateClientSecret(); - log.info("getAppleAccessTokenAndRefreshToken"); - log.info("client_id: {}", clientId); - log.info("client_secret: {}", clientSecret); + log.info("Exchange Apple credential"); MultiValueMap requestBody = new LinkedMultiValueMap<>(); requestBody.add("grant_type", "authorization_code"); requestBody.add("code", authCode); @@ -218,7 +216,7 @@ public AppleTokenResponseDto getAppleAccessTokenAndRefreshToken(String authCode) // clientsecret 생성 private String generateClientSecret() throws Exception { - log.info("generageClientSecret"); + log.info("Generate Apple client credential"); // Private Key String privateKeyContent = new String(Files.readAllBytes(Paths.get(privateKeyPath))) .replace("-----BEGIN PRIVATE KEY-----", "") diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginFilter.java b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginFilter.java index 928a90b..9cd7f1d 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginFilter.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginFilter.java @@ -59,7 +59,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ } } catch (Exception e) { - log.error("Google 로그인 실패: {}", e.getMessage(), e); + log.error("Google login failed: {}", e.getClass().getSimpleName()); throw new AuthenticationException("Google 로그인 실패") {}; } diff --git a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginService.java b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginService.java index 2be184b..5fc466f 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginService.java +++ b/ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginService.java @@ -164,7 +164,7 @@ public GoogleIdToken.Payload verifyIdentityToken(String identityToken) throws Ex GoogleIdToken.Payload payload = idToken.getPayload(); return payload; } else { - log.info("유효하지 않은 idtoken 입니다."); + log.info("Google identity credential is invalid"); return null; } } diff --git a/ontime-back/src/main/java/devkor/ontime_back/logging/RequestLogPolicy.java b/ontime-back/src/main/java/devkor/ontime_back/logging/RequestLogPolicy.java new file mode 100644 index 0000000..3927da9 --- /dev/null +++ b/ontime-back/src/main/java/devkor/ontime_back/logging/RequestLogPolicy.java @@ -0,0 +1,81 @@ +package devkor.ontime_back.logging; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Set; +import java.util.UUID; +import java.util.regex.Pattern; + +public final class RequestLogPolicy { + + public static final String REQUEST_ID_HEADER = "X-Request-Id"; + public static final String REQUEST_ID_ATTRIBUTE = RequestLogPolicy.class.getName() + ".requestId"; + + private static final Pattern SAFE_REQUEST_ID = Pattern.compile("[A-Za-z0-9._:-]{1,128}"); + + private static final Set SENSITIVE_FIELD_NAMES = Set.of( + "authorization", + "authCode", + "clientSecret", + "client_secret", + "currentPassword", + "firebaseToken", + "idToken", + "newPassword", + "password", + "refreshToken", + "secret", + "token" + ); + + private static final Set SAFE_FIELD_ALLOWLIST = Set.of( + "appVersion", + "armedScheduleCount", + "clientIp", + "deviceId", + "method", + "osVersion", + "platform", + "requestId", + "responseStatus", + "route", + "timeTakenMs", + "userId" + ); + + private RequestLogPolicy() { + } + + public static String resolveRequestId(HttpServletRequest request) { + Object existingRequestId = request.getAttribute(REQUEST_ID_ATTRIBUTE); + if (existingRequestId instanceof String existing && !existing.isBlank()) { + return existing; + } + + String requestId = request.getHeader(REQUEST_ID_HEADER); + if (requestId == null || !SAFE_REQUEST_ID.matcher(requestId).matches()) { + requestId = UUID.randomUUID().toString(); + } + + request.setAttribute(REQUEST_ID_ATTRIBUTE, requestId); + return requestId; + } + + public static void exposeRequestId(ServletRequestAttributes attributes, String requestId) { + HttpServletResponse response = attributes.getResponse(); + if (response != null) { + response.setHeader(REQUEST_ID_HEADER, requestId); + } + } + + public static boolean isSafeFieldForLogging(String fieldName) { + return SAFE_FIELD_ALLOWLIST.contains(fieldName) && !isSensitiveFieldName(fieldName); + } + + public static boolean isSensitiveFieldName(String fieldName) { + return SENSITIVE_FIELD_NAMES.stream() + .anyMatch(sensitiveField -> sensitiveField.equalsIgnoreCase(fieldName)); + } +} diff --git a/ontime-back/src/main/java/devkor/ontime_back/response/GlobalExceptionHandler.java b/ontime-back/src/main/java/devkor/ontime_back/response/GlobalExceptionHandler.java index d0d8f1b..b8c2159 100644 --- a/ontime-back/src/main/java/devkor/ontime_back/response/GlobalExceptionHandler.java +++ b/ontime-back/src/main/java/devkor/ontime_back/response/GlobalExceptionHandler.java @@ -1,5 +1,6 @@ package devkor.ontime_back.response; +import devkor.ontime_back.logging.RequestLogPolicy; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -30,11 +31,14 @@ public ResponseEntity> handleInvalidTokenException(Invalid @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex, HttpServletRequest request) { - log.error("[Error Log] requestUrl: {}, requestMethod: {}, userId: {}, clientIp: {}, exception: {}, message: {}, responseStatus: {}", - request.getRequestURI(), request.getMethod(), (request.getUserPrincipal() != null) ? request.getUserPrincipal().getName() : "Anonymous", request.getRemoteAddr(), "HttpMessageNotReadableException", "요청 형식이 올바르지 않습니다.", 400); + String requestId = RequestLogPolicy.resolveRequestId(request); + + log.error("[Error Log] requestId: {}, route: {}, method: {}, actor: {}, clientIp: {}, exception: {}, responseStatus: {}", + requestId, request.getRequestURI(), request.getMethod(), (request.getUserPrincipal() != null) ? request.getUserPrincipal().getName() : "Anonymous", request.getRemoteAddr(), "HttpMessageNotReadableException", 400); return ResponseEntity .status(HttpStatus.BAD_REQUEST) + .header(RequestLogPolicy.REQUEST_ID_HEADER, requestId) .body(ApiResponseForm.error(400, "요청 형식이 올바르지 않습니다.")); } -} \ No newline at end of file +} diff --git a/ontime-back/src/test/java/devkor/ontime_back/security/SensitiveLoggingPolicyTest.java b/ontime-back/src/test/java/devkor/ontime_back/security/SensitiveLoggingPolicyTest.java new file mode 100644 index 0000000..f0260b0 --- /dev/null +++ b/ontime-back/src/test/java/devkor/ontime_back/security/SensitiveLoggingPolicyTest.java @@ -0,0 +1,97 @@ +package devkor.ontime_back.security; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class SensitiveLoggingPolicyTest { + + private static final Path MAIN_SOURCE_ROOT = Path.of("src/main/java"); + private static final Pattern LOG_STATEMENT = Pattern.compile( + "log\\.(?:trace|debug|info|warn|error)\\s*\\((.*?)\\);", + Pattern.DOTALL + ); + private static final List SENSITIVE_LOG_TERMS = List.of( + "authorization", + "firebaseToken", + "password", + "secret", + "token" + ); + + @Test + void logStatementsDoNotReferenceSensitiveKeyNames() throws IOException { + try (Stream sourceFiles = Files.walk(MAIN_SOURCE_ROOT)) { + List violations = sourceFiles + .filter(path -> path.toString().endsWith(".java")) + .flatMap(this::findSensitiveLogStatements) + .toList(); + + assertThat(violations) + .as("Log statements must not reference sensitive key names or sensitive variables") + .isEmpty(); + } + } + + @Test + void requestLoggingAspectDoesNotReadRequestBodies() throws IOException { + String loggingAspect = Files.readString(MAIN_SOURCE_ROOT.resolve("devkor/ontime_back/LoggingAspect.java")); + + assertThat(loggingAspect) + .doesNotContain("org.springframework.web.bind.annotation.RequestBody") + .doesNotContain("requestBody") + .doesNotContain(".toString()"); + } + + @Test + void dtoPackageDoesNotGenerateStringRepresentations() throws IOException { + try (Stream dtoFiles = Files.walk(MAIN_SOURCE_ROOT.resolve("devkor/ontime_back/dto"))) { + List violations = dtoFiles + .filter(path -> path.toString().endsWith(".java")) + .filter(path -> { + try { + String source = Files.readString(path); + return source.contains("@ToString") || source.contains("String toString()"); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }) + .map(Path::toString) + .toList(); + + assertThat(violations) + .as("DTOs should not auto-render sensitive request payloads") + .isEmpty(); + } + } + + private Stream findSensitiveLogStatements(Path sourceFile) { + try { + String source = Files.readString(sourceFile); + Matcher matcher = LOG_STATEMENT.matcher(source); + Stream.Builder violations = Stream.builder(); + + while (matcher.find()) { + String statement = matcher.group(1); + String normalizedStatement = statement.toLowerCase(Locale.ROOT); + SENSITIVE_LOG_TERMS.stream() + .filter(term -> normalizedStatement.contains(term.toLowerCase(Locale.ROOT))) + .findFirst() + .ifPresent(term -> violations.add(sourceFile + " logs sensitive term: " + term)); + } + + return violations.build(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +}