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 @@ -33,12 +33,13 @@ public ResponseEntity<Void> signup(
) {
String email = (String)session.getAttribute("email");
Provider provider = (Provider)session.getAttribute("provider");
String providerId = (String)session.getAttribute("providerId");

if (email == null || provider == null) {
throw CustomException.of(ApiResponseCode.INVALID_SESSION);
}

Integer userId = userService.signup(email, provider, request);
Integer userId = userService.signup(email, providerId, provider, request);

session.invalidate();

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/gg/agit/konect/domain/user/enums/Provider.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ public enum Provider {

GOOGLE("email"),
NAVER("response.email"),
KAKAO("kakao_account.email");
KAKAO("kakao_account.email"),
APPLE("email");

private final String emailPath;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
@UniqueConstraint(
name = "uq_unregistered_user_email_provider",
columnNames = {"email", "provider"}
),
@UniqueConstraint(
name = "uq_unregistered_user_provider_provider_id",
columnNames = {"provider", "provider_id"}
)
}
)
Expand All @@ -43,10 +47,14 @@ public class UnRegisteredUser extends BaseEntity {
@Enumerated(EnumType.STRING)
private Provider provider;

@Column(name = "provider_id", length = 255)
private String providerId;

@Builder
private UnRegisteredUser(Integer id, String email, Provider provider) {
private UnRegisteredUser(Integer id, String email, Provider provider, String providerId) {
this.id = id;
this.email = email;
this.provider = provider;
this.providerId = providerId;
}
}
29 changes: 29 additions & 0 deletions src/main/java/gg/agit/konect/domain/user/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
@UniqueConstraint(
name = "uq_users_university_id_student_number",
columnNames = {"university_id", "student_number"}
),
@UniqueConstraint(
name = "uq_users_provider_provider_id",
columnNames = {"provider", "provider_id"}
)
}
)
Expand Down Expand Up @@ -69,6 +73,9 @@ public class User extends BaseEntity {
@Enumerated(EnumType.STRING)
private Provider provider;

@Column(name = "provider_id", length = 255)
private String providerId;

@Column(name = "is_marketing_agreement", nullable = false)
private Boolean isMarketingAgreement;

Expand All @@ -84,6 +91,7 @@ private User(
String phoneNumber,
String studentNumber,
Provider provider,
String providerId,
Boolean isMarketingAgreement,
String imageUrl
) {
Expand All @@ -94,10 +102,31 @@ private User(
this.phoneNumber = phoneNumber;
this.studentNumber = studentNumber;
this.provider = provider;
this.providerId = providerId;
this.isMarketingAgreement = isMarketingAgreement;
this.imageUrl = imageUrl;
}

public static User of(
University university,
UnRegisteredUser tempUser,
String name,
String studentNumber,
Boolean isMarketingAgreement,
String imageUrl
) {
return User.builder()
.university(university)
.email(tempUser.getEmail())
.name(name)
.studentNumber(studentNumber)
.provider(tempUser.getProvider())
.providerId(tempUser.getProviderId())
.isMarketingAgreement(isMarketingAgreement)
.imageUrl(imageUrl)
.build();
}

public void updateInfo(String name, String studentNumber, String phoneNumber) {
this.name = name;
this.studentNumber = studentNumber;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,27 @@

import gg.agit.konect.domain.user.enums.Provider;
import gg.agit.konect.domain.user.model.UnRegisteredUser;
import gg.agit.konect.global.code.ApiResponseCode;
import gg.agit.konect.global.exception.CustomException;

public interface UnRegisteredUserRepository extends Repository<UnRegisteredUser, Integer> {

Optional<UnRegisteredUser> findByEmailAndProvider(String email, Provider provider);

Optional<UnRegisteredUser> findByProviderIdAndProvider(String providerId, Provider provider);

boolean existsByProviderIdAndProvider(String providerId, Provider provider);

default UnRegisteredUser getByEmailAndProvider(String email, Provider provider) {
return findByEmailAndProvider(email, provider)
.orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_UNREGISTERED_USER));
}

default UnRegisteredUser getByProviderIdAndProvider(String providerId, Provider provider) {
return findByProviderIdAndProvider(providerId, provider)
.orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_UNREGISTERED_USER));
}

