Skip to content

Commit

Permalink
[BE] Feat/#378 Admin API ๊ตฌํ˜„ (#405)
Browse files Browse the repository at this point in the history
* feat: ์ „์ฒด ํšŒ์› ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„

* feat: ํšŒ์› ์‚ญ์ œ(ํƒˆํ‡ด) ๊ธฐ๋Šฅ ๊ตฌํ˜„

* feat: ํšŒ์› ์‚ญ์ œ(ํƒˆํ‡ด)์‹œ Pin/Topic Soft-deleting ๊ตฌํ˜„

* refactor: Admin DTO ๋ถ„๋ฆฌ

* feat: Member ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„

* feat: Topic ์‚ญ์ œ ๋ฐ ์ด๋ฏธ์ง€ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„

* feat: Pin ์‚ญ์ œ ๋ฐ ์ด๋ฏธ์ง€ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„

* feat: Admin API ๊ตฌํ˜„

* refactor: Member ์ƒํƒœ(์ฐจ๋‹จ, ํƒˆํ‡ด ๋“ฑ) ํ•„๋“œ์— ๋”ฐ๋ฅธ ๋กœ๊ทธ์ธ ๋กœ์ง ์ˆ˜์ •

* refactor: @SqlDelete ์‚ญ์ œ ๋ฐ JPQL ๋Œ€์ฒด

* feat: AdminInterceptor ๊ตฌํ˜„

* test: Repository soft-deleting ํ…Œ์ŠคํŠธ ๊ตฌํ˜„

* test: AdminQueryService ํ…Œ์ŠคํŠธ ๊ตฌํ˜„

* test: AdminCommandService ํ…Œ์ŠคํŠธ ๊ตฌํ˜„

* test: AdminController Restdocs ํ…Œ์ŠคํŠธ ๊ตฌํ˜„

* test: AdminInterceptor Mocking

* test: ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„

* refactor: ์˜คํƒˆ์ž ์ˆ˜์ •

* refactor: Auth ๊ด€๋ จ ์˜ˆ์™ธ ํด๋ž˜์Šค ์ถ”๊ฐ€

* refactor: ๋ถˆํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ ์ œ๊ฑฐ

* refactor: findMemberById ์˜ˆ์™ธ ์ˆ˜์ •

* test: GithubActions ์‹คํŒจ ํ…Œ์ŠคํŠธ ์ˆ˜์ •

* refactor: isAdmin() ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€

* refactor: ํšŒ์› ์‚ญ์ œ(ํƒˆํ‡ด)์‹œ, ์ถ”๊ฐ€ ์ •๋ณด(์ฆ๊ฒจ์ฐพ๊ธฐ ๋“ฑ) ์‚ญ์ œ
  • Loading branch information
cpot5620 authored and kpeel5839 committed Sep 17, 2023
1 parent 858501d commit 56ef862
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

