Skip to content

Commit edfde41

Browse files
committed
load function fixes
1 parent df7f919 commit edfde41

File tree

2 files changed

+134
-28
lines changed
  • plugins/core/jetbrains-community
    • src/software/aws/toolkits/jetbrains/core/credentials/sso
    • tst/software/aws/toolkits/jetbrains/core/credentials/sso

2 files changed

+134
-28
lines changed

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/sso/DiskCache.kt

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,27 @@ class DiskCache(
109109

110110
override fun loadClientRegistration(cacheKey: ClientRegistrationCacheKey): ClientRegistration? {
111111
LOG.debug { "loadClientRegistration for $cacheKey" }
112-
val inputStream = clientRegistrationCache(cacheKey).tryInputStreamIfExists()
113-
?: // try to load from in memory cache
114-
return InMemoryCache.get(clientRegistrationCache(cacheKey).toString())?.let { data ->
115-
ByteArrayInputStream(data).use { memoryStream ->
116-
loadClientRegistration(memoryStream)
117-
}
118-
} ?: run {
119-
val stage = LoadCredentialStage.ACCESS_FILE
120-
LOG.warn { "Failed to load Client Registration: cache file does not exist" }
121-
AuthTelemetry.modifyConnection(
122-
action = "Load cache file",
123-
source = "loadClientRegistration",
124-
result = Result.Failed,
125-
reason = "Failed to load Client Registration",
126-
reasonDesc = "Load Step:$stage failed. Unable to load file"
127-
)
128-
null
112+
val cacheFile = clientRegistrationCache(cacheKey)
113+
// try InMemoryCacheFirst in case of stale registration on full disk
114+
InMemoryCache.get(cacheFile.toString())?.let { data ->
115+
ByteArrayInputStream(data).use { memoryStream ->
116+
return loadClientRegistration(memoryStream)
129117
}
118+
}
119+
120+
val inputStream = cacheFile.tryInputStreamIfExists()
121+
if (inputStream == null) {
122+
val stage = LoadCredentialStage.ACCESS_FILE
123+
LOG.warn { "Failed to load Client Registration: cache file does not exist" }
124+
AuthTelemetry.modifyConnection(
125+
action = "Load cache file",
126+
source = "loadClientRegistration",
127+
result = Result.Failed,
128+
reason = "Failed to load Client Registration",
129+
reasonDesc = "Load Step:$stage failed. Unable to load file"
130+
)
131+
return null
132+
}
130133
return loadClientRegistration(inputStream)
131134
}
132135

@@ -172,21 +175,22 @@ class DiskCache(
172175
}
173176
}
174177

178+
175179
override fun loadAccessToken(cacheKey: AccessTokenCacheKey): AccessToken? {
176180
LOG.debug { "loadAccessToken for $cacheKey" }
177181
val cacheFile = accessTokenCache(cacheKey)
178-
// If file exists, returns InputStream, if not returns null
179-
return cacheFile.tryInputStreamIfExists()
180-
// try to load and parse access token, returns AccessToken or null if expired
181-
?.let { loadAccessToken(it) }
182-
// If file doesn't exist or loadAccessToken failed, try in-memory cache
183-
?: InMemoryCache.get(cacheFile.toString())?.let { data ->
184-
// If in-memory cache has data, create stream and try to load token
185-
ByteArrayInputStream(data).use { memoryStream ->
186-
loadAccessToken(memoryStream)
187-
}
188-
// If both file system and in-memory cache attempts fail, returns null
182+
// try InMemoryCacheFirst in case of stale token on full disk
183+
InMemoryCache.get(cacheFile.toString())?.let { data ->
184+
ByteArrayInputStream(data).use { memoryStream ->
185+
return loadAccessToken(memoryStream)
189186
}
187+
}
188+
189+
val inputStream = cacheFile.tryInputStreamIfExists() ?: return null
190+
191+
val token = loadAccessToken(inputStream)
192+
193+
return token
190194
}
191195

192196
override fun saveAccessToken(cacheKey: AccessTokenCacheKey, accessToken: AccessToken) {

plugins/core/jetbrains-community/tst/software/aws/toolkits/jetbrains/core/credentials/sso/DiskCacheTest.kt

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,4 +829,106 @@ class DiskCacheTest {
829829
val loadedRegistration = sut.loadClientRegistration(key)
830830
assertNull(loadedRegistration)
831831
}
832+
833+
@Test
834+
fun `test client registration update with disk error falls back to memory cache`() {
835+
// Create a cache key and initial registration
836+
val key = PKCEClientRegistrationCacheKey(
837+
issuerUrl = ssoUrl,
838+
scopes = scopes,
839+
region = ssoRegion,
840+
clientType = "public",
841+
grantTypes = listOf("authorization_code", "refresh_token"),
842+
redirectUris = listOf("http://127.0.0.1/oauth/callback")
843+
)
844+
val initialRegistration = PKCEClientRegistration(
845+
"stale_id",
846+
"stale_client_secret",
847+
Instant.now().plusSeconds(3600),
848+
scopes,
849+
ssoUrl,
850+
ssoRegion,
851+
"public",
852+
listOf("authorization_code", "refresh_token"),
853+
listOf("http://127.0.0.1/oauth/callback")
854+
)
855+
856+
// Save initial registration (should save to disk)
857+
sut.saveClientRegistration(key, initialRegistration)
858+
859+
// Verify initial save
860+
val loadedInitial = sut.loadClientRegistration(key)
861+
assertNotNull(loadedInitial)
862+
assertEquals(initialRegistration, loadedInitial)
863+
864+
// Setup mock to throw IOException for future disk writes
865+
setupMockOutputStreamThrowingIOException()
866+
867+
// Create updated registration
868+
val updatedRegistration = PKCEClientRegistration(
869+
"fresh_ID",
870+
"fresh_client_secret",
871+
Instant.now().plusSeconds(3600),
872+
scopes,
873+
ssoUrl,
874+
ssoRegion,
875+
"public",
876+
listOf("authorization_code", "refresh_token"),
877+
listOf("http://127.0.0.1/oauth/callback")
878+
)
879+
880+
// Try to save updated registration (should fall back to in-memory cache)
881+
sut.saveClientRegistration(key, updatedRegistration)
882+
883+
// Load registration again
884+
val loadedUpdated = sut.loadClientRegistration(key)
885+
886+
// Verify that we get the updated registration, not the initial one
887+
assertNotNull(loadedUpdated)
888+
assertEquals(updatedRegistration, loadedUpdated)
889+
}
890+
891+
@Test
892+
fun `test access token update with disk error falls back to memory cache`() {
893+
val key = PKCEAccessTokenCacheKey(ssoUrl, ssoRegion, scopes)
894+
val initialToken = PKCEAuthorizationGrantToken(
895+
ssoUrl,
896+
ssoRegion,
897+
"stale_access_token",
898+
"stale_refresh_token",
899+
Instant.now().plusSeconds(3600),
900+
Instant.now()
901+
)
902+
903+
// Save initial token (should save to disk)
904+
sut.saveAccessToken(key, initialToken)
905+
906+
// Verify initial save
907+
val loadedInitial = sut.loadAccessToken(key)
908+
assertNotNull(loadedInitial)
909+
assertEquals(initialToken, loadedInitial)
910+
911+
// Setup mock to throw IOException for future disk writes
912+
setupMockOutputStreamThrowingIOException()
913+
914+
// Create updated token
915+
val updatedToken = PKCEAuthorizationGrantToken(
916+
ssoUrl,
917+
ssoRegion,
918+
"fresh_access_token",
919+
"fresh_refresh_token",
920+
Instant.now().plusSeconds(3600),
921+
Instant.now()
922+
)
923+
924+
// Try to save updated token (should fall back to in-memory cache)
925+
sut.saveAccessToken(key, updatedToken)
926+
927+
// Load token again
928+
val loadedUpdated = sut.loadAccessToken(key)
929+
930+
// Verify that we get the updated token, not the initial one
931+
assertNotNull(loadedUpdated)
932+
assertEquals(updatedToken, loadedUpdated)
933+
}
832934
}

0 commit comments

Comments
 (0)