Skip to content

Commit ac46597

Browse files
authored
Implement onboarding authentication resolver and related classes (#9)
1 parent 78589fb commit ac46597

File tree

13 files changed

+182
-136
lines changed

13 files changed

+182
-136
lines changed

src/main/kotlin/com/moa/common/auth/Auth.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@ package com.moa.common.auth
33
@Target(AnnotationTarget.VALUE_PARAMETER)
44
@Retention(AnnotationRetention.RUNTIME)
55
annotation class Auth
6-
7-
data class AuthenticatedMemberInfo(
8-
val id: Long,
9-
)

src/main/kotlin/com/moa/common/auth/AuthConstants.kt

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.moa.common.auth
2+
3+
data class AuthMemberInfo(
4+
val id: Long,
5+
)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.moa.common.auth
2+
3+
import com.moa.common.exception.ErrorCode
4+
import com.moa.common.exception.ForbiddenException
5+
import com.moa.common.exception.UnauthorizedException
6+
import com.moa.repository.*
7+
import io.jsonwebtoken.ExpiredJwtException
8+
import jakarta.servlet.http.HttpServletRequest
9+
import org.springframework.core.MethodParameter
10+
import org.springframework.stereotype.Component
11+
import org.springframework.web.bind.support.WebDataBinderFactory
12+
import org.springframework.web.context.request.NativeWebRequest
13+
import org.springframework.web.method.support.HandlerMethodArgumentResolver
14+
import org.springframework.web.method.support.ModelAndViewContainer
15+
import java.time.LocalDate
16+
17+
@Component
18+
class AuthMemberResolver(
19+
private val jwtTokenProvider: JwtTokenProvider,
20+
private val request: HttpServletRequest,
21+
private val termRepository: TermRepository,
22+
private val termAgreementRepository: TermAgreementRepository,
23+
private val profileRepository: ProfileRepository,
24+
private val payrollVersionRepository: PayrollVersionRepository,
25+
private val workPolicyVersionRepository: WorkPolicyVersionRepository,
26+
) : HandlerMethodArgumentResolver {
27+
28+
override fun supportsParameter(parameter: MethodParameter): Boolean {
29+
return parameter.hasParameterAnnotation(Auth::class.java) &&
30+
parameter.parameterType == AuthMemberInfo::class.java
31+
}
32+
33+
override fun resolveArgument(
34+
parameter: MethodParameter,
35+
mavContainer: ModelAndViewContainer?,
36+
webRequest: NativeWebRequest,
37+
binderFactory: WebDataBinderFactory?,
38+
): AuthMemberInfo {
39+
val memberId = resolveMemberId()
40+
validateOnboardingCompleted(memberId)
41+
return AuthMemberInfo(id = memberId)
42+
}
43+
44+
private fun resolveMemberId(): Long {
45+
val token = jwtTokenProvider.extractToken(request)
46+
?: throw UnauthorizedException()
47+
48+
return try {
49+
jwtTokenProvider.validateToken(token)
50+
jwtTokenProvider.getUserIdFromToken(token)
51+
} catch (ex: ExpiredJwtException) {
52+
throw UnauthorizedException(ErrorCode.EXPIRED_TOKEN)
53+
} catch (ex: Exception) {
54+
throw UnauthorizedException()
55+
} ?: throw UnauthorizedException()
56+
}
57+
58+
private fun validateOnboardingCompleted(memberId: Long) {
59+
val today = LocalDate.now()
60+
61+
val profileCompleted = isProfileCompleted(memberId)
62+
val payrollCompleted = isPayrollCompleted(memberId, today)
63+
val workPolicyCompleted = isWorkPolicyCompleted(memberId, today)
64+
val hasRequiredTermsAgreed = hasRequiredTermsAgreed(memberId)
65+
66+
val onboardingCompleted =
67+
profileCompleted && payrollCompleted && workPolicyCompleted && hasRequiredTermsAgreed
68+
69+
if (!onboardingCompleted) {
70+
throw ForbiddenException(ErrorCode.ONBOARDING_INCOMPLETE)
71+
}
72+
}
73+
74+
private fun isProfileCompleted(memberId: Long): Boolean {
75+
return profileRepository.findByMemberId(memberId)
76+
?.let { it.nickname.isNotBlank() && it.workplace.isNotBlank() }
77+
?: false
78+
}
79+
80+
private fun isPayrollCompleted(memberId: Long, today: LocalDate): Boolean {
81+
return payrollVersionRepository
82+
.findTopByMemberIdAndEffectiveFromLessThanEqualOrderByEffectiveFromDesc(memberId, today) != null
83+
}
84+
85+
private fun isWorkPolicyCompleted(memberId: Long, today: LocalDate): Boolean {
86+
return workPolicyVersionRepository
87+
.findTopByMemberIdAndEffectiveFromLessThanEqualOrderByEffectiveFromDesc(memberId, today)
88+
?.workdays
89+
?.isNotEmpty()
90+
?: false
91+
}
92+
93+
private fun hasRequiredTermsAgreed(memberId: Long): Boolean {
94+
val requiredCodes = termRepository.findAll()
95+
.asSequence()
96+
.filter { it.required }
97+
.map { it.code }
98+
.toSet()
99+
100+
val agreements = termAgreementRepository.findAllByMemberId(memberId)
101+
.associate { it.termCode to it.agreed }
102+
103+
return requiredCodes.all { agreements[it] == true }
104+
}
105+
}

src/main/kotlin/com/moa/common/auth/AuthenticatedMemberResolver.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.moa.common.auth
2+
3+
@Target(AnnotationTarget.VALUE_PARAMETER)
4+
@Retention(AnnotationRetention.RUNTIME)
5+
annotation class OnboardingAuth
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.moa.common.auth
2+
3+
import com.moa.common.exception.ErrorCode
4+
import com.moa.common.exception.UnauthorizedException
5+
import io.jsonwebtoken.ExpiredJwtException
6+
import jakarta.servlet.http.HttpServletRequest
7+
import org.springframework.core.MethodParameter
8+
import org.springframework.stereotype.Component
9+
import org.springframework.web.bind.support.WebDataBinderFactory
10+
import org.springframework.web.context.request.NativeWebRequest
11+
import org.springframework.web.method.support.HandlerMethodArgumentResolver
12+
import org.springframework.web.method.support.ModelAndViewContainer
13+
14+
@Component
15+
class OnboardingAuthMemberResolver(
16+
private val jwtTokenProvider: JwtTokenProvider,
17+
private val request: HttpServletRequest,
18+
) : HandlerMethodArgumentResolver {
19+
20+
override fun supportsParameter(parameter: MethodParameter): Boolean {
21+
return parameter.hasParameterAnnotation(OnboardingAuth::class.java) &&
22+
parameter.parameterType == AuthMemberInfo::class.java
23+
}
24+
25+
override fun resolveArgument(
26+
parameter: MethodParameter,
27+
mavContainer: ModelAndViewContainer?,
28+
webRequest: NativeWebRequest,
29+
binderFactory: WebDataBinderFactory?,
30+
): AuthMemberInfo {
31+
val token = jwtTokenProvider.extractToken(request)
32+
?: throw UnauthorizedException()
33+
34+
try {
35+
jwtTokenProvider.validateToken(token)
36+
37+
val memberId = jwtTokenProvider.getUserIdFromToken(token)
38+
?: throw UnauthorizedException()
39+
40+
return AuthMemberInfo(id = memberId)
41+
} catch (ex: ExpiredJwtException) {
42+
throw UnauthorizedException(ErrorCode.EXPIRED_TOKEN)
43+
} catch (ex: Exception) {
44+
throw UnauthorizedException()
45+
}
46+
}
47+
}

src/main/kotlin/com/moa/common/config/SwaggerConfig.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.moa.common.config
22

33
import com.moa.common.auth.Auth
4+
import com.moa.common.auth.OnboardingAuth
45
import io.swagger.v3.oas.models.Components
56
import io.swagger.v3.oas.models.OpenAPI
67
import io.swagger.v3.oas.models.info.Info
@@ -15,7 +16,9 @@ import org.springframework.context.annotation.Configuration
1516
class SwaggerConfig {
1617

1718
init {
18-
SpringDocUtils.getConfig().addAnnotationsToIgnore(Auth::class.java)
19+
SpringDocUtils.getConfig()
20+
.addAnnotationsToIgnore(OnboardingAuth::class.java)
21+
.addAnnotationsToIgnore(Auth::class.java)
1922
}
2023

2124
@Bean
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package com.moa.common.config
22

3-
import com.moa.common.auth.AuthenticatedMemberResolver
3+
import com.moa.common.auth.AuthMemberResolver
4+
import com.moa.common.auth.OnboardingAuthMemberResolver
45
import org.springframework.context.annotation.Configuration
56
import org.springframework.web.method.support.HandlerMethodArgumentResolver
67
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
78

89
@Configuration
910
class WebConfig(
10-
private val authenticatedMemberResolver: AuthenticatedMemberResolver,
11+
private val onboardingAuthMemberResolver: OnboardingAuthMemberResolver,
12+
private val authMemberResolver: AuthMemberResolver,
1113
) : WebMvcConfigurer {
1214

1315
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
14-
resolvers.add(authenticatedMemberResolver)
16+
resolvers.add(onboardingAuthMemberResolver)
17+
resolvers.add(authMemberResolver)
1518
}
1619
}

src/main/kotlin/com/moa/common/exception/ErrorCode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ enum class ErrorCode(
1010
// 4xx
1111
BAD_REQUEST("BAD_REQUEST", "잘못된 요청입니다."),
1212
UNAUTHORIZED("UNAUTHORIZED", "인증되지 않은 사용자입니다"),
13-
FORBIDDEN("FORBIDDEN", "권한이 없습니다"),
13+
ONBOARDING_INCOMPLETE("ONBOARDING_INCOMPLETE", "온보딩이 완료되지 않았습니다"),
1414
RESOURCE_NOT_FOUND("RESOURCE_NOT_FOUND", "리소스를 찾을 수 없습니다"),
1515

1616
INVALID_PAYROLL_INPUT("INVALID_PAYROLL_INPUT", "급여 입력값이 유효하지 않습니다"),

0 commit comments

Comments
 (0)