Add registration

This commit is contained in:
David Hoppenbrouwers
2022-10-08 15:25:28 +02:00
parent 671f1c2ec6
commit e9ef9140f0
8 changed files with 105 additions and 8 deletions

20
captcha.py Normal file
View File

@@ -0,0 +1,20 @@
from random import randint
import hashlib, base64
# FIXME hash can be reused
def generate(key):
'''
Generate a simple CAPTCHA.
It is based on a simple math expression which stops the simplest of bots.
'''
# The parameters are chosen such that they are simple to solve on paper.
a = randint(1, 10)
b = randint(1, 10)
c = randint(10, 20)
return f'{a} * {b} + {c} = ', _hash_answer(key, str(a * b + c))
def verify(key, answer, hash):
return _hash_answer(key, answer) == hash
def _hash_answer(key, answer):
return base64.b64encode(hashlib.sha256((key + answer).encode('utf-8')).digest()).decode('ascii')

View File

@@ -283,5 +283,19 @@ class DB:
return True return True
return False return False
def add_user(self, username, password, time):
db = self._db()
c = db.cursor()
c.execute('''
insert into users(name, password, join_time)
values (?, ?, ?)
''',
(username, password, time)
)
if c.rowcount > 0:
db.commit()
return True
return False
def _db(self): def _db(self):
return sqlite3.connect(self.conn) return sqlite3.connect(self.conn)

39
main.py
View File

@@ -4,6 +4,7 @@ import os
import passlib.hash import passlib.hash
import time import time
from datetime import datetime from datetime import datetime
import captcha
app = Flask(__name__) app = Flask(__name__)
db = DB(os.getenv('DB')) db = DB(os.getenv('DB'))
@@ -11,6 +12,7 @@ NAME = 'Agrepy'
# TODO config file # TODO config file
app.config['SECRET_KEY'] = 'totally random' app.config['SECRET_KEY'] = 'totally random'
captcha_key = 'piss off bots'
app.jinja_env.trim_blocks = True app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True app.jinja_env.lstrip_blocks = True
@@ -70,7 +72,7 @@ def login():
v = db.get_user_password(request.form['username']) v = db.get_user_password(request.form['username'])
if v is not None: if v is not None:
id, hash = v id, hash = v
if passlib.hash.argon2.verify(request.form['password'], hash): if verify_password(request.form['password'], hash):
flash('Logged in', 'success') flash('Logged in', 'success')
session['user_id'] = id session['user_id'] = id
session['username'] = request.form['username'] session['username'] = request.form['username']
@@ -256,6 +258,34 @@ def edit_comment(comment_id):
text = text, text = text,
) )
@app.route('/register/', methods = ['GET', 'POST'])
def register():
if request.method == 'POST':
username, password = request.form['username'], request.form['password']
if len(username) < 3:
flash('Username must be at least 3 characters long', 'error')
elif len(password) < 8:
flash('Password must be at least 8 characters long', 'error')
elif not captcha.verify(
captcha_key,
request.form['captcha'],
request.form['answer'],
):
flash('CAPTCHA answer is incorrect', 'error')
elif not db.add_user(username, hash_password(password), time.time_ns()):
flash('Failed to create user (username may already be taken)')
else:
flash('Account has been created. You can login now.', 'success')
return redirect(url_for('index'))
capt, answer = captcha.generate(captcha_key)
return render_template(
'register.html',
title = 'Register',
captcha = capt,
answer = answer,
)
class Comment: class Comment:
def __init__(self, id, author_id, author, text, create_time, modify_time, parent_id): def __init__(self, id, author_id, author, text, create_time, modify_time, parent_id):
@@ -348,3 +378,10 @@ def utility_processor():
'format_since': format_since, 'format_since': format_since,
'minimd': minimd, 'minimd': minimd,
} }
def hash_password(password):
return passlib.hash.argon2.hash(password)
def verify_password(password, hash):
return passlib.hash.argon2.verify(password, hash)

View File

@@ -4,7 +4,7 @@ create table users (
password varchar(128) not null, password varchar(128) not null,
email varchar(254), email varchar(254),
about text not null default '', about text not null default '',
join_time integer, join_time integer not null,
role integer not null default 0 role integer not null default 0
); );

View File

@@ -95,3 +95,11 @@ table.form > * > tr > td, th {
border-radius: 5px; border-radius: 5px;
padding: 8px; padding: 8px;
} }
.login {
width: 50%;
}
.login input[type=text], .login input[type=password] {
width: 90%;
}

View File

@@ -14,6 +14,8 @@
<span>|</span> <span>|</span>
<a href="{{ url_for('logout') }}">Logout</a> <a href="{{ url_for('logout') }}">Logout</a>
{% else %} {% else %}
<a href="{{ url_for('register') }}">Register</a>
<span>|</span>
<a href="{{ url_for('login') }}">Login</a> <a href="{{ url_for('login') }}">Login</a>
{% endif %} {% endif %}
</nav> </nav>

13
templates/register.html Normal file
View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{%- block content %}
<form method="post" class=login>
<table>
<tr><td>Username</td><td><input type="text" name="username" minlength=3></td></tr>
<tr><td>Password</td><td><input type="password" name="password" minlength=8></td></tr>
<tr><td>{{ captcha }}</td><td><input type="text" name="captcha"></td></tr>
</table>
<input name="answer" value="{{ answer }}" hidden>
<input type="submit" value="Register">
</form>
{% endblock %}

View File

@@ -1,19 +1,22 @@
insert into users (name, password, email) values ( insert into users (name, password, email, join_time) values (
"Foo", "Foo",
-- supasecret -- supasecret
"$argon2id$v=19$m=65536,t=3,p=4$qBWCEAKgdA4BYOy915qzlg$KhGy3UF0QMlplt2eB7r7QNL2kDcggXUimRWUrWql8sI", "$argon2id$v=19$m=65536,t=3,p=4$qBWCEAKgdA4BYOy915qzlg$KhGy3UF0QMlplt2eB7r7QNL2kDcggXUimRWUrWql8sI",
"foo@bar.baz" "foo@bar.baz",
0
); );
insert into users (name, password, email) values ( insert into users (name, password, email, join_time) values (
"Bar", "Bar",
-- abraca -- abraca
"$argon2id$v=19$m=65536,t=3,p=4$klJKCUFoDaF07j3nPCeEUA$lCphd5n1YIs8MaVop2vGNirwknkh91qJIZHMuBOlgWA", "$argon2id$v=19$m=65536,t=3,p=4$klJKCUFoDaF07j3nPCeEUA$lCphd5n1YIs8MaVop2vGNirwknkh91qJIZHMuBOlgWA",
"bar@foo.baz" "bar@foo.baz",
0
); );
insert into users (name, password) values ( insert into users (name, password, join_time) values (
"bazzers", "bazzers",
-- e -- e
"$argon2id$v=19$m=65536,t=3,p=4$9v5fS2ktxTinNEbIGUOoFQ$LMdEuAuuTCJ7utOE88+nXn7o6R/DEKY8ZA6wV+YkVGQ" "$argon2id$v=19$m=65536,t=3,p=4$9v5fS2ktxTinNEbIGUOoFQ$LMdEuAuuTCJ7utOE88+nXn7o6R/DEKY8ZA6wV+YkVGQ",
0
); );
insert into forums (name, description, allowed_roles_mask) insert into forums (name, description, allowed_roles_mask)