feat: allow admin password overwrite during setup recovery
This commit is contained in:
@@ -58,13 +58,16 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Provision the admin account. Aborts cleanly if one already exists
|
// 1. Provision the admin account. An admin.json file may already exist
|
||||||
// (defence in depth - should be impossible in bootstrap state).
|
// from a previous ADMIN_PASSWORD env var or an aborted earlier wizard
|
||||||
const created = await setInitialAdminPassword(adminPassword);
|
// run while setupComplete is still false — accept the wizard's
|
||||||
|
// password as authoritative in that case. The finish route is gated
|
||||||
|
// by the bootstrap state + one-time setup token, so this is safe.
|
||||||
|
const created = await setInitialAdminPassword(adminPassword, { allowOverwrite: true });
|
||||||
if (!created) {
|
if (!created) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Admin account already exists; cannot finish setup again' },
|
{ error: 'Failed to write admin credentials' },
|
||||||
{ status: 409 },
|
{ status: 500 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-3
@@ -189,11 +189,21 @@ export async function changeAdminPassword(currentPassword: string, newPassword:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the admin password without verifying a current one. Used by the setup
|
* Set the admin password without verifying a current one. Used by the setup
|
||||||
* wizard during initial bootstrap. Refuses to overwrite an existing password.
|
* wizard during initial bootstrap.
|
||||||
|
*
|
||||||
|
* Refuses to overwrite an existing password unless `allowOverwrite` is true.
|
||||||
|
* The wizard's finish route passes `allowOverwrite: true` so a half-completed
|
||||||
|
* setup (admin.json left behind by an ADMIN_PASSWORD env var or an aborted
|
||||||
|
* earlier wizard run, while setupComplete is still false) can be recovered
|
||||||
|
* by simply running the wizard again. Safe because the finish route is
|
||||||
|
* already gated by the one-time setup token.
|
||||||
*/
|
*/
|
||||||
export async function setInitialAdminPassword(newPassword: string): Promise<boolean> {
|
export async function setInitialAdminPassword(
|
||||||
|
newPassword: string,
|
||||||
|
options: { allowOverwrite?: boolean } = {},
|
||||||
|
): Promise<boolean> {
|
||||||
const existing = await readConfigData();
|
const existing = await readConfigData();
|
||||||
if (existing) return false;
|
if (existing && !options.allowOverwrite) return false;
|
||||||
const hash = await hashPassword(newPassword);
|
const hash = await hashPassword(newPassword);
|
||||||
cachedConfig = { passwordHash: hash };
|
cachedConfig = { passwordHash: hash };
|
||||||
cachedState = freshState();
|
cachedState = freshState();
|
||||||
|
|||||||
Reference in New Issue
Block a user