1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-11-22 07:10:09 +01:00

fixing some Gunicorn bugs.. again..

This commit is contained in:
Donald Zou 2024-08-05 15:39:11 -04:00
parent bd71b6bad8
commit 0fdef6a0a2
8 changed files with 195 additions and 86 deletions

View File

@ -944,6 +944,7 @@ class DashboardConfig:
"username": "admin", "username": "admin",
"password": "admin", "password": "admin",
"enable_totp": "false", "enable_totp": "false",
"totp_verified": "false",
"totp_key": pyotp.random_base32() "totp_key": pyotp.random_base32()
}, },
"Server": { "Server": {
@ -1780,12 +1781,14 @@ Sign Up
@app.route('/api/isTotpEnabled') @app.route('/api/isTotpEnabled')
def API_isTotpEnabled(): def API_isTotpEnabled():
return ResponseObject(data=DashboardConfig.GetConfig("Account", "enable_totp")[1]) return (
ResponseObject(data=DashboardConfig.GetConfig("Account", "enable_totp")[1] and DashboardConfig.GetConfig("Account", "totp_verified")[1]))
@app.route('/api/Welcome_GetTotpLink') @app.route('/api/Welcome_GetTotpLink')
def API_Welcome_GetTotpLink(): def API_Welcome_GetTotpLink():
if DashboardConfig.GetConfig("Other", "welcome_session")[1]: if not DashboardConfig.GetConfig("Account", "totp_verified")[1]:
DashboardConfig.SetConfig("Account", "totp_key", pyotp.random_base32())
return ResponseObject( return ResponseObject(
data=pyotp.totp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).provisioning_uri( data=pyotp.totp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).provisioning_uri(
issuer_name="WGDashboard")) issuer_name="WGDashboard"))
@ -1795,11 +1798,11 @@ def API_Welcome_GetTotpLink():
@app.route('/api/Welcome_VerifyTotpLink', methods=["POST"]) @app.route('/api/Welcome_VerifyTotpLink', methods=["POST"])
def API_Welcome_VerifyTotpLink(): def API_Welcome_VerifyTotpLink():
data = request.get_json() data = request.get_json()
if DashboardConfig.GetConfig("Other", "welcome_session")[1]: totp = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now()
totp = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() if totp == data['totp']:
print(totp) DashboardConfig.SetConfig("Account", "totp_verified", "true")
return ResponseObject(totp == data['totp']) DashboardConfig.SetConfig("Account", "enable_totp", "true")
return ResponseObject(False) return ResponseObject(totp == data['totp'])
@app.route('/api/Welcome_Finish', methods=["POST"]) @app.route('/api/Welcome_Finish', methods=["POST"])
@ -1819,10 +1822,10 @@ def API_Welcome_Finish():
"repeatNewPassword": data["repeatNewPassword"], "repeatNewPassword": data["repeatNewPassword"],
"currentPassword": "admin" "currentPassword": "admin"
}) })
updateEnableTotp, updateEnableTotpErr = DashboardConfig.SetConfig("Account", "enable_totp", data["enable_totp"]) # updateEnableTotp, updateEnableTotpErr = DashboardConfig.SetConfig("Account", "enable_totp", data["enable_totp"])
if not updateUsername or not updatePassword or not updateEnableTotp: if not updateUsername or not updatePassword:
return ResponseObject(False, f"{updateUsernameErr},{updatePasswordErr},{updateEnableTotpErr}".strip(",")) return ResponseObject(False, f"{updateUsernameErr},{updatePasswordErr}".strip(","))
DashboardConfig.SetConfig("Other", "welcome_session", False) DashboardConfig.SetConfig("Other", "welcome_session", False)
@ -1888,14 +1891,18 @@ _, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
WireguardConfigurations: dict[str, WireguardConfiguration] = {} WireguardConfigurations: dict[str, WireguardConfiguration] = {}
WireguardConfigurations = _getConfigurationList() WireguardConfigurations = _getConfigurationList()
bgThread = threading.Thread(target=backGroundThread)
bgThread.daemon = True
bgThread.start()
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread)
scheduleJobThread.daemon = True def startThreads():
scheduleJobThread.start() bgThread = threading.Thread(target=backGroundThread)
bgThread.daemon = True
bgThread.start()
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread)
scheduleJobThread.daemon = True
scheduleJobThread.start()
if __name__ == "__main__": if __name__ == "__main__":
startThreads()
app.run(host=app_ip, debug=False, port=app_port) app.run(host=app_ip, debug=False, port=app_port)

