Implement simple email authentication
This commit is contained in:
parent
f31f5347e2
commit
8485aadbe4
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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']:
|
||||
|
@ -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'
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
{% block buttons %}
|
||||
<button class="ui left floated button" onclick="prevTemplate(2);">Back</a>
|
||||
<button class="ui right floated primary{% if not username %} hidden{% endif %} button" id="cast_button" onclick="castBallot();">Cast ballot</button>
|
||||
<button class="ui right floated primary button" id="cast_button" onclick="castBallot();"{% if not username %} style="display: none;"{% endif %}>Cast ballot</button>
|
||||
{% 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();
|
||||
}
|
||||
|
||||
|
54
eosweb/core/templates/auth/email/login.html
Normal file
54
eosweb/core/templates/auth/email/login.html
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% block title %}Log in{% endblock %}
|
||||
|
||||
{% block basecontent %}
|
||||
<div class="ui middle aligned center aligned grid" style="height: 100%;">
|
||||
<div class="column" style="max-width: 400px;">
|
||||
<h2 class="ui teal image header">
|
||||
<div class="content">
|
||||
Log in to Eos
|
||||
</div>
|
||||
</h2>
|
||||
<form class="ui large form" action="{{ url_for('email_authenticate') }}" method="post">
|
||||
<div class="ui stacked segment">
|
||||
<div class="field">
|
||||
<div class="ui left icon input">
|
||||
<i class="user icon"></i>
|
||||
<input type="text" name="email" placeholder="E-mail address">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui left icon input">
|
||||
<i class="lock icon"></i>
|
||||
<input type="password" name="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" class="ui fluid large teal submit button" value="Log in">
|
||||
</div>
|
||||
|
||||
{% if error %}
|
||||
<div class="ui visible error message">{{ error }}</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -18,11 +18,11 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block title %}Log in{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if session.user %}
|
||||
<p>You are currently logged in as {{ session.user.username }}. Please select an option from the list below to switch accounts.</p>
|
||||
<p>You are currently logged in as {{ session.user.name }}. Please select an option from the list below to switch accounts.</p>
|
||||
{% else %}
|
||||
<p>You are not currently logged in. Please select an option from the list below to log in.</p>
|
||||
{% 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 = "/";
|
||||
}
|
||||
</script>
|
||||
|
@ -18,7 +18,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block title %}Log in{% endblock %}
|
||||
|
||||
{% block basecontent %}
|
||||
<div class="ui middle aligned center aligned grid" style="height: 100%;">
|
||||
|
@ -18,7 +18,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block title %}Log in{% endblock %}
|
||||
|
||||
{% block basecontent %}
|
||||
<div class="ui middle aligned center aligned grid" style="height: 100%;">
|
||||
@ -34,7 +34,7 @@
|
||||
<script>
|
||||
window.setTimeout(function() {
|
||||
window.close();
|
||||
window.opener.callback_complete();
|
||||
window.opener.callback_complete("{{ session.user.name }}");
|
||||
}, 1000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -29,7 +29,7 @@
|
||||
<a href="https://github.com/RunasSudo/Eos" class="item">Source Code</a>
|
||||
{% if session.user %}
|
||||
<div class="ui simple dropdown item right">
|
||||
{{ session.user.username }} <i class="dropdown icon"></i>
|
||||
{{ session.user.name }} <i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<a href="{{ url_for('logout') }}?next={{ request.full_path|urlencode }}" class="item">Log out</a>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
var currentBoothTask = 0;
|
||||
var selection_model_view_map = {{ selection_model_view_map|safe }}; {# :rooWut: #}
|
||||
|
||||
var username = {% if session.user %}"{{ session.user.username }}"{% else %}null{% endif %};
|
||||
var username = {% if session.user %}"{{ session.user.name }}"{% else %}null{% endif %};
|
||||
var auth_methods = {{ auth_methods|safe }};
|
||||
|
||||
function resetBooth() {
|
||||
|
@ -1,12 +1,20 @@
|
||||
ORG_NAME = 'Your Organisation Here'
|
||||
|
||||
REDDIT_OAUTH_CLIENT_ID = 'xxxxxxxxxxxxxx'
|
||||
REDDIT_OAUTH_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
|
||||
SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
|
||||
REDDIT_USER_AGENT = 'Application Title by /u/Your_Username'
|
||||
|
||||
AUTH_METHODS = [
|
||||
('email', 'Email'),
|
||||
('reddit', 'Reddit')
|
||||
]
|
||||
|
||||
# Email
|
||||
|
||||
SMTP_HOST, SMTP_PORT = 'localhost', 25
|
||||
SMTP_USER, SMTP_PASS = None, None
|
||||
SMTP_FROM = 'eos@localhost'
|
||||
|
||||
# Reddit
|
||||
|
||||
REDDIT_OAUTH_CLIENT_ID = 'xxxxxxxxxxxxxx'
|
||||
REDDIT_OAUTH_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
REDDIT_USER_AGENT = 'Application Title by /u/Your_Username'
|
||||
|
Loading…
Reference in New Issue
Block a user