Skip to content

Commit

Permalink
I think all the code is done, now to debug
Browse files Browse the repository at this point in the history
  • Loading branch information
NovaFox161 committed Sep 1, 2023
1 parent f0bd75c commit a983bda
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.dreamexposure.discal.cam.business.cronjob

import com.fasterxml.jackson.databind.ObjectMapper
import kotlinx.coroutines.reactor.mono
import kotlinx.serialization.encodeToString
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
Expand All @@ -14,7 +14,6 @@ import org.dreamexposure.discal.core.`object`.rest.HeartbeatType
import org.dreamexposure.discal.core.utils.GlobalVal
import org.dreamexposure.discal.core.utils.GlobalVal.HTTP_CLIENT
import org.dreamexposure.discal.core.utils.GlobalVal.JSON
import org.dreamexposure.discal.core.utils.GlobalVal.JSON_FORMAT
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.stereotype.Component
Expand All @@ -23,7 +22,9 @@ import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers

@Component
class HeartbeatCronJob: ApplicationRunner {
class HeartbeatCronJob(
private val objectMapper: ObjectMapper,
): ApplicationRunner {
private final val apiUrl = Config.URL_API.getString()

override fun run(args: ApplicationArguments?) {
Expand All @@ -39,7 +40,7 @@ class HeartbeatCronJob: ApplicationRunner {

val request = Request.Builder()
.url("$apiUrl/v2/status/heartbeat")
.post(JSON_FORMAT.encodeToString(requestBody).toRequestBody(JSON))
.post(objectMapper.writeValueAsString(requestBody).toRequestBody(JSON))
.header("Authorization", Config.SECRET_DISCAL_API_KEY.getString())
.header("Content-Type", "application/json")
.build()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.dreamexposure.discal.cam.discord

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.coroutines.reactor.awaitSingle
import okhttp3.FormBody
import okhttp3.Request
import org.dreamexposure.discal.cam.json.discord.AccessTokenResponse
Expand All @@ -8,112 +11,106 @@ import org.dreamexposure.discal.core.config.Config
import org.dreamexposure.discal.core.exceptions.AuthenticationException
import org.dreamexposure.discal.core.utils.GlobalVal
import org.dreamexposure.discal.core.utils.GlobalVal.HTTP_CLIENT
import org.dreamexposure.discal.core.utils.GlobalVal.JSON_FORMAT
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers

@Component
class DiscordOauthHandler {
class DiscordOauthHandler(
private val objectMapper: ObjectMapper,
) {
private val cdnUrl = "https://cdn.discordapp.com"
private val redirectUrl = Config.URL_DISCORD_REDIRECT.getString()
private val clientId = Config.DISCORD_APP_ID.getString()
private val clientSecret = Config.SECRET_CLIENT_SECRET.getString()

fun doTokenExchange(code: String): Mono<AccessTokenResponse> {
return Mono.fromCallable {
val body = FormBody.Builder()
.addEncoded("client_id", clientId)
.addEncoded("client_secret", clientSecret)
.addEncoded("grant_type", "authorization_code")
.addEncoded("code", code)
.addEncoded("redirect_uri", redirectUrl)
.build()

val tokenExchangeRequest = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/token")
.post(body)
.header("Content-Type", "application/x-www-form-urlencoded")
.build()

HTTP_CLIENT.newCall(tokenExchangeRequest).execute()
}.subscribeOn(Schedulers.boundedElastic()).flatMap { response ->
if (response.isSuccessful) {
//Transform body into our object
val responseBody = JSON_FORMAT.decodeFromString(AccessTokenResponse.serializer(),
response.body!!.string())
// Close body to avoid mem leak
response.body?.close()

Mono.just(responseBody)
} else {
Mono.error(AuthenticationException("Discord authorization grant error"))
}
suspend fun doTokenExchange(code: String): AccessTokenResponse {
val body = FormBody.Builder()
.addEncoded("client_id", clientId)
.addEncoded("client_secret", clientSecret)
.addEncoded("grant_type", "authorization_code")
.addEncoded("code", code)
.addEncoded("redirect_uri", redirectUrl)
.build()
val tokenExchangeRequest = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/token")
.post(body)
.header("Content-Type", "application/x-www-form-urlencoded")
.build()

val response = Mono.fromCallable(HTTP_CLIENT.newCall(tokenExchangeRequest)::execute)
.subscribeOn(Schedulers.boundedElastic())
.awaitSingle()

if (response.isSuccessful) {
val responseBody = objectMapper.readValue<AccessTokenResponse>(response.body!!.string())
response.close()

return responseBody
} else {
throw AuthenticationException("Discord authorization grant error")
}
}

fun doTokenRefresh(refreshToken: String): Mono<AccessTokenResponse> {
return Mono.fromCallable {
val body = FormBody.Builder()
.addEncoded("client_id", clientId)
.addEncoded("client_secret", clientSecret)
.addEncoded("grant_type", "refresh_token")
.addEncoded("refresh_token", refreshToken)
.build()

val tokenExchangeRequest = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/token")
.post(body)
.header("Content-Type", "application/x-www-form-urlencoded")
.build()

HTTP_CLIENT.newCall(tokenExchangeRequest).execute()
}.subscribeOn(Schedulers.boundedElastic()).flatMap { response ->
if (response.isSuccessful) {
//Transform body into our object
val responseBody = JSON_FORMAT.decodeFromString(AccessTokenResponse.serializer(),
response.body!!.string())
// Close body to avoid mem leak
response.body?.close()

Mono.just(responseBody)
} else {
Mono.error(AuthenticationException("Discord refresh token error"))
}
suspend fun doTokenRefresh(refreshToken: String): AccessTokenResponse {
val body = FormBody.Builder()
.addEncoded("client_id", clientId)
.addEncoded("client_secret", clientSecret)
.addEncoded("grant_type", "refresh_token")
.addEncoded("refresh_token", refreshToken)
.build()

val tokenExchangeRequest = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/token")
.post(body)
.header("Content-Type", "application/x-www-form-urlencoded")
.build()

val response = Mono.fromCallable(HTTP_CLIENT.newCall(tokenExchangeRequest)::execute)
.subscribeOn(Schedulers.boundedElastic())
.awaitSingle()

if (response.isSuccessful) {
val responseBody = objectMapper.readValue<AccessTokenResponse>(response.body!!.string())
response.close()

return responseBody
} else {
throw AuthenticationException("Discord refresh token error")
}
}

fun getOauthInfo(accessToken: String): Mono<AuthorizationInfo> {
return Mono.fromCallable {
val request = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/@me")
.get()
.header("Authorization", "Bearer $accessToken")
.build()

HTTP_CLIENT.newCall(request).execute()
}.subscribeOn(Schedulers.boundedElastic()).flatMap { response ->
if (response.isSuccessful) {
//Transform body into our object
var responseBody = JSON_FORMAT.decodeFromString(AuthorizationInfo.serializer(),
response.body!!.string())

//Convert avatar hash to full URL
val avatar = if (responseBody.user!!.avatar != null) {
val userId = responseBody.user!!.id.asString()
val avatarHash = responseBody.user!!.avatar
"$cdnUrl/avatars/$userId/$avatarHash.png"
} else {
// No avatar present, get discord's default user avatar
val discrim = responseBody.user!!.discriminator
"$cdnUrl/embed/avatars/${discrim.toInt() % 5}.png"
}
responseBody = responseBody.copy(user = responseBody.user!!.copy(avatar = avatar))

Mono.just(responseBody)

suspend fun getOauthInfo(accessToken: String): AuthorizationInfo {
val request = Request.Builder()
.url("${GlobalVal.discordApiUrl}/oauth2/@me")
.get()
.header("Authorization", "Bearer $accessToken")
.build()

val response = Mono.fromCallable(HTTP_CLIENT.newCall(request)::execute)
.subscribeOn(Schedulers.boundedElastic())
.awaitSingle()

if (response.isSuccessful) {
var responseBody = objectMapper.readValue<AuthorizationInfo>(response.body!!.string())
response.close()

//Convert avatar hash to full URL
val avatar = if (responseBody.user!!.avatar != null) {
val userId = responseBody.user!!.id.asString()
val avatarHash = responseBody.user!!.avatar
"$cdnUrl/avatars/$userId/$avatarHash.png"
} else {
Mono.error(AuthenticationException("Discord auth info error"))
// No avatar present, get discord's default user avatar
val discrim = responseBody.user!!.discriminator
"$cdnUrl/embed/avatars/${discrim.toInt() % 5}.png"
}
responseBody = responseBody.copy(user = responseBody.user!!.copy(avatar = avatar))

return responseBody
} else {
throw AuthenticationException("Discord auth info error")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.dreamexposure.discal.cam.endpoints.v1.oauth2

import kotlinx.coroutines.reactor.awaitSingle
import org.dreamexposure.discal.cam.business.OauthStateService
import org.dreamexposure.discal.cam.discord.DiscordOauthHandler
import org.dreamexposure.discal.cam.json.discal.LoginResponse
Expand Down Expand Up @@ -57,8 +56,8 @@ class DiscordOauthEndpoint(
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid state")
}

val dTokens = discordOauthHandler.doTokenExchange(body.code).awaitSingle()
val authInfo = discordOauthHandler.getOauthInfo(dTokens.accessToken).awaitSingle()
val dTokens = discordOauthHandler.doTokenExchange(body.code)
val authInfo = discordOauthHandler.getOauthInfo(dTokens.accessToken)
val apiToken = KeyGenerator.csRandomAlphaNumericString(64)
val session = WebSession(
apiToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.dreamexposure.discal.cam.google

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.google.api.client.http.HttpStatusCodes.STATUS_CODE_BAD_REQUEST
import com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK
import kotlinx.coroutines.reactor.awaitSingle
Expand All @@ -20,7 +22,6 @@ import org.dreamexposure.discal.core.`object`.network.discal.CredentialData
import org.dreamexposure.discal.core.`object`.new.Calendar
import org.dreamexposure.discal.core.utils.GlobalVal.DEFAULT
import org.dreamexposure.discal.core.utils.GlobalVal.HTTP_CLIENT
import org.dreamexposure.discal.core.utils.GlobalVal.JSON_FORMAT
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
Expand All @@ -30,6 +31,7 @@ import java.time.Instant
class GoogleAuth(
private val credentialService: CredentialService,
private val calendarService: CalendarService,
private val objectMapper: ObjectMapper,
) {
private final val aes: AESEncryption = AESEncryption(Config.SECRET_GOOGLE_CREDENTIAL_KEY.getString())

Expand Down Expand Up @@ -88,15 +90,13 @@ class GoogleAuth(

return when (response.code) {
STATUS_CODE_OK -> {
val body = JSON_FORMAT.decodeFromString(RefreshData.serializer(), response.body!!.string())
response.body?.close()
val body = objectMapper.readValue<RefreshData>(response.body!!.string())
response.close()

CredentialData(body.accessToken, Instant.now().plusSeconds(body.expiresIn.toLong()))
}
STATUS_CODE_BAD_REQUEST -> {
val body = JSON_FORMAT.decodeFromString(ErrorData.serializer(), response.body!!.string())
response.body?.close()
val body = objectMapper.readValue<ErrorData>(response.body!!.string())
response.close()

LOGGER.error("[Google] Access Token Request: $body")
Expand All @@ -112,7 +112,6 @@ class GoogleAuth(
else -> {
// Failed to get OK. Send error info
LOGGER.error(DEFAULT, "[Google] Error requesting new access token | ${response.code} ${response.message} | ${response.body?.string()}")
response.body?.close()
response.close()

null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package org.dreamexposure.discal.cam.json.discal

import kotlinx.serialization.Serializable

@Serializable
data class LoginResponse(

val link: String
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package org.dreamexposure.discal.cam.json.discal

import kotlinx.serialization.Serializable

@Serializable
data class TokenRequest(
val state: String,

val code: String,
)
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package org.dreamexposure.discal.cam.json.discal

import kotlinx.serialization.Serializable
import org.dreamexposure.discal.cam.json.discord.SimpleUserData
import org.dreamexposure.discal.core.serializers.InstantAsStringSerializer
import java.time.Instant

@Serializable
data class TokenResponse(
val token: String,

@Serializable(with = InstantAsStringSerializer::class)
val expires: Instant,

val user: SimpleUserData,
)
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package org.dreamexposure.discal.cam.json.discord

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import com.fasterxml.jackson.annotation.JsonProperty

@Serializable
data class AccessTokenResponse(
@SerialName("access_token")
@JsonProperty("access_token")
val accessToken: String,

@SerialName("token_type")
@JsonProperty("token_type")
val type: String,

@SerialName("expires_in")
@JsonProperty("expires_in")
val expiresIn: Long,

@SerialName("refresh_token")
@JsonProperty("refresh_token")
val refreshToken: String,

val scope: String,
Expand Down
Loading

0 comments on commit a983bda

Please sign in to comment.