From e9ea65a046d42aedcfe6555bb97c514dfeefcd8a Mon Sep 17 00:00:00 2001 From: William Brown Date: Oct 01 2019 04:39:59 +0000 Subject: Add data persistence --- diff --git a/TODO b/TODO new file mode 100644 index 0000000..7389675 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 app:app + + diff --git a/server.py b/server.py index 8fa6309..4934467 100644 --- a/server.py +++ b/server.py @@ -117,34 +117,94 @@ def _bind_dn(ldapurl, binddn, password): inst.unbind_s() return True -@app.route('/', methods=['GET', 'POST']) -def index(): - # How to handle no auth? - if 'dn' not in session: +@app.errorhandler(404) +@app.route('/notfound') +def notfound(arg): + return render_template('notfound.html') + +@app.route('/error') +def error(): + return render_template('error.html'), 500 + +@app.route('/account/', methods=['PATCH']) +def account_dn_update(req_dn): + # I don't think this is correct for a post, because we can't redirect + # the main page. We may need to return a status code the ajax can interpret + try: + dn = session['dn'] + enc_token = session['token'] + app.logger.debug(f'{enc_token}') + token = FERNET.decrypt(enc_token).decode() + except Exception as e: + app.logger.debug(e) + app.logger.error('Failed to decrypt auth token or get account details.') + # TODO: Put a session invalid message here. return redirect(url_for('login')) + print("Got: %s" % request.json) - # Check and handle no dn/password still ... - dn = session['dn'] - enc_token = session['token'] - app.logger.debug(f'{enc_token}') - # Decrypt and turn to utf8 - # TODO: Handle decrypt failure. - token = FERNET.decrypt(enc_token).decode() - app.logger.debug(f'{dn}, {token}') + # Transform the request to the state dict expected by lib389 + state = {} + for v in request.json: + # This is a dict of { name, value } + # First strip the [] from name. + attr_name = v["name"].replace('[]', '') + # Now get the value + value = v["value"] + # Create at least an empty list, needed for purging. + l = state.setdefault(attr_name, []) + if value != '': + l.append(value) + app.logger.debug(state) + # Setup the ldap connection (if possible) inst = _get_ds_instance(CONFIG['ldapurl'], dn, token) try: inst.open() - except e: - print("ruh roh") - print(e) + # except Exception as e: + # app.logger.debug(e) + # return '', 500 finally: - # TODO pass try: nsaccts = nsUserAccounts(inst, CONFIG['basedn'], rdn=None) + acct = nsaccts.get(dn=req_dn) + acct.ensure_attr_state(state) + # except Exception as e: + # app.logger.debug(e) + # return '', 500 + finally: + inst.unbind_s() + + return '', 200 + +@app.route('/', methods=['GET']) +def index(): + # Are they authenticated? + try: + dn = session['dn'] + enc_token = session['token'] + app.logger.debug(f'{enc_token}') + token = FERNET.decrypt(enc_token).decode() + except Exception as e: + app.logger.debug(e) + app.logger.error('Failed to decrypt auth token or get account details.') + # TODO: Put a session invalid message here. + return redirect(url_for('login')) + + + app.logger.debug(f'{dn}, {token}') + + inst = _get_ds_instance(CONFIG['ldapurl'], dn, token) + try: + inst.open() + except Exception as e: + app.logger.debug(e) + return redirect(url_for('error')) + + try: + nsaccts = nsUserAccounts(inst, CONFIG['basedn'], rdn=None) acct = nsaccts.get(dn=dn) avas = acct.get_attrs_vals_utf8(GET_DISPLAY_ATTRS) @@ -152,33 +212,40 @@ def index(): for x in deets: vs = avas[x["a"]] x.update({"v": vs}) - print(deets) + except Exception as e: + app.logger.debug(e) + return redirect(url_for('error')) finally: - # Need to check the state befine unbind, else we get an exception - # if open failed. + # At this point we must have bound because if we don't an exception + # will be thrown in the first try/except block. inst.unbind_s() - return render_template('hello.html', dn=dn, details=deets) + return render_template('portal.html', dn=dn, details=deets) @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - app.logger.debug(request) - # Handle no username/password - name = request.form['username'] - pw = request.form['password'] - app.logger.debug(f'{name}, {pw}') - - dn = _get_name_to_dn(CONFIG['ldapurl'], name, CONFIG['basedn']) - app.logger.debug(f'authenticating as {dn}') - - if _bind_dn(CONFIG['ldapurl'], dn, pw): - session['dn'] = dn - # Enc the pw - token = FERNET.encrypt(str.encode(pw)) - session['token'] = token - return redirect(url_for('index')) + try: + app.logger.debug(request) + # Handle no username/password + name = request.form['username'] + pw = request.form['password'] + app.logger.debug(f'{name}, {pw}') + + dn = _get_name_to_dn(CONFIG['ldapurl'], name, CONFIG['basedn']) + app.logger.debug(f'authenticating as {dn}') + + # TODO: Handle incorrect password differently. + if _bind_dn(CONFIG['ldapurl'], dn, pw): + session['dn'] = dn + # Enc the pw + token = FERNET.encrypt(str.encode(pw)) + session['token'] = token + return redirect(url_for('index')) + except Exception as e: + app.logger.debug(e) + return redirect(url_for('error')) return render_template('login.html') @app.route('/logout') @@ -188,3 +255,4 @@ def logout(): session.pop('token', None) return redirect(url_for('index')) + diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..a6cbd0a --- /dev/null +++ b/templates/error.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} + +

