diff --git a/src/main/java/org/fastcampus/jober/config/SecurityConfig.java b/src/main/java/org/fastcampus/jober/config/SecurityConfig.java index 8646f63..f0b5187 100644 --- a/src/main/java/org/fastcampus/jober/config/SecurityConfig.java +++ b/src/main/java/org/fastcampus/jober/config/SecurityConfig.java @@ -15,6 +15,7 @@ import org.springframework.core.Ordered; import org.springframework.security.authentication.*; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; @@ -132,7 +133,13 @@ public SecurityFilterChain securityFilterChain( .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) .authorizeHttpRequests( auth -> - auth.requestMatchers(props.getPermitAll().toArray(String[]::new)) + auth + .requestMatchers("/register", "/user/register") + .access((authentication, context) -> { + boolean noSession = authentication.get().getPrincipal().equals("anonymousUser"); + return new AuthorizationDecision(noSession); + }) + .requestMatchers(props.getPermitAll().toArray(String[]::new)) .permitAll() .requestMatchers(props.getPermitUser().toArray(String[]::new)) .permitAll() diff --git a/src/main/java/org/fastcampus/jober/space/controller/SpaceMemberController.java b/src/main/java/org/fastcampus/jober/space/controller/SpaceMemberController.java index 088a682..9cfdbc4 100644 --- a/src/main/java/org/fastcampus/jober/space/controller/SpaceMemberController.java +++ b/src/main/java/org/fastcampus/jober/space/controller/SpaceMemberController.java @@ -66,17 +66,14 @@ public ResponseEntity acceptInvitation( return ResponseEntity.ok().build(); } - /** - * 특정 스페이스의 모든 멤버를 조회합니다. - * - * @param spaceId 멤버를 조회할 스페이스 ID - * @return 스페이스 멤버 목록과 HTTP 상태 코드 - */ - @Operation(summary = "스페이스 멤버 조회", description = "특정 spaceId에 속한 모든 멤버 정보를 가져옵니다.") - @Parameter(name = "spaceId", description = "멤버를 조회할 스페이스의 ID", required = true) + @Operation(summary = "스페이스 멤버 조회", description = "특정 스페이스에 속한 모든 멤버 정보를 조회합니다. 해당 스페이스의 멤버만 조회할 수 있습니다.") + @Parameter(name = "spaceId", description = "멤버를 조회할 스페이스의 ID", required = true) + @ApiResponse(responseCode = "200", description = "멤버 조회 성공") + @ApiResponse(responseCode = "403", description = "해당 스페이스 멤버가 아닌 경우") @GetMapping("/{spaceId}/members") - public ResponseEntity> getSpaceMembers(@PathVariable Long spaceId) { - List result = spaceMemberService.getSpaceMembers(spaceId); + public ResponseEntity> getSpaceMembers(@PathVariable Long spaceId, + @AuthenticationPrincipal CustomUserDetails principal) { + List result = spaceMemberService.getSpaceMembers(spaceId, principal); return ResponseEntity.ok(result); } @@ -161,7 +158,7 @@ public ResponseEntity> getMemberByTag( @Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails principal ) { - List result = spaceMemberService.getMemberByTag(spaceId, tag, principal.getUserId()); + List result = spaceMemberService.getMemberByTag(spaceId, tag, principal); return ResponseEntity.ok(result); } diff --git a/src/main/java/org/fastcampus/jober/space/dto/InviteStatus.java b/src/main/java/org/fastcampus/jober/space/dto/InviteStatus.java index 186e9c1..1d41ed6 100644 --- a/src/main/java/org/fastcampus/jober/space/dto/InviteStatus.java +++ b/src/main/java/org/fastcampus/jober/space/dto/InviteStatus.java @@ -11,6 +11,7 @@ import org.fastcampus.jober.space.entity.Space; import org.fastcampus.jober.space.entity.SpaceMember; import org.fastcampus.jober.user.entity.Users; +import org.hibernate.annotations.DynamicUpdate; import java.time.LocalDateTime; @@ -19,6 +20,7 @@ @Getter @NoArgsConstructor @AllArgsConstructor +@DynamicUpdate public class InviteStatus extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -48,13 +50,17 @@ public void updateStatus(InviteStatusType status) { this.status = status; } + /** + * 얘를 SpaceMemberMapper로 옮기면 this.tag, this.authority를 그대로 가져오지 못하는데 + * SpaceMember toSpaceMember(InviteStatus inviteStatus, Space space, Users user); 처럼 파라미터를 늘리는게 나은지 + * 아님 지금처럼 이 파일에 두는게 나은지? + */ public SpaceMember toSpaceMember(Space space, Users user) { return SpaceMember.builder() .space(space) .user(user) .tag(this.tag) .authority(this.authority) - .isDeleted(false) .build(); } } diff --git a/src/main/java/org/fastcampus/jober/space/entity/Space.java b/src/main/java/org/fastcampus/jober/space/entity/Space.java index b3773f4..619abe6 100644 --- a/src/main/java/org/fastcampus/jober/space/entity/Space.java +++ b/src/main/java/org/fastcampus/jober/space/entity/Space.java @@ -12,12 +12,14 @@ import org.fastcampus.jober.error.ErrorCode; import org.fastcampus.jober.space.dto.request.SpaceUpdateRequestDto; import org.fastcampus.jober.user.entity.Users; +import org.hibernate.annotations.DynamicUpdate; @Entity @Getter @NoArgsConstructor @AllArgsConstructor @Builder +@DynamicUpdate public class Space extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/fastcampus/jober/space/entity/SpaceMember.java b/src/main/java/org/fastcampus/jober/space/entity/SpaceMember.java index ab50c49..a332688 100644 --- a/src/main/java/org/fastcampus/jober/space/entity/SpaceMember.java +++ b/src/main/java/org/fastcampus/jober/space/entity/SpaceMember.java @@ -9,12 +9,14 @@ import org.fastcampus.jober.common.entity.BaseEntity; import org.fastcampus.jober.space.dto.request.MemberUpdateRequestDto; import org.fastcampus.jober.user.entity.Users; +import org.hibernate.annotations.DynamicUpdate; @Entity @Getter @AllArgsConstructor @NoArgsConstructor @Builder +@DynamicUpdate public class SpaceMember extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -33,8 +35,8 @@ public class SpaceMember extends BaseEntity { @JoinColumn(name = "userId") private Users user; - @Column(nullable = false) - @Builder.Default + @Column(nullable = false) // DB 스키마의 제약조건 + @Builder.Default // 애플리케이션 레벨 기본값 private Boolean isDeleted = false; // 멤버 논리삭제 유무 public void softDelete() {isDeleted = true;} diff --git a/src/main/java/org/fastcampus/jober/space/service/SpaceMemberService.java b/src/main/java/org/fastcampus/jober/space/service/SpaceMemberService.java index f6a8576..b83df6c 100644 --- a/src/main/java/org/fastcampus/jober/space/service/SpaceMemberService.java +++ b/src/main/java/org/fastcampus/jober/space/service/SpaceMemberService.java @@ -75,7 +75,7 @@ public InviteResult inviteSpaceMember(Long spaceId, List getSpaceMembers(Long spaceId) { + public List getSpaceMembers(Long spaceId, CustomUserDetails principal) { + spaceMemberRepository.findBySpaceIdAndUserId(spaceId, principal.getUserId()) + .orElseThrow(() -> new BusinessException(ErrorCode.FORBIDDEN, "해당 스페이스 멤버만 조회할 수 있습니다.")); + List spaceMembers = spaceMemberRepository.findBySpaceId(spaceId); return spaceMemberMapper.toMemberResponseDtoList(spaceMembers); } @@ -155,6 +158,13 @@ public void deleteSpaceMember(List memberIds, Long spaceId, CustomUserDeta if (members.size() != memberIds.size()) { throw new BusinessException(ErrorCode.NOT_FOUND, "해당하는 멤버가 없습니다.");} + boolean containsAdmin = members.stream() + .anyMatch(m -> m.getAuthority() == Authority.ADMIN); + + if (containsAdmin) { + throw new BusinessException(ErrorCode.BAD_REQUEST, "관리자는 삭제할 수 없습니다."); + } + members.forEach(SpaceMember::softDelete); // 3. 명시적 저장 (혹시 모를 Dirty Checking 문제 방지) @@ -172,15 +182,12 @@ public MemberUpdateResponseDto updateMember(Long memberId, Long spaceId, MemberU return spaceMemberMapper.toMemberUpdateResponseDto(member); } - public List getMemberByTag(Long spaceId, String tag, Long userId) { - - spaceRepository.findByIdOrThrow(spaceId); - - spaceMemberRepository.findBySpaceIdAndUserId(spaceId, userId) + public List getMemberByTag(Long spaceId, String tag, CustomUserDetails principal) { + spaceMemberRepository.findBySpaceIdAndUserId(spaceId, principal.getUserId()) .orElseThrow(() -> new BusinessException(ErrorCode.FORBIDDEN, "해당 스페이스 멤버만 조회할 수 있습니다.")); + spaceRepository.findByIdOrThrow(spaceId); List members = spaceMemberRepository.findBySpaceIdAndTag(spaceId, tag); - return spaceMemberMapper.toMemberResponseDtoList(members); } diff --git a/src/main/java/org/fastcampus/jober/space/service/SpaceService.java b/src/main/java/org/fastcampus/jober/space/service/SpaceService.java index 182cf0d..52d6afb 100644 --- a/src/main/java/org/fastcampus/jober/space/service/SpaceService.java +++ b/src/main/java/org/fastcampus/jober/space/service/SpaceService.java @@ -39,7 +39,6 @@ public void createSpace(SpaceCreateRequestDto dto, CustomUserDetails principal) .spaceId(savedSpace.getSpaceId()) .userId(principal.getUserId()) .authority(Authority.ADMIN) - .isDeleted(false) .build(); SpaceMember spaceMember = spaceMemberMapper.toEntity(adminUser); diff --git a/src/main/java/org/fastcampus/jober/user/service/UserService.java b/src/main/java/org/fastcampus/jober/user/service/UserService.java index aa5c25d..c33a01e 100644 --- a/src/main/java/org/fastcampus/jober/user/service/UserService.java +++ b/src/main/java/org/fastcampus/jober/user/service/UserService.java @@ -38,6 +38,11 @@ public Long getUserId(String username) throws UsernameNotFoundException { return userRepository.findByUsernameAndIsDeletedFalse(username).orElseThrow().getUserId(); } + /** + * 이메일로 초대받아 진행되는 회원가입 + * @param req + * @param spaceId + */ @Transactional public void register(RegisterRequestDto req, Long spaceId) { register(req); @@ -47,9 +52,6 @@ public void register(RegisterRequestDto req, Long spaceId) { } - /** - * 이메일로 초대받아 진행되는 회원가입 - */ @Transactional public void register(RegisterRequestDto req) { // 입력값 형식 검증 diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index f837e91..97601e5 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -47,7 +47,6 @@ security: - /h2-console/** - /admin/sessions/** permit-user: - - /user/register - /user/login - /user/id/check - /user/email/check diff --git a/src/main/resources/application-production.yml b/src/main/resources/application-production.yml index 14abd62..4a23ad8 100644 --- a/src/main/resources/application-production.yml +++ b/src/main/resources/application-production.yml @@ -51,7 +51,6 @@ security: - /swagger-ui.html - /admin/sessions/** permit-user: - - /user/register - /user/login - /user/id/check - /user/email/check