Skip to content

Commit c512e66

Browse files
authored
Merge pull request #183 from coder/asher/existing-token
Read URL and token from config
2 parents 288a8a4 + d353e6a commit c512e66

File tree

4 files changed

+90
-17
lines changed

4 files changed

+90
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Added
88

99
- support for Gateway 2023
10+
- ability to use existing tokens rather than request a new one
1011

1112
### Fixed
1213

src/main/kotlin/com/coder/gateway/models/CoderWorkspacesWizardModel.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ data class CoderWorkspacesWizardModel(
55
var token: String = "",
66
var buildVersion: String = "",
77
var localCliPath: String = "",
8-
var selectedWorkspace: WorkspaceAgentModel? = null
9-
)
8+
var selectedWorkspace: WorkspaceAgentModel? = null,
9+
var useExistingToken: Boolean = false
10+
)

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ import com.intellij.ui.RelativeFont
4646
import com.intellij.ui.ToolbarDecorator
4747
import com.intellij.ui.components.JBTextField
4848
import com.intellij.ui.components.dialog
49-
import com.intellij.ui.dsl.builder.AlignX
50-
import com.intellij.ui.dsl.builder.AlignY
51-
import com.intellij.ui.dsl.builder.BottomGap
52-
import com.intellij.ui.dsl.builder.RightGap
53-
import com.intellij.ui.dsl.builder.TopGap
54-
import com.intellij.ui.dsl.builder.bindText
55-
import com.intellij.ui.dsl.builder.panel
49+
import com.intellij.ui.dsl.builder.*
5650
import com.intellij.ui.table.TableView
5751
import com.intellij.util.ui.ColumnInfo
5852
import com.intellij.util.ui.JBFont
@@ -76,8 +70,12 @@ import java.awt.event.MouseListener
7670
import java.awt.event.MouseMotionListener
7771
import java.awt.font.TextAttribute
7872
import java.awt.font.TextAttribute.UNDERLINE_ON
73+
import java.nio.file.Files
74+
import java.nio.file.Path
75+
import java.nio.file.Paths
7976
import java.net.SocketTimeoutException
8077
import javax.swing.Icon
78+
import javax.swing.JCheckBox
8179
import javax.swing.JTable
8280
import javax.swing.JTextField
8381
import javax.swing.ListSelectionModel
@@ -100,6 +98,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
10098
private val appPropertiesService: PropertiesComponent = service()
10199

