Skip to content

Commit 94b329f

Browse files
authored
impl: store last used URL in Toolbox Settings Store (#200)
Context: Toolbox can store key/value pairs in two places: - a settings store which is backed by a clear text json file per each plugin - native keystore for sensitive data At the same time some of Coder's clients (ex: Netflix) would like to deploy at scale preconfigured settings for Toolbox. Most of the needed settings are part of json backed store except the last used URL. This PR reworks the code around the last used URL/token and moves the URL in the json backed store, making it easy to configure. At the same time we still support the pair stored in the native keystore for backward compatibility reasons.
1 parent 08c2912 commit 94b329f

File tree

9 files changed

+37
-37
lines changed

9 files changed

+37
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Changed
6+
7+
- simplified storage for last used url and token
8+
59
## 0.6.6 - 2025-09-24
610

711
### Changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
version=0.6.6
1+
version=0.7.0
22
group=com.coder.toolbox
33
name=coder-toolbox

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import com.coder.toolbox.sdk.ex.APIResponseException
77
import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
88
import com.coder.toolbox.util.CoderProtocolHandler
99
import com.coder.toolbox.util.DialogUi
10-
import com.coder.toolbox.util.toURL
1110
import com.coder.toolbox.util.waitForTrue
1211
import com.coder.toolbox.util.withPath
1312
import com.coder.toolbox.views.Action
@@ -364,8 +363,8 @@ class CoderRemoteProvider(
364363
if (shouldDoAutoSetup()) {
365364
try {
366365
CoderCliSetupContext.apply {
367-
url = context.secrets.lastDeploymentURL.toURL()
368-
token = context.secrets.lastToken
366+
url = context.deploymentUrl
367+
token = context.secrets.tokenFor(context.deploymentUrl)
369368
}
370369
CoderCliSetupWizardState.goToStep(WizardStep.CONNECT)
371370
return CoderCliSetupWizardPage(
@@ -399,14 +398,15 @@ class CoderRemoteProvider(
399398
* Auto-login only on first the firs run if there is a url & token configured or the auth
400399
* should be done via certificates.
401400
*/
402-
private fun shouldDoAutoSetup(): Boolean = firstRun && (context.secrets.canAutoLogin || !settings.requireTokenAuth)
401+
private fun shouldDoAutoSetup(): Boolean = firstRun && (canAutoLogin() || !settings.requireTokenAuth)
402+
403+
fun canAutoLogin(): Boolean = !context.secrets.tokenFor(context.deploymentUrl).isNullOrBlank()
403404

404405
private fun onConnect(client: CoderRestClient, cli: CoderCLIManager) {
405406
// Store the URL and token for use next time.
406-
context.secrets.lastDeploymentURL = client.url.toString()
407+
context.settingsStore.updateLastUsedUrl(client.url)
407408
if (context.settingsStore.requireTokenAuth) {
408-
context.secrets.lastToken = client.token ?: ""
409-
context.secrets.storeTokenFor(client.url, context.secrets.lastToken)
409+
context.secrets.storeTokenFor(client.url, client.token ?: "")
410410
context.logger.info("Deployment URL and token were stored and will be available for automatic connection")
411411
} else {
412412
context.logger.info("Deployment URL was stored and will be available for automatic connection")

src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,15 @@ data class CoderToolboxContext(
3636
*
3737
* In order of preference:
3838
*
39-
* 1. Last used URL.
40-
* 2. URL in settings.
41-
* 3. CODER_URL.
42-
* 4. URL in global cli config.
39+
* 1. Last used URL from the settings.
40+
* 2. Last used URL from the secrets store.
41+
* 3. Default URL
4342
*/
4443
val deploymentUrl: URL
4544
get() {
46-
if (this.secrets.lastDeploymentURL.isNotBlank()) {
47-
return this.secrets.lastDeploymentURL.toURL()
48-
}
49-
return this.settingsStore.defaultURL.toURL()
45+
return settingsStore.lastDeploymentURL?.takeIf { it.isNotBlank() }?.toURL()
46+
?: secrets.lastDeploymentURL.takeIf { it.isNotBlank() }?.toURL()
47+
?: settingsStore.defaultURL.toURL()
5048
}
5149

5250
suspend fun logAndShowError(title: String, error: String) {

src/main/kotlin/com/coder/toolbox/settings/ReadOnlyCoderSettings.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import java.util.Locale.getDefault
88
* Read-only interface for accessing Coder settings
99
*/
1010
interface ReadOnlyCoderSettings {
11+
12+
/**
13+
* The last used deployment URL.
14+
*/
15+
val lastDeploymentURL: String?
16+
1117
/**
1218
* The default URL to show in the connection window.
1319
*/

src/main/kotlin/com/coder/toolbox/store/CoderSecretsStore.kt

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,11 @@ import java.net.URL
88
* Provides Coder secrets backed by the secrets store service.
99
*/
1010
class CoderSecretsStore(private val store: PluginSecretStore) {
11-
private fun get(key: String): String = store[key] ?: ""
12-
13-
private fun set(key: String, value: String) {
14-
if (value.isBlank()) {
15-
store.clear(key)
16-
} else {
17-
store[key] = value
18-
}
19-
}
20-
21-
var lastDeploymentURL: String
22-
get() = get("last-deployment-url")
23-
set(value) = set("last-deployment-url", value)
24-
var lastToken: String
25-
get() = get("last-token")
26-
set(value) = set("last-token", value)
27-
val canAutoLogin: Boolean
28-
get() = lastDeploymentURL.isNotBlank() && lastToken.isNotBlank()
11+
@Deprecated(
12+
message = "The URL is now stored the JSON backed settings store. Use CoderSettingsStore#lastDeploymentURL",
13+
replaceWith = ReplaceWith("context.settingsStore.lastDeploymentURL")
14+
)
15+
val lastDeploymentURL: String = store["last-deployment-url"] ?: ""
2916

3017
fun tokenFor(url: URL): String? = store[url.host]
3118

src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class CoderSettingsStore(
3636
) : ReadOnlyTLSSettings
3737

3838
// Properties implementation
39+
override val lastDeploymentURL: String? get() = store[LAST_USED_URL]
3940
override val defaultURL: String get() = store[DEFAULT_URL] ?: "https://dev.coder.com"
4041
override val binarySource: String? get() = store[BINARY_SOURCE]
4142
override val binaryDirectory: String? get() = store[BINARY_DIRECTORY]
@@ -155,6 +156,10 @@ class CoderSettingsStore(
155156
fun readOnly(): ReadOnlyCoderSettings = this
156157

157158
// Write operations
159+
fun updateLastUsedUrl(url: URL) {
160+
store[LAST_USED_URL] = url.toString()
161+
}
162+
158163
fun updateBinarySource(source: String) {
159164
store[BINARY_SOURCE] = source
160165
}

src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.coder.toolbox.store
22

33
internal const val CODER_SSH_CONFIG_OPTIONS = "CODER_SSH_CONFIG_OPTIONS"
44

5-
internal const val CODER_URL = "CODER_URL"
5+
internal const val LAST_USED_URL = "lastDeploymentURL"
66

77
internal const val DEFAULT_URL = "defaultURL"
88

src/main/kotlin/com/coder/toolbox/views/DeploymentUrlStep.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class DeploymentUrlStep(
6363
errorField.textState.update {
6464
context.i18n.pnotr("")
6565
}
66-
urlField.textState.update {
67-
context.secrets.lastDeploymentURL
66+
urlField.contentState.update {
67+
context.deploymentUrl.toString()
6868
}
6969

7070
signatureFallbackStrategyField.checkedState.update {

0 commit comments

Comments
 (0)