Skip to content

Commit 23c70ab

Browse files
committed
feat: User EntryPoint에 APPLE을 추가한다
1 parent 903fe75 commit 23c70ab

28 files changed

Lines changed: 253 additions & 39 deletions

gradle/spring.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ jar {
55
dependencies {
66
implementation "org.springframework.boot:spring-boot-starter"
77
implementation "org.springframework.boot:spring-boot-starter-web"
8+
implementation "org.springframework.boot:spring-boot-starter-aop"
89
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
910
implementation "org.springframework.boot:spring-boot-starter-data-redis"
1011
implementation "org.springframework.boot:spring-boot-starter-actuator"

src/main/kotlin/org/gitanimals/core/auth/InternalAuth.kt

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.gitanimals.core.auth
33
import jakarta.servlet.http.HttpServletRequest
44
import org.gitanimals.core.AUTHORIZATION_EXCEPTION
55
import org.gitanimals.core.AuthorizationException
6+
import org.gitanimals.core.filter.MDCFilter.Companion.USER_ENTRY_POINT
67
import org.gitanimals.core.filter.MDCFilter.Companion.USER_ID
78
import org.slf4j.LoggerFactory
89
import org.slf4j.MDC
@@ -29,7 +30,7 @@ class InternalAuth(
2930
SecretKeySpec(decodedKey, "AES")
3031
}
3132

32-
fun getUserId(throwOnFailure: () -> Unit = throwCannotGetUserId): Long {
33+
fun getUserId(throwOnFailure: () -> Unit = throwCannotGetUserInfo): Long {
3334
val userId = findUserId()
3435

3536
if (userId == null) {
@@ -80,6 +81,36 @@ class InternalAuth(
8081
return userId
8182
}
8283

84+
fun getUserEntryPoint(throwOnFailure: () -> Unit = throwCannotGetUserInfo): String {
85+
val entryPoint = findUserEntryPoint()
86+
87+
if (entryPoint == null) {
88+
throwOnFailure.invoke()
89+
}
90+
91+
return entryPoint ?: throw AUTHORIZATION_EXCEPTION
92+
}
93+
94+
fun findUserEntryPoint(): String? {
95+
val entryPointInMdc = runCatching {
96+
MDC.get(USER_ENTRY_POINT)
97+
}.getOrNull()
98+
99+
if (entryPointInMdc != null) {
100+
return entryPointInMdc
101+
}
102+
103+
httpServletRequest.getHeader(INTERNAL_ENTRY_POINT_KEY)?.let {
104+
return it
105+
}
106+
107+
val token: String = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION) ?: return null
108+
109+
return runCatching {
110+
internalAuthClient.getUserByToken(token).entryPoint
111+
}.getOrNull()
112+
}
113+
83114
private fun decrypt(iv: ByteArray, secret: ByteArray): Long {
84115
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
85116
val spec = GCMParameterSpec(128, iv)
@@ -108,8 +139,9 @@ class InternalAuth(
108139
companion object {
109140
const val INTERNAL_AUTH_IV_KEY = "Internal-Auth-Iv"
110141
const val INTERNAL_AUTH_SECRET_KEY = "Internal-Auth-Secret"
142+
const val INTERNAL_ENTRY_POINT_KEY = "Internal-Entry-Point"
111143

112-
private val throwCannotGetUserId: () -> Unit = {
144+
private val throwCannotGetUserInfo: () -> Unit = {
113145
throw AUTHORIZATION_EXCEPTION
114146
}
115147
}

src/main/kotlin/org/gitanimals/core/auth/InternalAuthClient.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fun interface InternalAuthClient {
2222

2323
data class UserResponse(
2424
val id: String,
25+
val entryPoint: String,
2526
)
2627
}
2728

src/main/kotlin/org/gitanimals/core/auth/InternalAuthRequestInterceptor.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.gitanimals.core.auth
22

3+
import org.gitanimals.core.filter.MDCFilter.Companion.USER_ENTRY_POINT
34
import org.gitanimals.core.filter.MDCFilter.Companion.USER_ID
45
import org.slf4j.MDC
56
import org.springframework.http.HttpRequest
@@ -23,6 +24,10 @@ class InternalAuthRequestInterceptor(
2324
MDC.get(USER_ID).toLong()
2425
}.getOrNull()
2526

27+
val userEntryPoint = runCatching {
28+
MDC.get(USER_ENTRY_POINT)
29+
}.getOrNull()
30+
2631
if (userId != null) {
2732
val encrypt = internalAuth.encrypt(userId = userId)
2833

@@ -34,6 +39,10 @@ class InternalAuthRequestInterceptor(
3439
InternalAuth.INTERNAL_AUTH_IV_KEY,
3540
Base64.getEncoder().encodeToString(encrypt.iv),
3641
)
42+
request.headers.add(
43+
InternalAuth.INTERNAL_ENTRY_POINT_KEY,
44+
userEntryPoint,
45+
)
3746
}
3847

3948
return execution.execute(request, body)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.gitanimals.core.auth
2+
3+
import org.aspectj.lang.ProceedingJoinPoint
4+
import org.aspectj.lang.annotation.Around
5+
import org.aspectj.lang.annotation.Aspect
6+
import org.gitanimals.core.auth.UserEntryPointValidationExtension.withUserEntryPointValidation
7+
import org.springframework.stereotype.Component
8+
9+
@Aspect
10+
@Component
11+
class RequiredUserEntryPointAspect {
12+
13+
@Around("@annotation(requiredUserEntryPoints)")
14+
fun validate(
15+
proceedingJoinPoint: ProceedingJoinPoint,
16+
requiredUserEntryPoints: RequiredUserEntryPoints,
17+
): Any? {
18+
return withUserEntryPointValidation(
19+
expectedUserEntryPoints = requiredUserEntryPoints.expected,
20+
onSuccess = { proceedingJoinPoint.proceed() },
21+
)
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.gitanimals.core.auth
2+
3+
@Target(AnnotationTarget.FUNCTION)
4+
@Retention(AnnotationRetention.RUNTIME)
5+
annotation class RequiredUserEntryPoints(
6+
val expected: Array<UserEntryPoint>,
7+
)
8+
9+
enum class UserEntryPoint {
10+
ANY,
11+
GITHUB,
12+
APPLE,
13+
;
14+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.gitanimals.core.auth
2+
3+
import org.springframework.stereotype.Component
4+
5+
object UserEntryPointValidationExtension {
6+
7+
private lateinit var internalAuth: InternalAuth
8+
9+
fun <T> withUserEntryPointValidation(
10+
expectedUserEntryPoints: Array<UserEntryPoint>,
11+
onSuccess: () -> T,
12+
failMessage: () -> String = {
13+
"\"$expectedUserEntryPoints\" User cannot pass."
14+
}
15+
): T {
16+
val userEntryPoint = internalAuth.getUserEntryPoint {
17+
throw IllegalArgumentException(failMessage.invoke())
18+
}
19+
20+
require(
21+
expectedUserEntryPoints.contains(UserEntryPoint.ANY) ||
22+
UserEntryPoint.valueOf(userEntryPoint) in expectedUserEntryPoints
23+
) {
24+
failMessage.invoke()
25+
}
26+
27+
return onSuccess.invoke()
28+
}
29+
30+
@Component
31+
class UserEntryPointValidationExtensionBeanInjector(
32+
internalAuth: InternalAuth,
33+
) {
34+
35+
init {
36+
UserEntryPointValidationExtension.internalAuth = internalAuth
37+
}
38+
}
39+
}

src/main/kotlin/org/gitanimals/core/filter/MDCFilter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class MDCFilter(
3232
MDC.put(PATH, uri)
3333
if (uri != "/users") {
3434
internalAuth.findUserId()?.let { MDC.put(USER_ID, it.toString()) }
35+
internalAuth.findUserEntryPoint()?.let { MDC.put(USER_ENTRY_POINT, it) }
3536
}
3637

3738
val elapsedTime = measureTimeMillis {
@@ -47,12 +48,14 @@ class MDCFilter(
4748
MDC.remove(ELAPSED_TIME)
4849
MDC.remove(PATH)
4950
MDC.remove(USER_ID)
51+
MDC.remove(USER_ENTRY_POINT)
5052
}
5153
}
5254

5355
companion object {
5456
const val USER_ID = "userId"
5557
const val TRACE_ID = "traceId"
58+
const val USER_ENTRY_POINT = "userEntryPoint"
5659
const val ELAPSED_TIME = "elapsedTime"
5760
const val PATH = "path"
5861

src/main/kotlin/org/gitanimals/coupon/controller/CouponController.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.gitanimals.coupon.controller
22

3+
import org.gitanimals.core.auth.RequiredUserEntryPoints
4+
import org.gitanimals.core.auth.UserEntryPoint
35
import org.gitanimals.coupon.app.CouponFacade
46
import org.gitanimals.coupon.controller.request.CouponRequest
57
import org.gitanimals.coupon.controller.response.CouponResponses
@@ -14,13 +16,15 @@ class CouponController(
1416

1517
@PostMapping("/coupons")
1618
@ResponseStatus(HttpStatus.OK)
19+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
1720
fun useCoupon(
1821
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
1922
@RequestBody couponRequest: CouponRequest,
2023
) = couponFacade.useCoupon(token, couponRequest.code, couponRequest.dynamic)?.toResponse()
2124

2225
@GetMapping("/coupons/users")
2326
@ResponseStatus(HttpStatus.OK)
27+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
2428
fun getUsedCoupons(
2529
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
2630
): CouponResponses = CouponResponses.from(couponFacade.getUsedCoupons())

src/main/kotlin/org/gitanimals/gotcha/controller/GotchaController.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.gitanimals.gotcha.controller
22

3+
import org.gitanimals.core.auth.RequiredUserEntryPoints
4+
import org.gitanimals.core.auth.UserEntryPoint
35
import org.gitanimals.gotcha.app.GotchaFacadeV3
46
import org.gitanimals.gotcha.app.response.GotchaResponseV3
57
import org.gitanimals.gotcha.controller.response.ErrorResponse
@@ -13,6 +15,7 @@ class GotchaController(
1315
private val gotchaFacadeV3: GotchaFacadeV3,
1416
) {
1517

18+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
1619
@PostMapping(path = ["/gotchas"], headers = ["Api-Version=3"])
1720
fun gotchaV3(
1821
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,

0 commit comments

Comments
 (0)