@@ -3,6 +3,7 @@ package com.coder.gateway.views.steps
33import com.coder.gateway.CoderGatewayBundle
44import com.coder.gateway.icons.CoderIcons
55import com.coder.gateway.models.CoderWorkspacesWizardModel
6+ import com.coder.gateway.models.TokenSource
67import com.coder.gateway.models.WorkspaceAgentModel
78import com.coder.gateway.models.WorkspaceAgentStatus
89import com.coder.gateway.models.WorkspaceAgentStatus.FAILED
@@ -56,10 +57,12 @@ import com.intellij.ui.dsl.builder.bindSelected
5657import com.intellij.ui.dsl.builder.bindText
5758import com.intellij.ui.dsl.builder.panel
5859import com.intellij.ui.table.TableView
60+ import com.intellij.util.applyIf
5961import com.intellij.util.ui.ColumnInfo
6062import com.intellij.util.ui.JBFont
6163import com.intellij.util.ui.JBUI
6264import com.intellij.util.ui.ListTableModel
65+ import com.intellij.util.ui.UIUtil
6366import com.intellij.util.ui.table.IconTableCellRenderer
6467import com.jetbrains.rd.util.lifetime.LifetimeDefinition
6568import kotlinx.coroutines.CoroutineScope
@@ -348,7 +351,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
348351
349352 override fun onInit (wizardModel : CoderWorkspacesWizardModel ) {
350353 listTableModelOfWorkspaces.items = emptyList()
351- if (localWizardModel.coderURL.isNotBlank() && localWizardModel.token.isNotBlank() ) {
354+ if (localWizardModel.coderURL.isNotBlank() && localWizardModel.token != null ) {
352355 triggerWorkspacePolling(true )
353356 } else {
354357 val (url, token) = readStorageOrConfig()
@@ -357,10 +360,10 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
357360 tfUrl?.text = url
358361 }
359362 if (! token.isNullOrBlank()) {
360- localWizardModel.token = token
363+ localWizardModel.token = Pair ( token, TokenSource . CONFIG )
361364 }
362365 if (! url.isNullOrBlank() && ! token.isNullOrBlank()) {
363- connect(url.toURL(), token)
366+ connect(url.toURL(), Pair ( token, TokenSource . CONFIG ) )
364367 }
365368 }
366369 updateWorkspaceActions()
@@ -417,20 +420,21 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
417420 * If the token is invalid abort and start over from askTokenAndConnect()
418421 * unless retry is false.
419422 */
420- private fun askTokenAndConnect (openBrowser : Boolean = true) {
423+ private fun askTokenAndConnect (isRetry : Boolean = false) {
424+ val oldURL = localWizardModel.coderURL.toURL()
421425 component.apply () // Force bindings to be filled.
426+ val newURL = localWizardModel.coderURL.toURL()
422427 val pastedToken = askToken(
423- localWizardModel.coderURL.toURL(),
424- localWizardModel.token,
425- openBrowser,
428+ newURL,
429+ // If this is a new URL there is no point in trying to use the same
430+ // token.
431+ if (oldURL == newURL) localWizardModel.token else null ,
432+ isRetry,
426433 localWizardModel.useExistingToken,
427- )
428- if (pastedToken.isNullOrBlank()) {
429- return // User aborted.
430- }
434+ ) ? : return // User aborted.
431435 localWizardModel.token = pastedToken
432- connect(localWizardModel.coderURL.toURL(), localWizardModel.token ) {
433- askTokenAndConnect(false )
436+ connect(newURL, pastedToken ) {
437+ askTokenAndConnect(true )
434438 }
435439 }
436440
@@ -444,7 +448,11 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
444448 *
445449 * If the token is invalid invoke onAuthFailure.
446450 */
447- private fun connect (deploymentURL : URL , token : String , onAuthFailure : (() -> Unit )? = null): Job {
451+ private fun connect (
452+ deploymentURL : URL ,
453+ token : Pair <String , TokenSource >,
454+ onAuthFailure : (() -> Unit )? = null,
455+ ): Job {
448456 // Clear out old deployment details.
449457 poller?.cancel()
450458 tableOfWorkspaces.setEmptyState(" Connecting to $deploymentURL ..." )
@@ -465,16 +473,16 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
465473 )
466474 try {
467475 this .indicator.text = " Authenticating client..."
468- authenticate(deploymentURL, token)
476+ authenticate(deploymentURL, token.first )
469477 // Remember these in order to default to them for future attempts.
470478 appPropertiesService.setValue(CODER_URL_KEY , deploymentURL.toString())
471- appPropertiesService.setValue(SESSION_TOKEN , token)
479+ appPropertiesService.setValue(SESSION_TOKEN , token.first )
472480
473481 this .indicator.text = " Downloading Coder CLI..."
474482 cliManager.downloadCLI()
475483
476484 this .indicator.text = " Authenticating Coder CLI..."
477- cliManager.login(token)
485+ cliManager.login(token.first )
478486
479487 this .indicator.text = " Retrieving workspaces..."
480488 loadWorkspaces()
@@ -521,22 +529,29 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
521529 }
522530
523531 /* *
524- * Open a dialog for providing the token. Show the existing token so the
525- * user can validate it if a previous connection failed. Open a browser to
526- * the auth page if openBrowser is true and useExisting is false. If
527- * useExisting is true then populate the dialog with the token on disk if
528- * there is one and it matches the url (this will overwrite the provided
529- * token). Return the token submitted by the user.
532+ * Open a dialog for providing the token. Show any existing token so the
533+ * user can validate it if a previous connection failed. If we are not
534+ * retrying and the user has not checked the existing token box then open a
535+ * browser to the auth page. If the user has checked the existing token box
536+ * then populate the dialog with the token on disk (this will overwrite any
537+ * other existing token) unless this is a retry to avoid clobbering the
538+ * token that just failed. Return the token submitted by the user.
530539 */
531- private fun askToken (url : URL , token : String , openBrowser : Boolean , useExisting : Boolean ): String? {
532- var existingToken = token
540+ private fun askToken (
541+ url : URL ,
542+ token : Pair <String , TokenSource >? ,
543+ isRetry : Boolean ,
544+ useExisting : Boolean ,
545+ ): Pair <String , TokenSource >? {
546+ var (existingToken, tokenSource) = token ? : Pair (" " , TokenSource .USER )
533547 val getTokenUrl = url.withPath(" /login?redirect=%2Fcli-auth" )
534- if (openBrowser && ! useExisting) {
548+ if (! isRetry && ! useExisting) {
535549 BrowserUtil .browse(getTokenUrl)
536- } else if (useExisting) {
550+ } else if (! isRetry && useExisting) {
537551 val (u, t) = CoderCLIManager .readConfig()
538- if (url == u?.toURL() && ! t.isNullOrBlank()) {
539- logger.info(" Injecting valid token from CLI config" )
552+ if (url == u?.toURL() && ! t.isNullOrBlank() && t != existingToken) {
553+ logger.info(" Injecting token from CLI config" )
554+ tokenSource = TokenSource .CONFIG
540555 existingToken = t
541556 }
542557 }
@@ -549,11 +564,32 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
549564 CoderGatewayBundle .message(" gateway.connector.view.login.token.label" ),
550565 getTokenUrl.toString()
551566 )
552- sessionTokenTextField = textField().applyToComponent {
553- text = existingToken
554- minimumSize = Dimension (520 , - 1 )
555- }.component
556- }
567+ sessionTokenTextField = textField()
568+ .applyToComponent {
569+ text = existingToken
570+ minimumSize = Dimension (520 , - 1 )
571+ }.component
572+ }.layout(RowLayout .PARENT_GRID )
573+ row {
574+ cell() // To align with the text box.
575+ cell(
576+ ComponentPanelBuilder .createCommentComponent(
577+ CoderGatewayBundle .message(
578+ if (isRetry) " gateway.connector.view.workspaces.token.rejected"
579+ else if (tokenSource == TokenSource .CONFIG ) " gateway.connector.view.workspaces.token.injected"
580+ else if (existingToken.isNotBlank()) " gateway.connector.view.workspaces.token.comment"
581+ else " gateway.connector.view.workspaces.token.none"
582+ ),
583+ false ,
584+ - 1 ,
585+ true
586+ ).applyIf(isRetry) {
587+ apply {
588+ foreground = UIUtil .getErrorForeground()
589+ }
590+ }
591+ )
592+ }.layout(RowLayout .PARENT_GRID )
557593 }
558594 AppIcon .getInstance().requestAttention(null , true )
559595 if (! dialog(
@@ -566,7 +602,13 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
566602 }
567603 tokenFromUser = sessionTokenTextField.text
568604 }, ModalityState .any())
569- return tokenFromUser
605+ if (tokenFromUser.isNullOrBlank()) {
606+ return null
607+ }
608+ if (tokenFromUser != existingToken) {
609+ tokenSource = TokenSource .USER
610+ }
611+ return Pair (tokenFromUser!! , tokenSource)
570612 }
571613
572614 private fun triggerWorkspacePolling (fetchNow : Boolean ) {
0 commit comments