Skip to content

Commit 1b6388c

Browse files
authored
fix: IDE launch fails when opening a URI targeting a workspace owned … (#324)
…by a different user When a Coder URI pointed to a workspace owned by a user other than the one currently authenticated, the IDE would fail to launch with a misleading error claiming no matching IDE version was available. The real problem was that the workspace poller only fetched the authenticated user's own workspaces, so the target workspace never made it into the plugin's environment registry. When the IDE launch tried to inspect that workspace, Toolbox reported it as non-existent, which cascaded into empty IDE lists and the confusing version mismatch error. The fix intercepts URI handling — for both the same-deployment and new-deployment flows, and regardless of whether token, mTLS, or OAuth2 authentication is in use — and scopes the workspace filter to owner:<x> name:<y> before the protocol handler runs. This ensures the poller fetches exactly the target workspace and registers it with Toolbox in time for the IDE launch to succeed. When the URI targets the authenticated user's own workspace, the filter is reset to the default owner:me instead, which also cleans up any leftover non-owned filter from a previous URI. - resolves DEVEX-372 - resolves DEVEX-374
1 parent e014646 commit 1b6388c

3 files changed

Lines changed: 39 additions & 7 deletions

File tree

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import com.coder.toolbox.util.DialogUi
1414
import com.coder.toolbox.util.TOKEN
1515
import com.coder.toolbox.util.URL
1616
import com.coder.toolbox.util.WebUrlValidationResult.Invalid
17+
import com.coder.toolbox.util.owner
1718
import com.coder.toolbox.util.toQueryParameters
1819
import com.coder.toolbox.util.toURL
1920
import com.coder.toolbox.util.token
2021
import com.coder.toolbox.util.url
2122
import com.coder.toolbox.util.validateStrictWebUrl
2223
import com.coder.toolbox.util.withPath
24+
import com.coder.toolbox.util.workspace
2325
import com.coder.toolbox.views.Action
2426
import com.coder.toolbox.views.CoderDelimiter
2527
import com.coder.toolbox.views.CoderSettingsPage
@@ -385,7 +387,7 @@ class CoderRemoteProvider(
385387
} else {
386388
this.client!! to this.cli!!
387389
}
388-
linkHandler.handle(params, newUrl, activeSession.first, activeSession.second)
390+
handleLink(params, newUrl, activeSession.first, activeSession.second)
389391
} finally {
390392
coderHeaderPage.isBusy.update { false }
391393
}
@@ -662,18 +664,36 @@ class CoderRemoteProvider(
662664
}
663665

664666
/**
665-
* Returns a [SuspendBiConsumer] that handles the given link parameters.
666-
* Runs in a background coroutine so it doesn't block the connect step's
667-
* post-connection flow.
667+
* Applies the appropriate workspace filter for the URI then delegates to [linkHandler].
668+
* Scopes the filter to the target workspace when it is owned by a different user so the poll
669+
* fetches and registers it before the IDE launch runs. For own workspaces, resets to the
670+
* default filter, clearing any leftover non-owned filter from a previous URI.
668671
*/
672+
private suspend fun handleLink(
673+
params: Map<String, String>,
674+
url: URL,
675+
client: CoderRestClient,
676+
cli: CoderCLIManager,
677+
) {
678+
val uriOwner = params.owner()
679+
val uriWorkspace = params.workspace()
680+
if (!uriOwner.isNullOrBlank() && !uriWorkspace.isNullOrBlank() && uriOwner != client.me.username) {
681+
coderHeaderPage.setFilter("owner:$uriOwner name:$uriWorkspace")
682+
} else {
683+
coderHeaderPage.resetFilter()
684+
}
685+
linkHandler.handle(params, url, client, cli)
686+
}
687+
688+
/** Returns a [SuspendBiConsumer] that handles the given link parameters in a background coroutine. */
669689
private fun deferredLinkHandler(
670690
params: Map<String, String>,
671691
deploymentUrl: URL,
672692
): SuspendBiConsumer<CoderRestClient, CoderCLIManager> = SuspendBiConsumer { client, cli ->
673693
context.cs.launch(CoroutineName("Deferred Link Handler")) {
674694
coderHeaderPage.isBusy.update { true }
675695
try {
676-
linkHandler.handle(params, deploymentUrl, client, cli)
696+
handleLink(params, deploymentUrl, client, cli)
677697
} catch (ex: Exception) {
678698
context.logAndShowError(
679699
"Error handling deferred link",

src/main/kotlin/com/coder/toolbox/util/WorkspaceFilterQuery.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,6 @@ val WORKSPACE_FILTER_PRESETS = listOf(
7474
)
7575

7676
/** The preset name whose query exactly matches this query, or [CUSTOM_WORKSPACE_FILTER_NAME] if none. */
77-
fun String.presetNameForQuery(): String =
78-
WORKSPACE_FILTER_PRESETS.firstOrNull { it.query == trim() }?.name ?: CUSTOM_WORKSPACE_FILTER_NAME
77+
fun String.presetNameForQuery(): String {
78+
return WORKSPACE_FILTER_PRESETS.firstOrNull { it.query == this.trim() }?.name ?: CUSTOM_WORKSPACE_FILTER_NAME
79+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,17 @@ class NewEnvironmentPage(
177177
reportFilterError(null)
178178
}
179179

180+
/**
181+
* Sets the filter to an arbitrary query, e.g. when a URI targets a workspace owned by a
182+
* different user. Updates both the visible search field and the backing query so the poll
183+
* immediately uses the new value.
184+
*/
185+
fun setFilter(query: String) {
186+
workspaceSearchField.contentState.value = query
187+
mutableWorkspaceSearchQuery.value = query
188+
reportFilterError(null)
189+
}
190+
180191
/**
181192
* Shows the server's validation [message] under the search bar, or clears it when [message] is
182193
* null. Called by the provider when a workspace query is rejected or succeeds.

0 commit comments

Comments
 (0)