Skip to content

Commit

Permalink
feat: 관리자 권한 검증 구현
Browse files Browse the repository at this point in the history
- 헤더에 관리자 토큰이 포함되어있지 않으면 예외를 반환

#156
  • Loading branch information
amaran-th committed Nov 20, 2023
1 parent d44fb36 commit dc9acef
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.emmsale.activity.application.dto.ActivityResponse;
import com.emmsale.activity.domain.ActivityType;
import com.emmsale.admin.activity.api.AdminActivityApi;
import com.emmsale.member.domain.Member;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -72,8 +73,10 @@ void addTag() throws Exception {
ActivityType.CLUB.getValue(),
"DND"
);
final String accessToken = "Bearer accessToken";

when(activityCommandService.addActivity(any(ActivityAddRequest.class))).thenReturn(response);
when(activityCommandService.addActivity(any(ActivityAddRequest.class),
any(Member.class))).thenReturn(response);

final ResponseFieldsSnippet responseFields = responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("활동 식별자"),
Expand All @@ -83,6 +86,7 @@ void addTag() throws Exception {

//when & then
mockMvc.perform(post("/admin/activities")
.header("Authorization", accessToken)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.emmsale;

import static com.emmsale.member.MemberFixture.adminMember;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
Expand All @@ -20,6 +21,7 @@
import com.emmsale.event.domain.EventMode;
import com.emmsale.event.domain.EventType;
import com.emmsale.event.domain.PaymentType;
import com.emmsale.member.domain.Member;
import com.emmsale.tag.TagFixture;
import com.emmsale.tag.application.dto.TagRequest;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -56,6 +58,8 @@
@WebMvcTest({EventApi.class, AdminEventApi.class})
class EventApiTest extends MockMvcTestHelper {

private static final String accessToken = "Bearer accessToken";

private static final ResponseFieldsSnippet EVENT_DETAIL_RESPONSE_FILED = PayloadDocumentation.responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("event 식별자"),
fieldWithPath("name").type(JsonFieldType.STRING)
Expand Down Expand Up @@ -233,7 +237,8 @@ void updateEventTest() throws Exception {
"image1.jpg", request.getType().toString(),
List.of("imageUrl1", "imageUrl2"), "행사기관", "유료", "온라인");

Mockito.when(eventCommandService.updateEvent(eq(eventId), any(EventDetailRequest.class), any()))
Mockito.when(
eventCommandService.updateEvent(eq(eventId), any(EventDetailRequest.class), any(), any()))
.thenReturn(response);

final String contents = objectMapper.writeValueAsString(request);
Expand Down Expand Up @@ -266,7 +271,7 @@ void updateEventTest() throws Exception {
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)));

final ResultActions result = mockMvc.perform(builder);
final ResultActions result = mockMvc.perform(builder.header("Authorization", accessToken));

//when & then
result.andExpect(status().isOk())
Expand All @@ -283,10 +288,11 @@ void deleteEventTest() throws Exception {
//given
final long eventId = 1L;

Mockito.doNothing().when(eventCommandService).deleteEvent(eventId);
Mockito.doNothing().when(eventCommandService).deleteEvent(eventId, adminMember());
//when
final ResultActions result = mockMvc.perform(
delete("/admin/events/" + eventId));
delete("/admin/events/" + eventId)
.header("Authorization", accessToken));

//then
result.andExpect(status().isNoContent())
Expand Down Expand Up @@ -335,7 +341,8 @@ void addEventTest() throws Exception {
"image1.jpg", request.getType().toString(),
List.of("imageUrl1", "imageUrl2"), "행사기관", "무료", "오프라인");

Mockito.when(eventCommandService.addEvent(any(EventDetailRequest.class), any()))
Mockito.when(
eventCommandService.addEvent(any(EventDetailRequest.class), any(), any(Member.class)))
.thenReturn(response);

final String contents = objectMapper.writeValueAsString(request);
Expand Down Expand Up @@ -367,7 +374,7 @@ void addEventTest() throws Exception {
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)));

final ResultActions result = mockMvc.perform(builder);
final ResultActions result = mockMvc.perform(builder.header("Authorization", accessToken));

