Skip to content

Commit 5e1e828

Browse files
bpamiriclaudePeter Amiri
authored
fix(view): render debug bar env quick-switch links only when switching can work (#3141)
Since #2082 the ?reload=<environment> switch requires a non-empty reloadPassword plus a matching password parameter (and is gated by allowEnvironmentSwitchViaUrl), but the debug bar still rendered the Testing/Maintenance/Production quick-switch anchors only when NO reloadPassword was set — the exact configuration where switching is impossible. Clicking one restarted the app and silently stayed in the current environment. The Environment panel now renders the links only when a reloadPassword is configured AND allowEnvironmentSwitchViaUrl resolves true. The links never embed the password: a new wdbEnvSwitch() handler prompts for it at click time and issues the documented ?reload=<environment>&password=... request. The plain ?reload=true reload anchor keeps its existing no-password gate. Guides (debug-panel.mdx, environments-and-configuration.mdx) updated to describe the working behavior instead of the dead-link caveat. Fixes #3060 Signed-off-by: Peter Amiri <peter@alurium.com> Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Peter Amiri <petera@pai.com>
1 parent 96bc2ca commit 5e1e828

5 files changed

Lines changed: 122 additions & 6 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Debug bar: environment quick-switch links (Testing / Maintenance / Production) now render only when switching can actually work — a non-empty `reloadPassword` is configured and `allowEnvironmentSwitchViaUrl` allows it — and prompt for the reload password at click time instead of rendering dead anchors when no password is set (the configuration where `?reload=<environment>` has been a no-op since #2082). The password is never embedded in the page, and the plain `?reload=true` reload anchor keeps its existing behavior ([#3060](https://github.com/wheels-dev/wheels/issues/3060))

vendor/wheels/events/onrequestend/debug.cfm

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,20 @@ OR (StructKeyExists(url, "format") AND ListFindNoCase("json,xml,csv,pdf", url.fo
301301
<dd>
302302
<span class="wdb-env-dot" style="background:#local.envColor#;"></span>
303303
#capitalize($get("environment"))#
304-
<cfif NOT Len($get("reloadPassword"))>
304+
<!---
305+
Quick-switch links render only when switching can actually work:
306+
since ##2082 the ?reload=<env> switch requires a non-empty
307+
reloadPassword (plus a matching password parameter) and is gated
308+
by allowEnvironmentSwitchViaUrl. The password is never embedded
309+
in the page — wdbEnvSwitch() prompts for it at click time and
310+
builds the documented ?reload=<env>&password=... request.
311+
--->
312+
<cfif Len($get("reloadPassword")) AND $get("allowEnvironmentSwitchViaUrl")>
305313
<cfset local.environments = "development,testing,maintenance,production">
306314
&mdash;
307315
<cfloop list="#local.environments#" index="local.ei">
308316
<cfif $get("environment") IS NOT local.ei>
309-
<a href="#EncodeForHTMLAttribute(local.baseReloadURL & local.ei)#" style="color:##89b4fa;font-size:11px;margin-left:4px;">#capitalize(local.ei)#</a>
317+
<a href="##" data-wdb-reload="#EncodeForHTMLAttribute(local.baseReloadURL & local.ei)#" onclick="return wdbEnvSwitch(this);" title="Switch to #capitalize(local.ei)# (prompts for the reload password)" style="color:##89b4fa;font-size:11px;margin-left:4px;">#capitalize(local.ei)#</a>
310318
</cfif>
311319
</cfloop>
312320
</cfif>
@@ -545,6 +553,14 @@ OR (StructKeyExists(url, "format") AND ListFindNoCase("json,xml,csv,pdf", url.fo
545553
document.getElementById('wdb-minimized').style.display='none';
546554
try{sessionStorage.removeItem('wdb-hidden');}catch(e){}
547555
};
556+
window.wdbEnvSwitch=function(el){
557+
var target=el.getAttribute('data-wdb-reload');
558+
if(!target)return false;
559+
var pw=window.prompt('Enter the reload password to switch environments:');
560+
if(pw===null||pw==='')return false;
561+
window.location.href=target+'&password='+encodeURIComponent(pw);
562+
return false;
563+
};
548564
try{if(sessionStorage.getItem('wdb-hidden')==='1')wdbMinimize();}catch(e){}
549565
})();
550566
</script>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
component extends="wheels.WheelsTest" {
2+
3+
function run() {
4+
describe("debug.cfm environment quick-switch links (issue 3060)", () => {
5+
// Since ##2082, switching environments via ?reload=<env> requires a
6+
// non-empty reloadPassword plus a matching password parameter, and is
7+
// additionally gated by allowEnvironmentSwitchViaUrl. The debug bar's
8+
// Environment panel used to render the quick-switch anchors only when
9+
// NO reloadPassword was set — the exact configuration where switching
10+
// is impossible — and the anchors embedded no password, so clicking
11+
// one silently restarted the app in the same environment. The fixed
12+
// behavior renders the links only when switching CAN work, as
13+
// prompt-based links that never embed the password in the page.
14+
15+
it("renders no quick-switch links when reloadPassword is empty", () => {
16+
var output = $renderDebugBar(reloadPassword = "", allowSwitch = true);
17+
expect(output contains 'data-wdb-reload="').toBeFalse(
18+
"quick-switch links must not render without a reloadPassword (switching cannot work)"
19+
);
20+
expect(output contains "margin-left:4px").toBeFalse(
21+
"the legacy dead quick-switch anchors must not render when reloadPassword is empty"
22+
);
23+
// The plain one-click reload anchor keeps its pre-existing no-password gate.
24+
expect(output contains 'title="Reload Application"').toBeTrue(
25+
"the plain ?reload=true anchor must keep rendering when no reloadPassword is set"
26+
);
27+
});
28+
29+
it("renders prompt-based quick-switch links when a reloadPassword is set and switching is allowed", () => {
30+
var output = $renderDebugBar(reloadPassword = "spec-secret-pw-3060", allowSwitch = true);
31+
expect(output contains 'data-wdb-reload="').toBeTrue(
32+
"quick-switch links must render when a reloadPassword is configured and allowEnvironmentSwitchViaUrl is true"
33+
);
34+
expect(output contains "wdbEnvSwitch").toBeTrue(
35+
"quick-switch links must go through the password prompt handler"
36+
);
37+
expect(output contains "spec-secret-pw-3060").toBeFalse(
38+
"the reload password must never be embedded in the rendered page"
39+
);
40+
// The plain one-click reload anchor keeps its pre-existing password gate.
41+
expect(output contains 'title="Reload Application"').toBeFalse(
42+
"the plain ?reload=true anchor must stay hidden when a reloadPassword is set"
43+
);
44+
});
45+
46+
it("renders no quick-switch links when allowEnvironmentSwitchViaUrl is false even with a password set", () => {
47+
var output = $renderDebugBar(reloadPassword = "spec-secret-pw-3060", allowSwitch = false);
48+
expect(output contains 'data-wdb-reload="').toBeFalse(
49+
"quick-switch links must not render when environment switching via URL is disallowed"
50+
);
51+
});
52+
});
53+
}
54+
55+
/**
56+
* Renders the debug bar template with the given reloadPassword and
57+
* allowEnvironmentSwitchViaUrl applied, restoring all touched state.
58+
* Modeled on debugBarEncodingSpec.cfc.
59+
*/
60+
private string function $renderDebugBar(required string reloadPassword, required boolean allowSwitch) {
61+
var priorReloadPassword = application.wheels.reloadPassword;
62+
var hadAllowSwitch = StructKeyExists(application.wheels, "allowEnvironmentSwitchViaUrl");
63+
var priorAllowSwitch = hadAllowSwitch ? application.wheels.allowEnvironmentSwitchViaUrl : true;
64+
var priorReqWheels = StructKeyExists(request, "wheels") ? Duplicate(request.wheels) : {};
65+
// debug.cfm bails out (cfexit) when url.format is one of json/xml/csv/pdf
66+
// so it never breaks an API response. The test runner is hit with
67+
// format=json — clear it for the duration of the include.
68+
var hadUrlFormat = StructKeyExists(url, "format");
69+
var priorUrlFormat = hadUrlFormat ? url.format : "";
70+
var output = "";
71+
try {
72+
application.wheels.reloadPassword = arguments.reloadPassword;
73+
application.wheels.allowEnvironmentSwitchViaUrl = arguments.allowSwitch;
74+
if (!StructKeyExists(request, "wheels")) {
75+
request.wheels = {};
76+
}
77+
request.wheels.execution = {total = 0};
78+
request.wheels.params = {controller = "wheels", action = "tests", route = ""};
79+
if (hadUrlFormat) {
80+
StructDelete(url, "format");
81+
}
82+
output = application.wo.$includeAndReturnOutput($template = "/wheels/events/onrequestend/debug.cfm");
83+
} finally {
84+
application.wheels.reloadPassword = priorReloadPassword;
85+
if (hadAllowSwitch) {
86+
application.wheels.allowEnvironmentSwitchViaUrl = priorAllowSwitch;
87+
} else {
88+
StructDelete(application.wheels, "allowEnvironmentSwitchViaUrl");
89+
}
90+
request.wheels = priorReqWheels;
91+
if (hadUrlFormat) {
92+
url.format = priorUrlFormat;
93+
}
94+
}
95+
return output;
96+
}
97+
98+
}

web/sites/guides/src/content/docs/v4-0-0/core-concepts/environments-and-configuration.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ A running app can switch environments without a redeploy: request `?reload=<envi
8787

8888
The gate is `allowEnvironmentSwitchViaUrl`. Any explicit boolean you `set()` is honored in every environment. If you never touch it, the default is `false` in `production`, `testing`, and `maintenance`, and `true` everywhere else. Leave it disabled in production.
8989

90-
Two caveats, tracked as open issues:
90+
In development, the debug bar's Environment panel offers the same switch as quick-switch links: they render when a `reloadPassword` is configured and switching via the URL is allowed, and clicking one prompts for the reload password (it is never embedded in the page).
91+
92+
One caveat, tracked as an open issue:
9193

92-
- The debug bar's environment quick-switch links are currently broken ([#3060](https://github.com/wheels-dev/wheels/issues/3060)) — use the URL form above.
9394
- The `wheels reload` CLI command can report success even when the reload didn't take effect ([#3059](https://github.com/wheels-dev/wheels/issues/3059)) — verify with the URL form.
9495

9596
## Secrets handling

web/sites/guides/src/content/docs/v4-0-0/digging-deeper/debug-panel.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ The Environment tab label shows the current environment name (Development, Testi
111111

112112
Always shown. Lists the environment, the git branch (when the **web root**`public/` — contains a `.git` directory; in a standard app layout the `.git` directory lives at the project root, so the row does not appear), the Wheels version, the CFML engine and version, and the host name (when available).
113113

114-
When no reload password is set, the environment name is followed by quick-switch links. These links are dead: any URL-based reload — environment switch or plain restart — requires a configured `reloadPassword` plus a matching `password` parameter, which is exactly the state in which the links do not render, so clicking one serves the page normally without restarting anything. See [#3060](https://github.com/wheels-dev/wheels/issues/3060).
114+
When a `reloadPassword` is configured and `allowEnvironmentSwitchViaUrl` resolves to `true`, the environment name is followed by quick-switch links for the other environments. Clicking one prompts for the reload password — it is never embedded in the page — and then issues the documented `?reload=<environment>&password=<your password>` request. When no `reloadPassword` is set, or switching via the URL is disallowed, the links do not render because the switch cannot work. See [#3060](https://github.com/wheels-dev/wheels/issues/3060).
115115

116-
To actually switch environments at runtime, set a `reloadPassword` in `config/settings.cfm` and request `?reload=<environment>&password=<your password>`. The switch is additionally gated by the `allowEnvironmentSwitchViaUrl` setting: an explicit boolean you set is honored in every environment, and when unset it defaults to `false` in `production`, `testing`, and `maintenance` and `true` everywhere else. Switching to the environment you are already in is a no-op, and after a successful switch the redirect strips the reload parameters from the URL.
116+
You can also switch environments manually: set a `reloadPassword` in `config/settings.cfm` and request `?reload=<environment>&password=<your password>`. The switch is additionally gated by the `allowEnvironmentSwitchViaUrl` setting: an explicit boolean you set is honored in every environment, and when unset it defaults to `false` in `production`, `testing`, and `maintenance` and `true` everywhere else. Switching to the environment you are already in is a no-op, and after a successful switch the redirect strips the reload parameters from the URL.
117117

118118
<Aside type="caution">
119119
When no `reloadPassword` is configured, URL-based reload is disabled entirely — `?reload=true` (the bar's reload link) serves the page normally without restarting, and Wheels logs a `wheels_security` warning on boot telling you to set a password ([#3062](https://github.com/wheels-dev/wheels/issues/3062); before that fix the same state let any visitor restart the application anonymously).

0 commit comments

Comments
 (0)