From: Felix Dörre Date: Fri, 17 Nov 2017 18:41:00 +0000 (+0100) Subject: add: user management X-Git-Url: https://code.wpia.club/?p=motion.git;a=commitdiff_plain;h=fb14879eed07a490aab3a9263db29e4a0323ae41 add: user management --- diff --git a/motion.py b/motion.py index f292356..cef9a6b 100644 --- a/motion.py +++ b/motion.py @@ -20,6 +20,44 @@ app.register_blueprint(filters.blueprint) # Load config app.config.from_pyfile('config.py') +groups=["fellowship", "board"] + +@app.before_request +def lookup_user(): + env = request.environ + if "USER" not in env or "ROLES" not in env: + return "Server misconfigured", 500 + user = env.get("USER") + roles = env.get("ROLES").split(" ") + if roles == [""]: + roles = [] + + db = get_db() + with db.xact(): + rv = db.prepare("SELECT id FROM voter WHERE email=$1")(user) + if len(rv) == 0: + db.prepare("INSERT INTO voter(\"email\") VALUES($1)")(user) + rv = db.prepare("SELECT id FROM voter WHERE email=$1")(user) + g.voter = rv[0].get("id"); + g.roles = {} + + for r in roles: + a = r.split(":", 1) + val = a[1] + if a[0] not in g.roles: + g.roles[a[0]] = [] + if val == "*": + g.roles[a[0]] = groups + else: + g.roles[a[0]].append(val) + return None + +def get_allowed_cats(action): + return g.roles.get(action, []); + +def may(action, motion): + return motion in get_allowed_cats(action) + @app.teardown_appcontext def close_connection(exception): db = getattr(g, '_database', None) @@ -35,11 +73,11 @@ def init_db(): @app.route("/") def main(): start=int(request.args.get("start", "-1")); - q = "SELECT motion.*, votes.*, poser.email AS poser, canceler.email AS canceler, (motion.deadline > CURRENT_TIMESTAMP AND canceled is NULL) AS running FROM motion LEFT JOIN (SELECT motion_id, voter_id, "\ + q = "SELECT motion.*, votes.*, poser.email AS poser, canceler.email AS canceler, (motion.deadline > CURRENT_TIMESTAMP AND canceled is NULL) AS running FROM motion LEFT JOIN (SELECT motion_id, "\ + "COUNT(CASE WHEN result='yes' THEN 'yes' ELSE NULL END) as yes, "\ + "COUNT(CASE WHEN result='no' THEN 'no' ELSE NULL END) as no, "\ + "COUNT(CASE WHEN result='abstain' THEN 'abstain' ELSE NULL END) as abstain "\ - + "FROM vote GROUP BY motion_id, voter_id) as votes ON votes.motion_id=motion.id "\ + + "FROM vote GROUP BY motion_id) as votes ON votes.motion_id=motion.id "\ + "LEFT JOIN voter poser ON poser.id = motion.posed_by "\ + "LEFT JOIN voter canceler ON canceler.id = motion.canceled_by " prev=None @@ -54,29 +92,35 @@ def main(): prev = rs[9][0] else: prev = -1 - return render_template('index.html', motions=rv[:10], more=rv[10]["id"] if len(rv) == 11 else None, times=times, prev=prev) + return render_template('index.html', motions=rv[:10], more=rv[10]["id"] if len(rv) == 11 else None, times=times, prev=prev, + categories=get_allowed_cats("create")) @app.route("/motion", methods=['POST']) def put_motion(): + cat=request.form.get("category", "") + if cat not in get_allowed_cats("create"): + return "Forbidden", 403 time = int(request.form.get("days", "3")); if time not in times: - return "Error, invalid length" - p = get_db().prepare("INSERT INTO motion(\"name\", \"content\", \"deadline\", \"posed_by\") VALUES($1, $2, CURRENT_TIMESTAMP + $3 * interval '1 days', $4)") - p(request.form.get("title", ""), request.form.get("content",""), time, voter) + return "Error, invalid length", 500 + p = get_db().prepare("INSERT INTO motion(\"name\", \"content\", \"deadline\", \"posed_by\", \"type\") VALUES($1, $2, CURRENT_TIMESTAMP + $3 * interval '1 days', $4, $5)") + p(request.form.get("title", ""), request.form.get("content",""), time, g.voter, cat) return redirect("/") -voter=1 - def motion_edited(motion): return redirect("/?start=" + str(motion) + "#motion-" + str(motion)) -@app.route("/motion//cancel", methods=['POST']) -def cancel_motion(id): +@app.route("/motion//cancel", methods=['POST']) +def cancel_motion(motion): + rv = get_db().prepare("SELECT type FROM motion WHERE id=$1")(motion); + if len(rv) == 0: + return "Error, Not found", 404 + if not may("cancel", rv[0].get("type")): + return "Forbidden", 403 if request.form.get("reason", "none") == "none": - return "Error, form requires reason" - rv = get_db().prepare("UPDATE motion SET canceled=CURRENT_TIMESTAMP, cancelation_reason=$1, canceled_by=$2 WHERE id=$3 AND canceled is NULL")(request.form.get("reason", ""), voter, id) - print(rv) - return motion_edited(id) + return "Error, form requires reason", 500 + rv = get_db().prepare("UPDATE motion SET canceled=CURRENT_TIMESTAMP, cancelation_reason=$1, canceled_by=$2 WHERE id=$3 AND canceled is NULL")(request.form.get("reason", ""), g.voter, motion) + return motion_edited(motion) @app.route("/motion/") def show_motion(motion): @@ -85,25 +129,26 @@ def show_motion(motion): + "LEFT JOIN voter poser ON poser.id = motion.posed_by "\ + "LEFT JOIN voter canceler ON canceler.id = motion.canceled_by " + "WHERE motion.id=$1") - rv = p(motion,voter) - if len(rv) == 0: - return "Error, motion not found" # TODO 404 - return render_template('single_motion.html', motion=rv[0]) + rv = p(motion, g.voter) + return render_template('single_motion.html', motion=rv[0], may_vote=may("vote", rv[0].get("type")), may_cancel=may("cancel", rv[0].get("type"))) @app.route("/motion//vote", methods=['POST']) def vote(motion): v = request.form.get("vote", "abstain") db = get_db() with db.xact(): + rv = db.prepare("SELECT type FROM motion WHERE id=$1")(motion); + if len(rv) == 0: + return "Error, Not found", 404 + if not may("vote", rv[0].get("type")): + return "Forbidden", 403 p = db.prepare("SELECT deadline > CURRENT_TIMESTAMP FROM motion WHERE id = $1") if not p(motion)[0][0]: - return "Error, motion deadline has passed" + return "Error, motion deadline has passed", 500 p = db.prepare("SELECT * FROM vote WHERE motion_id = $1 AND voter_id = $2") - rv = p(motion, voter) + rv = p(motion, g.voter) if len(rv) == 0: - db.prepare("INSERT INTO vote(motion_id, voter_id, result) VALUES($1,$2,$3)")(motion,voter,v) + db.prepare("INSERT INTO vote(motion_id, voter_id, result) VALUES($1,$2,$3)")(motion, g.voter, v) else: - db.prepare("UPDATE vote SET result=$3, entered=CURRENT_TIMESTAMP WHERE motion_id=$1 AND voter_id = $2")(motion,voter,v) + db.prepare("UPDATE vote SET result=$3, entered=CURRENT_TIMESTAMP WHERE motion_id=$1 AND voter_id = $2")(motion, g.voter, v) return motion_edited(motion) - -# TODO authentication/user management diff --git a/schema.sql b/schema.sql index b3a6eaf..ed0d876 100644 --- a/schema.sql +++ b/schema.sql @@ -5,6 +5,7 @@ CREATE TABLE voter (id serial NOT NULL, email VARCHAR(255) NOT NULL, PRIMARY KEY DROP TABLE IF EXISTS motion; CREATE TABLE motion (id serial NOT NULL, name VARCHAR(250) NOT NULL, + type VARCHAR(250) NOT NULL, content text NOT NULL, posed timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, posed_by int NOT NULL, diff --git a/templates/base.html b/templates/base.html index 8f2b041..2dc6927 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,9 +7,16 @@ .motion { border: 1px solid black; } -.motion .motion-title input { +.motion .motion-title input.motion-title-input { width: 80%; } +.motion .motion-title .float { + float: right; +} +.motion .motion-title .motion-type { + margin: 5px; + float: right; +} .motion textarea { display: block; } @@ -36,7 +43,7 @@ -{% block body %} +{%- block body %} {% endblock %} diff --git a/templates/index.html b/templates/index.html index 9175db6..b53a236 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,11 +1,22 @@ {% extends "base.html" %} {% block body %}
+{%- if categories|length != 0 %}
- - + {%- if categories|length == 1 %} + + + {%- else %} + + {%- endif %} + {%- endif %} +{%- endif %} {%- endblock %}