Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException;
import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException;
import com.nowait.domaincorerdb.reservation.exception.ReservationViewUnauthorizedException;
import com.nowait.domaincorerdb.store.exception.StoreDeleteUnauthorizedException;
import com.nowait.domaincorerdb.store.exception.StoreUpdateUnauthorizedException;
import com.nowait.domaincorerdb.store.exception.StoreViewUnauthorizedException;
import com.nowait.domaincorerdb.token.exception.BusinessException;
import com.nowait.domaincorerdb.user.exception.UserNotFoundException;

Expand Down Expand Up @@ -200,6 +203,26 @@ public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedExcep
return new ErrorResponse(e.getMessage(), MENU_DELETE_UNAUTHORIZED.getCode());
}

@ResponseStatus(value = FORBIDDEN)
@ExceptionHandler(StoreViewUnauthorizedException.class)
public ErrorResponse storeViewUnauthorizedException(StoreViewUnauthorizedException e) {
log.error("storeViewUnauthorizedException", e);
return new ErrorResponse(e.getMessage(), STORE_VIEW_UNAUTHORIZED.getCode());
}

@ResponseStatus(value = FORBIDDEN)
@ExceptionHandler(StoreUpdateUnauthorizedException.class)
public ErrorResponse storeUpdateUnauthorizedException(StoreUpdateUnauthorizedException e) {
log.error("storeUpdateUnauthorizedException", e);
return new ErrorResponse(e.getMessage(), STORE_UPDATE_UNAUTHORIZED.getCode());
}

@ResponseStatus(value = FORBIDDEN)
@ExceptionHandler(StoreDeleteUnauthorizedException.class)
public ErrorResponse storeDeleteUnauthorizedException(StoreDeleteUnauthorizedException e) {
log.error("storeDeleteUnauthorizedException", e);
return new ErrorResponse(e.getMessage(), STORE_DELETE_UNAUTHORIZED.getCode());
}

