Basic administrator view

This commit is contained in:
RunasSudo 2017-12-07 15:03:11 +10:30
parent 5918cde53b
commit 95e6a56f81
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
13 changed files with 82 additions and 16 deletions

View File

@ -54,7 +54,16 @@ class Voter(EmbeddedObject):
votes = EmbeddedObjectListField() votes = EmbeddedObjectListField()
class User(EmbeddedObject): class User(EmbeddedObject):
pass admins = []
def matched_by(self, other):
return self == other
def is_admin(self):
for admin in User.admins:
if admin.matched_by(self):
return True
return False
def generate_password(): def generate_password():
if is_python: if is_python:

View File

@ -117,17 +117,21 @@ class Workflow(EmbeddedObject):
# ============== # ==============
class TaskConfigureElection(WorkflowTask): class TaskConfigureElection(WorkflowTask):
label = 'Configure the election and freeze the election'
#def on_enter(self): #def on_enter(self):
# self.status = WorkflowTask.Status.COMPLETE # self.status = WorkflowTask.Status.COMPLETE
pass
class TaskOpenVoting(WorkflowTask): class TaskOpenVoting(WorkflowTask):
label = 'Open voting'
depends_on = ['eos.base.workflow.TaskConfigureElection'] depends_on = ['eos.base.workflow.TaskConfigureElection']
class TaskCloseVoting(WorkflowTask): class TaskCloseVoting(WorkflowTask):
label = 'Close voting'
depends_on = ['eos.base.workflow.TaskOpenVoting'] depends_on = ['eos.base.workflow.TaskOpenVoting']
class TaskDecryptVotes(WorkflowTask): class TaskDecryptVotes(WorkflowTask):
label = 'Decrypt the votes'
depends_on = ['eos.base.workflow.TaskCloseVoting'] depends_on = ['eos.base.workflow.TaskCloseVoting']
def on_enter(self): def on_enter(self):
@ -148,6 +152,7 @@ class TaskDecryptVotes(WorkflowTask):
self.exit() self.exit()
class TaskReleaseResults(WorkflowTask): class TaskReleaseResults(WorkflowTask):
label = 'Release the results'
depends_on = ['eos.base.workflow.TaskDecryptVotes'] depends_on = ['eos.base.workflow.TaskDecryptVotes']
# Concrete workflows # Concrete workflows

View File

@ -22,6 +22,7 @@ import eos.base.workflow
# ============== # ==============
class TaskMixVotes(WorkflowTask): class TaskMixVotes(WorkflowTask):
label = 'Mix the votes'
depends_on = ['eos.base.workflow.TaskCloseVoting'] depends_on = ['eos.base.workflow.TaskCloseVoting']
def on_enter(self): def on_enter(self):
@ -40,6 +41,7 @@ class TaskMixVotes(WorkflowTask):
self.exit() self.exit()
class TaskProveMixes(WorkflowTask): class TaskProveMixes(WorkflowTask):
label = 'Prove the mixes'
depends_on = ['eos.psr.workflow.TaskMixVotes'] depends_on = ['eos.psr.workflow.TaskMixVotes']
def on_enter(self): def on_enter(self):

View File