500 Internal Server Error

+ +{% endblock %} diff --git a/templates/hello.html b/templates/hello.html deleted file mode 100644 index 88e8887..0000000 --- a/templates/hello.html +++ /dev/null @@ -1,138 +0,0 @@ -{% extends "base.html" %} - - -{% block content %} -
-
-
- -
-
-
-
- - - - {% set count = [1] %} - {% for ava in details %} - {% set attr = ava["a"] %} - {% set multi = ava["multi"] %} - {% set edit = ava["edit"] %} - -
-
- - - - {% endfor %} - -
- - - {% if multi == "true" %} - - {% for value in ava["v"] %} - {% if edit == "true" %} - - - - {% if count.append(count.pop() + 1) %}{% endif %} - {% else %} - - - - {% endif %} - {% endfor %} -
- - -
-

{{ value }}

-
- {% if edit == "true" %} -
- -
- {% endif %} - {% else %} - - {% if ava["v"]|count == 0 %} - {% set value = "" %} - {% else %} - {% set value = ava["v"][0] %} - {% endif %} - {% if edit == "true" %} - - - - {% else %} - - - - {% endif %} -
- -
-

{{ value }}

-
- {% endif %} -
- -
-
-
-
-

Account Self Service

-

- You can edit details of your own account here. -

-
-
-
- - - -{% endblock %} - diff --git a/templates/notfound.html b/templates/notfound.html new file mode 100644 index 0000000..9b3e7f2 --- /dev/null +++ b/templates/notfound.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} + +

404 Not Found

+ +{% endblock %} diff --git a/templates/portal.html b/templates/portal.html new file mode 100644 index 0000000..5bfad1f --- /dev/null +++ b/templates/portal.html @@ -0,0 +1,145 @@ +{% extends "base.html" %} + + +{% block content %} +
+
+
+ +
+
+
+
+

Account: {{ dn }}

+ + + {% set count = [1] %} + {% for ava in details %} + {% set attr = ava["a"] %} + {% set multi = ava["multi"] %} + {% set edit = ava["edit"] %} + +
+
+ + + + {% endfor %} + +
+ + + {% if multi == "true" %} + + {% for value in ava["v"] %} + {% if edit == "true" %} + + + + {% if count.append(count.pop() + 1) %}{% endif %} + {% else %} + + + + {% endif %} + {% endfor %} +
+ + +
+

{{ value }}

+
+ {% if edit == "true" %} +
+ +
+ {% endif %} + {% else %} + + {% if ava["v"]|count == 0 %} + {% set value = "" %} + {% else %} + {% set value = ava["v"][0] %} + {% endif %} + {% if edit == "true" %} + + + + {% else %} + + + + {% endif %} +
+ +
+

{{ value }}

+
+ {% endif %} +
+
+ +
+
+
+
+
+
+

Account Self Service

+

+ You can edit details of your own account here. +

+
+
+
+ + + +{% endblock %} +