diff --git a/src/main/java/com/dev/moim/domain/account/controller/AuthController.java b/src/main/java/com/dev/moim/domain/account/controller/AuthController.java index c06845a0..dcfcc895 100644 --- a/src/main/java/com/dev/moim/domain/account/controller/AuthController.java +++ b/src/main/java/com/dev/moim/domain/account/controller/AuthController.java @@ -4,8 +4,8 @@ import com.dev.moim.domain.account.entity.User; import com.dev.moim.domain.account.service.AuthService; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; -import com.dev.moim.global.security.annotation.ExtractToken; +import com.dev.moim.global.security.annotation.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.ExtractToken; import com.dev.moim.global.validation.annotation.QuitValidation; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/com/dev/moim/domain/moim/controller/MoimCalendarController.java b/src/main/java/com/dev/moim/domain/moim/controller/MoimCalendarController.java index c6ee1bea..ad950a57 100644 --- a/src/main/java/com/dev/moim/domain/moim/controller/MoimCalendarController.java +++ b/src/main/java/com/dev/moim/domain/moim/controller/MoimCalendarController.java @@ -5,7 +5,7 @@ import com.dev.moim.domain.moim.service.CalenderCommandService; import com.dev.moim.domain.moim.service.CalenderQueryService; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.validation.annotation.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/com/dev/moim/domain/moim/controller/MoimController.java b/src/main/java/com/dev/moim/domain/moim/controller/MoimController.java index 11d5f869..40f40c15 100644 --- a/src/main/java/com/dev/moim/domain/moim/controller/MoimController.java +++ b/src/main/java/com/dev/moim/domain/moim/controller/MoimController.java @@ -6,15 +6,12 @@ import com.dev.moim.domain.moim.controller.enums.MoimRequestType; import com.dev.moim.domain.moim.dto.*; import com.dev.moim.domain.moim.entity.Moim; -import com.dev.moim.domain.moim.entity.enums.MoimCategory; -import com.dev.moim.domain.moim.entity.enums.MoimRole; import com.dev.moim.domain.moim.service.MoimCommandService; import com.dev.moim.domain.moim.service.MoimQueryService; import com.dev.moim.domain.user.dto.UserPreviewListDTO; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.validation.annotation.*; -import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; diff --git a/src/main/java/com/dev/moim/domain/moim/controller/MoimPostController.java b/src/main/java/com/dev/moim/domain/moim/controller/MoimPostController.java index ca3c4873..accf18b1 100644 --- a/src/main/java/com/dev/moim/domain/moim/controller/MoimPostController.java +++ b/src/main/java/com/dev/moim/domain/moim/controller/MoimPostController.java @@ -5,7 +5,6 @@ import com.dev.moim.domain.moim.dto.post.*; import com.dev.moim.domain.moim.entity.Comment; import com.dev.moim.domain.moim.entity.Post; -import com.dev.moim.domain.moim.entity.PostBlock; import com.dev.moim.domain.moim.entity.UserMoim; import com.dev.moim.domain.moim.repository.UserMoimRepository; import com.dev.moim.domain.moim.service.PostCommandService; @@ -13,7 +12,7 @@ import com.dev.moim.domain.user.dto.UserPreviewDTO; import com.dev.moim.domain.user.service.UserQueryService; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.validation.annotation.CheckCursorValidation; import com.dev.moim.global.validation.annotation.CheckTakeValidation; import com.dev.moim.global.validation.annotation.UserMoimValidaton; diff --git a/src/main/java/com/dev/moim/domain/moim/controller/MoimTodoController.java b/src/main/java/com/dev/moim/domain/moim/controller/MoimTodoController.java index fe79102a..2d85e08a 100644 --- a/src/main/java/com/dev/moim/domain/moim/controller/MoimTodoController.java +++ b/src/main/java/com/dev/moim/domain/moim/controller/MoimTodoController.java @@ -5,7 +5,7 @@ import com.dev.moim.domain.moim.service.TodoCommandService; import com.dev.moim.domain.moim.service.TodoQueryService; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.validation.annotation.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -13,7 +13,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -33,6 +32,7 @@ public class MoimTodoController { @ApiResponse(responseCode = "COMMON_002", description = "입력된 정보에 오류가 있습니다. 필드별 오류 메시지를 참조하세요."), @ApiResponse(responseCode = "MOIM_002", description = "모임 관리자 회원이 아닙니다."), @ApiResponse(responseCode = "MOIM_003", description = "모임의 멤버가 아닙니다."), + @ApiResponse(responseCode = "MOIM_007", description = "모임 관리자가 아닙니다."), @ApiResponse(responseCode = "TODO_004", description = "Todo를 할당받을 유저를 지정하지 않았습니다."), @ApiResponse(responseCode = "TODO_005", description = "전체 선택인 경우 특정 assignee를 지정할 수 없습니다.") }) diff --git a/src/main/java/com/dev/moim/domain/moim/repository/UserMoimRepository.java b/src/main/java/com/dev/moim/domain/moim/repository/UserMoimRepository.java index a26abd66..dd3a9063 100644 --- a/src/main/java/com/dev/moim/domain/moim/repository/UserMoimRepository.java +++ b/src/main/java/com/dev/moim/domain/moim/repository/UserMoimRepository.java @@ -115,4 +115,30 @@ Slice findAllByUserProfileIdAndJoinStatus( @Param("joinStatus") JoinStatus joinStatus, @Param("cursor") Long cursor, Pageable pageable); + + @Query("SELECT um FROM UserMoim um " + + "JOIN FETCH um.moim " + + "JOIN FETCH um.user " + + "WHERE um.user.id = :userId " + + "AND um.moim.id = :moimId " + + "AND um.joinStatus = :joinStatus") + Optional findByUserIdAndMoimIdAndJoinStatusWithUserAndMoim(Long userId, Long moimId, JoinStatus joinStatus); + + @Query("SELECT um FROM UserMoim um " + + "JOIN FETCH um.moim " + + "JOIN FETCH um.user " + + "WHERE um.user.id = :userId " + + "AND um.moim.id = :moimId " + + "AND um.joinStatus = :joinStatus " + + "AND um.moimRole IN :moimRoleList " ) + Optional findByUserIdAndMoimIdAndJoinStatusInMoimRoleListWithUserAndMoim(Long userId, Long moimId, JoinStatus joinStatus, List moimRoleList); + + @Query("SELECT um FROM UserMoim um " + + "JOIN FETCH um.moim " + + "JOIN FETCH um.user " + + "WHERE um.user.id = :userId " + + "AND um.moim.id = :moimId " + + "AND um.joinStatus = :joinStatus " + + "AND um.moimRole = :moimRole " ) + Optional findByUserIdAndMoimIdAndJoinStatusAndMoimRoleWithUserAndMoim(Long userId, Long moimId, JoinStatus joinStatus, MoimRole moimRole); } diff --git a/src/main/java/com/dev/moim/domain/moim/service/MoimQueryService.java b/src/main/java/com/dev/moim/domain/moim/service/MoimQueryService.java index 87cbb00f..ed04b45f 100644 --- a/src/main/java/com/dev/moim/domain/moim/service/MoimQueryService.java +++ b/src/main/java/com/dev/moim/domain/moim/service/MoimQueryService.java @@ -9,7 +9,6 @@ import com.dev.moim.domain.moim.dto.MoimJoinRequestListDTO; import com.dev.moim.domain.moim.dto.MoimPreviewListDTO; import com.dev.moim.domain.moim.entity.enums.JoinStatus; -import com.dev.moim.domain.moim.entity.enums.MoimRole; import com.dev.moim.domain.user.dto.UserPreviewListDTO; import java.util.List; diff --git a/src/main/java/com/dev/moim/domain/moim/service/UserMoimQueryService.java b/src/main/java/com/dev/moim/domain/moim/service/UserMoimQueryService.java new file mode 100644 index 00000000..fdd22995 --- /dev/null +++ b/src/main/java/com/dev/moim/domain/moim/service/UserMoimQueryService.java @@ -0,0 +1,20 @@ +package com.dev.moim.domain.moim.service; + +import com.dev.moim.domain.moim.entity.UserMoim; +import com.dev.moim.domain.moim.entity.enums.JoinStatus; +import com.dev.moim.domain.moim.entity.enums.MoimRole; + +import java.util.List; +import java.util.Optional; + +public interface UserMoimQueryService { + + Optional findByUserIdAndMoimIdAndJoinStatusWithUserAndMoim( + Long userId, Long moimId, JoinStatus joinStatus); + + Optional findByUserIdAndMoimIdAndJoinStatusInMoimRoleListWithUserAndMoim( + Long userId, Long moimId, JoinStatus joinStatus, List moimRoleList); + + Optional findByUserIdAndMoimIdAndJoinStatusAndMoimRoleWithUserAndMoim( + Long userId, Long moimId, JoinStatus joinStatus, MoimRole moimRole); +} diff --git a/src/main/java/com/dev/moim/domain/moim/service/impl/UserMoimQueryServiceImpl.java b/src/main/java/com/dev/moim/domain/moim/service/impl/UserMoimQueryServiceImpl.java new file mode 100644 index 00000000..dc79e428 --- /dev/null +++ b/src/main/java/com/dev/moim/domain/moim/service/impl/UserMoimQueryServiceImpl.java @@ -0,0 +1,43 @@ +package com.dev.moim.domain.moim.service.impl; + +import com.dev.moim.domain.moim.entity.UserMoim; +import com.dev.moim.domain.moim.entity.enums.JoinStatus; +import com.dev.moim.domain.moim.entity.enums.MoimRole; +import com.dev.moim.domain.moim.repository.UserMoimRepository; +import com.dev.moim.domain.moim.service.UserMoimQueryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class UserMoimQueryServiceImpl implements UserMoimQueryService { + + private final UserMoimRepository userMoimRepository; + + @Override + public Optional findByUserIdAndMoimIdAndJoinStatusWithUserAndMoim(Long userId, Long moimId, JoinStatus joinStatus) { + return userMoimRepository.findByUserIdAndMoimIdAndJoinStatusWithUserAndMoim( + userId, moimId, joinStatus); + } + + @Override + public Optional findByUserIdAndMoimIdAndJoinStatusInMoimRoleListWithUserAndMoim( + Long userId, Long moimId, JoinStatus joinStatus, List moimRoleList) { + return userMoimRepository.findByUserIdAndMoimIdAndJoinStatusInMoimRoleListWithUserAndMoim( + userId, moimId, joinStatus, moimRoleList); + } + + @Override + public Optional findByUserIdAndMoimIdAndJoinStatusAndMoimRoleWithUserAndMoim( + Long userId, Long moimId, JoinStatus joinStatus, MoimRole moimRole) { + return userMoimRepository.findByUserIdAndMoimIdAndJoinStatusAndMoimRoleWithUserAndMoim( + userId, moimId, joinStatus, moimRole); + } +} diff --git a/src/main/java/com/dev/moim/domain/user/controller/AlarmController.java b/src/main/java/com/dev/moim/domain/user/controller/AlarmController.java index 5a8eaaa3..b48f228a 100644 --- a/src/main/java/com/dev/moim/domain/user/controller/AlarmController.java +++ b/src/main/java/com/dev/moim/domain/user/controller/AlarmController.java @@ -6,7 +6,7 @@ import com.dev.moim.domain.user.service.UserQueryService; import com.dev.moim.global.common.BaseResponse; import com.dev.moim.global.firebase.service.FcmService; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; diff --git a/src/main/java/com/dev/moim/domain/user/controller/UserController.java b/src/main/java/com/dev/moim/domain/user/controller/UserController.java index 227c6243..92f5a47f 100644 --- a/src/main/java/com/dev/moim/domain/user/controller/UserController.java +++ b/src/main/java/com/dev/moim/domain/user/controller/UserController.java @@ -13,7 +13,7 @@ import com.dev.moim.domain.user.service.UserCommandService; import com.dev.moim.domain.user.service.UserQueryService; import com.dev.moim.global.common.BaseResponse; -import com.dev.moim.global.security.annotation.AuthUser; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.validation.annotation.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/com/dev/moim/global/common/code/status/ErrorStatus.java b/src/main/java/com/dev/moim/global/common/code/status/ErrorStatus.java index 48ae6a21..20161617 100644 --- a/src/main/java/com/dev/moim/global/common/code/status/ErrorStatus.java +++ b/src/main/java/com/dev/moim/global/common/code/status/ErrorStatus.java @@ -88,6 +88,9 @@ public enum ErrorStatus implements BaseErrorCode { NOT_REQUEST_JOIN(HttpStatus.NOT_FOUND, "MOIM_011", "신청하지 않은 모임입니다."), USER_MOIM_NOT_FOUND(HttpStatus.NOT_FOUND, "MOIM_012", "user moim을 찾을 수 없습니다."), OWNER_NOT_EXIT(HttpStatus.NOT_FOUND, "MOIM_013", "owner는 모임을 나갈 수 없습니다."), + INVALID_MOIM_ID_FORMAT(HttpStatus.BAD_REQUEST, "MOIM_014", "잘못된 모임 ID 형식입니다."), + MISSING_MOIM_ID_IN_URI(HttpStatus.BAD_REQUEST, "MOIM_015", "URI에서 모임 ID가 누락되었습니다."), + USER_NOT_MOIM_OWNER(HttpStatus.UNAUTHORIZED, "MOIM_016", "모임장 회원이 아닙니다."), // UserProfile 관련 USER_PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "USERPROFILE_001", "프로필을 찾을 수 없습니다."), diff --git a/src/main/java/com/dev/moim/global/config/WebConfig.java b/src/main/java/com/dev/moim/global/config/WebConfig.java index 2dc04f84..ed9bcc8c 100644 --- a/src/main/java/com/dev/moim/global/config/WebConfig.java +++ b/src/main/java/com/dev/moim/global/config/WebConfig.java @@ -1,7 +1,6 @@ package com.dev.moim.global.config; -import com.dev.moim.global.security.annotation.AuthUserArgumentResolver; -import com.dev.moim.global.security.annotation.ExtractTokenArgumentResolver; +import com.dev.moim.global.security.annotation.resolver.*; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -15,11 +14,17 @@ public class WebConfig implements WebMvcConfigurer { private final AuthUserArgumentResolver authUserArgumentResolver; private final ExtractTokenArgumentResolver extractTokenArgumentResolver; + private final AuthUserMoimArgumentResolver authUserMoimArgumentResolver; + private final AuthUserMoimAdminArgumentResolver authUserMoimAdminArgumentResolver; + private final AuthUserMoimOwnerArgumentResolver authUserMoimOwnerArgumentResolver; @Override public void addArgumentResolvers(List resolvers) { resolvers.add(authUserArgumentResolver); resolvers.add(extractTokenArgumentResolver); + resolvers.add(authUserMoimArgumentResolver); + resolvers.add(authUserMoimAdminArgumentResolver); + resolvers.add(authUserMoimOwnerArgumentResolver); } @Override diff --git a/src/main/java/com/dev/moim/global/security/annotation/AuthUser.java b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUser.java similarity index 85% rename from src/main/java/com/dev/moim/global/security/annotation/AuthUser.java rename to src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUser.java index b439174a..ba705713 100644 --- a/src/main/java/com/dev/moim/global/security/annotation/AuthUser.java +++ b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUser.java @@ -1,4 +1,4 @@ -package com.dev.moim.global.security.annotation; +package com.dev.moim.global.security.annotation.annotation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoim.java b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoim.java new file mode 100644 index 00000000..52d29260 --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoim.java @@ -0,0 +1,13 @@ +package com.dev.moim.global.security.annotation.annotation; + +import io.swagger.v3.oas.annotations.Parameter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "userMoim", hidden = true) +public @interface AuthUserMoim {} diff --git a/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimAdmin.java b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimAdmin.java new file mode 100644 index 00000000..46968f7e --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimAdmin.java @@ -0,0 +1,13 @@ +package com.dev.moim.global.security.annotation.annotation; + +import io.swagger.v3.oas.annotations.Parameter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "userMoim", hidden = true) +public @interface AuthUserMoimAdmin {} diff --git a/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimOwner.java b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimOwner.java new file mode 100644 index 00000000..e89aaf58 --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/annotation/AuthUserMoimOwner.java @@ -0,0 +1,13 @@ +package com.dev.moim.global.security.annotation.annotation; + +import io.swagger.v3.oas.annotations.Parameter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "userMoim", hidden = true) +public @interface AuthUserMoimOwner {} diff --git a/src/main/java/com/dev/moim/global/security/annotation/ExtractToken.java b/src/main/java/com/dev/moim/global/security/annotation/annotation/ExtractToken.java similarity index 81% rename from src/main/java/com/dev/moim/global/security/annotation/ExtractToken.java rename to src/main/java/com/dev/moim/global/security/annotation/annotation/ExtractToken.java index 82c0888a..43cb21a5 100644 --- a/src/main/java/com/dev/moim/global/security/annotation/ExtractToken.java +++ b/src/main/java/com/dev/moim/global/security/annotation/annotation/ExtractToken.java @@ -1,4 +1,4 @@ -package com.dev.moim.global.security.annotation; +package com.dev.moim.global.security.annotation.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/com/dev/moim/global/security/annotation/AuthUserArgumentResolver.java b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserArgumentResolver.java similarity index 89% rename from src/main/java/com/dev/moim/global/security/annotation/AuthUserArgumentResolver.java rename to src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserArgumentResolver.java index c774c382..29aae6ec 100644 --- a/src/main/java/com/dev/moim/global/security/annotation/AuthUserArgumentResolver.java +++ b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserArgumentResolver.java @@ -1,9 +1,10 @@ -package com.dev.moim.global.security.annotation; +package com.dev.moim.global.security.annotation.resolver; import com.dev.moim.domain.account.entity.User; -import com.dev.moim.domain.account.repository.UserRepository; +import com.dev.moim.domain.user.service.UserQueryService; import com.dev.moim.global.error.handler.AuthException; import com.dev.moim.global.redis.util.RedisUtil; +import com.dev.moim.global.security.annotation.annotation.AuthUser; import com.dev.moim.global.security.util.JwtUtil; import jakarta.annotation.Nonnull; import jakarta.servlet.http.HttpServletRequest; @@ -27,7 +28,7 @@ @RequiredArgsConstructor public class AuthUserArgumentResolver implements HandlerMethodArgumentResolver { - private final UserRepository userRepository; + private final UserQueryService userQueryService; private final RedisUtil redisUtil; private final JwtUtil jwtUtil; @@ -55,7 +56,7 @@ public Object resolveArgument( return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) .map(authentication -> { String userId = authentication.getName(); - User user = userRepository.findById(Long.valueOf(userId)) + User user = userQueryService.findUserById(Long.valueOf(userId)) .orElseThrow(() -> new AuthException(USER_NOT_FOUND)); if (user.getDeviceId() == null) { diff --git a/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimAdminArgumentResolver.java b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimAdminArgumentResolver.java new file mode 100644 index 00000000..a20ab73a --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimAdminArgumentResolver.java @@ -0,0 +1,93 @@ +package com.dev.moim.global.security.annotation.resolver; + +import com.dev.moim.domain.moim.entity.UserMoim; +import com.dev.moim.domain.moim.entity.enums.JoinStatus; +import com.dev.moim.domain.moim.entity.enums.MoimRole; +import com.dev.moim.domain.moim.service.UserMoimQueryService; +import com.dev.moim.global.error.handler.AuthException; +import com.dev.moim.global.redis.util.RedisUtil; +import com.dev.moim.global.security.annotation.annotation.AuthUserMoimAdmin; +import com.dev.moim.global.security.util.JwtUtil; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.*; + +import static com.dev.moim.global.common.code.status.ErrorStatus.*; + +@Slf4j +@Component +@RequiredArgsConstructor +public class AuthUserMoimAdminArgumentResolver implements HandlerMethodArgumentResolver { + + private final UserMoimQueryService userMoimQueryService; + private final RedisUtil redisUtil; + private final JwtUtil jwtUtil; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthUserMoimAdmin.class) && parameter.getParameterType().equals(UserMoim.class); + } + + @Override + public Object resolveArgument( + @Nonnull MethodParameter parameter, + ModelAndViewContainer mavContainer, + @Nonnull NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) + throws Exception { + + HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class); + + if (httpServletRequest == null) { + throw new AuthException(HTTP_REQUEST_NULL); + } + + String accessToken = jwtUtil.resolveToken(httpServletRequest); + + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .map(authentication -> { + String userId = authentication.getName(); + Long moimId = extractMoimIdFromUri(httpServletRequest.getRequestURI()); + List moimRoleList = new ArrayList<>(Arrays.asList(MoimRole.OWNER, MoimRole.ADMIN)); + UserMoim userMoimAdmin = userMoimQueryService.findByUserIdAndMoimIdAndJoinStatusInMoimRoleListWithUserAndMoim( + Long.valueOf(userId), moimId, JoinStatus.COMPLETE, moimRoleList) + .orElseThrow(() -> new AuthException(USER_NOT_MOIM_ADMIN)); + + if (userMoimAdmin.getUser().getDeviceId() == null) { + Long now = new Date().getTime(); + Long expiration = jwtUtil.getExpiration(accessToken) - now; + redisUtil.setValue(accessToken, "deviceId_missing", expiration); + + throw new AuthException(FCM_TOKEN_REQUIRED); + } + + return userMoimAdmin; + }).orElseThrow(() -> new AuthException(AUTH_INVALID_TOKEN)); + } + + private Long extractMoimIdFromUri(String uri) { + return Arrays.stream(uri.split("/")) + .sequential() + .dropWhile(part -> !"moims".equals(part)) + .skip(1) + .findFirst() + .map(part -> { + try { + return Long.parseLong(part); + } catch (NumberFormatException e) { + throw new AuthException(INVALID_MOIM_ID_FORMAT); + } + }) + .orElseThrow(() -> new AuthException(MISSING_MOIM_ID_IN_URI)); + } +} diff --git a/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimArgumentResolver.java b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimArgumentResolver.java new file mode 100644 index 00000000..f2145e0a --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimArgumentResolver.java @@ -0,0 +1,94 @@ +package com.dev.moim.global.security.annotation.resolver; + +import com.dev.moim.domain.moim.entity.UserMoim; +import com.dev.moim.domain.moim.entity.enums.JoinStatus; +import com.dev.moim.domain.moim.service.UserMoimQueryService; +import com.dev.moim.global.error.handler.AuthException; +import com.dev.moim.global.redis.util.RedisUtil; +import com.dev.moim.global.security.annotation.annotation.AuthUserMoim; +import com.dev.moim.global.security.util.JwtUtil; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.Arrays; +import java.util.Date; +import java.util.Optional; + +import static com.dev.moim.global.common.code.status.ErrorStatus.*; +import static com.dev.moim.global.common.code.status.ErrorStatus.AUTH_INVALID_TOKEN; + +@Slf4j +@Component +@RequiredArgsConstructor +public class AuthUserMoimArgumentResolver implements HandlerMethodArgumentResolver { + + private final UserMoimQueryService userMoimQueryService; + private final RedisUtil redisUtil; + private final JwtUtil jwtUtil; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthUserMoim.class) && parameter.getParameterType().equals(UserMoim.class); + } + + @Override + public Object resolveArgument( + @Nonnull MethodParameter parameter, + ModelAndViewContainer mavContainer, + @Nonnull NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) + throws Exception { + + HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class); + + if (httpServletRequest == null) { + throw new AuthException(HTTP_REQUEST_NULL); + } + + String accessToken = jwtUtil.resolveToken(httpServletRequest); + + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .map(authentication -> { + String userId = authentication.getName(); + Long moimId = extractMoimIdFromUri(httpServletRequest.getRequestURI()); + UserMoim userMoim = userMoimQueryService.findByUserIdAndMoimIdAndJoinStatusWithUserAndMoim( + Long.valueOf(userId), moimId, JoinStatus.COMPLETE) + .orElseThrow(() -> new AuthException(USER_NOT_MOIM_JOIN)); + + if (userMoim.getUser().getDeviceId() == null) { + Long now = new Date().getTime(); + Long expiration = jwtUtil.getExpiration(accessToken) - now; + redisUtil.setValue(accessToken, "deviceId_missing", expiration); + + throw new AuthException(FCM_TOKEN_REQUIRED); + } + + return userMoim; + }).orElseThrow(() -> new AuthException(AUTH_INVALID_TOKEN)); + } + + private Long extractMoimIdFromUri(String uri) { + return Arrays.stream(uri.split("/")) + .sequential() + .dropWhile(part -> !"moims".equals(part)) + .skip(1) + .findFirst() + .map(part -> { + try { + return Long.parseLong(part); + } catch (NumberFormatException e) { + throw new AuthException(INVALID_MOIM_ID_FORMAT); + } + }) + .orElseThrow(() -> new AuthException(MISSING_MOIM_ID_IN_URI)); + } +} diff --git a/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimOwnerArgumentResolver.java b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimOwnerArgumentResolver.java new file mode 100644 index 00000000..c4dbb6c4 --- /dev/null +++ b/src/main/java/com/dev/moim/global/security/annotation/resolver/AuthUserMoimOwnerArgumentResolver.java @@ -0,0 +1,95 @@ +package com.dev.moim.global.security.annotation.resolver; + +import com.dev.moim.domain.moim.entity.UserMoim; +import com.dev.moim.domain.moim.entity.enums.JoinStatus; +import com.dev.moim.domain.moim.entity.enums.MoimRole; +import com.dev.moim.domain.moim.service.UserMoimQueryService; +import com.dev.moim.global.error.handler.AuthException; +import com.dev.moim.global.redis.util.RedisUtil; +import com.dev.moim.global.security.annotation.annotation.AuthUserMoimAdmin; +import com.dev.moim.global.security.util.JwtUtil; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.Arrays; +import java.util.Date; +import java.util.Optional; + +import static com.dev.moim.global.common.code.status.ErrorStatus.*; +import static com.dev.moim.global.common.code.status.ErrorStatus.MISSING_MOIM_ID_IN_URI; + +@Slf4j +@Component +@RequiredArgsConstructor +public class AuthUserMoimOwnerArgumentResolver implements HandlerMethodArgumentResolver { + + private final UserMoimQueryService userMoimQueryService; + private final RedisUtil redisUtil; + private final JwtUtil jwtUtil; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthUserMoimAdmin.class) && parameter.getParameterType().equals(UserMoim.class); + } + + @Override + public Object resolveArgument( + @Nonnull MethodParameter parameter, + ModelAndViewContainer mavContainer, + @Nonnull NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) + throws Exception { + + HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class); + + if (httpServletRequest == null) { + throw new AuthException(HTTP_REQUEST_NULL); + } + + String accessToken = jwtUtil.resolveToken(httpServletRequest); + + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .map(authentication -> { + String userId = authentication.getName(); + Long moimId = extractMoimIdFromUri(httpServletRequest.getRequestURI()); + UserMoim userMoim = userMoimQueryService.findByUserIdAndMoimIdAndJoinStatusAndMoimRoleWithUserAndMoim( + Long.valueOf(userId), moimId, JoinStatus.COMPLETE, MoimRole.OWNER) + .orElseThrow(() -> new AuthException(USER_NOT_MOIM_OWNER)); + + if (userMoim.getUser().getDeviceId() == null) { + Long now = new Date().getTime(); + Long expiration = jwtUtil.getExpiration(accessToken) - now; + redisUtil.setValue(accessToken, "deviceId_missing", expiration); + + throw new AuthException(FCM_TOKEN_REQUIRED); + } + + return userMoim; + }).orElseThrow(() -> new AuthException(AUTH_INVALID_TOKEN)); + } + + private Long extractMoimIdFromUri(String uri) { + return Arrays.stream(uri.split("/")) + .sequential() + .dropWhile(part -> !"moims".equals(part)) + .skip(1) + .findFirst() + .map(part -> { + try { + return Long.parseLong(part); + } catch (NumberFormatException e) { + throw new AuthException(INVALID_MOIM_ID_FORMAT); + } + }) + .orElseThrow(() -> new AuthException(MISSING_MOIM_ID_IN_URI)); + } +} diff --git a/src/main/java/com/dev/moim/global/security/annotation/ExtractTokenArgumentResolver.java b/src/main/java/com/dev/moim/global/security/annotation/resolver/ExtractTokenArgumentResolver.java similarity index 92% rename from src/main/java/com/dev/moim/global/security/annotation/ExtractTokenArgumentResolver.java rename to src/main/java/com/dev/moim/global/security/annotation/resolver/ExtractTokenArgumentResolver.java index 015ff517..aaa5ae26 100644 --- a/src/main/java/com/dev/moim/global/security/annotation/ExtractTokenArgumentResolver.java +++ b/src/main/java/com/dev/moim/global/security/annotation/resolver/ExtractTokenArgumentResolver.java @@ -1,6 +1,7 @@ -package com.dev.moim.global.security.annotation; +package com.dev.moim.global.security.annotation.resolver; import com.dev.moim.global.error.handler.AuthException; +import com.dev.moim.global.security.annotation.annotation.ExtractToken; import com.dev.moim.global.security.util.JwtUtil; import jakarta.annotation.Nonnull; import lombok.RequiredArgsConstructor;