@ -33,6 +33,7 @@ import functools
import importlib import importlib
import json import json
import os import os
import subprocess
app = flask.Flask(__name__, static_folder=None) app = flask.Flask(__name__, static_folder=None)
@ -50,6 +51,9 @@ if 'EOSWEB_SETTINGS' in os.environ:
# Connect to database # Connect to database
db_connect(app.config['DB_NAME'], app.config['DB_URI'], app.config['DB_TYPE']) db_connect(app.config['DB_NAME'], app.config['DB_URI'], app.config['DB_TYPE'])
# Set configs
User.admins = app.config['ADMINS']
# Make Flask's serialisation, e.g. for sessions, EosObject aware # Make Flask's serialisation, e.g. for sessions, EosObject aware
class EosObjectJSONEncoder(flask.json.JSONEncoder): class EosObjectJSONEncoder(flask.json.JSONEncoder):
def default(self, obj): def default(self, obj):
@ -126,14 +130,6 @@ def setup_test_election():
election.questions.append(question) election.questions.append(question)
election.save() election.save()
# Freeze election
election.workflow.get_task('eos.base.workflow.TaskConfigureElection').enter()
# Open voting
election.workflow.get_task('eos.base.workflow.TaskOpenVoting').enter()
election.save()
@app.cli.command('close_election') @app.cli.command('close_election')
@click.option('--electionid', default=None) @click.option('--electionid', default=None)
@ -194,6 +190,15 @@ def using_election(func):
return func(election) return func(election)
return wrapped return wrapped
def election_admin(func):
@functools.wraps(func)
def wrapped(election):
if 'user' in flask.session and flask.session['user'].is_admin():
return func(election)
else:
return flask.Response('Administrator credentials required', 403)
return wrapped
@app.route('/election/<election_id>/') @app.route('/election/<election_id>/')
@using_election @using_election
def election_api_json(election): def election_api_json(election):
@ -202,7 +207,7 @@ def election_api_json(election):
@app.route('/election/<election_id>/view') @app.route('/election/<election_id>/view')
@using_election @using_election
def election_view(election): def election_view(election):
return flask.render_template('election/view.html', election=election) return flask.render_template('election/view/view.html', election=election)
@app.route('/election/<election_id>/booth') @app.route('/election/<election_id>/booth')
@using_election @using_election
@ -210,22 +215,28 @@ def election_booth(election):
selection_model_view_map = EosObject.to_json({key._name: val for key, val in model_view_map.items()}) # ewww selection_model_view_map = EosObject.to_json({key._name: val for key, val in model_view_map.items()}) # ewww
auth_methods = EosObject.to_json(app.config['AUTH_METHODS']) auth_methods = EosObject.to_json(app.config['AUTH_METHODS'])
return flask.render_template('election/booth.html', election=election, selection_model_view_map=selection_model_view_map, auth_methods=auth_methods) return flask.render_template('election/view/booth.html', election=election, selection_model_view_map=selection_model_view_map, auth_methods=auth_methods)
@app.route('/election/<election_id>/view/questions') @app.route('/election/<election_id>/view/questions')
@using_election @using_election
def election_view_questions(election): def election_view_questions(election):
return flask.render_template('election/questions.html', election=election) return flask.render_template('election/view/questions.html', election=election)
@app.route('/election/<election_id>/view/ballots') @app.route('/election/<election_id>/view/ballots')
@using_election @using_election
def election_view_ballots(election): def election_view_ballots(election):
return flask.render_template('election/ballots.html', election=election) return flask.render_template('election/view/ballots.html', election=election)
@app.route('/election/<election_id>/view/trustees') @app.route('/election/<election_id>/view/trustees')
@using_election @using_election
def election_view_trustees(election): def election_view_trustees(election):
return flask.render_template('election/trustees.html', election=election) return flask.render_template('election/view/trustees.html', election=election)
@app.route('/election/<election_id>/admin')
@using_election
@election_admin
def election_admin_summary(election):
return flask.render_template('election/admin/admin.html', election=election)
@app.route('/election/<election_id>/cast_ballot', methods=['POST']) @app.route('/election/<election_id>/cast_ballot', methods=['POST'])
@using_election @using_election

View File

@ -29,7 +29,7 @@
<a href="https://github.com/RunasSudo/Eos" class="item">Source Code</a> <a href="https://github.com/RunasSudo/Eos" class="item">Source Code</a>
{% if session.user %} {% if session.user %}
<div class="ui simple dropdown item right"> <div class="ui simple dropdown item right">
{{ session.user.name }} <i class="dropdown icon"></i> <i class="{% if session.user.is_admin() %}legal{% else %}user circle{% endif %} icon"></i> {{ session.user.name }} <i class="dropdown icon"></i>
<div class="menu"> <div class="menu">
<a href="{{ url_for('logout') }}?next={{ request.full_path|urlencode }}" class="item">Log out</a> <a href="{{ url_for('logout') }}?next={{ request.full_path|urlencode }}" class="item">Log out</a>
</div> </div>

View File

@ -0,0 +1,31 @@
{% extends 'election/base.html' %}
{#
Eos - Verifiable elections
Copyright © 2017 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% block electioncontent %}
<h2>Next tasks</h2>
<ul>
{% for task in election.workflow.tasks %}
{% if task.status == eos.base.workflow.WorkflowTask.Status.READY %}
<li>{{ task.label }}</li>
{% endif %}
{% endfor %}
</ul>
{% endblock %}

View File

@ -31,6 +31,9 @@
<div class="ui secondary pointing menu" id="election-tab-menu"> <div class="ui secondary pointing menu" id="election-tab-menu">
{% include eosweb.core.main.model_view_map[election.__class__]['tabs'] %} {% include eosweb.core.main.model_view_map[election.__class__]['tabs'] %}
{% if session.user and session.user.is_admin() %}
<a href="{{ url_for('election_admin_summary', election_id=election._id) }}" class="election-tab-ajax item{% if request.endpoint == 'election_admin' %} active{% endif %} right"><i class="configure icon"></i> Administrate this election</a>
{% endif %}
</div> </div>
<div class="ui container" id="election-tab-content"> <div class="ui container" id="election-tab-content">
{% block electioncontent %} {% block electioncontent %}

View File

@ -9,6 +9,11 @@ AUTH_METHODS = [
('reddit', 'Reddit') ('reddit', 'Reddit')
] ]
import eos.redditauth.election
ADMINS = [
#eos.redditauth.election.RedditUser(username='xxxxxxxx')
]
# MongoDB # MongoDB
DB_TYPE = 'mongodb' DB_TYPE = 'mongodb'