diff --git a/proxstar/__init__.py b/proxstar/__init__.py index 31005ca..a596dc4 100644 --- a/proxstar/__init__.py +++ b/proxstar/__init__.py @@ -76,34 +76,28 @@ if 'process_expiring_vms' not in scheduler: @app.route("/") -@app.route("/user/") +@app.route("/user/") @auth.oidc_auth -def list_vms(user=None): +def list_vms(user_view=None): + user = build_user_dict(session, db) rtp_view = False - rtp = 'rtp' in session['userinfo']['groups'] - active = 'active' in session['userinfo']['groups'] proxmox = connect_proxmox() - if user and not rtp: + if user_view and not user['rtp']: return '', 403 - elif user and rtp: - vms = get_vms_for_user(proxmox, db, user) - rtp_view = user - user = session['userinfo']['preferred_username'] - elif rtp: - user = session['userinfo']['preferred_username'] + elif user_view and user['rtp']: + vms = get_vms_for_user(proxmox, db, user_view) + rtp_view = user_view + elif user['rtp']: vms = get_pool_cache(db) rtp_view = True else: - user = session['userinfo']['preferred_username'] - if active: - vms = get_vms_for_user(proxmox, db, user) + if user['active']: + vms = get_vms_for_user(proxmox, db, user['username']) else: vms = 'INACTIVE' return render_template( 'list_vms.html', - username=user, - rtp=rtp, - active=active, + user=user, rtp_view=rtp_view, vms=vms) @@ -131,11 +125,9 @@ def hostname(name): @app.route("/vm/") @auth.oidc_auth def vm_details(vmid): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] - active = 'active' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): vm = get_vm(proxmox, vmid) vm['vmid'] = vmid vm['config'] = get_vm_config(proxmox, vmid) @@ -150,16 +142,14 @@ def vm_details(vmid): get_ip_for_mac(starrs, interface[1])]) vm['expire'] = get_vm_expire( db, vmid, app.config['VM_EXPIRE_MONTHS']).strftime('%m/%d/%Y') - usage = get_user_usage(proxmox, db, user) - limits = get_user_usage_limits(db, user) - usage_check = check_user_usage(proxmox, db, user, + usage = get_user_usage(proxmox, db, user['username']) + limits = get_user_usage_limits(db, user['username']) + usage_check = check_user_usage(proxmox, db, user['username'], vm['config']['cores'], vm['config']['memory'], 0) return render_template( 'vm_details.html', - username=user, - rtp=rtp, - active=active, + user=user, vm=vm, usage=usage, limits=limits, @@ -171,13 +161,12 @@ def vm_details(vmid): @app.route("/vm//power/", methods=['POST']) @auth.oidc_auth def vm_power(vmid, action): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): if action == 'start': config = get_vm_config(proxmox, vmid) - usage_check = check_user_usage(proxmox, db, user, config['cores'], + usage_check = check_user_usage(proxmox, db, user['username'], config['cores'], config['memory'], 0) if usage_check: return usage_check @@ -190,10 +179,9 @@ def vm_power(vmid, action): @app.route("/vm//console", methods=['POST']) @auth.oidc_auth def vm_console(vmid): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): port = str(5900 + int(vmid)) token = add_vnc_target(port) node = "{}.csh.rit.edu".format(get_vm_node(proxmox, vmid)) @@ -212,18 +200,17 @@ def vm_console(vmid): @app.route("/vm//cpu/", methods=['POST']) @auth.oidc_auth def vm_cpu(vmid, cores): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): cur_cores = get_vm_config(proxmox, vmid)['cores'] if cores >= cur_cores: status = get_vm(proxmox, vmid)['qmpstatus'] if status == 'running' or status == 'paused': - usage_check = check_user_usage(proxmox, db, user, + usage_check = check_user_usage(proxmox, db, user['username'], cores - cur_cores, 0, 0) else: - usage_check = check_user_usage(proxmox, db, user, cores, 0, 0) + usage_check = check_user_usage(proxmox, db, user['username'], cores, 0, 0) if usage_check: return usage_check change_vm_cpu(proxmox, vmid, cores) @@ -235,18 +222,17 @@ def vm_cpu(vmid, cores): @app.route("/vm//mem/", methods=['POST']) @auth.oidc_auth def vm_mem(vmid, mem): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): cur_mem = get_vm_config(proxmox, vmid)['memory'] // 1024 if mem >= cur_mem: status = get_vm(proxmox, vmid)['qmpstatus'] if status == 'running' or status == 'paused': - usage_check = check_user_usage(proxmox, db, user, 0, + usage_check = check_user_usage(proxmox, db, user['username'], 0, mem - cur_mem, 0) else: - usage_check = check_user_usage(proxmox, db, user, 0, mem, 0) + usage_check = check_user_usage(proxmox, db, user['username'], 0, mem, 0) if usage_check: return usage_check change_vm_mem(proxmox, vmid, mem * 1024) @@ -258,10 +244,9 @@ def vm_mem(vmid, mem): @app.route("/vm//renew", methods=['POST']) @auth.oidc_auth def vm_renew(vmid): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): renew_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS']) for interface in get_vm_interfaces(proxmox, vmid): renew_ip(starrs, get_ip_for_mac(starrs, interface[1])) @@ -273,10 +258,9 @@ def vm_renew(vmid): @app.route("/vm//eject", methods=['POST']) @auth.oidc_auth def iso_eject(vmid): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user): eject_vm_iso(proxmox, vmid) return '', 200 else: @@ -286,10 +270,9 @@ def iso_eject(vmid): @app.route("/vm//mount/", methods=['POST']) @auth.oidc_auth def iso_mount(vmid, iso): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms(proxmox, db, user): + if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user['username']): iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso) mount_vm_iso(proxmox, vmid, iso) return '', 200 @@ -300,11 +283,10 @@ def iso_mount(vmid, iso): @app.route("/vm//delete", methods=['POST']) @auth.oidc_auth def delete(vmid): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if rtp or int(vmid) in get_user_allowed_vms( - proxmox, db, user) or 'rtp' in session['userinfo']['groups']: + if user['rtp'] or int(vmid) in get_user_allowed_vms( + proxmox, db, user['username']) or 'rtp' in session['userinfo']['groups']: q.enqueue(delete_vm_task, vmid) return '', 200 else: @@ -314,23 +296,19 @@ def delete(vmid): @app.route("/vm/create", methods=['GET', 'POST']) @auth.oidc_auth def create(): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] - active = 'active' in session['userinfo']['groups'] + user = build_user_dict(session, db) proxmox = connect_proxmox() - if active: + if user['active']: if request.method == 'GET': - usage = get_user_usage(proxmox, db, user) - limits = get_user_usage_limits(db, user) - percents = get_user_usage_percent(proxmox, user, usage, limits) + usage = get_user_usage(proxmox, db, user['username']) + limits = get_user_usage_limits(db, user['username']) + percents = get_user_usage_percent(proxmox, user['username'], usage, limits) isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE']) pools = get_pools(proxmox, db) templates = get_templates(db) return render_template( 'create.html', - username=user, - rtp=rtp, - active=active, + user=user, usage=usage, limits=limits, percents=percents, @@ -347,11 +325,11 @@ def create(): if iso != 'none': iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso) - if not rtp: - usage_check = check_user_usage(proxmox, db, user, 0, 0, disk) + if not user['rtp']: + usage_check = check_user_usage(proxmox, db, user['username'], 0, 0, disk) else: usage_check = None - user = request.form['user'] + user['username'] = request.form['user'] if usage_check: return usage_check else: @@ -360,7 +338,7 @@ def create(): if template == 'none': q.enqueue( create_vm_task, - user, + user['username'], name, cores, memory, @@ -373,7 +351,7 @@ def create(): setup_template, template, name, - user, + user['username'], password, cores, memory, @@ -412,19 +390,17 @@ def delete_user(user): @app.route("/settings") @auth.oidc_auth def settings(): - user = session['userinfo']['preferred_username'] - rtp = 'rtp' in session['userinfo']['groups'] - active = 'active' in session['userinfo']['groups'] - if rtp: + user = build_user_dict(session, db) + if user['rtp']: templates = get_templates(db) ignored_pools = get_ignored_pools(db) + allowed_users = get_allowed_users(db) return render_template( 'settings.html', - username=user, - rtp=rtp, - active=active, + user=user, templates=templates, - ignored_pools=ignored_pools) + ignored_pools=ignored_pools, + allowed_users=allowed_users) else: return '', 403 @@ -442,6 +418,29 @@ def ignored_pools(pool): return '', 403 +@app.route("/user//allow", methods=['POST', 'DELETE']) +@auth.oidc_auth +def allowed_users(user): + if 'rtp' in session['userinfo']['groups']: + if request.method == 'POST': + add_allowed_user(db, user) + elif request.method == "DELETE": + delete_allowed_user(db, user) + return '', 200 + else: + return '', 403 + + +@app.route("/targets/clear") +@auth.oidc_auth +def clear_targets(): + if 'rtp' in session['userinfo']['groups']: + clear_vnc_targets() + return '', 200 + else: + return '', 403 + + @app.route('/template//disk') @auth.oidc_auth def template_disk(template_id): diff --git a/proxstar/db.py b/proxstar/db.py index 11e794e..3e76dde 100644 --- a/proxstar/db.py +++ b/proxstar/db.py @@ -2,7 +2,7 @@ import datetime from sqlalchemy import exists from dateutil.relativedelta import relativedelta from proxstar.ldapdb import * -from proxstar.models import VM_Expiration, Usage_Limit, Pool_Cache, Ignored_Pools, Template, Base +from proxstar.models import VM_Expiration, Usage_Limit, Pool_Cache, Ignored_Pools, Template, Allowed_Users, Base def get_vm_expire(db, vmid, months): @@ -180,3 +180,25 @@ def get_template_disk(db, template_id): template = db.query(Template).filter(Template.id == template_id).one() disk = template.disk return str(disk) + + +def get_allowed_users(db): + allowed_users = [] + for user in db.query(Allowed_Users).all(): + allowed_users.append(user.id) + return allowed_users + + +def add_allowed_user(db, user): + if not db.query(exists().where(Allowed_Users.id == user)).scalar(): + allowed_user = Allowed_Users(id=user) + db.add(allowed_user) + db.commit() + + +def delete_allowed_user(db, user): + if db.query(exists().where(Allowed_Users.id == user)).scalar(): + allowed_user = db.query(Allowed_Users).filter( + Allowed_Users.id == user).one() + db.delete(allowed_user) + db.commit() diff --git a/proxstar/models.py b/proxstar/models.py index 598cfc5..9e46ee8 100644 --- a/proxstar/models.py +++ b/proxstar/models.py @@ -43,3 +43,8 @@ class Template(Base): class Ignored_Pools(Base): __tablename__ = 'ignored_pools' id = Column(String(32), primary_key=True) + + +class Allowed_Users(Base): + __tablename__ = 'allowed_users' + id = Column(String(32), primary_key=True) diff --git a/proxstar/static/js/script.js b/proxstar/static/js/script.js index 1f2a531..af774dc 100644 --- a/proxstar/static/js/script.js +++ b/proxstar/static/js/script.js @@ -777,8 +777,9 @@ $(".delete-ignored-pool").click(function(){ fetch(`/pool/${pool}/ignore`, { credentials: 'same-origin', method: 'delete' - }); + }).then((response) => { location.reload(); + }); }); $(".add-ignored-pool").click(function(){ @@ -786,8 +787,9 @@ $(".add-ignored-pool").click(function(){ fetch(`/pool/${pool}/ignore`, { credentials: 'same-origin', method: 'post' - }); + }).then((response) => { location.reload(); + }); }); function hide_for_template(obj) { @@ -822,3 +824,23 @@ $("#console-vm").click(function(){ } }); }); + +$(".delete-allowed-user").click(function(){ + const user = $(this).data('user'); + fetch(`/user/${user}/allow`, { + credentials: 'same-origin', + method: 'delete' + }).then((response) => { + location.reload(); + }); +}); + +$(".add-allowed-user").click(function(){ + const user = document.getElementById('user').value; + fetch(`/user/${user}/allow`, { + credentials: 'same-origin', + method: 'post' + }).then((response) => { + location.reload(); + }); +}); diff --git a/proxstar/templates/base.html b/proxstar/templates/base.html index b860279..d8d67c1 100644 --- a/proxstar/templates/base.html +++ b/proxstar/templates/base.html @@ -27,7 +27,7 @@ +
+
+
+