@Getter
public enum AuthErrorCode {
ILLEGAL_MEMBER_ID("01100", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
ILLEGAL_TOKEN("01101", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
FORBIDDEN_ADMIN_ACCESS("01102", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
BLOCKING_MEMBER_ACCESS("01103", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
EXPIRED_TOKEN("01104", "๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋œ ํ† ํฐ์ž…๋‹ˆ๋‹ค.")
ILLEGAL_MEMBER_ID("03100", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
ILLEGAL_TOKEN("03101", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
FORBIDDEN_ADMIN_ACCESS("03102", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
BLOCKING_MEMBER_ACCESS("03103", "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
;

private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
import com.mapbefine.mapbefine.auth.exception.AuthErrorCode;
import com.mapbefine.mapbefine.auth.exception.AuthException;
import com.mapbefine.mapbefine.auth.infrastructure.AuthorizationExtractor;
import com.mapbefine.mapbefine.auth.infrastructure.TokenProvider;
import com.mapbefine.mapbefine.auth.infrastructure.JwtTokenProvider;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Objects;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
Expand All @@ -19,24 +18,24 @@ public class AdminAuthInterceptor implements HandlerInterceptor {

private final AuthorizationExtractor<AuthInfo> authorizationExtractor;
private final AuthService authService;
private final TokenProvider tokenProvider;
private final JwtTokenProvider jwtTokenProvider;

public AdminAuthInterceptor(
AuthorizationExtractor<AuthInfo> authorizationExtractor,
AuthService authService,
TokenProvider tokenProvider
JwtTokenProvider jwtTokenProvider
) {
this.authorizationExtractor = authorizationExtractor;
this.authService = authService;
this.tokenProvider = tokenProvider;
this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public boolean preHandle(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull Object handler
) {
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
Expand All @@ -54,9 +53,11 @@ private Long extractMemberIdFromToken(HttpServletRequest request) {
if (Objects.isNull(authInfo)) {
return null;
}
tokenProvider.validateAccessToken(authInfo.accessToken());

return Long.parseLong(tokenProvider.getPayload(authInfo.accessToken()));
String accessToken = authInfo.accessToken();
if (jwtTokenProvider.validateToken(accessToken)) {
return Long.parseLong(jwtTokenProvider.getPayload(accessToken));
}
throw new AuthException.AuthUnauthorizedException(AuthErrorCode.ILLEGAL_TOKEN);
}

private void validateAdmin(Long memberId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import com.mapbefine.mapbefine.auth.application.AuthService;
import com.mapbefine.mapbefine.auth.domain.AuthMember;
import com.mapbefine.mapbefine.auth.dto.AuthInfo;
import com.mapbefine.mapbefine.auth.exception.AuthErrorCode;
import com.mapbefine.mapbefine.auth.exception.AuthException;
import com.mapbefine.mapbefine.auth.exception.AuthException.AuthUnauthorizedException;
import com.mapbefine.mapbefine.auth.infrastructure.AuthorizationExtractor;
import com.mapbefine.mapbefine.auth.infrastructure.TokenProvider;
import com.mapbefine.mapbefine.auth.infrastructure.JwtTokenProvider;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Objects;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
Expand All @@ -19,24 +21,24 @@ public class AuthInterceptor implements HandlerInterceptor {

private final AuthorizationExtractor<AuthInfo> authorizationExtractor;
private final AuthService authService;
private final TokenProvider tokenProvider;
private final JwtTokenProvider jwtTokenProvider;

public AuthInterceptor(
AuthorizationExtractor<AuthInfo> authorizationExtractor,
AuthService authService,
TokenProvider tokenProvider
JwtTokenProvider jwtTokenProvider
) {
this.authorizationExtractor = authorizationExtractor;
this.authService = authService;
this.tokenProvider = tokenProvider;
this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public boolean preHandle(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull Object handler
) {
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception {
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true;
}
Expand All @@ -47,14 +49,22 @@ public boolean preHandle(
Long memberId = extractMemberIdFromToken(request);

if (isLoginRequired((HandlerMethod) handler)) {
authService.validateMember(memberId);
validateMember(memberId);
}

request.setAttribute("memberId", memberId);

return true;
}

private void validateMember(Long memberId) {
if (authService.isMember(memberId)) {
return;
}

throw new AuthUnauthorizedException(AuthErrorCode.ILLEGAL_MEMBER_ID);
}

private boolean isAuthMemberNotRequired(HandlerMethod handlerMethod) {
return Arrays.stream(handlerMethod.getMethodParameters())
.noneMatch(parameter -> parameter.getParameterType().equals(AuthMember.class));
Expand All @@ -71,9 +81,11 @@ private Long extractMemberIdFromToken(HttpServletRequest request) {
if (Objects.isNull(authInfo)) {
return null;
}
tokenProvider.validateAccessToken(authInfo.accessToken());

return Long.parseLong(tokenProvider.getPayload(authInfo.accessToken()));
String accessToken = authInfo.accessToken();
if (!jwtTokenProvider.validateToken(accessToken)) {
throw new AuthException.AuthUnauthorizedException(AuthErrorCode.ILLEGAL_TOKEN);
}
return Long.parseLong(jwtTokenProvider.getPayload(accessToken));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,22 @@ private static String createNickname(String nickname) {
private static String createNicknameSuffix() {
return randomUUID()
.toString()
.replace("-", "")
.replaceAll("-", "")
.substring(0, DEFAULT_NICKNAME_SUFFIX_LENGTH);
}

public void update(
String nickName
String nickName,
String email,
String imageUrl
) {
memberInfo = memberInfo.createUpdatedMemberInfo(nickName);
memberInfo = MemberInfo.of(
nickName,
email,
imageUrl,
memberInfo.getRole(),
memberInfo.getStatus()
);
}

public void addTopic(Topic topic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Objects;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.ColumnDefault;

@Embeddable
@NoArgsConstructor(access = PROTECTED)
Expand All @@ -28,7 +29,7 @@ public class MemberInfo {
@Column(nullable = false, length = 20, unique = true)
private String nickName;

@Column(nullable = false)
@Column(nullable = false, unique = true)
private String email;

@Column(nullable = false)
Expand All @@ -39,6 +40,7 @@ public class MemberInfo {
private Role role;

@Enumerated(EnumType.STRING)
@ColumnDefault(value = "NORMAL")
@Column(nullable = false)
private Status status;

Expand Down Expand Up @@ -108,11 +110,6 @@ private static void validateStatus(Status status) {
}
}

public MemberInfo createUpdatedMemberInfo(String nickName) {

return MemberInfo.of(nickName, this.email, this.imageUrl.getImageUrl(), this.role, this.status);
}

public String getImageUrl() {
return imageUrl.getImageUrl();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface MemberRepository extends JpaRepository<Member, Long> {

List<Member> findAllByMemberInfoRole(Role role);

List<Member> findAllByMemberInfoRole(Role role);

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.mapbefine.mapbefine.member.exception;

import com.mapbefine.mapbefine.common.exception.BadRequestException;
import com.mapbefine.mapbefine.common.exception.ConflictException;
import com.mapbefine.mapbefine.common.exception.ErrorCode;
import com.mapbefine.mapbefine.common.exception.ForbiddenException;
import com.mapbefine.mapbefine.common.exception.NotFoundException;
Expand All @@ -26,11 +25,5 @@ public MemberForbiddenException(MemberErrorCode errorCode, Long id) {
}
}

public static class MemberConflictException extends ConflictException {
public MemberConflictException(MemberErrorCode errorCode, String value) {
super(new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), value));
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@

import com.mapbefine.mapbefine.auth.exception.AuthErrorCode;
import com.mapbefine.mapbefine.auth.exception.AuthException.AuthUnauthorizedException;
import com.mapbefine.mapbefine.auth.infrastructure.JwtTokenProvider;
import com.mapbefine.mapbefine.member.domain.Member;
import com.mapbefine.mapbefine.member.domain.MemberRepository;
import com.mapbefine.mapbefine.member.dto.response.MemberDetailResponse;
import com.mapbefine.mapbefine.oauth.domain.AuthCodeRequestUrlProviderComposite;
import com.mapbefine.mapbefine.oauth.domain.OauthMember;
import com.mapbefine.mapbefine.oauth.domain.OauthMemberClientComposite;
import com.mapbefine.mapbefine.oauth.domain.OauthServerType;
import com.mapbefine.mapbefine.oauth.dto.LoginInfoResponse;
import org.springframework.stereotype.Service;

@Service
public class OauthService {

private final MemberRepository memberRepository;
private final AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite;
private final OauthMemberClientComposite oauthMemberClientComposite;
private MemberRepository memberRepository;
private JwtTokenProvider jwtTokenProvider;
private AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite;
private OauthMemberClientComposite oauthMemberClientComposite;

public OauthService(
MemberRepository memberRepository,
JwtTokenProvider jwtTokenProvider,
AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite,
OauthMemberClientComposite oauthMemberClientComposite
) {
this.memberRepository = memberRepository;
this.jwtTokenProvider = jwtTokenProvider;
this.authCodeRequestUrlProviderComposite = authCodeRequestUrlProviderComposite;
this.oauthMemberClientComposite = oauthMemberClientComposite;
}
Expand All @@ -32,14 +36,16 @@ public String getAuthCodeRequestUrl(OauthServerType oauthServerType) {
return authCodeRequestUrlProviderComposite.provide(oauthServerType);
}

public MemberDetailResponse login(OauthServerType oauthServerType, String code) {
public LoginInfoResponse login(OauthServerType oauthServerType, String code) {
OauthMember oauthMember = oauthMemberClientComposite.fetch(oauthServerType, code);
Member savedMember = memberRepository.findByOauthId(oauthMember.getOauthId())
.orElseGet(() -> register(oauthMember));

validateMemberStatus(savedMember);

return MemberDetailResponse.from(savedMember);
String accessToken = jwtTokenProvider.createToken(String.valueOf(savedMember.getId()));

return LoginInfoResponse.of(accessToken, savedMember);
}

private Member register(OauthMember oauthMember) {
Expand All @@ -51,6 +57,7 @@ private void validateMemberStatus(Member member) {
if (member.isNormalStatus()) {
return;
}

throw new AuthUnauthorizedException(AuthErrorCode.BLOCKING_MEMBER_ACCESS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,6 @@ public int countBookmarks() {
return bookmarks.size();
}

public Publicity getPublicity() {
return topicStatus.getPublicity();
}
public void removeImage() {
this.topicInfo = topicInfo.removeImage();
}
Expand Down

0 comments on commit 56ef862

Please sign in to comment.