From 6b6ad05e3a32135757e37f4282f9d3f6d4796cc4 Mon Sep 17 00:00:00 2001 From: Donald Zou Date: Wed, 31 Jan 2024 12:06:44 -0500 Subject: [PATCH] The UI for New Configuration is done --- src/dashboard_new.py | 71 ++-- src/static/app/package-lock.json | 20 ++ src/static/app/package.json | 1 + .../app/src/components/configurationList.vue | 4 +- src/static/app/src/utilities/wireguard.js | 313 ++++++++++++++++++ src/static/app/src/views/newConfiguration.vue | 250 +++++++++++++- src/static/app2/app.js | 51 --- src/static/app2/cookie.js | 9 - src/static/app2/index.js | 5 - src/static/app2/signin/fetch.js | 28 -- src/static/app2/signin/signin.js | 52 --- src/static/app2/store.js | 5 - src/static/css/dashboard.css | 34 +- 13 files changed, 654 insertions(+), 189 deletions(-) create mode 100644 src/static/app/src/utilities/wireguard.js delete mode 100644 src/static/app2/app.js delete mode 100644 src/static/app2/cookie.js delete mode 100644 src/static/app2/index.js delete mode 100644 src/static/app2/signin/fetch.js delete mode 100644 src/static/app2/signin/signin.js delete mode 100644 src/static/app2/store.js diff --git a/src/dashboard_new.py b/src/dashboard_new.py index fadf2f5..dd1d868 100644 --- a/src/dashboard_new.py +++ b/src/dashboard_new.py @@ -99,27 +99,31 @@ class WireguardConfiguration: def __str__(self): return self.message - def __init__(self, name): - self.Name = name - self.__parser.read(os.path.join(WG_CONF_PATH, f'{self.Name}.conf')) - sections = self.__parser.sections() - if "Interface" not in sections: - raise self.InvalidConfigurationFileException( - "[Interface] section not found in " + os.path.join(WG_CONF_PATH, f'{self.Name}.conf')) - interfaceConfig = dict(self.__parser.items("Interface", True)) - for i in dir(self): - if str(i) in interfaceConfig.keys(): - if isinstance(getattr(self, i), bool): - setattr(self, i, _strToBool(interfaceConfig[i])) - else: - setattr(self, i, interfaceConfig[i]) + def __init__(self, name: str = None): + if name is not None: + self.Name = name + self.__parser.read(os.path.join(WG_CONF_PATH, f'{self.Name}.conf')) + sections = self.__parser.sections() + if "Interface" not in sections: + raise self.InvalidConfigurationFileException( + "[Interface] section not found in " + os.path.join(WG_CONF_PATH, f'{self.Name}.conf')) + interfaceConfig = dict(self.__parser.items("Interface", True)) + for i in dir(self): + if str(i) in interfaceConfig.keys(): + if isinstance(getattr(self, i), bool): + setattr(self, i, _strToBool(interfaceConfig[i])) + else: + setattr(self, i, interfaceConfig[i]) - if self.PrivateKey: - self.PublicKey = self.__getPublicKey() + if self.PrivateKey: + self.PublicKey = self.__getPublicKey() - self.Status = self.__getStatus() + self.Status = self.__getStatus() - # Create tables in database + # Create tables in database + self.__createDatabase() + + def __createDatabase(self): inspector = inspect(engine) existingTable = inspector.get_table_names() if self.Name not in existingTable: @@ -139,6 +143,9 @@ class WireguardConfiguration: self.Status = self.__getStatus() return self.__dict__ + def newConfiguration(self): + pass + def iPv46RegexCheck(ip): return re.match( @@ -447,7 +454,11 @@ def API_AuthenticateLogin(): resp.set_cookie("authToken", authToken) session.permanent = True return resp - return ResponseObject(False, "Username, password or OTP is incorrect.") + + if totpEnabled: + return ResponseObject(False, "Sorry, your username, password or OTP is incorrect.") + else: + return ResponseObject(False, "Sorry, your username or password is incorrect.") @app.route('/api/signout') @@ -463,6 +474,28 @@ def API_getWireguardConfigurations(): return ResponseObject(data=[wc.toJSON() for wc in WireguardConfigurations]) +@app.route('/api/addWireguardConfiguration', methods=["POST"]) +def API_addWireguardConfiguration(): + data = request.get_json() + keys = [ + "ConfigurationName", + "Address", + "ListenPort", + "PrivateKey", + "PublicKey", + "PresharedKey", + "PreUp", + "PreDown", + "PostUp", + "PostDown", + "UsePreSharedKey" + ] + requiredKeys = [ + "ConfigurationName", "Address", "ListenPort", "PrivateKey" + ] + + + @app.route('/api/getDashboardConfiguration', methods=["GET"]) def API_getDashboardConfiguration(): return ResponseObject(data=DashboardConfig.toJSON()) diff --git a/src/static/app/package-lock.json b/src/static/app/package-lock.json index b0c5eb5..adb34a8 100644 --- a/src/static/app/package-lock.json +++ b/src/static/app/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "bootstrap": "^5.3.2", "bootstrap-icons": "^1.11.2", + "cidr-tools": "^7.0.4", "pinia": "^2.1.7", "qrcode": "^1.5.3", "uuid": "^9.0.1", @@ -755,6 +756,17 @@ "node": ">=6" } }, + "node_modules/cidr-tools": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cidr-tools/-/cidr-tools-7.0.4.tgz", + "integrity": "sha512-bKd6xC01ObuVKvJPGdV9Rz02KFO3mtHwMe/QTlcVuFAmU5n3RN/F3FgppHZaQjM+c/1i9YB9rgKNH/5iVqwCoA==", + "dependencies": { + "ip-bigint": "^8.0.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -897,6 +909,14 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/ip-bigint": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/ip-bigint/-/ip-bigint-8.0.2.tgz", + "integrity": "sha512-UMKHGx7+4O2mD/6jnpNtt4UMA0tRQ3XAiNVYlbLssFU1LegKqKwPqbqtLVW7lQU/c6rCWI1hcxxs4TP96Xa+rQ==", + "engines": { + "node": ">=18" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", diff --git a/src/static/app/package.json b/src/static/app/package.json index 681bbe8..23c4adb 100644 --- a/src/static/app/package.json +++ b/src/static/app/package.json @@ -11,6 +11,7 @@ "dependencies": { "bootstrap": "^5.3.2", "bootstrap-icons": "^1.11.2", + "cidr-tools": "^7.0.4", "pinia": "^2.1.7", "qrcode": "^1.5.3", "uuid": "^9.0.1", diff --git a/src/static/app/src/components/configurationList.vue b/src/static/app/src/components/configurationList.vue index 243b4a2..677e3f2 100644 --- a/src/static/app/src/components/configurationList.vue +++ b/src/static/app/src/components/configurationList.vue @@ -17,9 +17,9 @@ export default {

