diff --git a/src/config.ini b/src/config.ini index 4d1d8e7..f4d74ee 100644 --- a/src/config.ini +++ b/src/config.ini @@ -18,7 +18,7 @@ dashboard_refresh_interval = 60000 dashboard_peer_list_display = grid dashboard_sort = status dashboard_theme = dark -dashboard_api_key = false +wgdashboard_apikey = false dashboard_language = en-US [Account] diff --git a/src/main.py b/src/main.py index 796bc3d..8699a88 100644 --- a/src/main.py +++ b/src/main.py @@ -2,20 +2,35 @@ import logging as log +import flask import json +import os from modules.config.reader import reader from modules.database.database import database from modules.utilities.utilities import utilities as util +from modules.routes.routes import routes + if __name__ == '__main__': log.basicConfig(level=log.DEBUG) - config_contents = reader.read_config() + ok, config_contents = reader.read_config() + if not ok: + exit(1) found, config_database = util.filter_config(config_contents, 'DATABASE') if not found: exit(1) ok, engine, session = database.create_session(config_database) - ok = database.verify_contents(engine) \ No newline at end of file + ok = database.ensure_contents(engine) + + app = flask.Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin")) + app.register_blueprint(routes) + + app.wgdashboard_config = config_contents + app.engine = engine + app.db_session = session + + app.run(debug=True, use_reloader=False) \ No newline at end of file diff --git a/src/modules/config/reader.py b/src/modules/config/reader.py index 97d3c1d..96178c8 100644 --- a/src/modules/config/reader.py +++ b/src/modules/config/reader.py @@ -6,19 +6,19 @@ from .utilities import checks class reader(): @staticmethod - def read_config() -> dict: + def read_config() -> tuple[bool, dict]: ''' check some basic things and then return the dict containing the config data ''' ok, candidate_path = checks.search_known_paths() if not ok: - return {} + return False, {} ok, config_contents = checks.verify_contents(candidate_path) if not ok: - return {} + return False, {} - return config_contents + return True, config_contents @staticmethod def refresh_config(config_contents: dict) -> dict: diff --git a/src/modules/config/utilities.py b/src/modules/config/utilities.py index 25223d8..62fa5d2 100644 --- a/src/modules/config/utilities.py +++ b/src/modules/config/utilities.py @@ -55,7 +55,7 @@ class checks(): log.debug(f'checking integrity of section: {section}') if len(config.items(section)) == 0: - log.warn('empty section, removing for runtime due to irrelevance') + log.warn(f'empty section: {section}, removing at runtime due to irrelevance') config.remove_section(section) config_dict = {} diff --git a/src/modules/database/database.py b/src/modules/database/database.py index 70728df..c166a14 100644 --- a/src/modules/database/database.py +++ b/src/modules/database/database.py @@ -15,8 +15,6 @@ class database(): if not ok: return False, None, None - log.info(connection_string) - try: engine = sqlalchemy.create_engine(connection_string, echo=False) @@ -30,7 +28,7 @@ class database(): return False, None, None @staticmethod - def verify_contents(engine) -> bool: + def ensure_contents(engine) -> bool: try: log.info('checking if all tables are present, and creating them if they are not') diff --git a/src/modules/database/functions.py b/src/modules/database/functions.py new file mode 100644 index 0000000..6077456 --- /dev/null +++ b/src/modules/database/functions.py @@ -0,0 +1,16 @@ +#!/bin/env python3 + +from datetime import datetime +import sqlalchemy.orm + +from .schema import Base +from .schema import Apikeys + +class functions(): + def retrieve_api_keys(session: sqlalchemy.orm.Session) -> dict: + time_now = datetime.now() + + api_keys = session.query(Apikeys).all() + print(api_keys) + + return {} \ No newline at end of file diff --git a/src/modules/database/schema.py b/src/modules/database/schema.py index b69f662..7934924 100644 --- a/src/modules/database/schema.py +++ b/src/modules/database/schema.py @@ -21,5 +21,90 @@ class User(Base): email = sqlalchemy.Column(sqlalchemy.String) +class Apikeys(Base): + __tablename__ = 'apikeys' + + apikey_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, index=True) + apikey_data = sqlalchemy.Column(sqlalchemy.String) + + apikey_creation = sqlalchemy.Column(sqlalchemy.String) + apikey_expiration = sqlalchemy.Column(sqlalchemy.String) + class Wireguard(Base): - __tablename__ = 'wireguard_interfaces' \ No newline at end of file + __tablename__ = 'wireguard_interfaces' + + iface_status = sqlalchemy.Column(sqlalchemy.String) + iface_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, index=True) + + iface_name = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_notes = sqlalchemy.Column(sqlalchemy.String) + + iface_privkey = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_pubkey = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_address = sqlalchemy.Column(sqlalchemy.String) + iface_listen_port = sqlalchemy.Column(sqlalchemy.Integer, unique=True) + iface_mtu = sqlalchemy.Column(sqlalchemy.Integer) + + iface_preup = sqlalchemy.Column(sqlalchemy.String) + iface_predown = sqlalchemy.Column(sqlalchemy.String) + iface_postup = sqlalchemy.Column(sqlalchemy.String) + iface_predown = sqlalchemy.Column(sqlalchemy.String) + + iface_save_config = sqlalchemy.Column(sqlalchemy.Boolean, default=True) + + iface_total_rx = sqlalchemy.Column(sqlalchemy.Float) + iface_total_tx = sqlalchemy.Column(sqlalchemy.Float) + iface_total_data = sqlalchemy.Column(sqlalchemy.Float) + +class Amnezia(Base): + __tablename__ = 'amnezia_interfaces' + + iface_status = sqlalchemy.Column(sqlalchemy.String) + iface_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, index=True) + + iface_name = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_notes = sqlalchemy.Column(sqlalchemy.String) + + iface_privkey = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_pubkey = sqlalchemy.Column(sqlalchemy.String, unique=True) + iface_address = sqlalchemy.Column(sqlalchemy.String) + iface_listen_port = sqlalchemy.Column(sqlalchemy.Integer, unique=True) + iface_mtu = sqlalchemy.Column(sqlalchemy.Integer) + + iface_preup = sqlalchemy.Column(sqlalchemy.String) + iface_predown = sqlalchemy.Column(sqlalchemy.String) + iface_postup = sqlalchemy.Column(sqlalchemy.String) + iface_predown = sqlalchemy.Column(sqlalchemy.String) + + iface_jc = sqlalchemy.Column(sqlalchemy.Integer) + iface_jmin = sqlalchemy.Column(sqlalchemy.Integer) + iface_jmax = sqlalchemy.Column(sqlalchemy.Integer) + + iface_s1 = sqlalchemy.Column(sqlalchemy.Integer) + iface_s2 = sqlalchemy.Column(sqlalchemy.Integer) + iface_s3 = sqlalchemy.Column(sqlalchemy.Integer) + iface_s4 = sqlalchemy.Column(sqlalchemy.Integer) + + iface_h1 = sqlalchemy.Column(sqlalchemy.String) + iface_h2 = sqlalchemy.Column(sqlalchemy.String) + iface_h3 = sqlalchemy.Column(sqlalchemy.String) + iface_h4 = sqlalchemy.Column(sqlalchemy.String) + + iface_i1 = sqlalchemy.Column(sqlalchemy.String) + iface_i2 = sqlalchemy.Column(sqlalchemy.String) + iface_i3 = sqlalchemy.Column(sqlalchemy.String) + iface_i4 = sqlalchemy.Column(sqlalchemy.String) + iface_i5 = sqlalchemy.Column(sqlalchemy.String) + + iface_save_config = sqlalchemy.Column(sqlalchemy.Boolean, default=True) + + iface_total_rx = sqlalchemy.Column(sqlalchemy.Float) + iface_total_tx = sqlalchemy.Column(sqlalchemy.Float) + iface_total_data = sqlalchemy.Column(sqlalchemy.Float) + +class Peers(Base): + __tablename__ = 'interface_peers' + + peer_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, index=True) + + assigned_iface = sqlalchemy.Column(sqlalchemy.String) \ No newline at end of file diff --git a/src/modules/routes/response.py b/src/modules/routes/response.py new file mode 100644 index 0000000..c9433f7 --- /dev/null +++ b/src/modules/routes/response.py @@ -0,0 +1,13 @@ +#!/bin/env python3 + +import flask + +def make_resp_obj(success: bool = True, message: str = "", data: dict = {}, http_code: int = 200) -> flask.wrappers.Response: + response = flask.make_response({ + "status": success, + "message": message, + "data": data + }, http_code + ) + + return response \ No newline at end of file diff --git a/src/modules/routes/routes.py b/src/modules/routes/routes.py new file mode 100644 index 0000000..d977d91 --- /dev/null +++ b/src/modules/routes/routes.py @@ -0,0 +1,57 @@ +#!/bin/env python3 + +import flask + +from .response import make_resp_obj +from ..database.functions import functions +from ..utilities.utilities import utilities + +routes = flask.Blueprint("routes", __name__) + +@routes.before_request +def auth_required(): + if flask.request.method.lower() == "options": + return make_resp_obj(True, "", flask.jsonify({"status": True}), 200) + + ok, config_server = utilities.filter_config(flask.current_app.wgdashboard_config, 'SERVER') + if not ok: + return make_resp_obj(False, "Internal Error", {}, 500) + + auth_required = config_server.get('auth_req', True) # Set to true for a safe default + + if not auth_required: + return + + whiteList = [ + '/client', + '/static/', + '/fileDownload', + 'validateAuthentication', + 'authenticate', + 'getDashboardConfiguration', + 'getDashboardTheme', + 'getDashboardVersion', + 'sharePeer/get', + 'isTotpEnabled', + 'locale' + ] + + request_path = flask.request.path + http_headers = flask.request.headers + api_key = http_headers.get("wgdashboard-apikey") + + api_key_enabled = config_server.get("wgdashboard_apikey", False) + registered_api_keys = functions.retrieve_api_keys(flask.current_app.db_session) + + if not api_key: + response = make_resp_obj() + return response + +@routes.route("/") +def index(): + return make_resp_obj(True, "/ Endpoint", flask.jsonify({"message": "Hello from routes file!"}), 200) + +@routes.route("/health") +@routes.route("/healthz") +def health(): + return make_resp_obj(True, "Health Endpoint", flask.jsonify({"status": "ok"}), 200) \ No newline at end of file diff --git a/src/modules/wireguard/utilities.py b/src/modules/wireguard/utilities.py index 7557381..92bc4e7 100644 --- a/src/modules/wireguard/utilities.py +++ b/src/modules/wireguard/utilities.py @@ -1 +1,2 @@ -#!/bin/env python3 \ No newline at end of file +#!/bin/env python3 + diff --git a/src/requirements.txt b/src/requirements.txt index 6607c1e..ce3874b 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,2 +1,3 @@ configparser==7.2.0 +Flask==3.1.3 sqlalchemy==2.0.47 \ No newline at end of file