void save(UnRegisteredUser user);

void delete(UnRegisteredUser user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ public interface UserRepository extends Repository<User, Integer> {

Optional<User> findByEmailAndProvider(String email, Provider provider);

Optional<User> findByProviderIdAndProvider(String providerId, Provider provider);

boolean existsByProviderIdAndProvider(String providerId, Provider provider);

Optional<User> findById(Integer id);

default User getById(Integer id) {
return findById(id).orElseThrow(() ->
CustomException.of(ApiResponseCode.NOT_FOUND_USER));
}

default User getByProviderIdAndProvider(String providerId, Provider provider) {
return findByProviderIdAndProvider(providerId, provider)
.orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_USER));
}

boolean existsByUniversityIdAndStudentNumberAndIdNot(Integer universityId, String studentNumber, Integer id);

boolean existsByUniversityIdAndStudentNumber(Integer universityId, String studentNumber);
Expand Down
47 changes: 32 additions & 15 deletions src/main/java/gg/agit/konect/domain/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,38 @@ public class UserService {
private final StudyTimeQueryService studyTimeQueryService;

@Transactional
public Integer signup(String email, Provider provider, SignupRequest request) {
public Integer signup(String email, String providerId, Provider provider, SignupRequest request) {
if (provider == Provider.APPLE && !StringUtils.hasText(providerId)) {
throw CustomException.of(ApiResponseCode.INVALID_SESSION);
}

if (StringUtils.hasText(providerId)) {
userRepository.findByProviderIdAndProvider(providerId, provider)
.ifPresent(u -> {
throw CustomException.of(ApiResponseCode.ALREADY_REGISTERED_USER);
});
}

userRepository.findByEmailAndProvider(email, provider)
.ifPresent(u -> {
throw CustomException.of(ApiResponseCode.ALREADY_REGISTERED_USER);
});

UnRegisteredUser tempUser = unRegisteredUserRepository
.findByEmailAndProvider(email, provider)
.orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_UNREGISTERED_USER));
UnRegisteredUser tempUser = findUnregisteredUser(email, providerId, provider);

University university = universityRepository.findById(request.universityId())
.orElseThrow(() -> CustomException.of(ApiResponseCode.UNIVERSITY_NOT_FOUND));

validateStudentNumberDuplicationOnSignup(university.getId(), request.studentNumber());

User newUser = User.builder()
.university(university)
.email(tempUser.getEmail())
.name(request.name())
.studentNumber(request.studentNumber())
.provider(tempUser.getProvider())
.isMarketingAgreement(request.isMarketingAgreement())
.imageUrl("https://stage-static.koreatech.in/konect/User_02.png")
.build();
User newUser = User.of(
university,
tempUser,
request.name(),
request.studentNumber(),
request.isMarketingAgreement(),
"https://stage-static.koreatech.in/konect/User_02.png"
);

User savedUser = userRepository.save(newUser);

Expand All @@ -77,11 +85,20 @@ public Integer signup(String email, Provider provider, SignupRequest request) {
return savedUser.getId();
}

private UnRegisteredUser findUnregisteredUser(String email, String providerId, Provider provider) {
if (StringUtils.hasText(providerId)) {
if (unRegisteredUserRepository.existsByProviderIdAndProvider(providerId, provider)) {
return unRegisteredUserRepository.getByProviderIdAndProvider(providerId, provider);
}
}

return unRegisteredUserRepository.getByEmailAndProvider(email, provider);
}

public UserInfoResponse getUserInfo(Integer userId) {
User user = userRepository.getById(userId);
List<ClubMember> clubMembers = clubMemberRepository.findAllByUserId(user.getId());
boolean isClubManager = clubMembers.stream()
.anyMatch(ClubMember::isPresident);
boolean isClubManager = clubMembers.stream().anyMatch(ClubMember::isPresident);
int joinedClubCount = clubMembers.size();
Long unreadCouncilNoticeCount = councilNoticeReadRepository.countUnreadNoticesByUserId(user.getId());
Long studyTime = studyTimeQueryService.getTotalStudyTime(userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import gg.agit.konect.domain.user.enums.Provider;
import gg.agit.konect.domain.user.model.User;
import gg.agit.konect.domain.user.repository.UnRegisteredUserRepository;
import gg.agit.konect.domain.user.repository.UserRepository;
import gg.agit.konect.global.code.ApiResponseCode;
import gg.agit.konect.global.config.SecurityProperties;
Expand All @@ -34,6 +36,7 @@ public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {
private static final int TEMP_SESSION_EXPIRATION_SECONDS = 600;

private final UserRepository userRepository;
private final UnRegisteredUserRepository unRegisteredUserRepository;
private final SecurityProperties securityProperties;

@Override
Expand All @@ -44,14 +47,32 @@ public void onAuthenticationSuccess(
) throws IOException {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken)authentication;
Provider provider = Provider.valueOf(oauthToken.getAuthorizedClientRegistrationId().toUpperCase());

OAuth2User oauthUser = (OAuth2User)authentication.getPrincipal();

String providerId = null;
String email = extractEmail(oauthUser, provider);
Optional<User> user;

Optional<User> user = userRepository.findByEmailAndProvider(email, provider);
if (provider == Provider.APPLE) {
providerId = extractProviderId(oauthUser);

if (!StringUtils.hasText(providerId)) {
throw CustomException.of(ApiResponseCode.FAILED_EXTRACT_PROVIDER_ID);
}
}

user = findUserByProvider(provider, email, providerId);

if (user.isEmpty()) {
sendAdditionalInfoRequiredResponse(request, response, email, provider);
if (provider == Provider.APPLE && !StringUtils.hasText(email)) {
email = resolveAppleEmail(providerId);

if (!StringUtils.hasText(email)) {
throw CustomException.of(ApiResponseCode.FAILED_EXTRACT_EMAIL);
}
}

sendAdditionalInfoRequiredResponse(request, response, email, provider, providerId);
return;
}

Expand All @@ -62,11 +83,17 @@ private void sendAdditionalInfoRequiredResponse(
HttpServletRequest request,
HttpServletResponse response,
String email,
Provider provider
Provider provider,
String providerId
) throws IOException {
HttpSession session = request.getSession(true);
session.setAttribute("email", email);
session.setAttribute("provider", provider);

if (StringUtils.hasText(providerId)) {
session.setAttribute("providerId", providerId);
}

session.setMaxInactiveInterval(TEMP_SESSION_EXPIRATION_SECONDS);

response.sendRedirect(frontendBaseUrl + "/signup");
Expand All @@ -88,18 +115,55 @@ private void sendLoginSuccessResponse(

private String extractEmail(OAuth2User oauthUser, Provider provider) {
Object current = oauthUser.getAttributes();
boolean allowMissing = provider == Provider.APPLE;

for (String key : provider.getEmailPath().split("\\.")) {
if (!(current instanceof Map<?, ?> map)) {
if (allowMissing) {
return null;
}

throw CustomException.of(ApiResponseCode.FAILED_EXTRACT_EMAIL);
}

current = map.get(key);
}

if (current == null && allowMissing) {
return null;
}

return (String)current;
}

private String extractProviderId(OAuth2User oauthUser) {
String providerId = oauthUser.getAttribute("sub");

if (!StringUtils.hasText(providerId)) {
providerId = oauthUser.getName();
}

return providerId;
}

private Optional<User> findUserByProvider(Provider provider, String email, String providerId) {
if (provider == Provider.APPLE) {
return userRepository.findByProviderIdAndProvider(providerId, provider);
}

return userRepository.findByEmailAndProvider(email, provider);
}

private String resolveAppleEmail(String providerId) {
if (!StringUtils.hasText(providerId)) {
return null;
}

return unRegisteredUserRepository.findByProviderIdAndProvider(providerId, Provider.APPLE)
.map(unRegisteredUser -> unRegisteredUser.getEmail())
.orElse(null);
}

private String resolveSafeRedirect(String redirectUri) {
if (redirectUri == null || redirectUri.isBlank()) {
return frontendBaseUrl + "/home";
Expand Down
Loading