102100
private var tfUrl: JTextField? = null
101+
private var cbExistingToken: JCheckBox? = null
103102
private var listTableModelOfWorkspaces = ListTableModel<WorkspaceAgentModel>(
104103
WorkspaceIconColumnInfo(""),
105104
WorkspaceNameColumnInfo("Name"),
@@ -201,13 +200,13 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
201200
font = JBFont.h3().asBold()
202201
icon = CoderIcons.LOGO_16
203202
}
204-
}.topGap(TopGap.SMALL).bottomGap(BottomGap.MEDIUM)
203+
}.topGap(TopGap.SMALL)
205204
row {
206205
cell(ComponentPanelBuilder.createCommentComponent(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"), false, -1, true))
207206
}
208207
row {
209208
browserLink(CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"), "https://coder.com/docs/coder-oss/latest/workspaces")
210-
}.bottomGap(BottomGap.MEDIUM)
209+
}
211210
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
212211
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL).bindText(localWizardModel::coderURL).applyToComponent {
213212
addActionListener {
@@ -225,6 +224,17 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
225224
}
226225
cell()
227226
}
227+
row {
228+
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
229+
.bindSelected(localWizardModel::useExistingToken)
230+
.component
231+
}
232+
row {
233+
cell(ComponentPanelBuilder.createCommentComponent(
234+
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.tooltip",
235+
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label")),
236+
false, -1, true))
237+
}
228238
row {
229239
scrollCell(toolbar.createPanel().apply {
230240
add(notificationBanner.component.apply { isVisible = false }, "South")
@@ -315,18 +325,71 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
315325
if (localWizardModel.coderURL.isNotBlank() && localWizardModel.token.isNotBlank()) {
316326
triggerWorkspacePolling(true)
317327
} else {
318-
val url = appPropertiesService.getValue(CODER_URL_KEY)
319-
val token = appPropertiesService.getValue(SESSION_TOKEN)
320-
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
328+
val (url, token) = readStorageOrConfig()
329+
if (!url.isNullOrBlank()) {
321330
localWizardModel.coderURL = url
322-
localWizardModel.token = token
323331
tfUrl?.text = url
332+
}
333+
if (!token.isNullOrBlank()) {
334+
localWizardModel.token = token
335+
}
336+
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
324337
loginAndLoadWorkspace(token, true)
325338
}
326339
}
327340
updateWorkspaceActions()
328341
}
329342

343+
/**
344+
* Return the URL and token from storage or the CLI config.
345+
*/
346+
private fun readStorageOrConfig(): Pair<String?, String?> {
347+
val url = appPropertiesService.getValue(CODER_URL_KEY)
348+
val token = appPropertiesService.getValue(SESSION_TOKEN)
349+
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
350+
return url to token
351+
}
352+
return readConfig()
353+
}
354+
355+
/**
356+
* Return the URL and token from the CLI config.
357+
*/
358+
private fun readConfig(): Pair<String?, String?> {
359+
val configDir = getConfigDir()
360+
logger.info("Reading config from $configDir")
361+
try {
362+
val url = Files.readString(configDir.resolve("url"))
363+
val token = Files.readString(configDir.resolve("session"))
364+
return url to token
365+
} catch (e: Exception) {
366+
return null to null // Probably has not configured the CLI yet.
367+
}
368+
}
369+
370+
/**
371+
* Return the config directory used by the CLI.
372+
*/
373+
private fun getConfigDir(): Path {
374+
var dir = System.getenv("CODER_CONFIG_DIR")
375+
if (!dir.isNullOrBlank()) {
376+
return Path.of(dir)
377+
}
378+
// The Coder CLI uses https://github.com/kirsle/configdir so this should
379+
// match how it behaves.
380+
return when(getOS()) {
381+
OS.WINDOWS -> Paths.get(System.getenv("APPDATA"), "coderv2")
382+
OS.MAC -> Paths.get(System.getenv("HOME"), "Library/Application Support/coderv2")
383+
else -> {
384+
dir = System.getenv("XDG_CACHE_HOME")
385+
if (!dir.isNullOrBlank()) {
386+
return Paths.get(dir, "coderv2")
387+
}
388+
return Paths.get(System.getenv("HOME"), ".config/coderv2")
389+
}
390+
}
391+
}
392+
330393
private fun updateWorkspaceActions() {
331394
goToDashboardAction.isEnabled = coderClient.isReady
332395
createWorkspaceAction.isEnabled = coderClient.isReady
@@ -442,8 +505,14 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
442505

443506
private fun askToken(openBrowser: Boolean): String? {
444507
val getTokenUrl = localWizardModel.coderURL.toURL().withPath("/login?redirect=%2Fcli-auth")
445-
if (openBrowser) {
508+
if (openBrowser && !localWizardModel.useExistingToken) {
446509
BrowserUtil.browse(getTokenUrl)
510+
} else if (localWizardModel.useExistingToken) {
511+
val (url, token) = readConfig()
512+
if (url == localWizardModel.coderURL && !token.isNullOrBlank()) {
513+
logger.info("Injecting valid token from CLI config")
514+
localWizardModel.token = token
515+
}
447516
}
448517
var tokenFromUser: String? = null
449518
ApplicationManager.getApplication().invokeAndWait({

src/main/resources/messages/CoderGatewayBundle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ gateway.connector.description=Connects to a Coder Workspace dev environment so t
33
gateway.connector.action.text=Connect to Coder
44
gateway.connector.view.login.documentation.action=Learn more about Coder
55
gateway.connector.view.login.url.label=URL:
6+
gateway.connector.view.login.existing-token.label=Use existing token
7+
gateway.connector.view.login.existing-token.tooltip=Checking "{0}" will prevent the browser from being launched for generating a new token. Additionally, if a token is already configured for this URL via the CLI it will appear as the default and can be used as-is or replaced.
68
gateway.connector.view.login.token.dialog=Paste your token here:
79
gateway.connector.view.login.token.label=Session Token:
810
gateway.connector.view.coder.workspaces.header.text=Coder Workspaces
@@ -28,4 +30,4 @@ gateway.connector.recentconnections.title=Recent Coder Workspaces
2830
gateway.connector.recentconnections.new.wizard.button.tooltip=Open a new Coder Workspace
2931
gateway.connector.recentconnections.remove.button.tooltip=Remove from Recent Connections
3032
gateway.connector.recentconnections.terminal.button.tooltip=Open SSH Web Terminal
31-
gateway.connector.coder.connection.provider.title=Connecting to Coder workspace...
33+
gateway.connector.coder.connection.provider.title=Connecting to Coder workspace...

0 commit comments

Comments
 (0)