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 @@ -10,6 +10,7 @@
import com.example.solidconnection.siteuser.domain.Role;
import com.example.solidconnection.siteuser.domain.SiteUser;
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,6 +21,7 @@
public class AuthTokenProvider {

private static final String ROLE_CLAIM_KEY = "role";
private static final String HOME_UNIVERSITY_CLAIM_KEY = "home_university";

private final TokenProvider tokenProvider;
private final TokenStorage tokenStorage;
Expand All @@ -29,9 +31,14 @@ public class AuthTokenProvider {
public AccessToken generateAccessToken(SiteUser siteUser) {
Subject subject = toSubject(siteUser);
Role role = siteUser.getRole();
Map<String, String> claims = new HashMap<>(Map.of(ROLE_CLAIM_KEY, role.name()));
if (siteUser.getHomeUniversityId() != null) {
claims.put(HOME_UNIVERSITY_CLAIM_KEY, String.valueOf(siteUser.getHomeUniversityId()));
}

String token = tokenProvider.generateToken(
subject,
Map.of(ROLE_CLAIM_KEY, role.name()),
claims,
tokenProperties.access().expireTime()
);
return new AccessToken(token);
Expand Down Expand Up @@ -71,6 +78,11 @@ public SiteUser parseSiteUser(String token) {
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
}

public Long parseHomeUniversityId(String token) {
String value = tokenProvider.parseClaims(token, HOME_UNIVERSITY_CLAIM_KEY, String.class);
return value != null ? Long.parseLong(value) : null;
}

public Subject toSubject(SiteUser siteUser) {
return new Subject(siteUser.getId().toString());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.solidconnection.common.exception;

import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT;
import static com.example.solidconnection.mentor.service.MentorApplicationService.MENTOR_APPLICATION_COUNT_LIMIT;
import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES;

import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -133,6 +134,7 @@ public enum ErrorCode {
UNAUTHORIZED_MENTORING(HttpStatus.FORBIDDEN.value(), "멘토링 권한이 없습니다."),
MENTORING_ALREADY_CONFIRMED(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토링입니다."),
MENTOR_APPLICATION_ALREADY_EXISTED(HttpStatus.CONFLICT.value(), "멘토 승격 요청이 이미 존재합니다."),
MENTOR_APPLICATION_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST.value(), "멘토 승격 요청은 " + MENTOR_APPLICATION_COUNT_LIMIT + "회까지만 가능합니다."),
INVALID_EXCHANGE_STATUS_FOR_MENTOR(HttpStatus.BAD_REQUEST.value(), "멘토 승격 지원 가능한 교환학생 상태가 아닙니다."),
UNIVERSITY_ID_REQUIRED_FOR_CATALOG(HttpStatus.BAD_REQUEST.value(), "목록에서 학교를 선택한 경우 학교 정보가 필요합니다."),
UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER(HttpStatus.BAD_REQUEST.value(), "기타 학교를 선택한 경우 학교 정보를 입력할 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.solidconnection.mentor.service;

import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_ALREADY_EXISTED;
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_LIMIT_EXCEEDED;
import static com.example.solidconnection.common.exception.ErrorCode.TERM_NOT_FOUND;
import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND;

Expand Down Expand Up @@ -28,6 +29,8 @@
@Slf4j
public class MentorApplicationService {

public static final int MENTOR_APPLICATION_COUNT_LIMIT = 5;

private final MentorApplicationRepository mentorApplicationRepository;
private final SiteUserRepository siteUserRepository;
private final S3Service s3Service;
Expand All @@ -40,6 +43,7 @@ public void submitMentorApplication(
MultipartFile file
) {
ensureNoPendingOrApprovedMentorApplication(siteUserId);
ensureApplicationCountNotExceeded(siteUserId);

SiteUser siteUser = siteUserRepository.findById(siteUserId)
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
Expand All @@ -66,4 +70,10 @@ private void ensureNoPendingOrApprovedMentorApplication(long siteUserId) {
throw new CustomException(MENTOR_APPLICATION_ALREADY_EXISTED);
}
}

private void ensureApplicationCountNotExceeded(long siteUserId) {
if (mentorApplicationRepository.countBySiteUserId(siteUserId) >= MENTOR_APPLICATION_COUNT_LIMIT) {
throw new CustomException(MENTOR_APPLICATION_LIMIT_EXCEEDED);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public class SiteUser extends BaseEntity {
@Column(name = "nickname", nullable = false, length = 100)
private String nickname;

@Setter
@Column(name = "home_university_id", nullable = true)
private Long homeUniversityId;

@Setter
@Column(name = "profile_image_url", length = 500)
private String profileImageUrl;
Expand Down Expand Up @@ -126,6 +130,27 @@ public SiteUser(
this.userStatus = userStatus;
}

public SiteUser(
String email,
String nickname,
Long homeUniversityId,
String profileImageUrl,
ExchangeStatus exchangeStatus,
Role role,
AuthType authType,
String password,
UserStatus userStatus) {
this.email = email;
this.nickname = nickname;
this.homeUniversityId = homeUniversityId;
this.profileImageUrl = profileImageUrl;
this.exchangeStatus = exchangeStatus;
this.role = role;
this.authType = authType;
this.password = password;
this.userStatus = userStatus;
}

public void updatePassword(String newEncodedPassword) {
this.password = newEncodedPassword;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config/application-variable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ cors:
allowed-origins:
- "https://www.solid-connection.com"
- "https://api.solid-connection.com"
- "https://admin.solid-connection.com"
- "https://admins.solid-connection.com"

sentry:
environment: "production"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE site_user
ADD COLUMN home_university_id BIGINT;

ALTER TABLE site_user
ADD CONSTRAINT fk_site_user_home_university
FOREIGN KEY (home_university_id) REFERENCES home_university(id) ON DELETE NO ACTION;
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.example.solidconnection.siteuser.domain.SiteUser;
import com.example.solidconnection.siteuser.fixture.SiteUserFixture;
import com.example.solidconnection.support.TestContainerSpringBootTest;
import com.example.solidconnection.university.fixture.HomeUniversityFixture;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -33,12 +34,19 @@ class AuthTokenProviderTest {
@Autowired
private SiteUserFixture siteUserFixture;

@Autowired
private HomeUniversityFixture homeUniversityFixture;

private SiteUser siteUser;
private SiteUser siteUserWithHomeUniversity;
private Long homeUniversityId;
private Subject expectedSubject;

@BeforeEach
void setUp() {
homeUniversityId = homeUniversityFixture.인하대학교().getId();
siteUser = siteUserFixture.사용자();
siteUserWithHomeUniversity = siteUserFixture.국내_대학_정보_소지_사용자(homeUniversityId);
expectedSubject = new Subject(siteUser.getId().toString());
}

Expand Down Expand Up @@ -70,6 +78,30 @@ void setUp() {
assertThat(actualSitUser.getId()).isEqualTo(siteUser.getId());
}

@Nested
class 액세스_토큰_homeUniversityId_클레임 {

@Test
void homeUniversityId가_있는_사용자는_액세스_토큰_클레임에_포함된다() {
// when
String token = authTokenProvider.generateAccessToken(siteUserWithHomeUniversity).token();

// then
Long actual = authTokenProvider.parseHomeUniversityId(token);
assertThat(actual).isEqualTo(homeUniversityId);
}

@Test
void homeUniversityId가_없는_사용자는_액세스_토큰_클레임에서_생략된다() {
// when
String token = authTokenProvider.generateAccessToken(siteUser).token();

// then
Long actual = authTokenProvider.parseHomeUniversityId(token);
assertThat(actual).isNull();
}
}

@Nested
class 리프레시_토큰을_제공한다 {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.example.solidconnection.mentor.service;

import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_ALREADY_EXISTED;
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_LIMIT_EXCEEDED;
import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER;
import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_ID_REQUIRED_FOR_CATALOG;
import static com.example.solidconnection.mentor.service.MentorApplicationService.MENTOR_APPLICATION_COUNT_LIMIT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
import static org.mockito.BDDMockito.given;
Expand Down Expand Up @@ -163,6 +165,24 @@ void setUp() {
.hasMessage(MENTOR_APPLICATION_ALREADY_EXISTED.getMessage());
}

@Test
void 멘토_승격_신청_횟수가_최대_횟수에_도달하면_예외가_발생한다() {
// given
for (int i = 0; i < MENTOR_APPLICATION_COUNT_LIMIT; i++) {
mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, 1L);
}

UniversitySelectType universitySelectType = UniversitySelectType.CATALOG;
Long universityId = 1L;
MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId);
MockMultipartFile file = createMentorProofFile();

// when & then
assertThatCode(() -> mentorApplicationService.submitMentorApplication(user.getId(), request, file))
.isInstanceOf(CustomException.class)
.hasMessage(MENTOR_APPLICATION_LIMIT_EXCEEDED.getMessage());
}

@Test
void 이미_REJECTED_상태인_멘토_승격_요청이_존재할_때_멘토_신청이_등록된다() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ public class SiteUserFixture {
.create();
}

public SiteUser 국내_대학_정보_소지_사용자(Long homeUniversityId) {
return siteUserFixtureBuilder.siteUser()
.email("university@example.com")
.authType(AuthType.EMAIL)
.nickname("국내대학사용자")
.homeUniversityId(homeUniversityId)
.profileImageUrl("profileImageUrl")
.role(Role.MENTEE)
.password("password123")
.userStatus(UserStatus.ACTIVE)
.create();
}

public SiteUser 멘토(int index, String nickname) {
return siteUserFixtureBuilder.siteUser()
.email("mentor" + index + "@example.com")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class SiteUserFixtureBuilder {
private String email;
private AuthType authType;
private String nickname;
private Long homeUniversityId;
private String profileImageUrl;
private Role role;
private String password;
Expand All @@ -44,6 +45,11 @@ public SiteUserFixtureBuilder nickname(String nickname) {
return this;
}

public SiteUserFixtureBuilder homeUniversityId(Long homeUniversityId) {
this.homeUniversityId = homeUniversityId;
return this;
}

public SiteUserFixtureBuilder profileImageUrl(String profileImageUrl) {
this.profileImageUrl = profileImageUrl;
return this;
Expand All @@ -68,6 +74,7 @@ public SiteUser create() {
SiteUser siteUser = new SiteUser(
email,
nickname,
homeUniversityId,
profileImageUrl,
ExchangeStatus.CONSIDERING,
role,
Expand Down
Loading