Skip to content
Open
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
2 changes: 1 addition & 1 deletion registry/coder/modules/amazon-dcv-windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Enable DCV Server and Web Client on Windows workspaces.
module "dcv" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/amazon-dcv-windows/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.main.id
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
run "plan_with_defaults" {
command = plan

variables {
agent_id = "test-agent-id"
}

assert {
condition = resource.coder_app.web-dcv.url == "https://localhost:8443/?username=Administrator&password=coderDCV%21"
error_message = "The default DCV app URL should preserve its credentials."
}

assert {
condition = strcontains(resource.coder_script.install-dcv.script, "$adminPassword = 'coderDCV!'")
error_message = "The default password should use a PowerShell single-quoted string."
}
}

run "plan_with_special_characters" {
command = plan

variables {
agent_id = "test-agent-id"
admin_password = "N;JVO*U\\mL^a*P\"'`$&<>|#%+"
}

assert {
condition = endswith(resource.coder_app.web-dcv.url, "username=Administrator&password=${urlencode(var.admin_password)}")
error_message = "The DCV app URL should encode each credential parameter."
}

assert {
condition = strcontains(resource.coder_script.install-dcv.script, format("$adminPassword = '%s'", replace(var.admin_password, "'", "''")))
error_message = "The PowerShell script should preserve special characters."
}
}
2 changes: 1 addition & 1 deletion registry/coder/modules/amazon-dcv-windows/install-dcv.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Terraform variables
$adminPassword = "${admin_password}"
$adminPassword = '${admin_password}'
$port = "${port}"
$webURLPath = "${web_url_path}"

Expand Down
105 changes: 105 additions & 0 deletions registry/coder/modules/amazon-dcv-windows/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { describe, it } from "bun:test";
import {
findResourceInstance,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";

type TestVariables = Readonly<{
agent_id: string;
admin_password?: string;
}>;

const assertCredentialRoundTrip = (
actual: string | null,
expected: string,
context: string,
) => {
if (actual !== expected) {
throw new Error(`${context} did not preserve the credential`);
}
};

const extractPowerShellPassword = (script: string): string | null => {
const assignment = /^\$adminPassword = '((?:[^']|'')*)'[ \t]*\r?$/m.exec(
script,
);

return assignment?.[1]?.replaceAll("''", "'") ?? null;
};

const renderCredentials = async (adminPassword?: string) => {
const variables: TestVariables = {
agent_id: "test-agent-id",
...(adminPassword === undefined ? {} : { admin_password: adminPassword }),
};
const state = await runTerraformApply<TestVariables>(
import.meta.dir,
variables,
);
const script = findResourceInstance(state, "coder_script", "install-dcv");
const app = findResourceInstance(state, "coder_app", "web-dcv");

return {
appUrl: new URL(app.url),
password: extractPowerShellPassword(script.script),
};
};

describe("amazon-dcv-windows", async () => {
await runTerraformInit(import.meta.dir);

testRequiredVariables<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
});

it("preserves default credentials", async () => {
const rendered = await renderCredentials();

assertCredentialRoundTrip(
rendered.password,
"coderDCV!",
"Default PowerShell rendering",
);
assertCredentialRoundTrip(
rendered.appUrl.searchParams.get("username"),
"Administrator",
"Default URL username decoding",
);
assertCredentialRoundTrip(
rendered.appUrl.searchParams.get("password"),
"coderDCV!",
"Default URL password decoding",
);
});

it("preserves special characters in the PowerShell script", async () => {
const specialPassword =
String.raw`N;JVO*U\mL^a*P"` + "'" + "`" + "$&<>|#%+";
const rendered = await renderCredentials(specialPassword);

assertCredentialRoundTrip(
rendered.password,
specialPassword,
"PowerShell rendering",
);
});

it("preserves special characters in URL parameters", async () => {
const specialPassword =
String.raw`N;JVO*U\mL^a*P"` + "'" + "`" + "$&<>|#%+";
const rendered = await renderCredentials(specialPassword);

assertCredentialRoundTrip(
rendered.appUrl.searchParams.get("username"),
"Administrator",
"URL username decoding",
);
assertCredentialRoundTrip(
rendered.appUrl.searchParams.get("password"),
specialPassword,
"URL password decoding",
);
});
});
9 changes: 5 additions & 4 deletions registry/coder/modules/amazon-dcv-windows/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ resource "coder_app" "web-dcv" {
agent_id = var.agent_id
slug = var.slug
display_name = "Web DCV"
url = "https://localhost:${var.port}${local.web_url_path}?username=${local.admin_username}&password=${var.admin_password}"
url = "https://localhost:${var.port}${local.web_url_path}?username=${urlencode(local.admin_username)}&password=${urlencode(var.admin_password)}"
icon = "/icon/dcv.svg"
subdomain = var.subdomain
order = var.order
Expand All @@ -67,7 +67,7 @@ resource "coder_script" "install-dcv" {
icon = "/icon/dcv.svg"
run_on_start = true
script = templatefile("${path.module}/install-dcv.ps1", {
admin_password : var.admin_password,
admin_password : local.ps_admin_password,
port : var.port,
web_url_path : local.web_url_path
})
Expand All @@ -77,8 +77,9 @@ data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

locals {
web_url_path = var.subdomain ? "/" : format("/@%s/%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.slug)
admin_username = "Administrator"
web_url_path = var.subdomain ? "/" : format("/@%s/%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.slug)
admin_username = "Administrator"
ps_admin_password = replace(var.admin_password, "'", "''")
}

output "web_url_path" {
Expand Down