Fix encryption race condition (#119)

This commit is contained in:
Nova Fox
2021-11-08 16:43:50 -06:00
committed by GitHub
parent 5fb02e2b28
commit 849bceab58
10 changed files with 172 additions and 88 deletions
@@ -6,8 +6,6 @@ import org.dreamexposure.discal.core.`object`.network.discal.CredentialData
import org.dreamexposure.discal.core.annotations.Authentication
import org.dreamexposure.discal.core.database.DatabaseManager
import org.dreamexposure.discal.core.enums.calendar.CalendarHost
import org.dreamexposure.discal.core.logger.LOGGER
import org.dreamexposure.discal.core.utils.GlobalVal.DEFAULT
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
@@ -33,8 +31,6 @@ class GetEndpoint {
}
}
}
}.doOnError {
LOGGER.error(DEFAULT, "Access token grant failure", it)
}
}
}
@@ -15,6 +15,7 @@ import org.dreamexposure.discal.core.database.DatabaseManager
import org.dreamexposure.discal.core.entities.google.DisCalGoogleCredential
import org.dreamexposure.discal.core.exceptions.AccessRevokedException
import org.dreamexposure.discal.core.exceptions.EmptyNotAllowedException
import org.dreamexposure.discal.core.exceptions.NotFoundException
import org.dreamexposure.discal.core.logger.LOGGER
import org.dreamexposure.discal.core.utils.GlobalVal.DEFAULT
import org.dreamexposure.discal.core.utils.GlobalVal.HTTP_CLIENT
@@ -40,16 +41,21 @@ object GoogleAuth {
}
fun requestNewAccessToken(calendarData: CalendarData): Mono<CredentialData> {
val aes = AESEncryption(calendarData.privateKey)
if (!calendarData.expired()) {
return Mono.just(CredentialData(aes.decrypt(calendarData.encryptedAccessToken), calendarData.expiresAt))
}
return Mono.just(AESEncryption(calendarData.privateKey)).flatMap { aes ->
if (!calendarData.expired()) {
return@flatMap aes.decrypt(calendarData.encryptedAccessToken)
.map { CredentialData(it, calendarData.expiresAt) }
}
return doAccessTokenRequest(aes.decrypt(calendarData.encryptedRefreshToken)).flatMap { data ->
calendarData.encryptedAccessToken = aes.encrypt(data.accessToken)
calendarData.expiresAt = data.validUntil
aes.decrypt(calendarData.encryptedRefreshToken)
.flatMap(this::doAccessTokenRequest)
.flatMap { data ->
//calendarData.encryptedAccessToken = aes.encrypt(data.accessToken)
calendarData.expiresAt = data.validUntil
DatabaseManager.updateCalendar(calendarData).thenReturn(data)
aes.encrypt(data.accessToken)
.then(DatabaseManager.updateCalendar(calendarData).thenReturn(data))
}
}
}
@@ -57,19 +63,18 @@ object GoogleAuth {
return CREDENTIALS
.filter { it.credentialData.credentialNumber == credentialId }
.next()
.switchIfEmpty(Mono.error(NotFoundException()))
.flatMap { credential ->
if (!credential.expired()) {
return@flatMap Mono.just(
CredentialData(credential.getAccessToken(), credential.credentialData.expiresAt)
)
return@flatMap credential.getAccessToken()
.map { CredentialData(it, credential.credentialData.expiresAt) }
}
doAccessTokenRequest(credential.getRefreshToken()).flatMap { data ->
credential.setAccessToken(data.accessToken)
credential.credentialData.expiresAt = data.validUntil
DatabaseManager.updateCredentialData(credential.credentialData).thenReturn(data)
}
credential.getRefreshToken()
.flatMap(this::doAccessTokenRequest)
.flatMap { credential.setAccessToken(it.accessToken).thenReturn(it) }
.doOnNext { credential.credentialData.expiresAt = it.validUntil }
.flatMap { DatabaseManager.updateCredentialData(credential.credentialData).thenReturn(it) }
}.switchIfEmpty(Mono.error(EmptyNotAllowedException()))
}
@@ -104,6 +109,8 @@ object GoogleAuth {
response.body?.close()
response.close()
LOGGER.error("[Google] Int Cred bad Request: $body")
if (body.error == "invalid_grant") {
LOGGER.debug(DEFAULT, "[Google] Access to resource has been revoked")
Mono.error<CredentialData>(AccessRevokedException())
@@ -5,12 +5,14 @@ import org.dreamexposure.discal.core.`object`.google.GoogleCredentialData
import org.dreamexposure.discal.core.`object`.google.InternalGoogleAuthPoll
import org.dreamexposure.discal.core.crypto.AESEncryption
import org.dreamexposure.discal.core.database.DatabaseManager
import org.dreamexposure.discal.core.exceptions.EmptyNotAllowedException
import org.dreamexposure.discal.core.exceptions.google.GoogleAuthCancelException
import org.dreamexposure.discal.core.logger.LOGGER
import org.dreamexposure.discal.core.utils.GlobalVal
import org.dreamexposure.discal.core.wrapper.google.GoogleAuthWrapper
import org.json.JSONObject
import reactor.core.publisher.Mono
import reactor.function.TupleUtils
import java.time.Instant
@Suppress("BlockingMethodInNonBlockingContext")
@@ -57,7 +59,7 @@ object GoogleInternalAuthHandler {
//Handle access denied
LOGGER.debug(GlobalVal.DEFAULT, "[!GDC!] Access denied for credential: ${poll.credNumber}")
Mono.error<GoogleAuthCancelException>(GoogleAuthCancelException())
Mono.error(GoogleAuthCancelException())
}
GlobalVal.STATUS_BAD_REQUEST, GlobalVal.STATUS_PRECONDITION_REQUIRED -> {
//See if auth is pending, if so, just reschedule.
@@ -93,14 +95,17 @@ object GoogleInternalAuthHandler {
val aprGrant = JSONObject(responseBody)
val aes = AESEncryption(BotSettings.CREDENTIALS_KEY.get())
val encryptedRefresh = aes.encrypt(aprGrant.getString("refresh_token"))
val encryptedAccess = aes.encrypt(aprGrant.getString("access_token"))
val expiresAt = Instant.now().plusSeconds(aprGrant.getLong("expires_in"))
val refreshMono = aes.encrypt(aprGrant.getString("refresh_token"))
val accessMono = aes.encrypt(aprGrant.getString("access_token"))
val creds = GoogleCredentialData(poll.credNumber, encryptedRefresh, encryptedAccess, expiresAt)
Mono.zip(refreshMono, accessMono).flatMap<GoogleAuthCancelException?>(TupleUtils.function { refresh, access ->
val expiresAt = Instant.now().plusSeconds(aprGrant.getLong("expires_in"))
DatabaseManager.updateCredentialData(creds)
.then(Mono.error(GoogleAuthCancelException()))
val creds = GoogleCredentialData(poll.credNumber, refresh, access, expiresAt)
DatabaseManager.updateCredentialData(creds)
.then(Mono.error(GoogleAuthCancelException()))
}).onErrorResume(EmptyNotAllowedException::class.java) { Mono.error(GoogleAuthCancelException()) }
}
else -> {
//Unknown network error...