Bug: Setup wizard permanently locks out after creating first user, before company/localization steps are done
Summary
Immediately after the "create first user" step in setup/index.php, the wizard closes itself
($config_enable_setup = 0) even though the company and localization steps have not been
completed yet. This leaves the install in a broken state: /setup refuses to continue, and
/login.php cannot fully log the user in because companies/settings data doesn't exist yet.
The two pages redirect back and forth indefinitely (ERR_TOO_MANY_REDIRECTS /
"page isn't redirecting properly").
Reproduced on a completely fresh Debian install using the official install script
(itflow_install.sh), with selfsigned SSL, PHP 8.4, Apache 2.4.68, MariaDB.
Steps to reproduce
- Fresh install via
itflow_install.sh (any SSL option).
- Go to
/setup, complete the DB step (already pre-filled by installer).
- Fill in "Create first user" and submit.
- Browser is redirected in an infinite loop between
/setup and /login.php, before ever
reaching the "create company" step.
Root cause
In setup/index.php (top of file, before the add_user/add_company handlers):
if (file_exists("../config.php") && $mysqli_available) {
$table_result = mysqli_query($mysqli, "SHOW TABLES LIKE 'users'");
if ($table_result && mysqli_num_rows($table_result) > 0) {
$should_skip_to_user = true;
$user_count_result = mysqli_query($mysqli, "SELECT COUNT(*) AS user_count FROM users");
if ($user_count_result) {
$user_count_row = mysqli_fetch_assoc($user_count_result);
if (intval($user_count_row['user_count']) > 0) {
$install_is_live = true; // <-- fires as soon as ANY user exists
}
}
...
}
}
if (!isset($config_enable_setup)) {
$config_enable_setup = $install_is_live ? 0 : 1; // <-- setup closes here
}
$install_is_live is set to true based only on the existence of a user row — it does not
check whether companies has been populated yet. As soon as the first user is created, the
very next request to /setup sees $install_is_live = true and appends
$config_enable_setup = 0 to config.php, permanently disabling the wizard for that install.
Since the company step never runs, login.php also can't complete a normal login (missing
company/settings data), and the two pages end up redirecting to each other.
Secondary bug (same file, add_user handler)
$user_count = mysqli_num_rows(mysqli_query($mysqli,"SELECT COUNT(*) FROM users"));
if ($user_count < 0) {
SELECT COUNT(*) always returns exactly one row (containing the count value), so
mysqli_num_rows() on it is always 1, never the actual number of users. The condition
$user_count < 0 can never be true — this check is dead code and never actually prevents a
duplicate user-creation submission. (A duplicate submission separately causes an uncaught
mysqli_sql_exception: Duplicate entry '1' for key 'PRIMARY' fatal error, since the first
user is hardcoded to user_id = 1 rather than relying on auto-increment.)
Expected behavior
The wizard should only consider the install "live" (and close setup) once all required
setup steps — user, company, and localization — have completed successfully, not just the
first one.
Suggested fix
Gate $install_is_live on the existence of a companies row (or a proper "setup complete"
flag written only at the very end of the wizard), not just on users having rows. Also fix
the add_user duplicate-submission check to use the actual row count
($user_count_row['user_count']) rather than mysqli_num_rows() on a COUNT(*) query.
Workaround used
Manually inserted the company/localization/seed data directly via a PHP CLI script that reuses
the app's own functions.php / seed_data.php logic, after the user step had already silently
closed setup. Not something a typical user could be expected to do.
Bug: Setup wizard permanently locks out after creating first user, before company/localization steps are done
Summary
Immediately after the "create first user" step in
setup/index.php, the wizard closes itself(
$config_enable_setup = 0) even though the company and localization steps have not beencompleted yet. This leaves the install in a broken state:
/setuprefuses to continue, and/login.phpcannot fully log the user in becausecompanies/settingsdata doesn't exist yet.The two pages redirect back and forth indefinitely (
ERR_TOO_MANY_REDIRECTS/"page isn't redirecting properly").
Reproduced on a completely fresh Debian install using the official install script
(
itflow_install.sh), withselfsignedSSL, PHP 8.4, Apache 2.4.68, MariaDB.Steps to reproduce
itflow_install.sh(any SSL option)./setup, complete the DB step (already pre-filled by installer)./setupand/login.php, before everreaching the "create company" step.
Root cause
In
setup/index.php(top of file, before theadd_user/add_companyhandlers):$install_is_liveis set totruebased only on the existence of a user row — it does notcheck whether
companieshas been populated yet. As soon as the first user is created, thevery next request to
/setupsees$install_is_live = trueand appends$config_enable_setup = 0toconfig.php, permanently disabling the wizard for that install.Since the company step never runs,
login.phpalso can't complete a normal login (missingcompany/settings data), and the two pages end up redirecting to each other.
Secondary bug (same file,
add_userhandler)SELECT COUNT(*)always returns exactly one row (containing the count value), somysqli_num_rows()on it is always1, never the actual number of users. The condition$user_count < 0can never be true — this check is dead code and never actually prevents aduplicate user-creation submission. (A duplicate submission separately causes an uncaught
mysqli_sql_exception: Duplicate entry '1' for key 'PRIMARY'fatal error, since the firstuser is hardcoded to
user_id = 1rather than relying on auto-increment.)Expected behavior
The wizard should only consider the install "live" (and close setup) once all required
setup steps — user, company, and localization — have completed successfully, not just the
first one.
Suggested fix
Gate
$install_is_liveon the existence of acompaniesrow (or a proper "setup complete"flag written only at the very end of the wizard), not just on
usershaving rows. Also fixthe
add_userduplicate-submission check to use the actual row count(
$user_count_row['user_count']) rather thanmysqli_num_rows()on aCOUNT(*)query.Workaround used
Manually inserted the company/localization/seed data directly via a PHP CLI script that reuses
the app's own
functions.php/seed_data.phplogic, after the user step had already silentlyclosed setup. Not something a typical user could be expected to do.