From 8485aadbe4381a4c7d0371991c7e9f9f7b421828 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 26 Nov 2017 20:48:15 +1100 Subject: [PATCH] Implement simple email authentication --- build_js.sh | 4 +- eos/base/election.py | 31 +++++++++++ eosweb/core/main.py | 31 +++++++++++ eosweb/core/settings.py | 10 +++- eosweb/core/static/nunjucks/booth/cast.html | 8 +-- eosweb/core/templates/auth/email/login.html | 54 +++++++++++++++++++ eosweb/core/templates/auth/login.html | 6 +-- .../core/templates/auth/login_cancelled.html | 2 +- .../core/templates/auth/login_complete.html | 4 +- eosweb/core/templates/base.html | 2 +- eosweb/core/templates/election/booth.html | 2 +- local_settings.example.py | 18 +++++-- 12 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 eosweb/core/templates/auth/email/login.html diff --git a/build_js.sh b/build_js.sh index 535e25d..9b5b3c9 100755 --- a/build_js.sh +++ b/build_js.sh @@ -33,10 +33,10 @@ for f in eos.js_tests; do # Disable handling of special attributes perl -0777 -pi -e 's/var __specialattrib__ = function \(attrib\) \{/var __specialattrib__ = function (attrib) { return false;/g' eos/__javascript__/$f.js - # Fix handling of properties + # Fix handling of properties, Transcrypt bug #407 perl -077 -pi -e 's/var __get__ = function \(self, func, quotedFuncName\) \{/var __get__ = function (self, func, quotedFuncName) { if(typeof(func) != "function"){return func;}/g' eos/__javascript__/$f.js - # Transcrypt bug + # Transcrypt bug #407 perl -0777 -pi -e 's/property.call \((.*?), \g1.\g1.__impl__(.*?)\)/property.call ($1, $1.__impl__$2)/g' eos/__javascript__/$f.js perl -0777 -pi -e 's/property.call \((.*?), \g1.\g1.__implpy_(.*?)\)/property.call ($1, $1.__impl__$2)/g' eos/__javascript__/$f.js done diff --git a/eos/base/election.py b/eos/base/election.py index 7738e3f..054d946 100644 --- a/eos/base/election.py +++ b/eos/base/election.py @@ -46,6 +46,37 @@ class Voter(EmbeddedObject): class User(EmbeddedObject): pass +def generate_password(): + if is_python: + #__pragma__('skip') + import random + return ''.join(random.SystemRandom().choices('23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz', k=12)) + #__pragma__('noskip') + else: + return None + +class EmailUser(User): + name = StringField() + email = StringField(is_protected=True) + password = StringField(is_protected=True, default=generate_password) + + def matched_by(self, other): + if not isinstance(other, EmailUser): + return False + return self.email == other.email and self.password == other.password + + def send_email(self, host, port, username, password, from_email, content): + #__pragma__('skip') + import smtplib + #__pragma__('noskip') + with smtplib.SMTP(host, port) as smtp: + if username is not None: + smtp.login(username, password) + smtp.sendmail(from_email, [self.email], content) + + def email_password(self, host, port, username, password, from_email): + self.send_email(host, port, username, password, from_email, 'Subject: Registered to vote in {1}\nFrom: {4}\nTo: {2}\n\nDear {0},\n\nYou are registered to vote in the election {1}. Your log in details are as follows:\n\nEmail: {2}\nPassword: {3}'.format(self.name, self.recurse_parents(Election).name, self.email, self.password, from_email)) + class UserVoter(Voter): user = EmbeddedObjectField() diff --git a/eosweb/core/main.py b/eosweb/core/main.py index ff7b0cd..05ade68 100644 --- a/eosweb/core/main.py +++ b/eosweb/core/main.py @@ -93,8 +93,16 @@ def setup_test_election(): election.name = 'Test Election' from eos.redditauth.election import RedditUser + election.voters.append(UserVoter(user=EmailUser(name='Alice', email='alice@localhost'))) + election.voters.append(UserVoter(user=EmailUser(name='Bob', email='bob@localhost'))) + election.voters.append(UserVoter(user=EmailUser(name='Carol', email='carol@localhost'))) election.voters.append(UserVoter(user=RedditUser(username='RunasSudo'))) + for voter in election.voters: + if isinstance(voter, UserVoter): + if isinstance(voter.user, EmailUser): + voter.user.email_password(app.config['SMTP_HOST'], app.config['SMTP_PORT'], app.config['SMTP_USER'], app.config['SMTP_PASS'], app.config['SMTP_FROM']) + election.mixing_trustees.append(InternalMixingTrustee(name='Eos Voting')) election.mixing_trustees.append(InternalMixingTrustee(name='Eos Voting')) @@ -247,6 +255,29 @@ def login_complete(): def login_cancelled(): return flask.render_template('auth/login_cancelled.html') +@app.route('/auth/email/login') +def email_login(): + return flask.render_template('auth/email/login.html') + +@app.route('/auth/email/authenticate', methods=['POST']) +def email_authenticate(): + user = None + + for election in Election.get_all(): + for voter in election.voters: + if isinstance(voter.user, EmailUser): + if voter.user.email == flask.request.form['email']: + if voter.user.password == flask.request.form['password']: + user = voter.user + break + + if user is None: + return flask.render_template('auth/email/login.html', error='The email or password you entered was invalid. Please check your details and try again. If the issue persists, contact the election administrator.') + + flask.session['user'] = user + + return flask.redirect(flask.url_for('login_complete')) + # === Apps === for app_name in app.config['APPS']: diff --git a/eosweb/core/settings.py b/eosweb/core/settings.py index e4e4e0f..e3f6113 100644 --- a/eosweb/core/settings.py +++ b/eosweb/core/settings.py @@ -21,10 +21,16 @@ BASE_URI = 'http://localhost:5000' MONGO_URI = 'mongodb://localhost:27017/' DB_NAME = 'eos' +SECRET_KEY = 'FIXME' + APPS = [ 'eosweb.redditauth' ] -AUTH_METHODS = [] +AUTH_METHODS = [ + ('email', 'Email') +] -SECRET_KEY = 'FIXME' +SMTP_HOST, SMTP_PORT = 'localhost', 25 +SMTP_USER, SMTP_PASS = None, None +SMTP_FROM = 'eos@localhost' diff --git a/eosweb/core/static/nunjucks/booth/cast.html b/eosweb/core/static/nunjucks/booth/cast.html index e50dccc..a48b589 100644 --- a/eosweb/core/static/nunjucks/booth/cast.html +++ b/eosweb/core/static/nunjucks/booth/cast.html @@ -55,7 +55,7 @@ {% block buttons %} + {% endblock %} {% block after %} @@ -67,9 +67,9 @@ function login(el) { window.open(el.getAttribute("href"), "eos_login_window", "width=400,height=600"); } - function callback_complete() { - $("#cast_button").removeClass("hidden"); - $("#booth_logged_in_as").text("You are currently logged in."); + function callback_complete(username) { + $("#cast_button").show(); + $("#booth_logged_in_as").text("You are currently logged in as " + username + "."); castBallot(); } diff --git a/eosweb/core/templates/auth/email/login.html b/eosweb/core/templates/auth/email/login.html new file mode 100644 index 0000000..23e8222 --- /dev/null +++ b/eosweb/core/templates/auth/email/login.html @@ -0,0 +1,54 @@ +{% extends 'semantic_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 . +#} + +{% block title %}Log in{% endblock %} + +{% block basecontent %} +
+
+