private static Map<String, String> getErrors(MethodArgumentNotValidException e) {
return e.getBindingResult()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -16,6 +17,7 @@
import com.nowait.applicationadmin.store.dto.StoreUpdateRequest;
import com.nowait.applicationadmin.store.service.StoreService;
import com.nowait.common.api.ApiUtils;
import com.nowait.domaincorerdb.user.entity.MemberDetails;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand Down Expand Up @@ -52,12 +54,12 @@ public ResponseEntity<?> createStore(@Valid @RequestBody StoreCreateRequest requ
@GetMapping("/{storeId}")
@Operation(summary = "주점 조회", description = "주점 ID로 주점을 조회합니다.")
@ApiResponse(responseCode = "200", description = "주점 조회 성공")
public ResponseEntity<?> getStoreById(@PathVariable Long storeId) {
public ResponseEntity<?> getStoreById(@PathVariable Long storeId,@AuthenticationPrincipal MemberDetails memberDetails) {
return ResponseEntity
.status(HttpStatus.OK)
.body(
ApiUtils.success(
storeService.getStoreByStoreId(storeId)
storeService.getStoreByStoreId(storeId,memberDetails)
)
);
}
Expand All @@ -67,26 +69,27 @@ public ResponseEntity<?> getStoreById(@PathVariable Long storeId) {
@ApiResponse(responseCode = "200", description = "주점 정보 수정 성공")
public ResponseEntity<?> updateStore(
@PathVariable Long storeId,
@Valid @RequestBody StoreUpdateRequest request
@Valid @RequestBody StoreUpdateRequest request,
@AuthenticationPrincipal MemberDetails memberDetails
) {
return ResponseEntity
.status(HttpStatus.OK)
.body(
ApiUtils.success(
storeService.updateStore(storeId, request)
storeService.updateStore(storeId, request, memberDetails)
)
);
}

@DeleteMapping("/{storeId}")
@Operation(summary = "주점 삭제", description = "주점 ID로 주점을 삭제합니다.")
@ApiResponse(responseCode = "200", description = "주점 삭제 성공")
public ResponseEntity<?> deleteStore(@PathVariable Long storeId) {
public ResponseEntity<?> deleteStore(@PathVariable Long storeId,@AuthenticationPrincipal MemberDetails memberDetails) {
return ResponseEntity
.ok()
.body(
ApiUtils.success(
storeService.deleteStore(storeId)
storeService.deleteStore(storeId,memberDetails)
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
import com.nowait.applicationadmin.store.dto.StoreCreateResponse;
import com.nowait.applicationadmin.store.dto.StoreReadDto;
import com.nowait.applicationadmin.store.dto.StoreUpdateRequest;
import com.nowait.domaincorerdb.user.entity.MemberDetails;

public interface StoreService {

StoreCreateResponse createStore(StoreCreateRequest request);

StoreReadDto getStoreByStoreId(Long storeId);
StoreReadDto getStoreByStoreId(Long storeId, MemberDetails memberDetails);

StoreReadDto updateStore(Long storeId, StoreUpdateRequest request);
StoreReadDto updateStore(Long storeId, StoreUpdateRequest request, MemberDetails memberDetails);

String deleteStore(Long storeId);
String deleteStore(Long storeId, MemberDetails memberDetails);

Boolean toggleActive(Long storeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@
import com.nowait.applicationadmin.store.dto.StoreImageUploadResponse;
import com.nowait.applicationadmin.store.dto.StoreReadDto;
import com.nowait.applicationadmin.store.dto.StoreUpdateRequest;
import com.nowait.common.enums.Role;
import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException;
import com.nowait.domaincorerdb.store.entity.Store;
import com.nowait.domaincorerdb.store.entity.StoreImage;
import com.nowait.domaincorerdb.store.exception.StoreDeleteUnauthorizedException;
import com.nowait.domaincorerdb.store.exception.StoreNotFoundException;
import com.nowait.domaincorerdb.store.exception.StoreParamEmptyException;
import com.nowait.domaincorerdb.store.exception.StoreUpdateUnauthorizedException;
import com.nowait.domaincorerdb.store.exception.StoreViewUnauthorizedException;
import com.nowait.domaincorerdb.store.repository.StoreImageRepository;
import com.nowait.domaincorerdb.store.repository.StoreRepository;
import com.nowait.domaincorerdb.user.entity.MemberDetails;
import com.nowait.domaincorerdb.user.entity.User;
import com.nowait.domaincorerdb.user.exception.UserNotFoundException;
import com.nowait.domaincorerdb.user.repository.UserRepository;


import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;

@Service
Expand All @@ -27,6 +34,7 @@ public class StoreServiceImpl implements StoreService {

private final StoreRepository storeRepository;
private final StoreImageRepository storeImageRepository;
private final UserRepository userRepository;

@Override
@Transactional
Expand All @@ -42,9 +50,12 @@ public StoreCreateResponse createStore(StoreCreateRequest request) {

@Override
@Transactional(readOnly = true)
public StoreReadDto getStoreByStoreId(Long storeId) {
public StoreReadDto getStoreByStoreId(Long storeId, MemberDetails memberDetails) {
if (storeId == null) throw new StoreParamEmptyException();

User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new);
if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
throw new StoreViewUnauthorizedException();
Comment on lines +56 to +57
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

사용자의 storeId가 null인 경우 처리 확인 필요

권한 검증 로직에서 user.getStoreId()가 null일 수 있는 경우를 고려해야 합니다. 매니저 회원가입 시 storeId를 수동으로 추가하는 프로세스 변경으로 인해 storeId가 null인 사용자가 존재할 수 있습니다.

다음 스크립트를 실행하여 User 엔티티의 storeId 필드가 nullable인지 확인하세요:

Also applies to: 75-76, 104-105


🏁 Script executed:

#!/bin/bash
# Description: User 엔티티의 storeId 필드가 nullable인지 확인

# User 엔티티에서 storeId 필드 정의 찾기
ast-grep --pattern 'class User {
  $$$
  $_ storeId
  $$$
}'

# @Column 어노테이션에서 nullable 속성 확인
rg -A 2 -B 2 "storeId" --glob "**/User.java"

Length of output: 2939


storeId가 null일 때 NPE 방지 로직 추가 필요

권한 검증 시 user.getStoreId()null일 수 있어 현재 equals 호출 시 NPE가 발생할 수 있습니다. 아래 위치들에 대해 null-safe 비교 로직으로 변경해주세요.

  • 대상 위치 (nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java)
    • 라인 56–57
    • 라인 75–76
    • 라인 104–105

수정 예시 1: Objects.equals 사용

+ import java.util.Objects;- if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
+ if (!Role.SUPER_ADMIN.equals(user.getRole()) && !Objects.equals(user.getStoreId(), storeId)) {
      throw new StoreViewUnauthorizedException();
  }

수정 예시 2: 명시적 null 체크

- if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
+ if (!Role.SUPER_ADMIN.equals(user.getRole())
+     && (user.getStoreId() == null || !user.getStoreId().equals(storeId))) {
      throw new StoreViewUnauthorizedException();
  }

이렇게 변경하면 storeIdnull일 때도 안전하게 권한 검증이 수행됩니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
throw new StoreViewUnauthorizedException();
import java.util.Objects;
// previously:
// if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
if (!Role.SUPER_ADMIN.equals(user.getRole())
&& !Objects.equals(user.getStoreId(), storeId)) {
throw new StoreViewUnauthorizedException();
}
🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java
at lines 56-57, 75-76, and 104-105, the current role and storeId comparison uses
user.getStoreId().equals(storeId), which can throw a NullPointerException if
user.getStoreId() is null. To fix this, replace these comparisons with a
null-safe approach such as using Objects.equals(user.getStoreId(), storeId) or
explicitly checking for null before calling equals. This change will ensure safe
permission validation even when storeId is null.

}
Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId)
.orElseThrow(StoreNotFoundException::new);

Expand All @@ -58,9 +69,12 @@ public StoreReadDto getStoreByStoreId(Long storeId) {

@Override
@Transactional
public StoreReadDto updateStore(Long storeId, StoreUpdateRequest request) {
public StoreReadDto updateStore(Long storeId, StoreUpdateRequest request, MemberDetails memberDetails) {
User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new);
if (storeId == null || request == null) throw new StoreParamEmptyException();

if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
throw new StoreUpdateUnauthorizedException();
}
Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId)
.orElseThrow(StoreNotFoundException::new);

Expand All @@ -82,10 +96,14 @@ public StoreReadDto updateStore(Long storeId, StoreUpdateRequest request) {

@Override
@Transactional
public String deleteStore(Long storeId) {
public String deleteStore(Long storeId, MemberDetails memberDetails) {
User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new);
if (storeId == null) {
throw new StoreParamEmptyException();
}
if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) {
throw new StoreDeleteUnauthorizedException();
}

Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId)
.orElseThrow(StoreNotFoundException::new);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ public class ManagerSignupRequestDto {
@Schema(description = "로그인타입", example = "LOCAL")
private String socialType;

@NotNull
@Schema(description = "관리자가 속한 storeId", example = "1")
private Long storeId;

public User toEntity() {
return User.builder()
Expand All @@ -46,7 +43,6 @@ public User toEntity() {
.nickname(nickname)
.socialType(SocialType.LOCAL)
.role(Role.MANAGER)
.storeId(storeId)
.build();

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public enum ErrorMessage {
// store
STORE_PARAMETER_EMPTY("주점 생성 시 파라미터 정보가 없습니다.", "store001"),
STORE_NOT_FOUND("해당 주점을 찾을 수 없습니다.", "store002"),
STORE_VIEW_UNAUTHORIZED("주점 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "store003"),
STORE_UPDATE_UNAUTHORIZED("주점 수정 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "store004"),
STORE_DELETE_UNAUTHORIZED("주점 삭제 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "store005"),

// image
IMAGE_FILE_EMPTY("이미지 파일을 업로드 해주세요", "image001"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.nowait.domaincorerdb.store.exception;

import com.nowait.common.exception.ErrorMessage;

public class StoreDeleteUnauthorizedException extends RuntimeException {
public StoreDeleteUnauthorizedException() {
super(ErrorMessage.STORE_DELETE_UNAUTHORIZED.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.nowait.domaincorerdb.store.exception;

import com.nowait.common.exception.ErrorMessage;

public class StoreUpdateUnauthorizedException extends RuntimeException {
public StoreUpdateUnauthorizedException() {
super(ErrorMessage.STORE_UPDATE_UNAUTHORIZED.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.nowait.domaincorerdb.store.exception;

import com.nowait.common.exception.ErrorMessage;

public class StoreViewUnauthorizedException extends RuntimeException {
public StoreViewUnauthorizedException() {
super(ErrorMessage.STORE_VIEW_UNAUTHORIZED.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public class User {
@Enumerated(EnumType.STRING)
private Role role;

@Column(nullable = false)
private Long storeId;

@Builder
Expand Down