#!/usr/bin/env python3 from flask import Flask, request, abort, make_response, \ render_template, redirect, url_for, \ json, jsonify, session, flash from collections import namedtuple from subprocess import check_call, call from fcntl import flock, LOCK_EX, LOCK_SH, LOCK_UN import hashlib import random import shutil import time import sys import re import os #app = Flask("ISABEL-2 Verifier") # That app name breaks Ubuntu 14.04 :-o app = Flask("main") app.secret_key = "6ab77f3c45447429c2ae163c260a626029519a66450e474c" users_file = "/etc/freeradius/users.dpto2" dhcp_hosts_file = "/etc/dnsmasq.d/dpto2/dhcp-hosts" dhcp_opts_file = "/etc/dnsmasq.d/dpto2/dhcp-opts" class Lock: def __init__(self, lockfile): self.lockfile = lockfile def __enter__(self): flock(self.lockfile, LOCK_EX) def __exit__(self, exc_type, exc_val, exc_tb): flock(self.lockfile, LOCK_UN) class SharedLock(Lock): def __init__(self, lockfile): super().__init__(lockfile) def __enter__(self, exc_type, exc_val, exc_tb): flock(self.lockfile, LOCK_SH) lock_file = open("/run/lock/dpto2.lock","w") exclusive_lock = Lock(lock_file) shared_lock = SharedLock(lock_file) def reload_freeradius(): call("./reload_freeradius") def delete_user(deluser): f = open(users_file) lines = f.readlines() f.close() timestamp = time.time() tempfile = "{}.{}".format(users_file, timestamp) f = open(tempfile,"w") for line in lines: if line.startswith(deluser): continue f.write(line) f.close() shutil.move(tempfile, users_file) reload_freeradius() def nthash(password): return hashlib.new('md4',password.encode('utf-16le')).hexdigest().upper() def create_user(username, password, creator): with exclusive_lock: f = open(users_file,"a") f.write('{} NT-Password := "{}" # created by {} \n'.format(username, nthash(password), creator)) f.close() reload_freeradius() @app.route("/") def index(): f = open(users_file) guestpass = "?" for line in f: if line.startswith("guest"): m = re.search(':=\s+"(.+?)"\s*$',line) if m: guestpass = m.group(1) break return render_template("index.html", guestpass=guestpass) def load_users(): users = [] with open(users_file) as f: for l in f: if l.strip().startswith("#"): continue m = re.match("(^\S+).*-Password\s+:=\s+\"(\S+)\"(?:\s+#.*created.by\s+(\S+))?", l) if m: users.append(m.groups()) return users def check_login(username, password): for u,p,c in load_users(): if u == username and p == nthash(password): return True raise ValueError("Invalid username or password") @app.route("/login",methods=['GET','POST']) def login(): if session.get('logged_in',False): return redirect(url_for('admin')) if request.method == 'GET': return render_template("login.html") if request.method == 'POST': username = request.form.get("username",None) password = request.form.get("password",None) if username is None or password is None: return render_template("login.html",error=True,errormsg="invalid username or password") if username == 'guest': return render_template("login.html",error=True,errormsg="guest user has no admin privileges") try: check_login(username, password) except Exception as e: return render_template("login.html",error=True,errormsg=str(e)) session['logged_in'] = True session['username'] = username return redirect(url_for('admin')) @app.route("/admin",methods=['GET','POST']) def admin(): if not session.get('logged_in', False): return redirect(url_for('login')) if request.method == 'POST': deluser = request.form.get('deluser',None) if deluser is not None: if deluser == 'guest': return render_template("admin.html", delete_error=True, errormsg="Cannot delete guest user") delete_user(deluser) flash("User deleted succesfully") username = request.form.get('username',None) pass1 = request.form.get('password1',None) pass2 = request.form.get('password2',None) creator = session['username'] if username is not None: if username == 'guest': return render_template("admin.html", create_error=True, errormsg="Cannot create guest user") if pass1 is None or \ pass2 is None or \ pass1 != pass2: return render_template("admin.html", create_error=True, errormsg="Password do not match") create_user(username,pass1,creator) flash("User created successfully") return render_template("admin.html") def render_users_tree(tree): lines = [] def _render(user, edges, is_last): lines.append((edges + (" └─" if is_last else " ├─"), user)) n = len(tree[user]) for i,u in enumerate(tree[user]): _render(u, edges + (" " if is_last else " │ "), i == n - 1) return lines # root = tree(""); n = len(root) # for i,u in enumerate(root): _render("", "", True) return lines @app.route("/users") def list_users(): if not session.get('logged_in', False): return redirect(url_for('login')) users = load_users() users_set = set(x[0] for x in users) tree = { "": [] } for user,passwd,creator in users: if user == "guest": continue tree[user] = [] if creator is None: tree[""].append(user) else: if creator not in users_set: creator += " (invalid username)" tree[""].append(creator) tree[creator] = tree.get(creator,[]) + [user] lines = render_users_tree(tree) return render_template("users.html", users=lines) mac_re = re.compile("(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}") ip_re = re.compile("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") DhcpHostLine = namedtuple("DhcpHostLine","macs ip tag") def parse_dchp_host(line): macs = [] ip = "" tag = "" parts = line.split(",") for p in parts: p = p.strip() m = mac_re.match(p) if m: macs.append(m.group(0)) continue m = ip_re.match(p) if m: ip = m.group(0) continue if p.startswith("set:"): tag = p[4:] return DhcpHostLine(macs, ip, tag) Ip = namedtuple("Ip","reserved_by dhcp locked") def load_ips(): ipmap = {} for i in range(1,255): ipmap[i] = Ip("",True,False) ipmap[0] = ipmap[255] = Ip("", False, False) prefix = None with open(dhcp_hosts_file) as f: meta = {} for line in f: line = line.strip() if line.startswith("#"): try: meta = json.loads(line[1:]) except: meta = {} continue if not isinstance(meta,dict): meta = {} continue if re.match("^[0-9a-fA-F]{2}:",line): r = parse_dchp_host(line) ip = int(r.ip.split(".")[-1]) if prefix is None: prefix = ".".join(r.ip.split(".")[:-1]) ipmap[ip] = Ip( reserved_by=meta.get("reserved-by",""), dhcp=False, locked=meta.get("locked",False) ) meta = {} ipmap["prefix"] = prefix return ipmap @app.route("/ips") def ips(): ipmap = load_ips() return render_template("ips.html", ipmap=ipmap) @app.route("/ip/") def ip(last_byte): ipmap = load_ips() addr = "{}.{}".format(ipmap["prefix"], last_byte) return render_template( "ip.html", addr=addr, meta=ipmap[last_byte], ) @app.route("/logout") def logout(): session.pop("logged_in",None) return redirect(url_for("index")) if __name__ == '__main__': if "debug" in sys.argv: users_file = "users.dpto2" dhcp_hosts_file = "dhcp-hosts.dpto2" dhcp_opts_file = "dhcp-opts.dpto2" app.debug = True app.run(host="0.0.0.0")