View File

@ -1,10 +1,15 @@
import dashboard import dashboard
from datetime import datetime from datetime import datetime
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger
app_host, app_port = dashboard.gunicornConfig() app_host, app_port = dashboard.gunicornConfig()
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S') date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
def post_worker_init(worker):
dashboard.startThreads()
worker_class = 'gthread' worker_class = 'gthread'
workers = 1 workers = 1
threads = 1 threads = 1

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
<script>
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import {v4} from "uuid";
import {fetchPost} from "@/utilities/fetch.js";
export default {
name: "accountSettingsMFA",
setup(){
const store = DashboardConfigurationStore();
const uuid = `input_${v4()}`;
return {store, uuid};
},
data(){
return {
status: false
}
},
mounted() {
this.status = this.store.Configuration.Account["enable_totp"]
},
methods: {
async resetMFA(){
await fetchPost("/api/updateDashboardConfigurationItem", {
section: "Account",
key: "totp_verified",
value: "false"
}, async (res) => {
await fetchPost("/api/updateDashboardConfigurationItem", {
section: "Account",
key: "enable_totp",
value: "false"
}, (res) => {
if (res.status){
this.$router.push("/2FASetup")
}
})
})
}
}
}
</script>
<template>
<div>
<div class="d-flex align-items-center">
<strong>Multi-Factor Authentication</strong>
<div class="form-check form-switch ms-3">
<input class="form-check-input" type="checkbox"
v-model="this.status"
role="switch" id="allowAPIKeysSwitch">
</div>
<button class="btn bg-warning-subtle text-warning-emphasis border-1 border-warning-subtle ms-auto rounded-3 shadow-sm"
v-if="this.status" @click="this.resetMFA()">
<i class="bi bi-shield-lock-fill me-2"></i>
{{this.store.Configuration.Account["totp_verified"] ? "Reset" : "Setup" }} MFA
</button>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,15 +1,17 @@
<script> <script>
import {fetchGet, fetchPost} from "@/utilities/fetch.js"; import {fetchGet, fetchPost} from "@/utilities/fetch.js";
import QRCode from "qrcode"; import QRCode from "qrcode";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
export default { export default {
name: "totp", name: "totp",
async setup(){ async setup(){
const store = DashboardConfigurationStore();
let l = "" let l = ""
await fetchGet("/api/Welcome_GetTotpLink", {}, (res => { await fetchGet("/api/Welcome_GetTotpLink", {}, (res => {
if (res.status) l = res.data; if (res.status) l = res.data;
})); }));
return {l} return {l, store}
}, },
mounted() { mounted() {
if (this.l) { if (this.l) {
@ -58,28 +60,58 @@ export default {
</script> </script>
<template> <template>
<div class="mb-3"> <div class="container-fluid login-container-fluid d-flex main pt-5 overflow-scroll"
<p class="mb-2"><small class="text-muted">1. Please scan the following QR Code to generate TOTP</small></p> :data-bs-theme="this.store.Configuration.Server.dashboard_theme">
<canvas id="qrcode" class="rounded-3 mb-2"></canvas> <div class="m-auto text-body" style="width: 500px">
<div class="p-3 bg-body-secondary rounded-3 border mb-3"> <div class="d-flex flex-column">
<p class="text-muted mb-0"><small>Or you can click the link below:</small> <div>
</p><a :href="this.l"><code style="line-break: anywhere">{{this.l}}</code></a> <h1 class="dashboardLogo display-4">Multi-Factor Authentication</h1>
</div> <p class="mb-2"><small class="text-muted">1. Please scan the following QR Code to generate TOTP</small></p>
<label for="totp" class="mb-2"><small class="text-muted">2. Enter the TOTP generated by your authenticator to verify</small></label> <canvas id="qrcode" class="rounded-3 mb-2"></canvas>
<div class="form-group"> <div class="p-3 bg-body-secondary rounded-3 border mb-3">
<input class="form-control text-center totp" <p class="text-muted mb-0"><small>Or you can click the link below:</small>
id="totp" maxlength="6" type="text" inputmode="numeric" autocomplete="one-time-code" </p><a :href="this.l"><code style="line-break: anywhere">{{this.l}}</code></a>
v-model="this.totp" </div>
:disabled="this.verified" <label for="totp" class="mb-2"><small class="text-muted">2. Enter the TOTP generated by your authenticator to verify</small></label>
> <div class="form-group mb-2">
<div class="invalid-feedback"> <input class="form-control text-center totp"
{{this.totpInvalidMessage}} id="totp" maxlength="6" type="text" inputmode="numeric" autocomplete="one-time-code"
</div> v-model="this.totp"
<div class="valid-feedback"> :disabled="this.verified"
TOTP verified! >
<div class="invalid-feedback">
{{this.totpInvalidMessage}}
</div>
<div class="valid-feedback">
TOTP verified!
</div>
</div>
<div class="alert alert-warning rounded-3">
<i class="bi bi-exclamation-triangle-fill me-2"></i> If you ever lost your TOTP and can't login, please follow instruction on
<a href="https://github.com/donaldzou/WGDashboard" target="_blank">readme.md</a> to reset.
</div>
</div>
<hr>
<div class="d-flex gap-3 mt-5 flex-column">
<RouterLink
to="/"
v-if="!this.verified"
class="btn bg-secondary-subtle text-secondary-emphasis
rounded-3
flex-grow-1 btn-lg border-1 border-secondary-subtle shadow d-flex">
I don't need MFA <i class="bi bi-chevron-right ms-auto"></i>
</RouterLink>
<RouterLink
to="/"
v-else class="btn btn-dark btn-lg d-flex btn-brand shadow align-items-center flex-grow-1 rounded-3">
Complete <i class="bi bi-chevron-right ms-auto"></i>
</RouterLink>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>

View File

@ -14,6 +14,7 @@ import PeerList from "@/components/configurationComponents/peerList.vue";
import PeerCreate from "@/components/configurationComponents/peerCreate.vue"; import PeerCreate from "@/components/configurationComponents/peerCreate.vue";
import Ping from "@/views/ping.vue"; import Ping from "@/views/ping.vue";
import Traceroute from "@/views/traceroute.vue"; import Traceroute from "@/views/traceroute.vue";
import Totp from "@/components/setupComponent/totp.vue";
const checkAuth = async () => { const checkAuth = async () => {
let result = false let result = false
@ -103,6 +104,12 @@ const router = createRouter({
meta: { meta: {
requiresAuth: true requiresAuth: true
}, },
},
{
path: '/2FASetup', component: Totp,
meta: {
requiresAuth: true
},
} }
] ]
}); });

View File

@ -11,11 +11,13 @@ import DashboardTheme from "@/components/settingsComponent/dashboardTheme.vue";
import DashboardSettingsInputIPAddressAndPort import DashboardSettingsInputIPAddressAndPort
from "@/components/settingsComponent/dashboardSettingsInputIPAddressAndPort.vue"; from "@/components/settingsComponent/dashboardSettingsInputIPAddressAndPort.vue";
import DashboardAPIKeys from "@/components/settingsComponent/dashboardAPIKeys.vue"; import DashboardAPIKeys from "@/components/settingsComponent/dashboardAPIKeys.vue";
import AccountSettingsMFA from "@/components/settingsComponent/accountSettingsMFA.vue";
export default { export default {
name: "settings", name: "settings",
methods: {ipV46RegexCheck}, methods: {ipV46RegexCheck},
components: { components: {
AccountSettingsMFA,
DashboardAPIKeys, DashboardAPIKeys,
DashboardSettingsInputIPAddressAndPort, DashboardSettingsInputIPAddressAndPort,
DashboardTheme, DashboardTheme,
@ -49,7 +51,7 @@ export default {
<PeersDefaultSettingsInput targetData="peer_mtu" title="MTU (Max Transmission Unit)"></PeersDefaultSettingsInput> <PeersDefaultSettingsInput targetData="peer_mtu" title="MTU (Max Transmission Unit)"></PeersDefaultSettingsInput>
<PeersDefaultSettingsInput targetData="peer_keep_alive" title="Persistent Keepalive"></PeersDefaultSettingsInput> <PeersDefaultSettingsInput targetData="peer_keep_alive" title="Persistent Keepalive"></PeersDefaultSettingsInput>
<PeersDefaultSettingsInput targetData="remote_endpoint" title="Peer Remote Endpoint" <PeersDefaultSettingsInput targetData="remote_endpoint" title="Peer Remote Endpoint"
:warning="true" warningText="This will be change globally, and will be apply to all peer's QR code and configuration file." :warning="true" warningText="This will be changed globally, and will be apply to all peer's QR code and configuration file."
></PeersDefaultSettingsInput> ></PeersDefaultSettingsInput>
</div> </div>
</div> </div>
@ -67,14 +69,16 @@ export default {
</div> </div>
<div class="card mb-4 shadow rounded-3"> <div class="card mb-4 shadow rounded-3">
<p class="card-header">Account Settings</p> <p class="card-header">Account Settings</p>
<div class="card-body"> <div class="card-body d-flex gap-4 flex-column">
<AccountSettingsInputUsername targetData="username" <AccountSettingsInputUsername targetData="username"
title="Username" title="Username"
></AccountSettingsInputUsername> ></AccountSettingsInputUsername>
<hr> <hr class="m-0">
<AccountSettingsInputPassword <AccountSettingsInputPassword
targetData="password"> targetData="password">
</AccountSettingsInputPassword> </AccountSettingsInputPassword>
<hr class="m-0">
<AccountSettingsMFA></AccountSettingsMFA>
</div> </div>
</div> </div>
<DashboardAPIKeys></DashboardAPIKeys> <DashboardAPIKeys></DashboardAPIKeys>

View File

@ -1,11 +1,9 @@
<script> <script>
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"; import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import QRCode from 'qrcode'
import Totp from "@/components/setupComponent/totp.vue";
import {fetchPost} from "@/utilities/fetch.js"; import {fetchPost} from "@/utilities/fetch.js";
export default { export default {
name: "setup", name: "setup",
components: {Totp}, components: {},
setup(){ setup(){
const store = DashboardConfigurationStore(); const store = DashboardConfigurationStore();
return {store} return {store}
@ -16,8 +14,7 @@ export default {
username: "", username: "",
newPassword: "", newPassword: "",
repeatNewPassword: "", repeatNewPassword: "",
enable_totp: false, enable_totp: true
verified_totp: false
}, },
loading: false, loading: false,
errorMessage: "", errorMessage: "",
@ -30,7 +27,6 @@ export default {
&& this.setup.newPassword.length >= 8 && this.setup.newPassword.length >= 8
&& this.setup.repeatNewPassword.length >= 8 && this.setup.repeatNewPassword.length >= 8
&& this.setup.newPassword === this.setup.repeatNewPassword && this.setup.newPassword === this.setup.repeatNewPassword
&& ((this.setup.enable_totp && this.setup.verified_totp) || !this.setup.enable_totp)
} }
}, },
methods: { methods: {
@ -39,9 +35,7 @@ export default {
fetchPost("/api/Welcome_Finish", this.setup, (res) => { fetchPost("/api/Welcome_Finish", this.setup, (res) => {
if (res.status){ if (res.status){
this.done = true; this.done = true;
setTimeout(() => { this.$router.push('/2FASetup')
this.$router.push('/')
}, 500)
}else{ }else{
document.querySelectorAll("#createAccount input").forEach(x => x.classList.add("is-invalid")) document.querySelectorAll("#createAccount input").forEach(x => x.classList.add("is-invalid"))
this.errorMessage = res.message; this.errorMessage = res.message;
@ -62,7 +56,7 @@ export default {
<template> <template>
<div class="container-fluid login-container-fluid d-flex main pt-5 overflow-scroll" <div class="container-fluid login-container-fluid d-flex main pt-5 overflow-scroll"
:data-bs-theme="this.store.Configuration.Server.dashboard_theme"> :data-bs-theme="this.store.Configuration.Server.dashboard_theme">
<div class="mx-auto text-body" style="width: 500px"> <div class="m-auto text-body" style="width: 500px">
<span class="dashboardLogo display-4">Nice to meet you!</span> <span class="dashboardLogo display-4">Nice to meet you!</span>
<p class="mb-5">Please fill in the following fields to finish setup 😊</p> <p class="mb-5">Please fill in the following fields to finish setup 😊</p>
<div> <div>
@ -94,26 +88,23 @@ export default {
class="form-control" id="confirmPassword" name="confirmPassword" placeholder="and you can remember it :)" required> class="form-control" id="confirmPassword" name="confirmPassword" placeholder="and you can remember it :)" required>
</div> </div>
</div> </div>
<hr> <!-- <div class="form-check form-switch">-->
<div class="form-check form-switch"> <!-- <input class="form-check-input" type="checkbox" role="switch" id="enable_totp" -->
<input class="form-check-input" type="checkbox" role="switch" id="enable_totp" <!-- v-model="this.setup.enable_totp">-->
v-model="this.setup.enable_totp"> <!-- <label class="form-check-label" -->
<label class="form-check-label" <!-- for="enable_totp">Enable 2 Factor Authentication? <strong>Strongly recommended</strong></label>-->
for="enable_totp">Enable 2 Factor Authentication? <strong>Strongly recommended</strong></label> <!-- </div>-->
</div> <!-- <Suspense>-->
<Suspense> <!-- <Transition name="fade">-->
<Transition name="fade"> <!-- <Totp v-if="this.setup.enable_totp" @verified="this.setup.verified_totp = true"></Totp>-->
<Totp v-if="this.setup.enable_totp" @verified="this.setup.verified_totp = true"></Totp> <!-- </Transition>-->
</Transition> <!-- </Suspense>-->
</Suspense>
<button class="btn btn-dark btn-lg mb-5 d-flex btn-brand shadow align-items-center" <button class="btn btn-dark btn-lg mb-5 d-flex btn-brand shadow align-items-center"
ref="signInBtn" ref="signInBtn"
:disabled="!this.goodToSubmit || this.loading || this.done" @click="this.submit()"> :disabled="!this.goodToSubmit || this.loading || this.done" @click="this.submit()">
<span class="d-flex align-items-center w-100" v-if="!this.loading && !this.done"> <span class="d-flex align-items-center w-100" v-if="!this.loading && !this.done">
Finish<i class="bi bi-chevron-right ms-auto"></i></span> Next<i class="bi bi-chevron-right ms-auto"></i></span>
<span class="d-flex align-items-center w-100" v-else-if="this.done">
Welcome to WGDashboard!</span>
<span class="d-flex align-items-center w-100" v-else> <span class="d-flex align-items-center w-100" v-else>
Saving...<span class="spinner-border ms-auto spinner-border-sm" role="status"> Saving...<span class="spinner-border ms-auto spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span> <span class="visually-hidden">Loading...</span>