+
+ Log in to Eos +
+

+
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+ + {% if error %} +
{{ error }}
+ {% endif %} +
+
+
+{% endblock %} diff --git a/eosweb/core/templates/auth/login.html b/eosweb/core/templates/auth/login.html index a25aad9..a53667a 100644 --- a/eosweb/core/templates/auth/login.html +++ b/eosweb/core/templates/auth/login.html @@ -18,11 +18,11 @@ along with this program. If not, see . #} -{% block title %}Login{% endblock %} +{% block title %}Log in{% endblock %} {% block content %} {% if session.user %} -

You are currently logged in as {{ session.user.username }}. Please select an option from the list below to switch accounts.

+

You are currently logged in as {{ session.user.name }}. Please select an option from the list below to switch accounts.

{% else %}

You are not currently logged in. Please select an option from the list below to log in.

{% endif %} @@ -40,7 +40,7 @@ window.open(el.getAttribute("href"), "eos_login_window", "width=400,height=600"); } - function callback_complete() { + function callback_complete(name) { window.location = "/"; } diff --git a/eosweb/core/templates/auth/login_cancelled.html b/eosweb/core/templates/auth/login_cancelled.html index e200a94..5debc81 100644 --- a/eosweb/core/templates/auth/login_cancelled.html +++ b/eosweb/core/templates/auth/login_cancelled.html @@ -18,7 +18,7 @@ along with this program. If not, see . #} -{% block title %}Login{% endblock %} +{% block title %}Log in{% endblock %} {% block basecontent %}
diff --git a/eosweb/core/templates/auth/login_complete.html b/eosweb/core/templates/auth/login_complete.html index 749ddf5..7da05e8 100644 --- a/eosweb/core/templates/auth/login_complete.html +++ b/eosweb/core/templates/auth/login_complete.html @@ -18,7 +18,7 @@ along with this program. If not, see . #} -{% block title %}Login{% endblock %} +{% block title %}Log in{% endblock %} {% block basecontent %}
@@ -34,7 +34,7 @@ {% endblock %} diff --git a/eosweb/core/templates/base.html b/eosweb/core/templates/base.html index f2b2d4d..65a47ac 100644 --- a/eosweb/core/templates/base.html +++ b/eosweb/core/templates/base.html @@ -29,7 +29,7 @@ Source Code {% if session.user %}