Basic administrator view
This commit is contained in:
parent
5918cde53b
commit
95e6a56f81
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
31
eosweb/core/templates/election/admin/admin.html
Normal file
31
eosweb/core/templates/election/admin/admin.html
Normal 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 %}
|
@ -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 %}
|
||||||
|
@ -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'
|
||||||
|
Loading…
Reference in New Issue
Block a user