Skip to content

Commit 20e1959

Browse files
authored
release: 2.0.9 (#375)
2 parents bf46a86 + 0931769 commit 20e1959

29 files changed

Lines changed: 411 additions & 36 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-cache"
910
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.slf4j.LoggerFactory
8+
import org.springframework.stereotype.Component
9+
10+
@Aspect
11+
@Component
12+
class RequiredUserEntryPointAspect {
13+
14+
private val logger = LoggerFactory.getLogger(this::class.simpleName)
15+
16+
@Around("@annotation(requiredUserEntryPoints)")
17+
fun validate(
18+
proceedingJoinPoint: ProceedingJoinPoint,
19+
requiredUserEntryPoints: RequiredUserEntryPoints,
20+
): Any? {
21+
return withUserEntryPointValidation(
22+
expectedUserEntryPoints = requiredUserEntryPoints.expected,
23+
onSuccess = { proceedingJoinPoint.proceed() },
24+
failMessage = {
25+
val message =
26+
"ExpectedUserEntryPoints: \"${requiredUserEntryPoints.expected}\" but request user entryPoint is \"$it\""
27+
logger.info("[RequiredUserEntryPointAspect] $message")
28+
message
29+
}
30+
)
31+
}
32+
}
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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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: (UserEntryPoint?) -> String = { userEntryPoint ->
13+
"ExpectedUserEntryPoints: \"$expectedUserEntryPoints\" but request user entryPoint is \"$userEntryPoint\""
14+
}
15+
): T {
16+
val userEntryPoint = runCatching {
17+
val userEntryPointString = internalAuth.getUserEntryPoint {
18+
throw IllegalArgumentException(failMessage.invoke(null))
19+
}
20+
UserEntryPoint.valueOf(userEntryPointString)
21+
}.getOrElse {
22+
throw IllegalArgumentException(failMessage.invoke(null))
23+
}
24+
25+
require(
26+
expectedUserEntryPoints.contains(UserEntryPoint.ANY)
27+
|| userEntryPoint in expectedUserEntryPoints
28+
) { failMessage.invoke(userEntryPoint) }
29+
30+
return onSuccess.invoke()
31+
}
32+
33+
@Component
34+
class UserEntryPointValidationExtensionBeanInjector(
35+
internalAuth: InternalAuth,
36+
) {
37+
38+
init {
39+
UserEntryPointValidationExtension.internalAuth = internalAuth
40+
}
41+
}
42+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class MDCFilter(
2929
MDC.put(TRACE_ID, traceId)
3030
MDC.put(PATH, request.requestURI)
3131
internalAuth.findUserId()?.let { MDC.put(USER_ID, it.toString()) }
32+
internalAuth.findUserEntryPoint()?.let { MDC.put(USER_ENTRY_POINT, it) }
3233

3334
val elapsedTime = measureTimeMillis {
3435
filterChain.doFilter(request, response)
@@ -43,12 +44,14 @@ class MDCFilter(
4344
MDC.remove(ELAPSED_TIME)
4445
MDC.remove(PATH)
4546
MDC.remove(USER_ID)
47+
MDC.remove(USER_ENTRY_POINT)
4648
}
4749
}
4850

4951
companion object {
5052
const val USER_ID = "userId"
5153
const val TRACE_ID = "traceId"
54+
const val USER_ENTRY_POINT = "userEntryPoint"
5255
const val ELAPSED_TIME = "elapsedTime"
5356
const val PATH = "path"
5457

src/main/kotlin/org/gitanimals/guild/controller/GuildController.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import jakarta.servlet.http.HttpServletResponse
44
import org.gitanimals.core.AuthorizationException
55
import org.gitanimals.core.ErrorResponse
66
import org.gitanimals.core.FieldType
7+
import org.gitanimals.core.auth.RequiredUserEntryPoints
8+
import org.gitanimals.core.auth.UserEntryPoint
79
import org.gitanimals.guild.app.*
810
import org.gitanimals.guild.app.request.CreateGuildRequest
911
import org.gitanimals.guild.app.response.*
@@ -35,13 +37,15 @@ class GuildController(
3537

3638
@ResponseStatus(HttpStatus.OK)
3739
@PostMapping("/guilds")
40+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
3841
fun createGuild(
3942
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
4043
@RequestBody createGuildRequest: CreateGuildRequest,
4144
) = createGuildFacade.createGuild(token, createGuildRequest)
4245

4346
@ResponseStatus(HttpStatus.OK)
4447
@PostMapping("/guilds/{guildId}")
48+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
4549
fun joinGuild(
4650
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
4751
@PathVariable("guildId") guildId: Long,
@@ -51,6 +55,7 @@ class GuildController(
5155

5256
@ResponseStatus(HttpStatus.OK)
5357
@PostMapping("/guilds/{guildId}/accepts")
58+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
5459
fun acceptJoinGuild(
5560
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
5661
@PathVariable("guildId") guildId: Long,
@@ -60,6 +65,7 @@ class GuildController(
6065

6166
@ResponseStatus(HttpStatus.OK)
6267
@DeleteMapping("/guilds/{guildId}")
68+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
6369
fun kickFromGuild(
6470
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
6571
@PathVariable("guildId") guildId: Long,
@@ -69,6 +75,7 @@ class GuildController(
6975

7076
@ResponseStatus(HttpStatus.OK)
7177
@PatchMapping("/guilds/{guildId}")
78+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
7279
fun changeGuild(
7380
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
7481
@PathVariable("guildId") guildId: Long,
@@ -89,6 +96,7 @@ class GuildController(
8996

9097
@ResponseStatus(HttpStatus.OK)
9198
@GetMapping("/guilds")
99+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
92100
fun getAllJoinedGuild(
93101
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
94102
): GuildsResponse {
@@ -130,6 +138,7 @@ class GuildController(
130138
}
131139

132140
@PostMapping("/guilds/{guildId}/personas")
141+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
133142
fun changeMainPersona(
134143
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
135144
@PathVariable("guildId") guildId: Long,
@@ -141,12 +150,14 @@ class GuildController(
141150
)
142151

143152
@DeleteMapping("/guilds/{guildId}/leave")
153+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
144154
fun leaveGuild(
145155
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
146156
@PathVariable("guildId") guildId: Long,
147157
) = leaveGuildFacade.leave(guildId)
148158

149159
@PostMapping("guilds/{guildId}/deny")
160+
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
150161
fun deny(
151162
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String,
152163
@PathVariable("guildId") guildId: Long,

src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.gitanimals.render.app
22

33
import org.gitanimals.core.Mode
44
import org.gitanimals.core.filter.MDCFilter.Companion.TRACE_ID
5+
import org.gitanimals.render.domain.EntryPoint
56
import org.gitanimals.render.domain.User
67
import org.gitanimals.render.domain.UserService
78
import org.gitanimals.render.domain.event.NewUserCreated
@@ -18,46 +19,71 @@ class AnimationFacade(
1819
private val contributionApi: ContributionApi,
1920
private val sagaManager: SagaManager,
2021
private val eventPublisher: ApplicationEventPublisher,
22+
private val githubOpenApi: GithubOpenApi,
2123
) {
2224

2325
fun getFarmAnimation(username: String): String {
2426
return when (userService.existsByName(username)) {
2527
true -> {
28+
setUserAuthInfoIfNotSet(username)
29+
2630
val svgAnimation = userService.getFarmAnimationByUsername(username)
2731
eventPublisher.publishEvent(Visited(username, MDC.get(TRACE_ID)))
2832
svgAnimation
2933
}
3034

3135
false -> {
3236
val user = createNewUser(username)
33-
sagaManager.startSync(NewUserCreated(user.id, user.name))
34-
userService.getFarmAnimationByUsername(user.name)
37+
sagaManager.startSync(NewUserCreated(user.id, user.getName()))
38+
userService.getFarmAnimationByUsername(user.getName())
3539
}
3640
}
3741
}
3842

3943
fun getLineAnimation(username: String, personaId: Long, mode: Mode): String {
4044
return when (userService.existsByName(username)) {
4145
true -> {
46+
setUserAuthInfoIfNotSet(username)
47+
4248
val svgAnimation = userService.getLineAnimationByUsername(username, personaId, mode)
4349
eventPublisher.publishEvent(Visited(username, MDC.get(TRACE_ID)))
4450
svgAnimation
4551
}
4652

4753
false -> {
4854
val user = createNewUser(username)
49-
sagaManager.startSync(NewUserCreated(user.id, user.name))
50-
userService.getLineAnimationByUsername(user.name, personaId, mode)
55+
sagaManager.startSync(NewUserCreated(user.id, user.getName()))
56+
userService.getLineAnimationByUsername(user.getName(), personaId, mode)
5157
}
5258
}
5359
}
5460

61+
private fun setUserAuthInfoIfNotSet(username: String) {
62+
val user = userService.getUserByName(username)
63+
64+
if (user.isAuthInfoSet().not()) {
65+
val githubUserAuthInfo = githubOpenApi.getGithubUser(user.getName())
66+
userService.setAuthInfo(
67+
name = user.getName(),
68+
entryPoint = EntryPoint.GITHUB,
69+
githubUserAuthInfo.id,
70+
)
71+
}
72+
}
73+
5574
fun createNewUser(username: String): User {
5675
return runCatching {
76+
val githubUserResponse = githubOpenApi.getGithubUser(username)
5777
val contributionYears = contributionApi.getAllContributionYears(username)
5878
val contributionCountPerYear =
5979
contributionApi.getContributionCount(username, contributionYears)
60-
userService.createNewUser(username, contributionCountPerYear)
80+
81+
userService.createNewUser(
82+
name = username,
83+
entryPoint = EntryPoint.GITHUB,
84+
authenticationId = githubUserResponse.id,
85+
contributions = contributionCountPerYear,
86+
)
6187
}.getOrElse {
6288
require(it !is RestClientException) { "Cannot create new user from username \"$username\"" }
6389
throw it

0 commit comments

Comments
 (0)