Wireguard Configurations

- - + Configuration +

You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".

diff --git a/src/static/app/src/utilities/wireguard.js b/src/static/app/src/utilities/wireguard.js new file mode 100644 index 0000000..ac51a15 --- /dev/null +++ b/src/static/app/src/utilities/wireguard.js @@ -0,0 +1,313 @@ +/*! SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +(function() { + function gf(init) { + var r = new Float64Array(16); + if (init) { + for (var i = 0; i < init.length; ++i) + r[i] = init[i]; + } + return r; + } + + function pack(o, n) { + var b, m = gf(), t = gf(); + for (var i = 0; i < 16; ++i) + t[i] = n[i]; + carry(t); + carry(t); + carry(t); + for (var j = 0; j < 2; ++j) { + m[0] = t[0] - 0xffed; + for (var i = 1; i < 15; ++i) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + cswap(t, m, 1 - b); + } + for (var i = 0; i < 16; ++i) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } + } + + function carry(o) { + var c; + for (var i = 0; i < 16; ++i) { + o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); + o[i] &= 0xffff; + } + } + + function cswap(p, q, b) { + var t, c = ~(b - 1); + for (var i = 0; i < 16; ++i) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + } + + function add(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] + b[i]) | 0; + } + + function subtract(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] - b[i]) | 0; + } + + function multmod(o, a, b) { + var t = new Float64Array(31); + for (var i = 0; i < 16; ++i) { + for (var j = 0; j < 16; ++j) + t[i + j] += a[i] * b[j]; + } + for (var i = 0; i < 15; ++i) + t[i] += 38 * t[i + 16]; + for (var i = 0; i < 16; ++i) + o[i] = t[i]; + carry(o); + carry(o); + } + + function invert(o, i) { + var c = gf(); + for (var a = 0; a < 16; ++a) + c[a] = i[a]; + for (var a = 253; a >= 0; --a) { + multmod(c, c, c); + if (a !== 2 && a !== 4) + multmod(c, c, i); + } + for (var a = 0; a < 16; ++a) + o[a] = c[a]; + } + + function clamp(z) { + z[31] = (z[31] & 127) | 64; + z[0] &= 248; + } + + function generatePublicKey(privateKey) { + var r, z = new Uint8Array(32); + var a = gf([1]), + b = gf([9]), + c = gf(), + d = gf([1]), + e = gf(), + f = gf(), + _121665 = gf([0xdb41, 1]), + _9 = gf([9]); + for (var i = 0; i < 32; ++i) + z[i] = privateKey[i]; + clamp(z); + for (var i = 254; i >= 0; --i) { + r = (z[i >>> 3] >>> (i & 7)) & 1; + cswap(a, b, r); + cswap(c, d, r); + add(e, a, c); + subtract(a, a, c); + add(c, b, d); + subtract(b, b, d); + multmod(d, e, e); + multmod(f, a, a); + multmod(a, c, a); + multmod(c, b, e); + add(e, a, c); + subtract(a, a, c); + multmod(b, a, a); + subtract(c, d, f); + multmod(a, c, _121665); + add(a, a, d); + multmod(c, c, a); + multmod(a, d, f); + multmod(d, b, _9); + multmod(b, e, e); + cswap(a, b, r); + cswap(c, d, r); + } + invert(c, c); + multmod(a, a, c); + pack(z, a); + return z; + } + + function generatePresharedKey() { + var privateKey = new Uint8Array(32); + window.crypto.getRandomValues(privateKey); + return privateKey; + } + + function generatePrivateKey() { + var privateKey = generatePresharedKey(); + clamp(privateKey); + return privateKey; + } + + function encodeBase64(dest, src) { + var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); + for (var i = 0; i < 4; ++i) + dest[i] = input[i] + 65 + + (((25 - input[i]) >> 8) & 6) - + (((51 - input[i]) >> 8) & 75) - + (((61 - input[i]) >> 8) & 15) + + (((62 - input[i]) >> 8) & 3); + } + + function keyToBase64(key) { + var i, base64 = new Uint8Array(44); + for (i = 0; i < 32 / 3; ++i) + encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); + encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); + base64[43] = 61; + return String.fromCharCode.apply(null, base64); + } + + function base64ToKey(base64) { + let binary_string = window.atob(base64); + let len = binary_string.length; + let bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binary_string.charCodeAt(i); + } + let uint8 = new Uint8Array(bytes.buffer); + return uint8; + } + + function putU32(b, n) + { + b.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff); + } + + function putU16(b, n) + { + b.push(n & 0xff, (n >>> 8) & 0xff); + } + + function putBytes(b, a) + { + for (var i = 0; i < a.length; ++i) + b.push(a[i] & 0xff); + } + + function encodeString(s) + { + var utf8 = unescape(encodeURIComponent(s)); + var b = new Uint8Array(utf8.length); + for (var i = 0; i < utf8.length; ++i) + b[i] = utf8.charCodeAt(i); + return b; + } + + function crc32(b) + { + if (!crc32.table) { + crc32.table = []; + for (var c = 0, n = 0; n < 256; c = ++n) { + for (var k = 0; k < 8; ++k) + c = ((c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1)); + crc32.table[n] = c; + } + } + var crc = -1; + for (var i = 0; i < b.length; ++i) + crc = (crc >>> 8) ^ crc32.table[(crc ^ b[i]) & 0xff]; + return (crc ^ (-1)) >>> 0; + } + + function createZipFile(files) + { + var b = []; + var cd = []; + var offset = 0; + + for (var i = 0; i < files.length; ++i) { + var name = encodeString(files[i].filename); + var contents = encodeString(files[i].content); + var crc = crc32(contents); + + putU32(b, 0x04034b50); /* signature */ + putU16(b, 20); /* version needed */ + putU16(b, 0); /* flags */ + putU16(b, 0); /* compression method */ + putU16(b, 0); /* mtime */ + putU16(b, 0); /* mdate */ + putU32(b, crc); /* crc32 */ + putU32(b, contents.length); /* compressed size */ + putU32(b, contents.length); /* uncompressed size */ + putU16(b, name.length); /* file name length */ + putU16(b, 0); /* extra field length */ + putBytes(b, name); + putBytes(b, contents); + + putU32(cd, 0x02014b50); /* signature */ + putU16(cd, 0); /* version made */ + putU16(cd, 20); /* version needed */ + putU16(cd, 0); /* flags */ + putU16(cd, 0); /* compression method */ + putU16(cd, 0); /* mtime */ + putU16(cd, 0); /* mdate */ + putU32(cd, crc); /* crc32 */ + putU32(cd, contents.length); /* compressed size */ + putU32(cd, contents.length); /* uncompressed size */ + putU16(cd, name.length); /* file name length */ + putU16(cd, 0); /* extra field length */ + putU16(cd, 0); /* file comment length */ + putU16(cd, 0); /* disk number start */ + putU16(cd, 0); /* internal file attributes */ + putU32(cd, 32); /* external file attributes - 'archive' bit set (32) */ + putU32(cd, offset); /* relative offset of local header */ + putBytes(cd, name); /* file name */ + + offset += 30 + contents.length + name.length + } + putBytes(b, cd); /* central directory */ + putU32(b, 0x06054b50); /* end of central directory signature */ + putU16(b, 0); /* number of this disk */ + putU16(b, 0); /* number of disk with central directory start */ + putU16(b, files.length); /* number of entries on disk */ + putU16(b, files.length); /* number of entries */ + putU32(b, cd.length); /* length of central directory */ + putU32(b, offset); /* offset to start of central directory */ + putU16(b, 0); /* zip comment size */ + return Uint8Array.from(b); + } + + window.wireguard = { + generateKeypair: function() { + var privateKey = generatePrivateKey(); + var publicKey = generatePublicKey(privateKey); + var presharedKey = generatePresharedKey(); + return { + publicKey: keyToBase64(publicKey), + privateKey: keyToBase64(privateKey), + presharedKey: keyToBase64(presharedKey) + }; + }, + generatePublicKey: function (privateKey){ + privateKey = base64ToKey(privateKey); + return keyToBase64(generatePublicKey(privateKey)); + }, + + generateZipFiles: function(res){ + var files = res.peers; + var zipFile = createZipFile(files); + var blob = new Blob([zipFile], { type: "application/zip" }); + var a = document.createElement("a"); + a.download = res.filename; + a.href = URL.createObjectURL(blob); + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + } + }; +})(); diff --git a/src/static/app/src/views/newConfiguration.vue b/src/static/app/src/views/newConfiguration.vue index e9d2e0b..1886cc1 100644 --- a/src/static/app/src/views/newConfiguration.vue +++ b/src/static/app/src/views/newConfiguration.vue @@ -1,22 +1,250 @@ diff --git a/src/static/app2/app.js b/src/static/app2/app.js deleted file mode 100644 index 48bb6ad..0000000 --- a/src/static/app2/app.js +++ /dev/null @@ -1,51 +0,0 @@ -const { createApp, ref } = Vue; -import Index from './index.js' -import Signin from './signin/signin.js' -const {createPinia} = Pinia -import {cookie} from "./cookie.js"; - -const app = createApp({ - template: ` - - - ` -}); -const pinia = createPinia() -const routes = [ - { - path: '/', - component: Index, - meta: { - requiresAuth: true - } - }, - { - path: '/signin', component: Signin - } -] - -const router = VueRouter.createRouter({ - // 4. Provide the history implementation to use. We are using the hash history for simplicity here. - history: VueRouter.createWebHashHistory(), - routes, // short for `routes: routes` -}); - -router.beforeEach((to, from, next) => { - if (to.meta.requiresAuth){ - if (cookie.getCookie("auth")){ - next() - }else{ - next("/signin") - } - }else { - next(); - } -}); - -app.use(router); -app.use(pinia) -app.mount('#app1'); \ No newline at end of file diff --git a/src/static/app2/cookie.js b/src/static/app2/cookie.js deleted file mode 100644 index 964df54..0000000 --- a/src/static/app2/cookie.js +++ /dev/null @@ -1,9 +0,0 @@ -export const cookie = { - - //https://stackoverflow.com/a/15724300 - getCookie(name) { - const value = `; ${document.cookie}`; - const parts = value.split(`; ${name}=`); - if (parts.length === 2) return parts.pop().split(';').shift(); - } -} \ No newline at end of file diff --git a/src/static/app2/index.js b/src/static/app2/index.js deleted file mode 100644 index 98402f0..0000000 --- a/src/static/app2/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export default { - template: ` - this is idex - ` -} \ No newline at end of file diff --git a/src/static/app2/signin/fetch.js b/src/static/app2/signin/fetch.js deleted file mode 100644 index 0836721..0000000 --- a/src/static/app2/signin/fetch.js +++ /dev/null @@ -1,28 +0,0 @@ -export const fetchGet = async (url, params=undefined, callback=undefined) => { - const urlSearchParams = new URLSearchParams(params); - await fetch(`${url}?${urlSearchParams.toString()}}`, { - headers: { - "content-type": "application/json" - } - }) - .then(x => x.json()) - .then(x => callback ? callback(x) : undefined) - .catch(() => { - alert("Error occurred! Check console") - }); -} - -export const fetchPost = async (url, body, callback) => { - await fetch(`${url}`, { - headers: { - "content-type": "application/json" - }, - method: "POST", - body: JSON.stringify(body) - }) - .then(x => x.json()) - .then(x => callback ? callback(x) : undefined) - // .catch(() => { - // alert("Error occurred! Check console") - // }); -} \ No newline at end of file diff --git a/src/static/app2/signin/signin.js b/src/static/app2/signin/signin.js deleted file mode 100644 index 0a2ae77..0000000 --- a/src/static/app2/signin/signin.js +++ /dev/null @@ -1,52 +0,0 @@ -import {fetchPost} from "./fetch.js"; - -export default { - data(){ - return { - username: "", - password: "" - } - }, - template: ` - - `, - methods: { - async auth(){ - if (this.username && this.password){ - await fetchPost("/auth", { - username: this.username, - password: this.password - }, (response) => { - console.log(response) - }) - }else{ - document.querySelectorAll("input[required]").forEach(x => { - if (x.value.length === 0){ - x.classList.remove("is-valid") - x.classList.add("is-invalid") - }else{ - x.classList.remove("is-invalid") - x.classList.add("is-valid") - } - }) - } - } - } -} \ No newline at end of file diff --git a/src/static/app2/store.js b/src/static/app2/store.js deleted file mode 100644 index 1e10f01..0000000 --- a/src/static/app2/store.js +++ /dev/null @@ -1,5 +0,0 @@ -import { defineStore } from 'pinia' - -export const wgdStore = defineStore('WGDashboardStore', { - -}) \ No newline at end of file diff --git a/src/static/css/dashboard.css b/src/static/css/dashboard.css index 9e2dd96..56ab9ab 100644 --- a/src/static/css/dashboard.css +++ b/src/static/css/dashboard.css @@ -61,35 +61,55 @@ inherits: false; } -@property --degree{ +@property --distance2{ syntax: ''; initial-value: 0%; inherits: false; } +@property --degree{ + syntax: ''; + initial-value: 234deg; + inherits: false; +} + .dashboardLogo{ background: rgb(23,139,255); - background: linear-gradient(234deg, var(--brandColor1) var(--degree), var(--brandColor2) 100%); + background: linear-gradient(234deg, var(--brandColor1) var(--distance2), var(--brandColor2) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; - transition: --brandColor1 1s, --brandColor2 0.3s, --degree 1s !important; + transition: --brandColor1 1s, --brandColor2 0.3s, --distance2 1s !important; } .btn-brand{ /*background: rgb(23,139,255);*/ - background: linear-gradient(234deg, var(--brandColor1) var(--degree), var(--brandColor2) 100%); + background: linear-gradient(var(--degree), var(--brandColor1) var(--distance2), var(--brandColor2) 100%); border: 0 !important; - transition: --brandColor1 1s, --brandColor2 1s, --degree 0.5s !important; + transition: --brandColor1 1s, --brandColor2 1s, --distance2 0.5s !important; } + +.btn-brand.loading{ + animation: spin infinite forwards 3s linear; +} + .btn-brand:hover, .dashboardLogo:hover{ --brandColor1: #009dff; --brandColor2: #ff875b; - --degree: 30%; + --distance2: 30%; } .signInBtn.signedIn{ - --degree: 100%; + --distance2: 100%; +} + +@keyframes spin { + 0%{ + --degree: 234deg; + } + 100%{ + --degree: 594deg; + } } [data-bs-theme="dark"].main,