use markdown2, add forced login. Added breadcrumbs

This commit is contained in:
Ville Rantanen
2023-07-23 20:23:48 +03:00
parent 09f56bd1fe
commit 9437e64936
13 changed files with 771 additions and 527 deletions

View File

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

View File

@@ -1,18 +1,25 @@
import sqlite3 import sqlite3
class DB: class DB:
def __init__(self, conn): def __init__(self, conn):
self.conn = conn self.conn = conn
pass pass
def get_config(self): def get_config(self):
return self._db().execute(''' return (
select version, name, description, secret_key, captcha_key, registration_enabled from config self._db()
''' .execute(
).fetchone() """
select version, name, description, secret_key, captcha_key, registration_enabled, login_required from config
"""
)
.fetchone()
)
def get_forums(self): def get_forums(self):
return self._db().execute(''' return self._db().execute(
"""
select f.forum_id, name, description, thread_id, title, update_time select f.forum_id, name, description, thread_id, title, update_time
from forums f from forums f
left join threads t left join threads t
@@ -23,20 +30,41 @@ class DB:
order by update_time desc order by update_time desc
limit 1 limit 1
) )
''' """
) )
def get_forum(self, forum_id): def get_forum(self, forum_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select name, description select name, description
from forums from forums
where forum_id = ? where forum_id = ?
''', """,
(forum_id,) (forum_id,),
).fetchone() )
.fetchone()
)
def get_thread_forum(self, thread_id):
""" Returns forum_id of a thread """
return (
self._db()
.execute(
"""
select forum_id
from threads
where thread_id = ?
""",
(thread_id,),
)
.fetchone()[0]
)
def get_threads(self, forum_id, offset, limit, user_id): def get_threads(self, forum_id, offset, limit, user_id):
return self._db().execute(''' return self._db().execute(
"""
select select
t.thread_id, t.thread_id,
title, title,
@@ -70,20 +98,22 @@ class DB:
order by t.update_time desc order by t.update_time desc
limit ? limit ?
offset ? offset ?
''', """,
(forum_id, user_id, limit, offset) (forum_id, user_id, limit, offset),
) )
def get_thread(self, thread): def get_thread(self, thread):
db = self._db() db = self._db()
title, text, author, author_id, create_time, modify_time, hidden = db.execute(''' title, text, author, author_id, create_time, modify_time, hidden, forum_id = db.execute(
select title, text, name, author_id, create_time, modify_time, hidden """
select title, text, name, author_id, create_time, modify_time, hidden, forum_id
from threads, users from threads, users
where thread_id = ? and author_id = user_id where thread_id = ? and author_id = user_id
''', """,
(thread,) (thread,),
).fetchone() ).fetchone()
comments = db.execute(''' comments = db.execute(
"""
select select
comment_id, comment_id,
parent_id, parent_id,
@@ -97,59 +127,91 @@ class DB:
left join users left join users
on author_id = user_id on author_id = user_id
where thread_id = ? where thread_id = ?
''', """,
(thread,) (thread,),
)
return (
title,
text,
author,
author_id,
create_time,
modify_time,
comments,
hidden,
forum_id
) )
return title, text, author, author_id, create_time, modify_time, comments, hidden
def get_thread_title(self, thread_id): def get_thread_title(self, thread_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select title select title
from threads from threads
where thread_id = ? where thread_id = ?
''', """,
(thread_id,) (thread_id,),
).fetchone() )
.fetchone()
)
def get_thread_title_text(self, thread_id): def get_thread_title_text(self, thread_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select title, text select title, text
from threads from threads
where thread_id = ? where thread_id = ?
''', """,
(thread_id,) (thread_id,),
).fetchone() )
.fetchone()
)
def get_recent_threads(self, limit): def get_recent_threads(self, limit):
return self._db().execute(''' return self._db().execute(
"""
select thread_id, title, modify_date select thread_id, title, modify_date
from threads from threads
order by modify_date order by modify_date
limit ? limit ?
''', """,
(limit,) (limit,),
) )
def get_comment(self, comment_id): def get_comment(self, comment_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select title, c.text select title, c.text
from comments c, threads t from comments c, threads t
where comment_id = ? and c.thread_id = t.thread_id where comment_id = ? and c.thread_id = t.thread_id
''', """,
(comment_id,) (comment_id,),
).fetchone() )
.fetchone()
)
def get_subcomments(self, comment_id): def get_subcomments(self, comment_id):
db = self._db() db = self._db()
thread_id, parent_id, title = db.execute(''' thread_id, parent_id, title = db.execute(
"""
select threads.thread_id, parent_id, title select threads.thread_id, parent_id, title
from threads, comments from threads, comments
where comment_id = ? and threads.thread_id = comments.thread_id where comment_id = ? and threads.thread_id = comments.thread_id
''', """,
(comment_id,) (comment_id,),
).fetchone() ).fetchone()
# Recursive CTE, see https://www.sqlite.org/lang_with.html # Recursive CTE, see https://www.sqlite.org/lang_with.html
return thread_id, parent_id, title, db.execute(''' return (
thread_id,
parent_id,
title,
db.execute(
"""
with recursive with recursive
descendant_of(id) as ( descendant_of(id) as (
select comment_id from comments where comment_id = ? select comment_id from comments where comment_id = ?
@@ -171,112 +233,148 @@ class DB:
users users
where id = comment_id where id = comment_id
and user_id = author_id and user_id = author_id
''', """,
(comment_id,) (comment_id,),
),
) )
def get_user_password(self, username): def get_user_password(self, username):
return self._db().execute(''' return (
self._db()
.execute(
"""
select user_id, password select user_id, password
from users from users
where name = lower(?) where name = lower(?)
''', """,
(username,) (username,),
).fetchone() )
.fetchone()
)
def get_user_password_by_id(self, user_id): def get_user_password_by_id(self, user_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select password select password
from users from users
where user_id = ? where user_id = ?
''', """,
(user_id,) (user_id,),
).fetchone() )
.fetchone()
)
def set_user_password(self, user_id, password): def set_user_password(self, user_id, password):
return self.change_one(''' return self.change_one(
"""
update users update users
set password = ? set password = ?
where user_id = ? where user_id = ?
''', """,
(password, user_id) (password, user_id),
) )
def get_user_public_info(self, user_id): def get_user_public_info(self, user_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select name, about, banned_until select name, about, banned_until
from users from users
where user_id = ? where user_id = ?
''', """,
(user_id,) (user_id,),
).fetchone() )
.fetchone()
)
def get_user_private_info(self, user_id): def get_user_private_info(self, user_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select about select about
from users from users
where user_id = ? where user_id = ?
''', """,
(user_id,) (user_id,),
).fetchone() )
.fetchone()
)
def set_user_private_info(self, user_id, about): def set_user_private_info(self, user_id, about):
db = self._db() db = self._db()
db.execute(''' db.execute(
"""
update users update users
set about = ? set about = ?
where user_id = ? where user_id = ?
''', """,
(about, user_id) (about, user_id),
) )
db.commit() db.commit()
def get_user_name_role_banned(self, user_id): def get_user_name_role_banned(self, user_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select name, role, banned_until select name, role, banned_until
from users from users
where user_id = ? where user_id = ?
''', """,
(user_id,) (user_id,),
).fetchone() )
.fetchone()
)
def get_user_name(self, user_id): def get_user_name(self, user_id):
return self._db().execute(''' return (
self._db()
.execute(
"""
select name select name
from users from users
where user_id = ? where user_id = ?
''', """,
(user_id,) (user_id,),
).fetchone() )
.fetchone()
)
def add_thread(self, author_id, forum_id, title, text, time): def add_thread(self, author_id, forum_id, title, text, time):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
insert into threads (author_id, forum_id, title, text, insert into threads (author_id, forum_id, title, text,
create_time, modify_time, update_time) create_time, modify_time, update_time)
select ?, ?, ?, ?, ?, ?, ? select ?, ?, ?, ?, ?, ?, ?
from users from users
where user_id = ? and banned_until < ? where user_id = ? and banned_until < ?
''', """,
(author_id, forum_id, title, text, time, time, time, author_id, time) (author_id, forum_id, title, text, time, time, time, author_id, time),
) )
rowid = c.lastrowid rowid = c.lastrowid
if rowid is None: if rowid is None:
return None return None
db.commit() db.commit()
return db.execute(''' return db.execute(
"""
select thread_id select thread_id
from threads from threads
where rowid = ? where rowid = ?
''', """,
(rowid,) (rowid,),
).fetchone() ).fetchone()
def delete_thread(self, user_id, thread_id): def delete_thread(self, user_id, thread_id):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
delete delete
from threads from threads
-- 1 = moderator, 2 = admin -- 1 = moderator, 2 = admin
@@ -284,8 +382,8 @@ class DB:
author_id = ? author_id = ?
or (select 1 from users where user_id = ? and (role = 1 or role = 2)) or (select 1 from users where user_id = ? and (role = 1 or role = 2))
) )
''', """,
(thread_id, user_id, user_id) (thread_id, user_id, user_id),
) )
db.commit() db.commit()
return c.rowcount > 0 return c.rowcount > 0
@@ -293,7 +391,8 @@ class DB:
def delete_comment(self, user_id, comment_id): def delete_comment(self, user_id, comment_id):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
delete delete
from comments from comments
where comment_id = ? where comment_id = ?
@@ -304,8 +403,8 @@ class DB:
) )
-- Don't allow deleting comments with children -- Don't allow deleting comments with children
and (select 1 from comments where parent_id = ?) is null and (select 1 from comments where parent_id = ?) is null
''', """,
(comment_id, user_id, user_id, comment_id) (comment_id, user_id, user_id, comment_id),
) )
db.commit() db.commit()
return c.rowcount > 0 return c.rowcount > 0
@@ -313,21 +412,23 @@ class DB:
def add_comment_to_thread(self, thread_id, author_id, text, time): def add_comment_to_thread(self, thread_id, author_id, text, time):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
insert into comments(thread_id, author_id, text, create_time, modify_time) insert into comments(thread_id, author_id, text, create_time, modify_time)
select ?, ?, ?, ?, ? select ?, ?, ?, ?, ?
from threads, users from threads, users
where thread_id = ? and user_id = ? and banned_until < ? where thread_id = ? and user_id = ? and banned_until < ?
''', """,
(thread_id, author_id, text, time, time, thread_id, author_id, time) (thread_id, author_id, text, time, time, thread_id, author_id, time),
) )
if c.rowcount > 0: if c.rowcount > 0:
c.execute(''' c.execute(
"""
update threads update threads
set update_time = ? set update_time = ?
where thread_id = ? where thread_id = ?
''', """,
(time, thread_id) (time, thread_id),
) )
db.commit() db.commit()
return True return True
@@ -336,16 +437,18 @@ class DB:
def add_comment_to_comment(self, parent_id, author_id, text, time): def add_comment_to_comment(self, parent_id, author_id, text, time):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
insert into comments(thread_id, parent_id, author_id, text, create_time, modify_time) insert into comments(thread_id, parent_id, author_id, text, create_time, modify_time)
select thread_id, ?, ?, ?, ?, ? select thread_id, ?, ?, ?, ?, ?
from comments, users from comments, users
where comment_id = ? and user_id = ? and banned_until < ? where comment_id = ? and user_id = ? and banned_until < ?
''', """,
(parent_id, author_id, text, time, time, parent_id, author_id, time) (parent_id, author_id, text, time, time, parent_id, author_id, time),
) )
if c.rowcount > 0: if c.rowcount > 0:
c.execute(''' c.execute(
"""
update threads update threads
set update_time = ? set update_time = ?
where threads.thread_id = ( where threads.thread_id = (
@@ -353,8 +456,8 @@ class DB:
from comments c from comments c
where comment_id = ? where comment_id = ?
) )
''', """,
(time, parent_id) (time, parent_id),
) )
db.commit() db.commit()
return True return True
@@ -363,7 +466,8 @@ class DB:
def modify_thread(self, thread_id, user_id, title, text, time): def modify_thread(self, thread_id, user_id, title, text, time):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
update threads update threads
set title = ?, text = ?, modify_time = ? set title = ?, text = ?, modify_time = ?
where thread_id = ? and ( where thread_id = ? and (
@@ -371,13 +475,17 @@ class DB:
-- 1 = moderator, 2 = admin -- 1 = moderator, 2 = admin
or (select 1 from users where user_id = ? and (role = 1 or role = 2)) or (select 1 from users where user_id = ? and (role = 1 or role = 2))
) )
''', """,
( (
title, text, time, title,
text,
time,
thread_id, thread_id,
user_id, user_id, time,
user_id, user_id,
) user_id,
time,
user_id,
),
) )
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
@@ -387,7 +495,8 @@ class DB:
def modify_comment(self, comment_id, user_id, text, time): def modify_comment(self, comment_id, user_id, text, time):
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
update comments update comments
set text = ?, modify_time = ? set text = ?, modify_time = ?
where comment_id = ? and ( where comment_id = ? and (
@@ -395,13 +504,16 @@ class DB:
-- 1 = moderator, 2 = admin -- 1 = moderator, 2 = admin
or (select 1 from users where user_id = ? and (role = 1 or role = 2)) or (select 1 from users where user_id = ? and (role = 1 or role = 2))
) )
''', """,
( (
text, time, text,
time,
comment_id, comment_id,
user_id, user_id, time,
user_id, user_id,
) user_id,
time,
user_id,
),
) )
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
@@ -409,19 +521,20 @@ class DB:
return False return False
def register_user(self, username, password, time): def register_user(self, username, password, time):
''' """
Add a user if registrations are enabled. Add a user if registrations are enabled.
''' """
try: try:
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
insert into users(name, password, join_time) insert into users(name, password, join_time)
select lower(?), ?, ? select lower(?), ?, ?
from config from config
where registration_enabled = 1 where registration_enabled = 1
''', """,
(username, password, time) (username, password, time),
) )
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
@@ -429,12 +542,13 @@ class DB:
# up by name. # up by name.
# ROWID is *probably* not always consistent (race conditions). # ROWID is *probably* not always consistent (race conditions).
# Ideally we get the ID immediately on insert. # Ideally we get the ID immediately on insert.
return c.execute(''' return c.execute(
"""
select user_id select user_id
from users from users
where name = lower(?) where name = lower(?)
''', """,
(username,) (username,),
).fetchone() ).fetchone()
return None return None
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
@@ -442,17 +556,18 @@ class DB:
return None return None
def add_user(self, username, password, time): def add_user(self, username, password, time):
''' """
Add a user without checking if registrations are enabled. Add a user without checking if registrations are enabled.
''' """
try: try:
db = self._db() db = self._db()
c = db.cursor() c = db.cursor()
c.execute(''' c.execute(
"""
insert into users(name, password, join_time) insert into users(name, password, join_time)
values (lower(?), ?, ?) values (lower(?), ?, ?)
''', """,
(username, password, time) (username, password, time),
) )
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
@@ -463,90 +578,102 @@ class DB:
return False return False
def get_users(self): def get_users(self):
return self._db().execute(''' return self._db().execute(
"""
select user_id, name, join_time, role, banned_until select user_id, name, join_time, role, banned_until
from users from users
''', """,
) )
def set_forum_name(self, forum_id, name): def set_forum_name(self, forum_id, name):
return self.change_one(''' return self.change_one(
"""
update forums update forums
set name = ? set name = ?
where forum_id = ? where forum_id = ?
''', """,
(name, forum_id) (name, forum_id),
) )
def set_forum_description(self, forum_id, description): def set_forum_description(self, forum_id, description):
return self.change_one(''' return self.change_one(
"""
update forums update forums
set description = ? set description = ?
where forum_id = ? where forum_id = ?
''', """,
(description, forum_id) (description, forum_id),
) )
def add_forum(self, name, description): def add_forum(self, name, description):
db = self._db() db = self._db()
db.execute(''' db.execute(
"""
insert into forums(name, description) insert into forums(name, description)
values (?, ?) values (?, ?)
''', """,
(name, description) (name, description),
) )
db.commit() db.commit()
def set_config(self, server_name, server_description, registration_enabled): def set_config(
return self.change_one(''' self, server_name, server_description, registration_enabled, login_required
):
return self.change_one(
"""
update config update config
set name = ?, description = ?, registration_enabled = ? set name = ?, description = ?, registration_enabled = ?, login_required = ?
''', """,
(server_name, server_description, registration_enabled) (server_name, server_description, registration_enabled, login_required),
) )
def set_config_secrets(self, secret_key, captcha_key): def set_config_secrets(self, secret_key, captcha_key):
return self.change_one(''' return self.change_one(
"""
update config update config
set secret_key = ?, captcha_key = ? set secret_key = ?, captcha_key = ?
''', """,
(secret_key, captcha_key) (secret_key, captcha_key),
) )
def set_user_ban(self, user_id, until): def set_user_ban(self, user_id, until):
return self.change_one(''' return self.change_one(
"""
update users update users
set banned_until = ? set banned_until = ?
where user_id = ? where user_id = ?
''', """,
(until, user_id) (until, user_id),
) )
def set_user_role(self, user_id, role): def set_user_role(self, user_id, role):
return self.change_one(''' return self.change_one(
"""
update users update users
set role = ? set role = ?
where user_id = ? where user_id = ?
''', """,
(role, user_id) (role, user_id),
) )
def set_thread_hidden(self, thread_id, hide): def set_thread_hidden(self, thread_id, hide):
return self.change_one(''' return self.change_one(
"""
update threads update threads
set hidden = ? set hidden = ?
where thread_id = ? where thread_id = ?
''', """,
(hide, thread_id) (hide, thread_id),
) )
def set_comment_hidden(self, comment_id, hide): def set_comment_hidden(self, comment_id, hide):
return self.change_one(''' return self.change_one(
"""
update comments update comments
set hidden = ? set hidden = ?
where comment_id = ? where comment_id = ?
''', """,
(hide, comment_id) (hide, comment_id),
) )
def change_one(self, query, values): def change_one(self, query, values):

View File

@@ -35,7 +35,8 @@ $SQLITE "$1" -init schema.txt "insert into config (
description, description,
secret_key, secret_key,
captcha_key, captcha_key,
registration_enabled registration_enabled,
login_required
) )
values ( values (
'agreper-v0.1.1', 'agreper-v0.1.1',
@@ -43,7 +44,8 @@ values (
'', '',
'$(head -c 30 /dev/urandom | base64)', '$(head -c 30 /dev/urandom | base64)',
'$(head -c 30 /dev/urandom | base64)', '$(head -c 30 /dev/urandom | base64)',
0 0,
0
)" )"
if [ "$2" != --no-admin ] if [ "$2" != --no-admin ]
then then

772
main.py

File diff suppressed because it is too large Load Diff

View File

@@ -1,58 +1,70 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re import re
import markdown2
# https://stackoverflow.com/a/6041965 # https://stackoverflow.com/a/6041965
RE_URL = re.compile(r'(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))') RE_URL = re.compile(
RE_EM = re.compile(r'\*(.*?)\*') r"(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))"
RE_LIST = re.compile(r'(-|[0-9]\.) .*') )
RE_EM = re.compile(r"\*(.*?)\*")
RE_LIST = re.compile(r"(-|[0-9]\.) .*")
RE_PLAINURL = re.compile(
r"([ |\n])(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))[^\)]"
)
def html(text): def html(text):
text = RE_PLAINURL.sub(r'\1[\2](\2)', text)
return markdown2.markdown(text)
def html_old(text):
# Replace angle brackets to prevent XSS # Replace angle brackets to prevent XSS
# Also replace ampersands to prevent surprises. # Also replace ampersands to prevent surprises.
text = text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;') text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
html = ['<p>'] html = ["<p>"]
lines = text.split('\n') lines = text.split("\n")
in_code = False in_code = False
in_list = False in_list = False
for l in lines: for l in lines:
if l == '': if l == "":
in_list = False in_list = False
if in_code: if in_code:
html.append('</pre>') html.append("</pre>")
in_code = False in_code = False
html.append('</p><p>') html.append("</p><p>")
continue continue
if l.startswith(' '): if l.startswith(" "):
in_list = False in_list = False
l = l[2:] l = l[2:]
if not in_code: if not in_code:
html.append('<pre>') html.append("<pre>")
in_code = True in_code = True
html.append(l) html.append(l)
continue continue
if in_code: if in_code:
html.append('</pre>') html.append("</pre>")
in_code = False in_code = False
l = RE_EM.sub(r'<em>\1</em>', l) l = RE_EM.sub(r"<em>\1</em>", l)
l = RE_URL.sub(r'<a href="\1">\1</a>', l) l = RE_URL.sub(r'<a href="\1">\1</a>', l)
if RE_LIST.match(l): if RE_LIST.match(l):
if in_list: if in_list:
html.append('<br>') html.append("<br>")
in_list = True in_list = True
else: else:
in_list = False in_list = False
html.append(l) html.append(l)
if in_code: if in_code:
html.append('</pre>') html.append("</pre>")
html.append('</p>') html.append("</p>")
return '\n'.join(html) return "\n".join(html)
if __name__ == '__main__': if __name__ == "__main__":
import sys import sys
print(html(sys.stdin.read()))
print(html_old(sys.stdin.read()))

View File

@@ -1,9 +1,9 @@
import passlib.hash import passlib.hash
def hash(password): def hash(password):
return passlib.hash.argon2.hash(password) return passlib.hash.argon2.hash(password)
def verify(password, hash): def verify(password, hash):
return passlib.hash.argon2.verify(password, hash) return passlib.hash.argon2.verify(password, hash)

View File

@@ -2,3 +2,4 @@ argon2-cffi==21.3.0
Flask==2.2.2 Flask==2.2.2
gunicorn==20.1.0 gunicorn==20.1.0
passlib==1.7.4 passlib==1.7.4
markdown2==2.4.9

View File

@@ -4,7 +4,8 @@ create table config (
description text not null, description text not null,
secret_key text not null, secret_key text not null,
captcha_key text not null, captcha_key text not null,
registration_enabled boolean not null registration_enabled boolean not null,
login_required boolean not null
); );
create table users ( create table users (

View File

@@ -21,6 +21,10 @@
<td>Registration enabled</td> <td>Registration enabled</td>
<td><input name=registration_enabled type=checkbox {{ 'checked' if config.registration_enabled else '' }}></td> <td><input name=registration_enabled type=checkbox {{ 'checked' if config.registration_enabled else '' }}></td>
</tr> </tr>
<tr>
<td>Login required</td>
<td><input name=login_required type=checkbox {{ 'checked' if config.login_required else '' }}></td>
</tr>
</table> </table>
<input type=submit value=Update> <input type=submit value=Update>
</form> </form>

View File

@@ -2,7 +2,7 @@
{% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply with context %} {% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply with context %}
{% block content %} {% block content %}
<p><span> &laquo; </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a><span> &laquo; </span><a href="{{ url_for('thread', thread_id = thread_id) }}">{{ title }}</a></p>
{{ render_comment_pre(reply_comment, thread_id, comments | length == 0) }} {{ render_comment_pre(reply_comment, thread_id, comments | length == 0) }}
{{ reply() }} {{ reply() }}

View File

@@ -11,7 +11,7 @@
{% block content -%} {% block content -%}
<p>{{ minimd(description) | safe }}</p> <p>{{ minimd(description) | safe }}</p>
<p><a href="{{ url_for('new_thread', forum_id = forum_id) }}">Create thread</a></p> <p><span> &laquo; </span><a href="{{ url_for('index') }}">Forum list</a><span> | </span><a href="{{ url_for('new_thread', forum_id = forum_id) }}">Create thread</a></p>
{{- nav() -}} {{- nav() -}}
<table> <table>
<tr> <tr>

View File

@@ -3,6 +3,7 @@
{%- from 'moderator.html' import moderate_thread with context %} {%- from 'moderator.html' import moderate_thread with context %}
{%- block content %} {%- block content %}
<p><span> &laquo; </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a></p>
{%- if user is not none and user.is_moderator() -%} {%- if user is not none and user.is_moderator() -%}
<p>{{ moderate_thread(thread_id, hidden) }}</p> <p>{{ moderate_thread(thread_id, hidden) }}</p>
{%- endif -%} {%- endif -%}

13
tool.py
View File

@@ -2,24 +2,27 @@
import sys, password import sys, password
def arg(i, s): def arg(i, s):
if i < len(sys.argv): if i < len(sys.argv):
return sys.argv[i] return sys.argv[i]
print(s) print(s)
sys.exit(1) sys.exit(1)
def arg_last(i, s): def arg_last(i, s):
if i == len(sys.argv) - 1: if i == len(sys.argv) - 1:
return sys.argv[i] return sys.argv[i]
print(s) print(s)
sys.exit(1) sys.exit(1)
proc = 'tool.py' if len(sys.argv) < 1 else sys.argv[0]
cmd = arg(1, f'usage: {proc} <command> [...]')
if cmd == 'password': proc = "tool.py" if len(sys.argv) < 1 else sys.argv[0]
pwd = arg_last(2, 'usage: {proc} password <pwd>') cmd = arg(1, f"usage: {proc} <command> [...]")
if cmd == "password":
pwd = arg_last(2, "usage: {proc} password <pwd>")
print(password.hash(pwd)) print(password.hash(pwd))
else: else:
print('unknown command ', cmd) print("unknown command ", cmd)
sys.exit(1) sys.exit(1)