Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand Down Expand Up @@ -316,11 +316,18 @@
initializeResult
}

DefaultAuthCredentialsService(project, encryptionManager, this)
TextDocumentServiceHandler(project, this)
WorkspaceServiceHandler(project, this)
cs.launch {
DefaultModuleDependenciesService(project, this@AmazonQServerInstance)
// invokeOnCompletion results in weird lock/timeout error
initializeResult.asCompletableFuture().handleAsync { r, ex ->

Check warning on line 320 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt#L320

Added line #L320 was not covered by tests
if (ex != null) {
return@handleAsync

Check warning on line 322 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt#L322

Added line #L322 was not covered by tests
}

[email protected] {
DefaultAuthCredentialsService(project, encryptionManager, this)
TextDocumentServiceHandler(project, this)
WorkspaceServiceHandler(project, this)
DefaultModuleDependenciesService(project, this)
}

Check warning on line 330 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt#L325-L330

Added lines #L325 - L330 were not covered by tests
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
BearerTokenProviderListener {
init {
project.messageBus.connect(serverInstance).subscribe(BearerTokenProviderListener.TOPIC, this)

onChange("init", null)

Check warning on line 30 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L30

Added line #L30 was not covered by tests
}

override fun onChange(providerId: String, newScopes: List<String>?) {
Expand All @@ -39,7 +41,8 @@
?: return

provider.currentToken()?.accessToken?.let { token ->
updateTokenCredentials(token, false)
// assume encryption is always on
updateTokenCredentials(token, true)

Check warning on line 45 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L45

Added line #L45 was not covered by tests
}
}

Expand All @@ -48,13 +51,7 @@
}

override fun updateTokenCredentials(accessToken: String, encrypted: Boolean): CompletableFuture<ResponseMessage> {
val token = if (encrypted) {
encryptionManager.decrypt(accessToken)
} else {
accessToken
}

val payload = createUpdateCredentialsPayload(token)
val payload = createUpdateCredentialsPayload(accessToken, encrypted)

Check warning on line 54 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L54

Added line #L54 was not covered by tests

return AmazonQLspService.executeIfRunning(project) { server ->
server.updateTokenCredentials(payload)
Expand All @@ -69,13 +66,20 @@
} ?: completableFuture.completeExceptionally(IllegalStateException("LSP Server not running"))
}

private fun createUpdateCredentialsPayload(token: String): UpdateCredentialsPayload =
UpdateCredentialsPayload(
data = encryptionManager.encrypt(
UpdateCredentialsPayloadData(
BearerCredentials(token)
)
),
encrypted = true
)
private fun createUpdateCredentialsPayload(token: String, encrypted: Boolean): UpdateCredentialsPayload =
if (encrypted) {
UpdateCredentialsPayload(
data = encryptionManager.encrypt(
UpdateCredentialsPayloadData(
BearerCredentials(token)

Check warning on line 74 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L71-L74

Added lines #L71 - L74 were not covered by tests
)
),
encrypted = true

Check warning on line 77 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L77

Added line #L77 was not covered by tests
)
} else {
UpdateCredentialsPayload(
data = token,
encrypted = false

Check warning on line 82 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L80-L82

Added lines #L80 - L82 were not covered by tests
)
}

Check warning on line 84 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L84

Added line #L84 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,51 @@
package software.aws.toolkits.jetbrains.services.amazonq.lsp.auth

import com.intellij.openapi.Disposable
import com.intellij.openapi.components.service
import com.intellij.openapi.components.serviceIfCreated
import com.intellij.openapi.project.Project
import com.intellij.testFramework.ProjectExtension
import com.intellij.util.messages.MessageBus
import com.intellij.util.messages.MessageBusConnection
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.spyk
import io.mockk.verify
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import software.aws.toolkits.core.TokenConnectionSettings
import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.sso.PKCEAuthorizationGrantToken
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.InteractiveBearerTokenProvider
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLanguageServer
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload
import java.time.Instant
import java.util.concurrent.CompletableFuture

class DefaultAuthCredentialsServiceTest {
companion object {
@JvmField
@RegisterExtension
val projectExtension = ProjectExtension()
}

private lateinit var project: Project
private lateinit var mockLanguageServer: AmazonQLanguageServer
private lateinit var mockEncryptionManager: JwtEncryptionManager
private lateinit var sut: DefaultAuthCredentialsService

@Before
// maybe better to use real project via junit extension
@BeforeEach
fun setUp() {
project = mockk<Project>()
project = spyk(projectExtension.project)
mockLanguageServer = mockk<AmazonQLanguageServer>()
mockEncryptionManager = mockk<JwtEncryptionManager>()
every { mockEncryptionManager.encrypt(any()) } returns "mock-encrypted-data"
Expand All @@ -54,6 +73,32 @@ class DefaultAuthCredentialsServiceTest {
every { messageBus.connect(any<Disposable>()) } returns mockConnection
every { mockConnection.subscribe(any(), any()) } just runs

// Mock ToolkitConnectionManager
val connectionManager = mockk<ToolkitConnectionManager>()
val connection = mockk<AwsBearerTokenConnection>()
val connectionSettings = mockk<TokenConnectionSettings>()
val provider = mockk<ToolkitBearerTokenProvider>()
val tokenDelegate = mockk<InteractiveBearerTokenProvider>()
val token = PKCEAuthorizationGrantToken(
issuerUrl = "https://example.com",
refreshToken = "refreshToken",
accessToken = "accessToken",
expiresAt = Instant.MAX,
createdAt = Instant.now(),
region = "us-fake-1",
)

every { project.service<ToolkitConnectionManager>() } returns connectionManager
every { connectionManager.activeConnectionForFeature(any()) } returns connection
every { connection.getConnectionSettings() } returns connectionSettings
every { connectionSettings.tokenProvider } returns provider
every { provider.delegate } returns tokenDelegate
every { tokenDelegate.currentToken() } returns token

every {
mockLanguageServer.updateTokenCredentials(any())
} returns CompletableFuture.completedFuture(ResponseMessage())

sut = DefaultAuthCredentialsService(project, this.mockEncryptionManager, mockk())
}

Expand All @@ -62,17 +107,15 @@ class DefaultAuthCredentialsServiceTest {
val token = "unencryptedToken"
val isEncrypted = false

every {
mockLanguageServer.updateTokenCredentials(any())
} returns CompletableFuture.completedFuture(ResponseMessage())

sut.updateTokenCredentials(token, isEncrypted)

verify(exactly = 0) {
mockEncryptionManager.decrypt(any())
}
verify(exactly = 1) {
mockLanguageServer.updateTokenCredentials(any())
mockLanguageServer.updateTokenCredentials(
UpdateCredentialsPayload(
token,
isEncrypted
)
)
}
}

Expand All @@ -82,16 +125,18 @@ class DefaultAuthCredentialsServiceTest {
val decryptedToken = "decryptedToken"
val isEncrypted = true

every { mockEncryptionManager.decrypt(encryptedToken) } returns decryptedToken
every { mockEncryptionManager.encrypt(any()) } returns "mock-encrypted-data"
every {
mockLanguageServer.updateTokenCredentials(any())
} returns CompletableFuture.completedFuture(ResponseMessage())
every { mockEncryptionManager.encrypt(any()) } returns encryptedToken

sut.updateTokenCredentials(encryptedToken, isEncrypted)
sut.updateTokenCredentials(decryptedToken, isEncrypted)

verify(exactly = 1) { mockEncryptionManager.decrypt(encryptedToken) }
verify(exactly = 1) { mockLanguageServer.updateTokenCredentials(any()) }
verify(atLeast = 1) {
mockLanguageServer.updateTokenCredentials(
UpdateCredentialsPayload(
encryptedToken,
isEncrypted
)
)
}
}

@Test
Expand All @@ -102,4 +147,9 @@ class DefaultAuthCredentialsServiceTest {

verify(exactly = 1) { mockLanguageServer.deleteTokenCredentials() }
}

@Test
fun `init results in token update`() {
verify(exactly = 1) { mockLanguageServer.updateTokenCredentials(any()) }
}
}
Loading