mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-06-28 17:26:56 +00:00
Update
This commit is contained in:
parent
68fae3b23c
commit
41bf9b8baa
@ -55,9 +55,9 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
|
|||||||
|
|
||||||
@client.get(f'{prefix}/api/signout')
|
@client.get(f'{prefix}/api/signout')
|
||||||
def ClientAPI_SignOut():
|
def ClientAPI_SignOut():
|
||||||
session.pop('username')
|
session['username'] = None
|
||||||
session.pop('role')
|
session['role'] = None
|
||||||
session.pop('totpVerified')
|
session['totpVerified'] = None
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
@client.get(f'{prefix}/api/signin/totp')
|
@client.get(f'{prefix}/api/signin/totp')
|
||||||
@ -81,7 +81,7 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
|
|||||||
if session.get('username') is None:
|
if session.get('username') is None:
|
||||||
return ResponseObject(False, "Sign in status is invalid", status_code=401)
|
return ResponseObject(False, "Sign in status is invalid", status_code=401)
|
||||||
session['totpVerified'] = True
|
session['totpVerified'] = True
|
||||||
# return ResponseObject(True, data=)
|
return ResponseObject(True)
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
@client.get(prefix)
|
@client.get(prefix)
|
||||||
|
@ -6,6 +6,7 @@ import pyotp
|
|||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString
|
from .ConnectionString import ConnectionString
|
||||||
|
from .DashboardClientsPeerAssignment import DashboardClientsPeerAssignment
|
||||||
from .DashboardClientsTOTP import DashboardClientsTOTP
|
from .DashboardClientsTOTP import DashboardClientsTOTP
|
||||||
from .Utilities import ValidatePasswordStrength
|
from .Utilities import ValidatePasswordStrength
|
||||||
from .DashboardLogger import DashboardLogger
|
from .DashboardLogger import DashboardLogger
|
||||||
@ -18,7 +19,6 @@ class DashboardClients:
|
|||||||
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
|
|
||||||
|
|
||||||
self.dashboardClientsTable = db.Table(
|
self.dashboardClientsTable = db.Table(
|
||||||
'DashboardClients', self.metadata,
|
'DashboardClients', self.metadata,
|
||||||
db.Column('ClientID', db.String(255), nullable=False, primary_key=True),
|
db.Column('ClientID', db.String(255), nullable=False, primary_key=True),
|
||||||
@ -46,6 +46,7 @@ class DashboardClients:
|
|||||||
self.Clients = []
|
self.Clients = []
|
||||||
self.__getClients()
|
self.__getClients()
|
||||||
self.DashboardClientsTOTP = DashboardClientsTOTP()
|
self.DashboardClientsTOTP = DashboardClientsTOTP()
|
||||||
|
self.DashboardClientsPeerAssignment = DashboardClientsPeerAssignment()
|
||||||
|
|
||||||
def __getClients(self):
|
def __getClients(self):
|
||||||
with self.engine.connect() as conn:
|
with self.engine.connect() as conn:
|
||||||
@ -76,6 +77,7 @@ class DashboardClients:
|
|||||||
|
|
||||||
def SignIn_GetTotp(self, Token: str, UserProvidedTotp: str = None) -> tuple[bool, str] or tuple[bool, None, str]:
|
def SignIn_GetTotp(self, Token: str, UserProvidedTotp: str = None) -> tuple[bool, str] or tuple[bool, None, str]:
|
||||||
status, data = self.DashboardClientsTOTP.GetTotp(Token)
|
status, data = self.DashboardClientsTOTP.GetTotp(Token)
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
return False, "TOTP Token is invalid"
|
return False, "TOTP Token is invalid"
|
||||||
if UserProvidedTotp is None:
|
if UserProvidedTotp is None:
|
||||||
@ -83,7 +85,7 @@ class DashboardClients:
|
|||||||
return True, pyotp.totp.TOTP(data.get('TotpKey')).provisioning_uri(name=data.get('Email'),
|
return True, pyotp.totp.TOTP(data.get('TotpKey')).provisioning_uri(name=data.get('Email'),
|
||||||
issuer_name="WGDashboard Client")
|
issuer_name="WGDashboard Client")
|
||||||
else:
|
else:
|
||||||
totpMatched = pyotp.TOTP(data.get('TotpKey')).verify(UserProvidedTotp)
|
totpMatched = pyotp.totp.TOTP(data.get('TotpKey')).verify(UserProvidedTotp)
|
||||||
if not totpMatched:
|
if not totpMatched:
|
||||||
return False, "TOTP is does not match"
|
return False, "TOTP is does not match"
|
||||||
else:
|
else:
|
||||||
|
46
src/modules/DashboardClientsPeerAssignment.py
Normal file
46
src/modules/DashboardClientsPeerAssignment.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from .ConnectionString import ConnectionString
|
||||||
|
from .DashboardLogger import DashboardLogger
|
||||||
|
import sqlalchemy as db
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardClientsPeerAssignment:
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = DashboardLogger()
|
||||||
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
|
self.metadata = db.MetaData()
|
||||||
|
|
||||||
|
self.dashboardClientsPeerAssignmentTable = db.Table(
|
||||||
|
'DashboardClientsPeerAssignment', self.metadata,
|
||||||
|
db.Column('AssignmentID', db.String(255), nullable=False, primary_key=True),
|
||||||
|
db.Column('ClientID', db.String(255), nullable=False, index=True),
|
||||||
|
db.Column('ConfigurationName', db.String(255)),
|
||||||
|
db.Column('PeerID', db.String(500)),
|
||||||
|
db.Column('AssignedDate',
|
||||||
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
|
server_default=db.func.now()),
|
||||||
|
db.Column('UnassignedDate',
|
||||||
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
|
extend_existing=True
|
||||||
|
)
|
||||||
|
self.metadata.create_all(self.engine)
|
||||||
|
self.assignments = []
|
||||||
|
|
||||||
|
def __getAssignments(self):
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
self.assignments = conn.execute(
|
||||||
|
self.dashboardClientsPeerAssignmentTable.select().where(
|
||||||
|
self.dashboardClientsPeerAssignmentTable.c.UnassignedDate is None
|
||||||
|
)
|
||||||
|
).mappings().fetchall()
|
||||||
|
|
||||||
|
def AssignClient(self, ClientID, ConfigurationName, PeerID):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def UnassignClient(self, AssignmentID):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def GetAssignedClient(self, ConfigurationName, PeerID):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def GetAssignedPeers(self, ClientID):
|
||||||
|
pass
|
22
src/static/client/src/components/SignIn/qrcode.vue
Normal file
22
src/static/client/src/components/SignIn/qrcode.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import QRCode from "qrcode";
|
||||||
|
|
||||||
|
const props = defineProps([
|
||||||
|
"content"
|
||||||
|
])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
QRCode.toCanvas(document.getElementById('qrcode'), props.content, function (error) {})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<canvas id="qrcode" class="rounded-3 shadow"></canvas>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -2,7 +2,7 @@
|
|||||||
import {computed, reactive, ref} from "vue";
|
import {computed, reactive, ref} from "vue";
|
||||||
import {clientStore} from "@/stores/clientStore.js";
|
import {clientStore} from "@/stores/clientStore.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {requestURl} from "@/utilities/request.js";
|
import {axiosPost, requestURl} from "@/utilities/request.js";
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
@ -20,15 +20,14 @@ const signIn = async (e) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await axios.post(requestURl("/api/signin"), formData).then(res => {
|
|
||||||
let data = res.data;
|
const data = await axiosPost("/api/signin", formData)
|
||||||
if (!data.status){
|
if (!data.status){
|
||||||
store.newNotification(data.message, "danger")
|
store.newNotification(data.message, "danger")
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}else{
|
}else{
|
||||||
emits("totpToken", data.message)
|
emits("totpToken", data.message)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const formFilled = computed(() => {
|
const formFilled = computed(() => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script setup async>
|
<script setup async>
|
||||||
import {computed, onMounted, reactive, ref} from "vue";
|
import {computed, onMounted, reactive, ref} from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {requestURl} from "@/utilities/request.js";
|
import {axiosPost, requestURl} from "@/utilities/request.js";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {clientStore} from "@/stores/clientStore.js";
|
import {clientStore} from "@/stores/clientStore.js";
|
||||||
import QRCode from "qrcode";
|
import Qrcode from "@/components/SignIn/qrcode.vue";
|
||||||
|
|
||||||
const props = defineProps([
|
const props = defineProps([
|
||||||
'totpToken'
|
'totpToken'
|
||||||
@ -47,30 +47,28 @@ onMounted(() => {
|
|||||||
const emits = defineEmits(['clearToken'])
|
const emits = defineEmits(['clearToken'])
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (totpKey.value){
|
|
||||||
QRCode.toCanvas(document.getElementById('qrcode'), totpKey.value, function (error) {})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const verify = async (e) => {
|
const verify = async (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (formFilled){
|
if (formFilled){
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await axios.post(requestURl('/api/signin/totp'), {
|
const data = await axiosPost('/api/signin/totp', {
|
||||||
Token: props.totpToken,
|
Token: props.totpToken,
|
||||||
UserProvidedTOTP: formData.TOTP
|
UserProvidedTOTP: formData.TOTP
|
||||||
}).then(res => {
|
})
|
||||||
loading.value = false
|
loading.value = false
|
||||||
let data = res.data
|
if (data){
|
||||||
if (data.status){
|
if (data.status){
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}else{
|
}else{
|
||||||
store.newNotification(data.message, "danger")
|
store.newNotification(data.message, "danger")
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}else{
|
||||||
store.newNotification("Sign in status is invalid", "danger")
|
store.newNotification("Sign in status is invalid", "danger")
|
||||||
emits('clearToken')
|
emits('clearToken')
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -88,7 +86,7 @@ const verify = async (e) => {
|
|||||||
<div class="card-body d-flex gap-3 flex-column">
|
<div class="card-body d-flex gap-3 flex-column">
|
||||||
<h2 class="mb-0">Initial Setup</h2>
|
<h2 class="mb-0">Initial Setup</h2>
|
||||||
<p class="mb-0">Please scan the following QR Code to generate TOTP with your choice of authenticator</p>
|
<p class="mb-0">Please scan the following QR Code to generate TOTP with your choice of authenticator</p>
|
||||||
<canvas id="qrcode" class="rounded-3 shadow "></canvas>
|
<Qrcode :content="totpKey"></Qrcode>
|
||||||
<p class="mb-0">Or you can click the link below:</p>
|
<p class="mb-0">Or you can click the link below:</p>
|
||||||
<div class="card rounded-3 ">
|
<div class="card rounded-3 ">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@ -3,7 +3,7 @@ import Index from "@/views/index.vue";
|
|||||||
import SignIn from "@/views/signin.vue";
|
import SignIn from "@/views/signin.vue";
|
||||||
import SignUp from "@/views/signup.vue";
|
import SignUp from "@/views/signup.vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {requestURl} from "@/utilities/request.js";
|
import {axiosGet, requestURl} from "@/utilities/request.js";
|
||||||
import {clientStore} from "@/stores/clientStore.js";
|
import {clientStore} from "@/stores/clientStore.js";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
@ -37,6 +37,8 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
const store = clientStore()
|
const store = clientStore()
|
||||||
|
|
||||||
if (to.path === '/signout'){
|
if (to.path === '/signout'){
|
||||||
|
|
||||||
|
|
||||||
await axios.get(requestURl('/api/signout')).then(() => {
|
await axios.get(requestURl('/api/signout')).then(() => {
|
||||||
next('/signin')
|
next('/signin')
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
@ -45,13 +47,13 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
store.newNotification("Sign in session ended, please sign in again", "warning")
|
store.newNotification("Sign in session ended, please sign in again", "warning")
|
||||||
}else{
|
}else{
|
||||||
if (to.meta.auth){
|
if (to.meta.auth){
|
||||||
await axios.get(requestURl('/api/validateAuthentication')).then(res => {
|
const status = await axiosGet('/api/validateAuthentication')
|
||||||
|
if (status){
|
||||||
next()
|
next()
|
||||||
}).catch(() => {
|
}else{
|
||||||
|
|
||||||
store.newNotification("Sign in session ended, please sign in again", "warning")
|
store.newNotification("Sign in session ended, please sign in again", "warning")
|
||||||
next('/signin')
|
next('/signin')
|
||||||
})
|
}
|
||||||
}else{
|
}else{
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,26 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
export const requestURl = (url) => {
|
export const requestURl = (url) => {
|
||||||
return import.meta.env.MODE === 'development' ? '/client' + url
|
return import.meta.env.MODE === 'development' ? '/client' + url
|
||||||
: `${window.location.protocol}//${(window.location.host + window.location.pathname + url).replace(/\/\//g, '/')}`
|
: `${window.location.protocol}//${(window.location.host + window.location.pathname + url).replace(/\/\//g, '/')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const axiosPost = async (URL, body = {}) => {
|
||||||
|
try{
|
||||||
|
const res = await axios.post(requestURl(URL), body)
|
||||||
|
return res.data
|
||||||
|
} catch (error){
|
||||||
|
console.log(error)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const axiosGet = async (URL, query = {}) => {
|
||||||
|
try{
|
||||||
|
const res = await axios.get(requestURl(URL), query)
|
||||||
|
return res.data
|
||||||
|
} catch (error){
|
||||||
|
console.log(error)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user