Skip to content

Commit 4f3cd21

Browse files
Improve Client Registration Error Collection (#4993)
* Add try-catch blocks for refresh steps. * add metric that indicates when token was successfully written to the json & stored in cache * remove comment * add more metrics to catch blocks. * Added reauth for expired client registration * fix try-catch Exception blocks. remove bad reauth logic * add metrics to saveClientRegistration * make metrics same in saveClientRegistration * Add modifyCredentials to invalidateClientRegistration * Move save/modify credentials to diskCache * linter * saveCredentials moved to writeKey * saveCredentials moved to writeKey * capture saveCredentials per saveAccessToken/ClientRegistration calls * add modifyCredentials + load stages to LoadClientRegistration flow * remove success metrics * add logs * add auth_ModifyConnection to Override * Fix Tests * nit fixes * consistent metrics * null e message, clean up unused enums * add more modifyConnection * nits * add enums to diskCache stages * try block success metric * detekt --------- Co-authored-by: Manodnya Jaydeep Bhoite <[email protected]>
1 parent ecda124 commit 4f3cd21

File tree

4 files changed

+230
-24
lines changed

4 files changed

+230
-24
lines changed

plugins/core/jetbrains-community/resources/telemetryOverride.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
],
6565
"description": "Explict user intent associated with a chat message"
6666
},
67+
{
68+
"name": "connectionState",
69+
"type": "string",
70+
"description": "A detailed state of a specific auth connection. Use `authStatus` for a higher level view of an extension's general connection."
71+
},
6772
{
6873
"name": "cwsprChatHasCodeSnippet",
6974
"type": "boolean",
@@ -703,6 +708,53 @@
703708
}
704709
]
705710
},
711+
{
712+
"name": "auth_modifyConnection",
713+
"description": "An auth connection was modified in some way, e.g. deleted, updated",
714+
"metadata": [
715+
{
716+
"type": "action",
717+
"required": true
718+
},
719+
{
720+
"type": "authScopes",
721+
"required": false
722+
},
723+
{
724+
"type": "authStatus",
725+
"required": false
726+
},
727+
{
728+
"type": "connectionState",
729+
"required": false
730+
},
731+
{
732+
"type": "credentialStartUrl",
733+
"required": false
734+
},
735+
{
736+
"type": "sessionDuration",
737+
"required": false
738+
},
739+
{
740+
"type": "source",
741+
"required": true
742+
},
743+
{
744+
"type": "ssoRegistrationClientId",
745+
"required": false
746+
},
747+
{
748+
"type": "ssoRegistrationExpiresAt",
749+
"required": false
750+
},
751+
{
752+
"type": "id",
753+
"required": false
754+
}
755+
],
756+
"passive": true
757+
},
706758
{
707759
"name": "auth_userState",
708760
"description": "The state of the user's authentication.",

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

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import software.aws.toolkits.core.utils.touch
3232
import software.aws.toolkits.core.utils.tryDirOp
3333
import software.aws.toolkits.core.utils.tryFileOp
3434
import software.aws.toolkits.core.utils.tryOrNull
35+
import software.aws.toolkits.telemetry.AuthTelemetry
36+
import software.aws.toolkits.telemetry.Result
3537
import java.io.InputStream
3638
import java.io.OutputStream
3739
import java.nio.file.Path
@@ -101,7 +103,18 @@ class DiskCache(
101103
override fun loadClientRegistration(cacheKey: ClientRegistrationCacheKey): ClientRegistration? {
102104
LOG.debug { "loadClientRegistration for $cacheKey" }
103105
val inputStream = clientRegistrationCache(cacheKey).tryInputStreamIfExists()
104-
?: return null
106+
if (inputStream == null) {
107+
val stage = LoadCredentialStage.ACCESS_FILE
108+
LOG.warn("Failed to load Client Registration: cache file does not exist")
109+
AuthTelemetry.modifyConnection(
110+
action = "Load cache file",
111+
source = "loadClientRegistration",
112+
result = Result.Failed,
113+
reason = "Failed to load Client Registration",
114+
reasonDesc = "Load Step:$stage failed. Unable to load file"
115+
)
116+
return null
117+
}
105118
return loadClientRegistration(inputStream)
106119
}
107120

@@ -115,12 +128,34 @@ class DiskCache(
115128

116129
override fun invalidateClientRegistration(cacheKey: ClientRegistrationCacheKey) {
117130
LOG.debug { "invalidateClientRegistration for $cacheKey" }
118-
clientRegistrationCache(cacheKey).tryDeleteIfExists()
131+
try {
132+
clientRegistrationCache(cacheKey).tryDeleteIfExists()
133+
} catch (e: Exception) {
134+
AuthTelemetry.modifyConnection(
135+
action = "Delete cache file",
136+
source = "invalidateClientRegistration",
137+
result = Result.Failed,
138+
reason = "Failed to invalidate Client Registration",
139+
reasonDesc = e.message ?: e::class.java.name
140+
)
141+
throw e
142+
}
119143
}
120144

121145
override fun invalidateAccessToken(ssoUrl: String) {
122146
LOG.debug { "invalidateAccessToken for $ssoUrl" }
123-
accessTokenCache(ssoUrl).tryDeleteIfExists()
147+
try {
148+
accessTokenCache(ssoUrl).tryDeleteIfExists()
149+
} catch (e: Exception) {
150+
AuthTelemetry.modifyConnection(
151+
action = "Delete cache file",
152+
source = "invalidateAccessToken",
153+
result = Result.Failed,
154+
reason = "Failed to invalidate Access Token",
155+
reasonDesc = e.message ?: e::class.java.name
156+
)
157+
throw e
158+
}
124159
}
125160

126161
override fun loadAccessToken(cacheKey: AccessTokenCacheKey): AccessToken? {
@@ -143,7 +178,18 @@ class DiskCache(
143178

144179
override fun invalidateAccessToken(cacheKey: AccessTokenCacheKey) {
145180
LOG.debug { "invalidateAccessToken for $cacheKey" }
146-
accessTokenCache(cacheKey).tryDeleteIfExists()
181+
try {
182+
accessTokenCache(cacheKey).tryDeleteIfExists()
183+
} catch (e: Exception) {
184+
AuthTelemetry.modifyConnection(
185+
action = "Delete cache file",
186+
source = "invalidateAccessToken",
187+
result = Result.Failed,
188+
reason = "Failed to invalidate Access Token",
189+
reasonDesc = e.message ?: e::class.java.name
190+
)
191+
throw e
192+
}
147193
}
148194

149195
private fun clientRegistrationCache(ssoRegion: String): Path = cacheDir.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json")
@@ -170,15 +216,36 @@ class DiskCache(
170216
return cacheDir.resolve(fileName)
171217
}
172218

173-
private fun loadClientRegistration(inputStream: InputStream) =
174-
tryOrNull {
219+
private fun loadClientRegistration(inputStream: InputStream): ClientRegistration? {
220+
var stage = LoadCredentialStage.VALIDATE_CREDENTIALS
221+
try {
175222
val clientRegistration = objectMapper.readValue<ClientRegistration>(inputStream)
223+
stage = LoadCredentialStage.CHECK_EXPIRATION
176224
if (clientRegistration.expiresAt.isNotExpired()) {
177-
clientRegistration
225+
return clientRegistration
178226
} else {
179-
null
227+
LOG.warn("Client Registration is expired")
228+
AuthTelemetry.modifyConnection(
229+
action = "Validate Credentials",
230+
source = "loadClientRegistration",
231+
result = Result.Failed,
232+
reason = "Failed to load Client Registration",
233+
reasonDesc = "Load Step:$stage failed: Client Registration is expired"
234+
)
235+
return null
180236
}
237+
} catch (e: Exception) {
238+
LOG.warn("Client Registration could not be read")
239+
AuthTelemetry.modifyConnection(
240+
action = "Validate Credentials",
241+
source = "loadClientRegistration",
242+
result = Result.Failed,
243+
reason = "Failed to load Client Registration",
244+
reasonDesc = "Load Step:$stage failed: File could not be read"
245+
)
246+
return null
181247
}
248+
}
182249

183250
private fun loadAccessToken(inputStream: InputStream) = tryOrNull {
184251
val accessToken = objectMapper.readValue<AccessToken>(inputStream)
@@ -202,11 +269,22 @@ class DiskCache(
202269

203270
private fun writeKey(path: Path, consumer: (OutputStream) -> Unit) {
204271
LOG.debug { "writing to $path" }
205-
path.tryDirOp(LOG) { createParentDirectories() }
272+
try {
273+
path.tryDirOp(LOG) { createParentDirectories() }
206274

207-
path.tryFileOp(LOG) {
208-
touch(restrictToOwner = true)
209-
outputStream().use(consumer)
275+
path.tryFileOp(LOG) {
276+
touch(restrictToOwner = true)
277+
outputStream().use(consumer)
278+
}
279+
} catch (e: Exception) {
280+
AuthTelemetry.modifyConnection(
281+
action = "Write file",
282+
source = "writeKey",
283+
result = Result.Failed,
284+
reason = "Failed to write to cache",
285+
reasonDesc = e.message ?: e::class.java.name
286+
)
287+
throw e
210288
}
211289
}
212290

@@ -230,6 +308,12 @@ class DiskCache(
230308
}
231309
}
232310

311+
private enum class LoadCredentialStage {
312+
ACCESS_FILE,
313+
VALIDATE_CREDENTIALS,
314+
CHECK_EXPIRATION,
315+
}
316+
233317
companion object {
234318
val EXPIRATION_THRESHOLD = Duration.ofMinutes(15)
235319
private val LOG = getLogger<DiskCache>()

0 commit comments

Comments
 (0)