Skip to content

Commit

Permalink
Update telemetry: emit auth scopes in user state (#4944)
Browse files Browse the repository at this point in the history
* add authScopes to emitUserState telemetry

* add authScopes to emitUserState telemetry

* Replace userState function with direct TelemetryService record function to capture authScopes

* add value parameter

* fix whitespace

* Update plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/gettingstarted/GettingStartedAuthUtils.kt

Co-authored-by: Richard Li <[email protected]>

* Check current explorerConnection for scopes only

* Add function to grab scopes from each activeConnection used that is connected/expired.

* remove TelemetryOverride definition and use common's telemetry function instead

* remove unused imports

* Change the way explorer connection scope is checked

* remove check for CodeWhispererConnection: merged into qConnection & qScopes

* add common logic to one function

* Update telemetryOverride.json

missed {

---------

Co-authored-by: Richard Li <[email protected]>
  • Loading branch information
samgst-amazon and rli authored Nov 7, 2024
1 parent ad03841 commit 82f2275
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 62 deletions.
19 changes: 0 additions & 19 deletions plugins/core/jetbrains-community/resources/telemetryOverride.json
Original file line number Diff line number Diff line change
Expand Up @@ -755,25 +755,6 @@
],
"passive": true
},
{
"name": "auth_userState",
"description": "The state of the user's authentication.",
"metadata": [
{
"type": "source",
"required": true
},
{
"type": "authStatus",
"required": true
},
{
"type": "authEnabledConnections",
"required": true
}
],
"passive": true
},
{
"name": "webview_amazonqSignInOpened",
"description": "Called when a Amazon Q sign in webview is opened.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
import software.aws.toolkits.jetbrains.core.credentials.reauthConnectionIfNeeded
import software.aws.toolkits.jetbrains.core.credentials.sono.Q_SCOPES
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.SourceOfEntry
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.getAuthScopes
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.getAuthStatus
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.getConnectionCount
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.getEnabledConnections
Expand Down Expand Up @@ -238,10 +239,10 @@ fun reauthenticateWithQ(project: Project) {
fun emitUserState(project: Project) {
AuthTelemetry.userState(
project,
source = getStartupState().toString(),
authEnabledConnections = getEnabledConnections(project),
authScopes = getAuthScopes(project),
authStatus = getAuthStatus(project),
passive = true
source = getStartupState().toString()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.core.gettingstarted.editor
import com.intellij.openapi.project.Project
import com.intellij.ui.dsl.builder.Panel
import software.aws.toolkits.core.credentials.CredentialIdentifier
import software.aws.toolkits.core.credentials.CredentialType
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.ConnectionState
Expand Down Expand Up @@ -115,6 +116,20 @@ fun checkIamConnectionValidity(project: Project): ActiveConnection {
}
}

fun checkIamProfileByCredentialType(project: Project): ActiveConnection {
val currConn = AwsConnectionManager.getInstance(project).selectedCredentialIdentifier ?: return ActiveConnection.NotConnected
val invalidConnection = AwsConnectionManager.getInstance(project).connectionState.let { it.isTerminal && it !is ConnectionState.ValidConnection }
val connectionType = when (currConn.credentialType) {
CredentialType.SsoProfile -> ActiveConnectionType.IAM_IDC
else -> ActiveConnectionType.IAM
}
return if (invalidConnection) {
ActiveConnection.ExpiredIam(connectionType = connectionType, activeConnectionIam = currConn)
} else {
ActiveConnection.ValidIam(connectionType = connectionType, activeConnectionIam = currConn)
}
}

/**
* Finds the first valid [ActiveConnection] and returns it.
*
Expand Down Expand Up @@ -146,6 +161,7 @@ fun checkConnectionValidity(project: Project): ActiveConnection {
return result
}

@Deprecated("Does not work for current config file setup. Old versions still utilize this logic.")
fun isCredentialSso(providerId: String): ActiveConnectionType {
val profileName = providerId.split("-").first()
val ssoSessionIds = CredentialManager.getInstance().getSsoSessionIdentifiers().map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import software.aws.toolkits.core.utils.tryOrNull
import software.aws.toolkits.jetbrains.core.credentials.CredentialManager
import software.aws.toolkits.jetbrains.core.credentials.ToolkitAuthManager
import software.aws.toolkits.jetbrains.core.credentials.profiles.ProfileCredentialsIdentifierSso
import software.aws.toolkits.jetbrains.core.credentials.sono.IDENTITY_CENTER_ROLE_ACCESS_SCOPE
import software.aws.toolkits.jetbrains.settings.AwsSettings
import software.aws.toolkits.telemetry.AuthStatus
import software.aws.toolkits.telemetry.StartUpState
Expand All @@ -22,54 +23,66 @@ fun getConnectionCount(): Long {

fun getEnabledConnectionsForTelemetry(project: Project?): Set<AuthFormId> {
project ?: return emptySet()
val enabledConnections = mutableSetOf<AuthFormId>()

val explorerConnection = checkIamConnectionValidity(project)
if (explorerConnection !is ActiveConnection.NotConnected) {
if (explorerConnection.connectionType == ActiveConnectionType.IAM_IDC) {
enabledConnections.add(AuthFormId.IDENTITYCENTER_EXPLORER)
} else {
enabledConnections.add(
AuthFormId.IAMCREDENTIALS_EXPLORER
)
}
}
val codeCatalystConnection = checkBearerConnectionValidity(project, BearerTokenFeatureSet.CODECATALYST)
if (codeCatalystConnection !is ActiveConnection.NotConnected) {
if (codeCatalystConnection.connectionType == ActiveConnectionType.IAM_IDC) {
enabledConnections.add(AuthFormId.IDENTITYCENTER_CODECATALYST)
} else {
enabledConnections.add(AuthFormId.BUILDERID_CODECATALYST)
}
}
val enabledConnections = mutableSetOf<Any>()

val codeWhispererConnection = checkBearerConnectionValidity(project, BearerTokenFeatureSet.CODEWHISPERER)
if (codeWhispererConnection !is ActiveConnection.NotConnected) {
if (codeWhispererConnection.connectionType == ActiveConnectionType.IAM_IDC) {
enabledConnections.add(AuthFormId.IDENTITYCENTER_CODEWHISPERER)
} else {
enabledConnections.add(
AuthFormId.BUILDERID_CODEWHISPERER
)
}
}
addConnectionInfoToSet(
checkIamConnectionValidity(project),
enabledConnections,
AuthFormId.IDENTITYCENTER_EXPLORER,
AuthFormId.IAMCREDENTIALS_EXPLORER
)

val qConnection = checkBearerConnectionValidity(project, BearerTokenFeatureSet.Q)
if (qConnection !is ActiveConnection.NotConnected) {
if (qConnection.connectionType == ActiveConnectionType.IAM_IDC) {
enabledConnections.add(AuthFormId.IDENTITYCENTER_Q)
} else {
enabledConnections.add(
AuthFormId.BUILDERID_Q
)
}
}
return enabledConnections
addConnectionInfoToSet(
checkBearerConnectionValidity(project, BearerTokenFeatureSet.CODECATALYST),
enabledConnections,
AuthFormId.IDENTITYCENTER_CODECATALYST,
AuthFormId.BUILDERID_CODECATALYST
)

addConnectionInfoToSet(
checkBearerConnectionValidity(project, BearerTokenFeatureSet.CODEWHISPERER),
enabledConnections,
AuthFormId.IDENTITYCENTER_CODEWHISPERER,
AuthFormId.BUILDERID_CODEWHISPERER
)

addConnectionInfoToSet(
checkBearerConnectionValidity(project, BearerTokenFeatureSet.Q),
enabledConnections,
AuthFormId.IDENTITYCENTER_Q,
AuthFormId.BUILDERID_Q
)
return enabledConnections.mapTo(mutableSetOf()) { it as AuthFormId }
}

fun getEnabledConnections(project: Project?): String =
getEnabledConnectionsForTelemetry(project).joinToString(",")

fun getAuthScopesForTelemetry(project: Project?): Set<String> {
project ?: return emptySet()
val scopes = mutableSetOf<Any>()

val explorerConnection = checkIamProfileByCredentialType(project)
if (explorerConnection !is ActiveConnection.NotConnected && explorerConnection.connectionType == ActiveConnectionType.IAM_IDC) {
scopes.add(IDENTITY_CENTER_ROLE_ACCESS_SCOPE)
}

addConnectionInfoToSet(
checkBearerConnectionValidity(project, BearerTokenFeatureSet.CODECATALYST),
dataSet = scopes
)

addConnectionInfoToSet(
checkBearerConnectionValidity(project, BearerTokenFeatureSet.Q),
dataSet = scopes
)

return scopes.mapTo(mutableSetOf()) { it as String }
}

fun getAuthScopes(project: Project?): String =
getAuthScopesForTelemetry(project).joinToString(",")

fun getStartupState(): StartUpState {
val hasStartedToolkitBefore = tryOrNull {
getPersistentStateComponentStorageLocation(AwsSettings::class.java)?.exists()
Expand All @@ -87,6 +100,38 @@ fun getAuthStatus(project: Project) = when (checkConnectionValidity(project)) {
else -> AuthStatus.NotConnected
}

fun addConnectionInfoToSet(
activeConnection: ActiveConnection,
dataSet: MutableSet<Any>,
idcConnection: AuthFormId? = null,
defaultConnection: AuthFormId? = null,
) {
if (activeConnection is ActiveConnection.NotConnected) {
return
}

// add enabled connections
when (activeConnection.connectionType) {
ActiveConnectionType.IAM_IDC -> {
idcConnection ?.let {
dataSet.add(idcConnection)
return
}
} else -> {
defaultConnection?.let {
dataSet.add(defaultConnection)
return
}
}
}

// add scopes
val connectionScopes = activeConnection.activeConnectionBearer?.scopes
if (!connectionScopes.isNullOrEmpty()) {
dataSet.addAll(connectionScopes)
}
}

enum class AuthFormId {
IAMCREDENTIALS_EXPLORER,
IDENTITYCENTER_EXPLORER,
Expand Down

0 comments on commit 82f2275

Please sign in to comment.