mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Add the possibility to still configure reverse proxy to web UI even if an admin user already exists
This commit is contained in:
parent
5ed0813c65
commit
33ad4c5f29
2 changed files with 134 additions and 115 deletions
|
|
@ -412,7 +412,7 @@ def set_csp_header(response):
|
|||
+ " style-src 'self' 'unsafe-inline';"
|
||||
+ " img-src 'self' data: https://assets.bunkerity.com;"
|
||||
+ " font-src 'self' data:;"
|
||||
+ (" connect-src *;" if not app.config["USER"] else "")
|
||||
+ " connect-src *;"
|
||||
)
|
||||
return response
|
||||
|
||||
|
|
@ -503,31 +503,34 @@ def setup():
|
|||
return redirect(url_for("home"))
|
||||
|
||||
db_config = app.config["CONFIG"].get_config(methods=False)
|
||||
db_user = db.get_ui_user()
|
||||
|
||||
for server_name in db_config["SERVER_NAME"].split(" "):
|
||||
if db_config.get(f"{server_name}_USE_UI", "no") == "yes":
|
||||
return redirect(url_for("login"), 301)
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
is_request_form("setup")
|
||||
|
||||
if not any(key in request.form for key in ("admin_username", "admin_password", "admin_password_check", "server_name", "ui_host", "ui_url")):
|
||||
return redirect_flash_error(
|
||||
"Missing either admin_username, admin_password, admin_password_check, server_name, ui_host, ui_url or auto_lets_encrypt parameter.", "setup"
|
||||
)
|
||||
required_keys = ["server_name", "ui_host", "ui_url"]
|
||||
if not db_user:
|
||||
required_keys.extend(["admin_username", "admin_password", "admin_password_check"])
|
||||
|
||||
if len(request.form["admin_username"]) > 256:
|
||||
return redirect_flash_error("The admin username is too long. It must be less than 256 characters.", "setup")
|
||||
if not any(key in request.form for key in required_keys):
|
||||
return redirect_flash_error(f"Missing either one of the following parameters: {', '.join(required_keys)}.", "setup")
|
||||
|
||||
if request.form["admin_password"] != request.form["admin_password_check"]:
|
||||
return redirect_flash_error("The passwords do not match.", "setup")
|
||||
if not db_user:
|
||||
if len(request.form["admin_username"]) > 256:
|
||||
return redirect_flash_error("The admin username is too long. It must be less than 256 characters.", "setup")
|
||||
|
||||
if not USER_PASSWORD_RX.match(request.form["admin_password"]):
|
||||
return redirect_flash_error(
|
||||
"The admin password is not strong enough. It must contain at least 8 characters, including at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character (#@?!$%^&*-).",
|
||||
"setup",
|
||||
)
|
||||
if request.form["admin_password"] != request.form["admin_password_check"]:
|
||||
return redirect_flash_error("The passwords do not match.", "setup")
|
||||
|
||||
if not USER_PASSWORD_RX.match(request.form["admin_password"]):
|
||||
return redirect_flash_error(
|
||||
"The admin password is not strong enough. It must contain at least 8 characters, including at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character (#@?!$%^&*-).",
|
||||
"setup",
|
||||
)
|
||||
|
||||
server_names = db_config["SERVER_NAME"].split(" ")
|
||||
if request.form["server_name"] in server_names:
|
||||
|
|
@ -540,13 +543,14 @@ def setup():
|
|||
if not REVERSE_PROXY_PATH.match(request.form["ui_host"]):
|
||||
return redirect_flash_error("The hostname is not valid.", "setup")
|
||||
|
||||
app.config["USER"] = User(request.form["admin_username"], request.form["admin_password"], method="ui")
|
||||
if not db_user:
|
||||
app.config["USER"] = User(request.form["admin_username"], request.form["admin_password"], method="ui")
|
||||
|
||||
ret = db.create_ui_user(request.form["admin_username"], app.config["USER"].password_hash, method="ui")
|
||||
if ret:
|
||||
return redirect_flash_error(f"Couldn't create the admin user in the database: {ret}", "setup", False, "error")
|
||||
ret = db.create_ui_user(request.form["admin_username"], app.config["USER"].password_hash, method="ui")
|
||||
if ret:
|
||||
return redirect_flash_error(f"Couldn't create the admin user in the database: {ret}", "setup", False, "error")
|
||||
|
||||
flash("The admin user was created successfully", "success")
|
||||
flash("The admin user was created successfully", "success")
|
||||
|
||||
app.config["RELOADING"] = True
|
||||
app.config["LAST_RELOAD"] = time()
|
||||
|
|
@ -575,6 +579,7 @@ def setup():
|
|||
|
||||
return render_template(
|
||||
"setup.html",
|
||||
ui_user=db_user,
|
||||
username=getenv("ADMIN_USERNAME", ""),
|
||||
password=getenv("ADMIN_PASSWORD", ""),
|
||||
ui_host=db_config.get("UI_HOST", getenv("UI_HOST", "")),
|
||||
|
|
|
|||
204
src/ui/templates/setup.html
vendored
204
src/ui/templates/setup.html
vendored
|
|
@ -121,58 +121,62 @@
|
|||
name="next"
|
||||
value="{{ request.values.get('next', '') }}" />
|
||||
<h2 class="col-span-12 block text-left font-bold my-4 text-2xl">Account</h2>
|
||||
<!-- username inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Username<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_username">Username</label>
|
||||
<input type="text"
|
||||
id="admin_username"
|
||||
name="admin_username"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="enter username"
|
||||
value="{{ username }}"
|
||||
pattern="(.*?)"
|
||||
maxlength="256"
|
||||
required />
|
||||
</div>
|
||||
<!-- end username inpt-->
|
||||
<!-- password inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Password<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_password">Password</label>
|
||||
<input type="password"
|
||||
id="admin_password"
|
||||
name="admin_password"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="enter password"
|
||||
value="{{ password }}"
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
required />
|
||||
</div>
|
||||
<!-- end password inpt-->
|
||||
<!-- password inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Confirm Password<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_password_check">Confirm Password</label>
|
||||
<input type="password"
|
||||
id="admin_password_check"
|
||||
name="admin_password_check"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="confirm password"
|
||||
value="{{ password }}"
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
required />
|
||||
<strong class="opacity-0 font-normal text-sm text-red-500" data-pw-alert>Value does not match password</strong>
|
||||
</div>
|
||||
<!-- end password inpt-->
|
||||
{% if not ui_user %}
|
||||
<!-- username inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Username<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_username">Username</label>
|
||||
<input type="text"
|
||||
id="admin_username"
|
||||
name="admin_username"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="enter username"
|
||||
value="{{ username }}"
|
||||
pattern="(.*?)"
|
||||
maxlength="256"
|
||||
required />
|
||||
</div>
|
||||
<!-- end username inpt-->
|
||||
<!-- password inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Password<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_password">Password</label>
|
||||
<input type="password"
|
||||
id="admin_password"
|
||||
name="admin_password"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="enter password"
|
||||
value="{{ password }}"
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
required />
|
||||
</div>
|
||||
<!-- end password inpt-->
|
||||
<!-- password inpt-->
|
||||
<div class="flex flex-col relative col-span-12 my-3 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0">
|
||||
Confirm Password<strong class="required-mark">*</strong>
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_password_check">Confirm Password</label>
|
||||
<input type="password"
|
||||
id="admin_password_check"
|
||||
name="admin_password_check"
|
||||
class="col-span-12 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="confirm password"
|
||||
value="{{ password }}"
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
required />
|
||||
<strong class="opacity-0 font-normal text-sm text-red-500" data-pw-alert>Value does not match password</strong>
|
||||
</div>
|
||||
<!-- end password inpt-->
|
||||
{% else %}
|
||||
<h6 class="col-span-12 block text-left font-bold mb-4 mt-2">🧑🚀 An admin user already exists</h6>
|
||||
{% endif %}
|
||||
<!-- email inpt-->
|
||||
<div class="flex flex-col relative col-span-12 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base mb-1 transition duration-300 ease-in-out text-md font-bold m-0">Email</h5>
|
||||
|
|
@ -511,9 +515,11 @@
|
|||
|
||||
class SubmitForm {
|
||||
constructor() {
|
||||
this.pwEl = document.querySelector("#admin_password");
|
||||
this.pwCheckEl = document.querySelector("#admin_password_check");
|
||||
this.pwAlertEl = document.querySelector("[data-pw-alert]");
|
||||
{% if not ui_user %}
|
||||
this.pwEl = document.querySelector("#admin_password");
|
||||
this.pwCheckEl = document.querySelector("#admin_password_check");
|
||||
this.pwAlertEl = document.querySelector("[data-pw-alert]");
|
||||
{% endif %}
|
||||
this.formEl = document.querySelector("#setup-form");
|
||||
this.servInp = document.querySelector("#server_name");
|
||||
this.sslCheck = document.querySelector("#auto_lets_encrypt");
|
||||
|
|
@ -529,15 +535,17 @@
|
|||
}
|
||||
|
||||
init() {
|
||||
// Check password to send
|
||||
this.checkSamePW();
|
||||
this.pwCheckEl.addEventListener("input", (e) => {
|
||||
{% if not ui_user %}
|
||||
// Check password to send
|
||||
this.checkSamePW();
|
||||
});
|
||||
this.pwCheckEl.addEventListener("input", (e) => {
|
||||
this.checkSamePW();
|
||||
});
|
||||
|
||||
this.pwEl.addEventListener("input", (e) => {
|
||||
this.checkSamePW();
|
||||
});
|
||||
this.pwEl.addEventListener("input", (e) => {
|
||||
this.checkSamePW();
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
// Case error and display msg
|
||||
window.addEventListener("click", (e) => {
|
||||
|
|
@ -554,9 +562,11 @@
|
|||
|
||||
// Submit
|
||||
this.formEl.addEventListener("submit", (e) => {
|
||||
// Case not same PW
|
||||
if (this.pwEl.value !== this.pwCheckEl.value)
|
||||
return e.preventDefault();
|
||||
{% if not ui_user %}
|
||||
// Case not same PW
|
||||
if (this.pwEl.value !== this.pwCheckEl.value)
|
||||
return e.preventDefault();
|
||||
{% endif %}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
|
|
@ -622,15 +632,17 @@
|
|||
document.querySelector('#newsletter-form').submit();
|
||||
}
|
||||
|
||||
checkSamePW() {
|
||||
if (!this.pwEl.value || !this.pwCheckEl.value)
|
||||
{% if not ui_user %}
|
||||
checkSamePW() {
|
||||
if (!this.pwEl.value || !this.pwCheckEl.value)
|
||||
return this.hidePWAlert();
|
||||
|
||||
if (this.pwEl.value !== this.pwCheckEl.value)
|
||||
return this.showPWAlert();
|
||||
|
||||
return this.hidePWAlert();
|
||||
|
||||
if (this.pwEl.value !== this.pwCheckEl.value)
|
||||
return this.showPWAlert();
|
||||
|
||||
return this.hidePWAlert();
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
showErrMsg() {
|
||||
this.flashMsg.classList.remove("hidden");
|
||||
|
|
@ -642,29 +654,31 @@
|
|||
this.flashMsg.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
|
||||
hidePWAlert() {
|
||||
this.pwCheckEl.classList.remove(
|
||||
"focus:!border-red-500",
|
||||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.add("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
{% if not ui_user %}
|
||||
hidePWAlert() {
|
||||
this.pwCheckEl.classList.remove(
|
||||
"focus:!border-red-500",
|
||||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.add("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
|
||||
showPWAlert() {
|
||||
this.pwCheckEl.classList.add(
|
||||
"focus:!border-red-500",
|
||||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.remove("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "false");
|
||||
}
|
||||
showPWAlert() {
|
||||
this.pwCheckEl.classList.add(
|
||||
"focus:!border-red-500",
|
||||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.remove("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "false");
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
showLoader() {
|
||||
this.loaderMsg.textContent = "Setting up...";
|
||||
|
|
|
|||
Loading…
Reference in a new issue