//then
result.andExpect(status().isCreated())
Expand Down Expand Up @@ -413,6 +420,7 @@ void addEventWithEmptyNameTest(final String eventName) throws Exception {
.file("images", image2.getBytes())
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)))
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest());
}
Expand Down Expand Up @@ -454,6 +462,7 @@ void addEventWithEmptyLocationTest(final String eventLocation) throws Exception
.file("images", image2.getBytes())
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)))
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest());
}
Expand Down Expand Up @@ -496,6 +505,7 @@ void addEventWithInvalidInformationUrlTest(final String informationUrl) throws E
.file("images", image2.getBytes())
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)))
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest());
}
Expand Down Expand Up @@ -544,6 +554,7 @@ void addEventWithUnformattedStartDateTimeTest(final String startDateTime)
.file("images", image2.getBytes())
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)))
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest());
}
Expand Down Expand Up @@ -591,6 +602,7 @@ void addEventWithUnformattedEndDateTimeTest(final String endDateTime) throws Exc
.file("images", image2.getBytes())
.file(new MockMultipartFile("request", "", "application/json", contents.getBytes(
StandardCharsets.UTF_8)))
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.emmsale.admin.report.api.AdminReportApi;
import com.emmsale.member.domain.Member;
import com.emmsale.report.api.ReportApi;
import com.emmsale.report.application.dto.ReportCreateRequest;
import com.emmsale.report.application.dto.ReportCreateResponse;
Expand Down Expand Up @@ -75,6 +76,7 @@ void addReport() throws Exception {
@DisplayName("신고 목록을 조회할 수 있다.")
void findReports() throws Exception {
// given
final String accessToken = "Bearer accessToken";
final ResponseFieldsSnippet responseFields = responseFields(
fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("신고 id"),
fieldWithPath("[].reporterId").type(JsonFieldType.NUMBER).description("신고자의 Id"),
Expand All @@ -98,10 +100,11 @@ void findReports() throws Exception {

);

when(reportQueryService.findReports()).thenReturn(reportFindResponse);
when(reportQueryService.findReports(any(Member.class))).thenReturn(reportFindResponse);

// when & then
mockMvc.perform(get("/admin/reports"))
mockMvc.perform(get("/admin/reports")
.header("Authorization", accessToken))
.andExpect(status().isOk())
.andDo(document("find-reports", responseFields));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.emmsale.admin.tag.api.AdminTagApi;
import com.emmsale.member.domain.Member;
import com.emmsale.tag.api.TagApi;
import com.emmsale.tag.application.dto.TagRequest;
import com.emmsale.tag.application.dto.TagResponse;
Expand Down Expand Up @@ -58,8 +59,9 @@ void addTag() throws Exception {
);
final TagRequest request = new TagRequest("프론트");
final TagResponse response = new TagResponse(3L, "프론트");
final String accessToken = "Bearer accessToken";

when(tagCommandService.addTag(any(TagRequest.class))).thenReturn(response);
when(tagCommandService.addTag(any(TagRequest.class), any(Member.class))).thenReturn(response);

final ResponseFieldsSnippet responseFields = responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("태그 식별자"),
Expand All @@ -68,6 +70,7 @@ void addTag() throws Exception {

//when & then
mockMvc.perform(post("/admin/tags")
.header("Authorization", accessToken)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.emmsale.activity.application.dto.ActivityAddRequest;
import com.emmsale.activity.application.dto.ActivityResponse;
import com.emmsale.admin.activity.application.ActivityCommandService;
import com.emmsale.member.domain.Member;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -20,9 +21,10 @@ public class AdminActivityApi {

@PostMapping
public ResponseEntity<ActivityResponse> create(
@RequestBody final ActivityAddRequest request
@RequestBody final ActivityAddRequest request,
final Member admin
) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(activityCommandService.addActivity(request));
.body(activityCommandService.addActivity(request, admin));
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.emmsale.admin.activity.application;

import static com.emmsale.admin.login.utils.AdminValidator.validateAuthorization;

import com.emmsale.activity.application.dto.ActivityAddRequest;
import com.emmsale.activity.application.dto.ActivityResponse;
import com.emmsale.activity.domain.Activity;
import com.emmsale.activity.domain.ActivityRepository;
import com.emmsale.activity.exception.ActivityException;
import com.emmsale.activity.exception.ActivityExceptionType;
import com.emmsale.member.domain.Member;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -17,7 +20,8 @@ public class ActivityCommandService {

private final ActivityRepository activityRepository;

public ActivityResponse addActivity(final ActivityAddRequest request) {
public ActivityResponse addActivity(final ActivityAddRequest request, final Member admin) {
validateAuthorization(admin);
final String name = request.getName();
validateAlreadyExist(name);
final Activity activity = new Activity(request.getActivityType(), name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.emmsale.admin.event.application.EventCommandService;
import com.emmsale.event.application.dto.EventDetailRequest;
import com.emmsale.event.application.dto.EventResponse;
import com.emmsale.member.domain.Member;
import java.util.List;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -28,21 +29,25 @@ public class AdminEventApi {
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public EventResponse addEvent(@RequestPart @Valid final EventDetailRequest request,
@RequestPart final List<MultipartFile> images) {
return eventCommandService.addEvent(request, images);
@RequestPart final List<MultipartFile> images,
final Member admin
) {
return eventCommandService.addEvent(request, images, admin);
}

@PutMapping(path = "/{eventId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.OK)
public EventResponse updateEvent(@PathVariable final Long eventId,
@RequestPart @Valid final EventDetailRequest request,
@RequestPart final List<MultipartFile> images) {
return eventCommandService.updateEvent(eventId, request, images);
@RequestPart final List<MultipartFile> images,
final Member admin
) {
return eventCommandService.updateEvent(eventId, request, images, admin);
}

@DeleteMapping("/{eventId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteEvent(@PathVariable final Long eventId) {
eventCommandService.deleteEvent(eventId);
public void deleteEvent(@PathVariable final Long eventId, final Member admin) {
eventCommandService.deleteEvent(eventId, admin);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.emmsale.admin.event.application;

import static com.emmsale.admin.login.utils.AdminValidator.validateAuthorization;
import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_EVENT;
import static java.util.stream.Collectors.toList;

Expand All @@ -14,6 +15,7 @@
import com.emmsale.image.application.ImageCommandService;
import com.emmsale.image.domain.AllImagesOfContent;
import com.emmsale.image.domain.ImageType;
import com.emmsale.member.domain.Member;
import com.emmsale.tag.application.dto.TagRequest;
import com.emmsale.tag.domain.Tag;
import com.emmsale.tag.domain.TagRepository;
Expand All @@ -36,7 +38,8 @@ public class EventCommandService {
private final ImageCommandService imageCommandService;

public EventResponse addEvent(final EventDetailRequest request,
final List<MultipartFile> images) {
final List<MultipartFile> images, final Member admin) {
validateAuthorization(admin);
final Event event = eventRepository.save(request.toEvent());
final List<Tag> tags = findAllPersistTagsOrElseThrow(request.getTags());
event.addAllEventTags(tags);
Expand All @@ -49,7 +52,8 @@ public EventResponse addEvent(final EventDetailRequest request,
}

public EventResponse updateEvent(final Long eventId, final EventDetailRequest request,
final List<MultipartFile> images) {
final List<MultipartFile> images, final Member admin) {
validateAuthorization(admin);
final Event event = eventRepository.findById(eventId)
.orElseThrow(() -> new EventException(NOT_FOUND_EVENT));

Expand Down Expand Up @@ -77,7 +81,8 @@ public EventResponse updateEvent(final Long eventId, final EventDetailRequest re
return EventResponse.from(updatedEvent, savedImages);
}

public void deleteEvent(final Long eventId) {
public void deleteEvent(final Long eventId, final Member admin) {
validateAuthorization(admin);
if (!eventRepository.existsById(eventId)) {
throw new EventException(NOT_FOUND_EVENT);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.emmsale.admin.login.utils;

import com.emmsale.login.exception.LoginException;
import com.emmsale.login.exception.LoginExceptionType;
import com.emmsale.member.domain.Member;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class AdminValidator {

private static Long adminMemberId;

public static void validateAuthorization(final Member admin) {
if (admin.isNotMe(adminMemberId)) {
throw new LoginException(LoginExceptionType.INVALID_ACCESS_TOKEN);
}
}

@Value("${data.admin_login.member_id}")
public void setAdminMemberId(final Long adminMemberId) {
AdminValidator.adminMemberId = adminMemberId;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.emmsale.admin.report.api;

import com.emmsale.admin.report.application.ReportQueryService;
import com.emmsale.member.domain.Member;
import com.emmsale.report.application.dto.ReportFindResponse;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -16,7 +17,7 @@ public class AdminReportApi {
private final ReportQueryService reportQueryService;

@GetMapping
public List<ReportFindResponse> findReports() {
return reportQueryService.findReports();
public List<ReportFindResponse> findReports(final Member admin) {
return reportQueryService.findReports(admin);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.emmsale.admin.report.application;

import static com.emmsale.admin.login.utils.AdminValidator.validateAuthorization;

import com.emmsale.member.domain.Member;
import com.emmsale.report.application.dto.ReportFindResponse;
import com.emmsale.report.domain.repository.ReportRepository;
import java.util.List;
Expand All @@ -14,7 +17,8 @@ public class ReportQueryService {

private final ReportRepository reportRepository;

public List<ReportFindResponse> findReports() {
public List<ReportFindResponse> findReports(final Member admin) {
validateAuthorization(admin);
return ReportFindResponse.from(reportRepository.findAll());
}
}
Loading

0 comments on commit dc9acef

Please sign in to comment.