mirror of
https://github.com/dec0dOS/zero-ui.git
synced 2025-08-19 13:01:30 -07:00
Add backend in python/flask
This commit is contained in:
parent
ae44b8eed7
commit
c8f40ccac3
20 changed files with 948 additions and 13 deletions
9
backend-py/config/db.json
Normal file
9
backend-py/config/db.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"users": {
|
||||||
|
"1": {
|
||||||
|
"username": "admin",
|
||||||
|
"salted_passwort": "$2b$12$leojcBt.FeMsX4i3NjQKQ.MRrI/TVg4sa7T/RI/zLOoDzDeDb4L5u",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
backend-py/constants/constants.py
Normal file
29
backend-py/constants/constants.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import os, sys
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from untils.db import Zero_DB
|
||||||
|
from untils.textTransform import HASH
|
||||||
|
from untils.zero_tier_api import ZEROTIER_REST_API
|
||||||
|
from untils.zerologger import LogurLogger
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
|
||||||
|
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
if os.path.isdir(sys_vars.default_config_path) == False:
|
||||||
|
os.mkdir(sys_vars.default_config_path)
|
||||||
|
|
||||||
|
|
||||||
|
db = Zero_DB()
|
||||||
|
hash = HASH()
|
||||||
|
zerotier_api = ZEROTIER_REST_API()
|
||||||
|
logger = LogurLogger(logger).getlogger()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pw_hash = hash.hash_password(sys_vars.default_admin_creditals.get("passwort"))
|
||||||
|
|
||||||
|
if db.read_user_credentials(sys_vars.default_admin_creditals.get("username")) == []:
|
||||||
|
db.write_user_credentials(**{"username" : sys_vars.default_admin_creditals.get("username"), "salted_passwort" : pw_hash, "role" : "admin"})
|
26
backend-py/constants/default_rules.json
Normal file
26
backend-py/constants/default_rules.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "MATCH_ETHERTYPE",
|
||||||
|
"not": true,
|
||||||
|
"or": false,
|
||||||
|
"etherType": 2048
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "MATCH_ETHERTYPE",
|
||||||
|
"not": true,
|
||||||
|
"or": false,
|
||||||
|
"etherType": 2054
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "MATCH_ETHERTYPE",
|
||||||
|
"not": true,
|
||||||
|
"or": false,
|
||||||
|
"etherType": 34525
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ACTION_DROP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ACTION_ACCEPT"
|
||||||
|
}
|
||||||
|
]
|
25
backend-py/constants/default_rules_source.txt
Normal file
25
backend-py/constants/default_rules_source.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# This is a default rule set that allows IPv4 and IPv6 traffic but otherwise
|
||||||
|
# behaves like a standard Ethernet switch.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Allow only IPv4, IPv4 ARP, and IPv6 Ethernet frames.
|
||||||
|
#
|
||||||
|
drop
|
||||||
|
not ethertype ipv4
|
||||||
|
and not ethertype arp
|
||||||
|
and not ethertype ipv6
|
||||||
|
;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uncomment to drop non-ZeroTier issued and managed IP addresses.
|
||||||
|
#
|
||||||
|
# This prevents IP spoofing but also blocks manual IP management at the OS level and
|
||||||
|
# bridging unless special rules to exempt certain hosts or traffic are added before
|
||||||
|
# this rule.
|
||||||
|
#
|
||||||
|
#drop
|
||||||
|
# not chr ipauth
|
||||||
|
#;
|
||||||
|
|
||||||
|
# Accept anything else. This is required since default is 'drop'.
|
||||||
|
accept;
|
44
backend-py/main.py
Normal file
44
backend-py/main.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
from flask import Flask, send_from_directory
|
||||||
|
from flask_cors import CORS
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__, static_folder="app")
|
||||||
|
app.config['CORS_HEADERS'] = 'Content-Type'
|
||||||
|
cors = CORS(app, resources={r"/*": {"origins": "*"}})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
from routes.network import route_network
|
||||||
|
from routes.auth import route_auth
|
||||||
|
from routes.member import route_member
|
||||||
|
from routes.user_managment import route_user_managment
|
||||||
|
|
||||||
|
app.register_blueprint(route_auth)
|
||||||
|
app.register_blueprint(route_network)
|
||||||
|
app.register_blueprint(route_member)
|
||||||
|
app.register_blueprint(route_user_managment)
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def not_found(e):
|
||||||
|
return app.send_static_file('index.html')
|
||||||
|
|
||||||
|
@app.route('/', defaults={'path': ''})
|
||||||
|
@app.route('/<path:path>')
|
||||||
|
def serve(path):
|
||||||
|
if path != "" and os.path.exists(app.static_folder + '/' + path):
|
||||||
|
return send_from_directory(app.static_folder, path)
|
||||||
|
else:
|
||||||
|
return send_from_directory(app.static_folder, 'index.html')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print("""
|
||||||
|
/-----------------\\
|
||||||
|
| 𝕬̊𝖗𝖊 𝖛𝖔𝖒 𝕰𝖎𝖘𝖛𝖔𝖑𝖐 |
|
||||||
|
\-----------------/
|
||||||
|
""")
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=80)
|
6
backend-py/requirements.txt
Normal file
6
backend-py/requirements.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
flask
|
||||||
|
flask_cors
|
||||||
|
bcrypt
|
||||||
|
tinydb
|
||||||
|
loguru
|
||||||
|
requests
|
28
backend-py/routes/auth.py
Normal file
28
backend-py/routes/auth.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from flask import jsonify, request, Blueprint
|
||||||
|
from untils.response import Response
|
||||||
|
import os
|
||||||
|
from werkzeug.exceptions import BadRequest
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
route_auth = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
@route_auth.route('/auth/login', methods=['POST'])
|
||||||
|
def login():
|
||||||
|
|
||||||
|
login_data = request.json
|
||||||
|
userdata = db.read_user_credentials(login_data.get("username"))
|
||||||
|
if userdata != []:
|
||||||
|
if hash.is_correct_password(userdata[0].get("salted_passwort"), login_data.get("password")):
|
||||||
|
response_object = Response(200, "ok", {"role" : userdata[0].get("role"), "token" : "eisvolk"})
|
||||||
|
else:
|
||||||
|
raise BadRequest()
|
||||||
|
else:
|
||||||
|
raise BadRequest()
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
67
backend-py/routes/member.py
Normal file
67
backend-py/routes/member.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
from flask import jsonify, request, Blueprint
|
||||||
|
from untils.response import Response
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
|
||||||
|
from services.member import MEMBER
|
||||||
|
services_member = MEMBER()
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
route_member = Blueprint('member', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@route_member.route('/api/network/<string:nwid>/member', methods=['GET', 'DELETE'])
|
||||||
|
def api_network_nwid_member(nwid : str):
|
||||||
|
if request.method == "GET":
|
||||||
|
|
||||||
|
mids = zerotier_api.make_request(request.method, f"/controller/network/{nwid}/member")
|
||||||
|
|
||||||
|
data = services_member.getMembersData(nwid, mids.json().keys())
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", data)
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
|
||||||
|
@route_member.route('/api/network/<string:nwid>/member/<string:mid>', methods=['DELETE', 'POST'])
|
||||||
|
def api_network_nwid_member_member(nwid : str, mid : str):
|
||||||
|
if request.method == "POST":
|
||||||
|
|
||||||
|
services_member.updateMemberAdditionalData(nwid, mid, request.json)
|
||||||
|
|
||||||
|
if type(request.json) == list:
|
||||||
|
zerotier_api.make_request(request.method, f"/controller/network/{nwid}/member/{mid}", json = {"authorized" : request.json[0]["authorized"]})
|
||||||
|
data = services_member.getMembersData(nwid, [mid])
|
||||||
|
|
||||||
|
elif (request.json.get("config")):
|
||||||
|
|
||||||
|
zerotier_api.make_request(request.method, f"/controller/network/{nwid}/member/{mid}", json = request.json["config"])
|
||||||
|
data = services_member.getMembersData(nwid, [mid])
|
||||||
|
|
||||||
|
else:
|
||||||
|
data = services_member.getMembersData(nwid, [mid])
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", data)
|
||||||
|
|
||||||
|
elif request.method == "DELETE":
|
||||||
|
services_member.deleteMemberAdditionalData(nwid, mid)
|
||||||
|
|
||||||
|
zerotier_api.make_request(request.method, f"/controller/network/{nwid}/member/{mid}")
|
||||||
|
|
||||||
|
defaultConfig = {
|
||||||
|
"authorized": False,
|
||||||
|
"ipAssignments": [],
|
||||||
|
"capabilities": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
|
||||||
|
zerotier_api.make_request("POST", f"/controller/network/{nwid}/member/{mid}", json = defaultConfig)
|
||||||
|
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
72
backend-py/routes/network.py
Normal file
72
backend-py/routes/network.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from flask import jsonify, request, Blueprint
|
||||||
|
from untils.response import Response
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
|
||||||
|
from services.network import NETWORK
|
||||||
|
|
||||||
|
services_network = NETWORK()
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
route_network = Blueprint('network', __name__)
|
||||||
|
|
||||||
|
@route_network.route('/api/network', methods=['GET', 'POST'])
|
||||||
|
def api_network():
|
||||||
|
"""Get all Networks"""
|
||||||
|
if request.method == 'GET':
|
||||||
|
controllerResponse = zerotier_api.make_request("GET", "/controller/network")
|
||||||
|
|
||||||
|
nwids = controllerResponse.json()
|
||||||
|
data = services_network.getNetworksData(nwids)
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", data)
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
"""Create new Network"""
|
||||||
|
zt = zerotier_api.make_request("GET", "/status").json()["address"]
|
||||||
|
|
||||||
|
reqData = request.json["config"]
|
||||||
|
reqData["rules"] = sys_vars.default_rules
|
||||||
|
|
||||||
|
response = zerotier_api.make_request("POST", f"/controller/network/{zt}______", json = reqData)
|
||||||
|
|
||||||
|
data = services_network.getNetworksData([response.json()["id"]])
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", data[0])
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
@route_network.route('/api/network/<string:nwid>', methods=['GET', 'DELETE', 'POST'])
|
||||||
|
def api_network_nwid(nwid : str):
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
network = services_network.getNetworksData([nwid])[0]
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", network)
|
||||||
|
|
||||||
|
elif request.method == "DELETE":
|
||||||
|
|
||||||
|
zerotier_api.make_request(request.method, f"/controller/network/{nwid}")
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", db.delete_network(nwid))
|
||||||
|
|
||||||
|
elif request.method == "POST":
|
||||||
|
|
||||||
|
|
||||||
|
services_network.updateNetworkAdditionalData(nwid, request.json)
|
||||||
|
|
||||||
|
if request.json.get("config"):
|
||||||
|
services_network.updateNetworkAdditionalData(nwid, request.json["config"])
|
||||||
|
zerotier_api.make_request("POST", f"/controller/network/{nwid}", json = request.json.get("config"))
|
||||||
|
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
84
backend-py/routes/user_managment.py
Normal file
84
backend-py/routes/user_managment.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
from flask import jsonify, request, Blueprint
|
||||||
|
from untils.response import Response
|
||||||
|
from threading import Timer
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
from services.user_managment import USER_MANAGMENT
|
||||||
|
|
||||||
|
|
||||||
|
services_user_managment = USER_MANAGMENT()
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
route_user_managment = Blueprint('user_managment', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@route_user_managment.route('/api/user-managment', methods=['GET', 'POST'])
|
||||||
|
def api_user_managment():
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
user = services_user_managment.createUserData(request.json["username"], request.json["passwort"], request.json["role"])
|
||||||
|
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
|
||||||
|
elif request.method == 'GET':
|
||||||
|
users = []
|
||||||
|
data = services_user_managment.getUsersData()
|
||||||
|
for user in data:
|
||||||
|
users.append(user.get("username"))
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", users)
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
@route_user_managment.route('/api/user-managment/del/<string:user>', methods=['GET', 'POST'])
|
||||||
|
def api_user_managment_del(user : str):
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
|
||||||
|
user = services_user_managment.delUserData(user)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
@route_user_managment.route('/api/user-managment/member/<string:user>', methods=['GET', 'POST'])
|
||||||
|
def api_user_managment_update_member(user : str):
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
|
||||||
|
member = services_user_managment.getUserMember(user)
|
||||||
|
for network in member:
|
||||||
|
for idx, user_member in enumerate(member[network]):
|
||||||
|
auth = services_user_managment.isUserMemberAuth(network, user_member["address"])
|
||||||
|
member[network][idx]["authorized"] = auth
|
||||||
|
|
||||||
|
response_object = Response(200, "ok", member)
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
services_user_managment.updateUserMember(user, request.json["right"])
|
||||||
|
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
|
||||||
|
@route_user_managment.route('/api/user-managment/member/<string:user>/reason', methods=['POST'])
|
||||||
|
def api_user_managment_log_reason(user : str):
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
logger.info(f'Reason : {request.json["reason"]} | Duration : {request.json["duration"]}', ip=request.remote_addr, username=user)
|
||||||
|
t = Timer(services_user_managment.convert(request.json["duration"]), services_user_managment.deauth, args=(request.json["nwid"],request.json["mid"]))
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
response_object = Response(200, "ok")
|
||||||
|
return jsonify(response_object)
|
||||||
|
|
||||||
|
|
||||||
|
|
121
backend-py/services/member.py
Normal file
121
backend-py/services/member.py
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import sys, os, time
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
|
||||||
|
class MEMBER():
|
||||||
|
"""Classe um die Anfragen rund ums MemberVerwaltung zu handeln"""
|
||||||
|
|
||||||
|
def getPeer(self, mid : str) -> dict:
|
||||||
|
|
||||||
|
return zerotier_api.make_request("GET", f"/peer/{mid}")
|
||||||
|
|
||||||
|
def filterDeleted(self, nwid : str, mid : str) -> str:
|
||||||
|
try:
|
||||||
|
if db.read_networks_member_value(nwid, mid).get("deleted") != True:
|
||||||
|
|
||||||
|
return mid
|
||||||
|
except:
|
||||||
|
|
||||||
|
return mid
|
||||||
|
|
||||||
|
|
||||||
|
def getMembersData(self, nwid : str , mids : str) -> list:
|
||||||
|
members = []
|
||||||
|
for mid in mids:
|
||||||
|
if self.filterDeleted(nwid, mid):
|
||||||
|
member = zerotier_api.make_request("GET", f"/controller/network/{nwid}/member/{mid}").json()
|
||||||
|
members.append(self.getMemberAdditionalData(member))
|
||||||
|
|
||||||
|
return members
|
||||||
|
|
||||||
|
def getMemberAdditionalData(self, data : dict) -> dict:
|
||||||
|
|
||||||
|
#DB BUG INITIALIZATION MIGRATION
|
||||||
|
if db.read_networks_member_value(data.get("nwid"),data.get("id")) == None:
|
||||||
|
members = db.read_networks_value(data.get("nwid"))[0].get("members")
|
||||||
|
if members != None:
|
||||||
|
members.append({"id" : data.get("address")})
|
||||||
|
db.table_update("networks", {"members" : members } , db.read_networks_value(data.get("nwid"))[0].doc_id)
|
||||||
|
else:
|
||||||
|
db.table_update("networks", {"members" : [{"id" : data.get("address")}] } , db.read_networks_value(data.get("nwid"))[0].doc_id)
|
||||||
|
#END MIGRATION SECTION
|
||||||
|
|
||||||
|
|
||||||
|
peerData = {}
|
||||||
|
zt = zerotier_api.make_request("GET", "/status").json()["address"]
|
||||||
|
peer = self.getPeer(data.get("id")).json()
|
||||||
|
|
||||||
|
if (peer):
|
||||||
|
peerData["latency"] = peer.get("latency")
|
||||||
|
peerData["online"] = 1 if peer.get("latency") != -1 else 2
|
||||||
|
peerData["clientVersion"] = peer.get("version")
|
||||||
|
if peer.get("paths"):
|
||||||
|
peerData["lastOnline"] = peer.get("paths")[0]["lastReceive"]
|
||||||
|
peerData["physicalAddress"] = peer.get("paths")[0]["address"].split("/")[0]
|
||||||
|
peerData["physicalPort"] = peer.get("paths")[0]["address"].split("/")[1]
|
||||||
|
else:
|
||||||
|
peerData["online"] = 0
|
||||||
|
|
||||||
|
del data["lastAuthorizedCredential"]
|
||||||
|
del data["lastAuthorizedCredentialType"]
|
||||||
|
del data["objtype"]
|
||||||
|
del data["remoteTraceLevel"]
|
||||||
|
del data["remoteTraceTarget"]
|
||||||
|
|
||||||
|
additionalData = {}
|
||||||
|
|
||||||
|
try :
|
||||||
|
additionalData = db.read_networks_member_value(data.get("nwid"),data.get("id"))["additionalConfig"]
|
||||||
|
except:
|
||||||
|
additionalData = {}
|
||||||
|
|
||||||
|
memberData = {
|
||||||
|
"id" : data.get("nwid") + "-" + data.get("id"),
|
||||||
|
"type" : "Member",
|
||||||
|
"clock" : time.time(),
|
||||||
|
"networkId" : data.get("nwid"),
|
||||||
|
"nodeId" : data.get("id"),
|
||||||
|
"controllerId" : zt,
|
||||||
|
**additionalData,
|
||||||
|
**peerData,
|
||||||
|
"config" : data
|
||||||
|
}
|
||||||
|
|
||||||
|
return memberData
|
||||||
|
|
||||||
|
def updateMemberAdditionalData(self, nwid : str, mid : str, data : dict) -> None:
|
||||||
|
try:
|
||||||
|
additionalConfig = db.read_networks_member_value(nwid, mid)["additionalConfig"]
|
||||||
|
except:
|
||||||
|
additionalConfig = {}
|
||||||
|
|
||||||
|
## Only for Usermanagment
|
||||||
|
if type(data) == list:
|
||||||
|
data = data[0]
|
||||||
|
authorized = data["authorized"] if data["authorized"] == True else False
|
||||||
|
db.update_network_members(nwid, mid, {"additionalConfig" : db.merge_two_dicts(additionalConfig, {"authorized" : authorized})} )
|
||||||
|
##
|
||||||
|
else:
|
||||||
|
if data.get("config") and data.get("config").get("authorized") != None:
|
||||||
|
authorized = data.get("config")["authorized"] if data.get("config")["authorized"] == True else False
|
||||||
|
db.update_network_members(nwid, mid, {"additionalConfig" : db.merge_two_dicts(additionalConfig, {"authorized" : authorized})} )
|
||||||
|
|
||||||
|
additionalData = data.get("config") if data.get("config") != None else {}
|
||||||
|
|
||||||
|
if (data.get("name")):
|
||||||
|
additionalData["name"] = data["name"]
|
||||||
|
|
||||||
|
if (data.get("description")):
|
||||||
|
additionalData["description"] = data["description"]
|
||||||
|
|
||||||
|
if additionalData:
|
||||||
|
db.update_network_members(nwid, mid, {"additionalConfig" : db.merge_two_dicts(additionalConfig, additionalData)})
|
||||||
|
|
||||||
|
def deleteMemberAdditionalData(self, nwid, mid) -> None:
|
||||||
|
db.update_network_members(nwid, mid, {"id": mid, "deleted" : True})
|
79
backend-py/services/network.py
Normal file
79
backend-py/services/network.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import sys, os, time
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
|
||||||
|
class NETWORK():
|
||||||
|
"""Classe um die Anfragen rund ums Networking zu handeln"""
|
||||||
|
|
||||||
|
def getNetworksData(self, nwids : list) -> list:
|
||||||
|
networks = []
|
||||||
|
for nwid in nwids:
|
||||||
|
controllerResponse = zerotier_api.make_request("GET", f"/controller/network/{nwid}").json()
|
||||||
|
networks.append(self.getNetworkAdditionalData(controllerResponse))
|
||||||
|
|
||||||
|
return networks
|
||||||
|
|
||||||
|
def getNetworkAdditionalData(self, data : dict) -> dict:
|
||||||
|
if db.read_networks_value(data.get("id")) == []:
|
||||||
|
self.createNetworkAdditionalData(data.get("id"))
|
||||||
|
|
||||||
|
additionalData = db.read_networks_value(data.get("id"))[0].get("additionalConfig")
|
||||||
|
|
||||||
|
del data["rulesSource"]
|
||||||
|
del data["objtype"]
|
||||||
|
del data["revision"]
|
||||||
|
del data["remoteTraceLevel"]
|
||||||
|
del data["remoteTraceTarget"]
|
||||||
|
|
||||||
|
networkData = {
|
||||||
|
"id" : data.get("id"),
|
||||||
|
"type" : "Network",
|
||||||
|
"clock" : time.time(),
|
||||||
|
"config" : data,
|
||||||
|
}
|
||||||
|
|
||||||
|
networkData = db.merge_two_dicts(networkData,additionalData)
|
||||||
|
|
||||||
|
return networkData
|
||||||
|
|
||||||
|
def createNetworkAdditionalData(self, nwid : str) -> None:
|
||||||
|
saveData = {
|
||||||
|
"id": nwid,
|
||||||
|
"additionalConfig": {
|
||||||
|
"description": "",
|
||||||
|
"rulesSource": sys_vars.default_rule_source,
|
||||||
|
"tagsByName": {},
|
||||||
|
"capabilitiesByName": {},
|
||||||
|
},
|
||||||
|
"members": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
db.write_value("networks", saveData)
|
||||||
|
|
||||||
|
def updateNetworkAdditionalData(self, nwid : str, data : dict) -> None:
|
||||||
|
additionalData = {}
|
||||||
|
|
||||||
|
if (data.get("name")):
|
||||||
|
additionalData["name"] = data.get("name")
|
||||||
|
|
||||||
|
if (data.get("description")):
|
||||||
|
additionalData["description"] = data.get("description")
|
||||||
|
|
||||||
|
if (data.get("rulesSource")):
|
||||||
|
additionalData["rulesSource"] = data.get("rulesSource")
|
||||||
|
|
||||||
|
if (data.get("tagsByName")):
|
||||||
|
additionalData["tagsByName"] = data.get("tagsByName")
|
||||||
|
|
||||||
|
if (data.get("capabilitiesByName")):
|
||||||
|
additionalData["capabilitiesByName"] = data.get("capabilitiesByName")
|
||||||
|
|
||||||
|
if (additionalData):
|
||||||
|
db.update_network(nwid, additionalData)
|
||||||
|
|
||||||
|
|
||||||
|
|
66
backend-py/services/user_managment.py
Normal file
66
backend-py/services/user_managment.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import sys, os, time
|
||||||
|
from typing import Hashable
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from constants.constants import *
|
||||||
|
from services.member import MEMBER
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
|
||||||
|
class USER_MANAGMENT():
|
||||||
|
"""Classe um die Anfragen rund ums UserManagment zu handeln"""
|
||||||
|
|
||||||
|
def _hash_passwort(self, passwort : str) -> Hashable:
|
||||||
|
|
||||||
|
return hash.hash_password(passwort)
|
||||||
|
|
||||||
|
def createUserData(self, username : str, passwort : str, role : str):
|
||||||
|
if db.read_user_credentials(username) == []:
|
||||||
|
db.write_user_credentials(**{"username" : username, "salted_passwort" : self._hash_passwort(passwort), "role" : role})
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def delUserData(self, username : str) -> bool:
|
||||||
|
if db.read_user_credentials(username) != []:
|
||||||
|
db.delete_user(username)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getUsersData(self) -> list:
|
||||||
|
return db.read_users()
|
||||||
|
|
||||||
|
def getUserMember(self, user : str) -> dict:
|
||||||
|
members = db.read_user_member(user)[0].get("members")
|
||||||
|
if members:
|
||||||
|
return members
|
||||||
|
|
||||||
|
def updateUserMember(self, user : str, data : dict) -> None:
|
||||||
|
db.clean_user_member(user)
|
||||||
|
|
||||||
|
if data[0].get("config") != None:
|
||||||
|
for member in data:
|
||||||
|
memberData = {"name" : self.fix_none_username(member.get("name")), "address" : member.get("config")["address"]}
|
||||||
|
time.sleep(0.5)
|
||||||
|
db.update_user_member(user, member["networkId"], memberData)
|
||||||
|
else:
|
||||||
|
db.clean_user_member(user)
|
||||||
|
|
||||||
|
def isUserMemberAuth(self, nwid : str, mid : str) -> bool:
|
||||||
|
data = MEMBER().getMembersData(nwid, [mid])
|
||||||
|
return data[0]["config"]["authorized"]
|
||||||
|
|
||||||
|
|
||||||
|
def fix_none_username(self, name : str) -> str:
|
||||||
|
if name == None:
|
||||||
|
name = "Dummy Name"
|
||||||
|
return name
|
||||||
|
|
||||||
|
def deauth(self, nwid, mid):
|
||||||
|
zerotier_api.make_request("POST", f"/controller/network/{nwid}/member/{mid}", json = {"authorized" : False})
|
||||||
|
|
||||||
|
|
||||||
|
def convert(self, hours : int):
|
||||||
|
minuts = hours * 60
|
||||||
|
seconds = minuts * 60
|
||||||
|
return seconds
|
142
backend-py/untils/db.py
Normal file
142
backend-py/untils/db.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import os, time
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
from tinydb import TinyDB, Query
|
||||||
|
from tinydb import TinyDB, where
|
||||||
|
from tinydb.table import Document
|
||||||
|
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
class Zero_DB():
|
||||||
|
"""TinyDB Json Datenbank um verschiedene Werte zu speicher"""
|
||||||
|
"""PS: Das war teilweiße ein ganz schöner Krampf"""
|
||||||
|
|
||||||
|
def __init__(self, path=sys_vars.default_config_path, **kwargs) -> None:
|
||||||
|
self.db = TinyDB(path + 'db.json', indent=4)
|
||||||
|
self.users = self.db.table("users")
|
||||||
|
self.networks = self.db.table("networks")
|
||||||
|
self.query = Query()
|
||||||
|
|
||||||
|
def write_user_credentials(self, username, salted_passwort, role):
|
||||||
|
self.users.insert({
|
||||||
|
"username" : username,
|
||||||
|
"salted_passwort" : salted_passwort,
|
||||||
|
"role" : role
|
||||||
|
})
|
||||||
|
def merge_two_dicts(self, x, y):
|
||||||
|
"""Given two dictionaries, merge them into a new dict as a shallow copy."""
|
||||||
|
z = x.copy()
|
||||||
|
z.update(y)
|
||||||
|
return z
|
||||||
|
|
||||||
|
def read_user_credentials(self, user):
|
||||||
|
return self.users.search(self.query.username == user)
|
||||||
|
|
||||||
|
def read_user_member(self, user):
|
||||||
|
return self.users.search(self.query.username == user)
|
||||||
|
|
||||||
|
def read_users(self):
|
||||||
|
return self.users.all()
|
||||||
|
|
||||||
|
def read_networks_value(self, value):
|
||||||
|
return self.networks.search(self.query.id == value)
|
||||||
|
|
||||||
|
def read_networks_member_value(self, nwid, mid):
|
||||||
|
for item in self.networks.search(self.query.id == nwid):
|
||||||
|
if item.get("members") != None:
|
||||||
|
for member in item.get("members"):
|
||||||
|
if member.get("id") == mid:
|
||||||
|
return member
|
||||||
|
|
||||||
|
def update_network(self, nwid, data):
|
||||||
|
self.networks.upsert(Document({ "additionalConfig" : self.merge_two_dicts(self.read_networks_value(nwid)[0].get("additionalConfig"), data)}, doc_id=self.read_networks_value(nwid)[0].doc_id))
|
||||||
|
|
||||||
|
def update_network_members(self, nwid, mid, data):
|
||||||
|
upd = []
|
||||||
|
for item in self.read_networks_value(nwid)[0]["members"]:
|
||||||
|
if item["id"] == mid:
|
||||||
|
upd.append(self.merge_two_dicts(item, data))
|
||||||
|
else:
|
||||||
|
upd.append(item)
|
||||||
|
|
||||||
|
self.networks.upsert(Document({ "members" : upd }, doc_id=self.read_networks_value(nwid)[0].doc_id))
|
||||||
|
|
||||||
|
def find_networks_id_by_member(self, value):
|
||||||
|
for network in self.networks.all():
|
||||||
|
for member in network.get("members"):
|
||||||
|
if member == value:
|
||||||
|
return network.get("id")
|
||||||
|
|
||||||
|
def clean_user_member(self, user):
|
||||||
|
|
||||||
|
self.users.upsert(Document({ "members" : {} }, doc_id=self.read_user_member(user)[0].doc_id))
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_member(self, user, key, data : dict):
|
||||||
|
|
||||||
|
members = {}
|
||||||
|
|
||||||
|
def _get_dict_values(item : str):
|
||||||
|
return self.read_user_member(user)[0].get("members")[item]
|
||||||
|
|
||||||
|
def _get_dict_key(item : dict):
|
||||||
|
return item
|
||||||
|
|
||||||
|
if self.read_user_member(user)[0].get("members") != None and self.read_user_member(user)[0].get("members") != {}:
|
||||||
|
|
||||||
|
popd = self.read_user_member(user)[0].get("members")
|
||||||
|
try:
|
||||||
|
popd[key]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
members.update(popd)
|
||||||
|
|
||||||
|
for item in self.read_user_member(user)[0].get("members"):
|
||||||
|
if _get_dict_key(item) == key:
|
||||||
|
members.update({key : [data, *_get_dict_values(item)]})
|
||||||
|
else:
|
||||||
|
members.update({key : [data]})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
members.update({key : [data]})
|
||||||
|
|
||||||
|
self.users.upsert(Document({ "members" : members }, doc_id=self.read_user_member(user)[0].doc_id))
|
||||||
|
|
||||||
|
def delete_network(self, value):
|
||||||
|
return self.networks.remove(self.query.id == value)
|
||||||
|
|
||||||
|
def delete_user(self, value):
|
||||||
|
return self.users.remove(self.query.username == value)
|
||||||
|
|
||||||
|
def write_value(self, table, value):
|
||||||
|
self.db.table(table).insert(value)
|
||||||
|
|
||||||
|
def read_table_values(self, table):
|
||||||
|
return self.db.table(table).all()
|
||||||
|
|
||||||
|
def table_update(self, table, value, doc_id):
|
||||||
|
self.db.table(table).update(value, doc_ids=[doc_id])
|
||||||
|
|
||||||
|
def return_db(self):
|
||||||
|
return self.db
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
db = Zero_DB()
|
||||||
|
nwid = "95004cf76d12a86e"
|
||||||
|
mid = "95004cf76d"
|
||||||
|
from tinydb.operations import set
|
||||||
|
from tinydb.table import Document
|
||||||
|
db.networks.remove(where('id') == 'xxx')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
9
backend-py/untils/response.py
Normal file
9
backend-py/untils/response.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Response():
|
||||||
|
"""Default Response für Frontend"""
|
||||||
|
|
||||||
|
success : int
|
||||||
|
message : str
|
||||||
|
data : list = field(default=None)
|
26
backend-py/untils/textTransform.py
Normal file
26
backend-py/untils/textTransform.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from typing import Tuple
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
class HASH():
|
||||||
|
"""Classe zum Hashen und zum Salzen des Passwortest"""
|
||||||
|
|
||||||
|
def hash_password(self, password: str) -> Tuple[bytes, bytes]:
|
||||||
|
"""
|
||||||
|
Hash the provided password with a randomly-generated salt and return the
|
||||||
|
hash to store in the database.
|
||||||
|
"""
|
||||||
|
salt = bcrypt.gensalt()
|
||||||
|
pw_hash = bcrypt.hashpw(password.encode(encoding='utf-8'), salt)
|
||||||
|
|
||||||
|
return pw_hash.decode()
|
||||||
|
|
||||||
|
def is_correct_password(self, pw_hash: str, password: str) -> bool:
|
||||||
|
"""
|
||||||
|
Given a previously-stored hash, and a password provided by a user
|
||||||
|
trying to log in, check whether the password is correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
success = bcrypt.checkpw(password.encode('utf-8'), pw_hash.encode())
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
21
backend-py/untils/zero_tier_api.py
Normal file
21
backend-py/untils/zero_tier_api.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
|
||||||
|
from vars import DARWIN, LINUX
|
||||||
|
sys_vars = DARWIN() if os.uname().sysname == "Darwin" else LINUX()
|
||||||
|
|
||||||
|
class ZEROTIER_REST_API():
|
||||||
|
"""Classe um Requests an die Zerotier API zu machen"""
|
||||||
|
|
||||||
|
token = sys_vars.zerotier_token
|
||||||
|
headers = {"Accept": "application/json", "Content-Type": "application/json", "X-ZT1-Auth": token}
|
||||||
|
base_URL = "http://localhost:9993" if os.environ.get("ZU_CONTROLLER_ENDPOINT") == None else str(os.environ["ZU_CONTROLLER_ENDPOINT"])
|
||||||
|
|
||||||
|
def make_request(self, method: str, url_piece: str, **kwargs) -> dict:
|
||||||
|
response = requests.request(method, url=self.base_URL + url_piece, headers=self.headers, **kwargs)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
27
backend-py/untils/zerologger.py
Normal file
27
backend-py/untils/zerologger.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class LogurLogger(object):
|
||||||
|
def __init__(self, logger) -> None:
|
||||||
|
self.logger = logger
|
||||||
|
self.logger.remove(0)
|
||||||
|
self.logger.add("./config/UserAuth.log", format=self.default_formatter, colorize=False, rotation="10 MB")
|
||||||
|
self.logger.add(sys.stderr, format=self.default_formatter,colorize=True)
|
||||||
|
|
||||||
|
|
||||||
|
def rainbow(self, text):
|
||||||
|
colors = ["red", "yellow", "green", "cyan", "blue", "magenta"]
|
||||||
|
chars = ("<{}>{}</>".format(colors[i % len(colors)], c) for i, c in enumerate(text.split()))
|
||||||
|
return " ".join(chars)
|
||||||
|
|
||||||
|
def default_formatter(self, record):
|
||||||
|
rainbow_message = self.rainbow(record["message"])
|
||||||
|
# Prevent '{}' in message (if any) to be incorrectly parsed during formatting
|
||||||
|
escaped = rainbow_message.replace("{", "{{").replace("}", "}}")
|
||||||
|
return "<blue>{time}</blue> | <green>{level}</green> | {extra[ip]} | {extra[username]} | " + escaped + "\n{exception}"
|
||||||
|
|
||||||
|
|
||||||
|
def getlogger(self):
|
||||||
|
return self.logger
|
||||||
|
|
||||||
|
|
41
backend-py/vars.py
Normal file
41
backend-py/vars.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import pathlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
class OS_DEFAULTS():
|
||||||
|
"""Classe um die Default Pfade und Variablen bereitzustellen"""
|
||||||
|
|
||||||
|
default_config_path = './config/'
|
||||||
|
|
||||||
|
try:
|
||||||
|
username = str(os.environ['ZU_DEFAULT_USERNAME'])
|
||||||
|
password = str(os.environ['ZU_DEFAULT_PASSWORD'])
|
||||||
|
|
||||||
|
except:
|
||||||
|
username = "admin"
|
||||||
|
password = "zero-ui"
|
||||||
|
|
||||||
|
default_admin_creditals = {
|
||||||
|
"username" : username,
|
||||||
|
"passwort" : password
|
||||||
|
}
|
||||||
|
|
||||||
|
default_rule_source = open("./constants/default_rules_source.txt").read()
|
||||||
|
default_rules = open("./constants/default_rules.json").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DARWIN(OS_DEFAULTS):
|
||||||
|
"""Classe um die Pfade und Variablen des Betriebsystems bereitzustellen"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
self.default_path = str(pathlib.Path.home()) + "/Zerotier"
|
||||||
|
self.zerotier_token = open("/Library/Application Support/ZeroTier/One/authtoken.secret", encoding="utf-8").read()
|
||||||
|
|
||||||
|
class LINUX(OS_DEFAULTS):
|
||||||
|
"""Classe um die Pfade und Variablen des Betriebsystems bereitzustellen"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
self.default_path = "/Zerotier"
|
||||||
|
self.zerotier_token = open("/var/lib/zerotier-one/authtoken.secret", encoding="utf-8").read()
|
|
@ -11,20 +11,33 @@ COPY ./frontend /app/frontend
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
|
||||||
FROM node:lts-alpine
|
### Node Backend
|
||||||
|
#FROM node:lts-alpine
|
||||||
|
#
|
||||||
|
#WORKDIR /app/frontend/build
|
||||||
|
#COPY --from=build-stage /app/frontend/build /app/frontend/build/
|
||||||
|
#
|
||||||
|
#WORKDIR /app/backend
|
||||||
|
#COPY ./backend/package*.json /app/backend
|
||||||
|
#RUN yarn install
|
||||||
|
#
|
||||||
|
#COPY ./backend /app/backend
|
||||||
|
#
|
||||||
|
#EXPOSE 4000
|
||||||
|
#ENV NODE_ENV=production
|
||||||
|
#ENV ZU_SECURE_HEADERS=true
|
||||||
|
#ENV ZU_SERVE_FRONTEND=true
|
||||||
|
#
|
||||||
|
#CMD [ "node", "./bin/www" ]
|
||||||
|
|
||||||
WORKDIR /app/frontend/build
|
#### Python Backend
|
||||||
COPY --from=build-stage /app/frontend/build /app/frontend/build/
|
FROM tiangolo/uwsgi-nginx-flask:python3.9
|
||||||
|
|
||||||
WORKDIR /app/backend
|
COPY ./backend-py/requirements.txt /app/requirements.txt
|
||||||
COPY ./backend/package*.json /app/backend
|
|
||||||
RUN yarn install
|
|
||||||
|
|
||||||
COPY ./backend /app/backend
|
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||||
|
|
||||||
EXPOSE 4000
|
COPY ./backend-py /app
|
||||||
ENV NODE_ENV=production
|
COPY --from=build-stage /app/frontend/build /app/app/
|
||||||
ENV ZU_SECURE_HEADERS=true
|
ENV FLASK_APP=main.py
|
||||||
ENV ZU_SERVE_FRONTEND=true
|
EXPOSE 80
|
||||||
|
|
||||||
CMD [ "node", "./bin/www" ]
|
|
Loading…
Add table
Add a link
Reference in a new issue