Allowed Users

+
+
+ + + + + + + + + {% for username in allowed_users %} + + + + + {% endfor %} + +
UserDelete
{{ username }} + +
+
+ +
+
+
diff --git a/proxstar/util.py b/proxstar/util.py index d454a74..41e0d5b 100644 --- a/proxstar/util.py +++ b/proxstar/util.py @@ -1,5 +1,6 @@ import string import random +from proxstar.db import * def gen_password( @@ -8,3 +9,11 @@ def gen_password( ): # use secrets module once this works in python 3.6 return ''.join(random.choice(charset) for x in range(length)) + + +def build_user_dict(session, db): + user_dict = dict() + user_dict['username'] = session['userinfo']['preferred_username'] + user_dict['active'] = 'active' in session['userinfo']['groups'] or user_dict['username'] in get_allowed_users(db) + user_dict['rtp'] = 'rtp' in session['userinfo']['groups'] + return user_dict diff --git a/proxstar/vnc.py b/proxstar/vnc.py index 45ee9e8..aacf6e9 100644 --- a/proxstar/vnc.py +++ b/proxstar/vnc.py @@ -60,6 +60,10 @@ def add_vnc_target(port): return token +def clear_vnc_targets(): + open(app.config['WEBSOCKIFY_TARGET_FILE'], 'w').close() + + def start_ssh_tunnel(node, port): port = int(port) server = SSHTunnelForwarder(