Secure initial Roller setup with one-time operator token - #189
Conversation
snoopdave
left a comment
There was a problem hiding this comment.
PR-Review: 1 inline blocking comment posted.
| || uri.endsWith("/bootstrap-token!redeem.rol")); | ||
| boolean installer = uri != null && (tokenPage || uri.contains("/roller-ui/install/") | ||
| || uri.endsWith("/roller-ui/register.rol") | ||
| || uri.endsWith("/roller-ui/register!save.rol") |
There was a problem hiding this comment.
🐞Codex Issue: Blocking: The gate only matches /register.rol and /register!save.rol, but Struts dynamic method invocation also exposes URLs such as /register!execute.rol and /register!activate.rol. Those direct routes can reach the initial registration flow without the token. Gate the resolved register action/method (or reject all register methods while bootstrap is incomplete) and add regression coverage for every allowed method.
|
PR-Review: General Issues The following issue needs action but is not tied to one changed line:
|
|
Need to test this on the 5.2 -> 6.1.6 upgrade, that is the last actual DB schema change |
snoopdave
left a comment
There was a problem hiding this comment.
🤖Claude: Tested the full upgrade path against a real Apache Roller 5.2.0 binary release (the Nov 2017 roller-release-5.2.0-standard.zip) rather than a synthetic schema — installed 5.2.0 on MySQL 8, created a user, weblog and published entry through the UI, then deployed this branch (20cc99c) over the same database and upgraded in place.
What passed
| Check | Result |
|---|---|
| Startup on a 520 database prints the one-time token banner | ✅ |
/ redirects to bootstrap-token.rol |
✅ |
| Token never appears in the rendered page or in a URL | ✅ |
| Wrong token → "invalid or has expired" | ✅ |
| Correct token → upgrade screen → 520→616 upgrade runs | ✅ |
| Restart with nothing to upgrade → no upgrade screen, no token issued | ✅ |
| Second browser session blocked while the first holds the grant | ✅ |
| Token replay in a second session rejected (one-time use) | ✅ |
| Fresh random token per JVM start | ✅ |
The 5.2.0 user, weblog and entry all survived the upgrade, the weblog renders, and roller.log is clean.
Three comments inline. One more that can't be line-anchored because it's outside the diff:
DatabaseInstaller.tableExists() (line 820) misdetects tables across schemas on Connector/J 8
ResultSet rs = con.getMetaData().getTables(null, null, "%", null);Connector/J 5.1 read a null catalog as "the current database"; Connector/J 8 reads it as every database on the server (nullCatalogMeansCurrent now defaults to false). On a MySQL server hosting more than one Roller schema, isCreationRequired() finds userrole / roller_user in some other database and returns false, so a genuinely empty schema is treated as already installed. WebloggerStartup.prepare() then falls through to isUpgradeRequired(), logs ERROR setting database version, and the context dies with Table '<schema>.roller_properties' doesn't exist — while Tomcat reports only "One or more listeners failed to start".
This bit while setting up the 5.2.0 instance for this test and took a while to diagnose, since it presents as a datasource or schema problem. Worked around with nullCatalogMeansCurrent=true in the JDBC URL; passing con.getCatalog() instead of null would fix it properly. Not introduced by this PR — raising it because this PR is already reshaping install/upgrade gating in the same class, and the same tableExists() is present unchanged back in 5.2.0.
BootstrapSecurity.complete() was only reached from RollerContext at startup and from Register when the first administrator is committed. The upgrade path hits neither: an existing site already has users, so no registration follows, and contextInitialized does not run again after the installer bootstraps the business tier. That left the gate half-open between the end of the web upgrade and the next restart. Anonymous requests to the installer were redirected to a setup-token page that could no longer be satisfied, because the token digest was consumed at redemption, and the marker was not written until Tomcat was restarted. Move the completion policy into BootstrapSecurity.completeIfInstalled and call it from both RollerContext and Install.bootstrap(). A database that already has users has no first administrator left to register, so setup is finished and the gate closes immediately. A brand new install still leaves it open for Register to close. Verified against a real Roller 5.2.0 instance upgraded in place to 616: the marker is now written without a restart, and both the anonymous and the redeeming session are refused the installer. The fresh-install path still reaches registration, and unauthorized sessions are still turned away. Claude-Session: https://claude.ai/code/session_015X69HHQ5XnjRkJP8ymzwFf
Summary
Validation
mvn -pl app -DskipTests compileBootstrapSecurityTestandInstallTestgit diff --checkNew log messages
Here's what the new seucurity code looks like in
roller.log:New security code page
And here's the new securoty code page:
Release consideration
If we do an RC4, we should consider including this PR in that release candidate.