start work on invite keys

This commit is contained in:
2026-06-03 11:07:50 +03:00
parent 5853c8b7a8
commit 4083c950c5
9 changed files with 147 additions and 31 deletions

View File

@@ -4,7 +4,7 @@ from flask import (
abort, flash, current_app
)
from functools import wraps
from secrets import compare_digest as compare_timesafe
from secrets import compare_digest as compare_timesafe, token_urlsafe
from wand.image import Image
from wand.color import Color
from wand.exceptions import WandException
@@ -14,9 +14,9 @@ from ..auth import (
login_required, revoke_session, get_active_user,
parse_display_name, revoke_all_sessions, csrf_verified
)
from ..models import Users, Posts, Reactions, Threads, Avatars, PostHistory, Mentions, BookmarkCollections
from ..models import Users, Posts, Reactions, Threads, Avatars, PostHistory, Mentions, BookmarkCollections, InviteKeys
from ..constants import PermissionLevel, InfoboxKind
from ..util import get_form_checkbox
from ..util import get_form_checkbox, time_now
from ..lib.babycode import babycode_to_html
from ..db import db
import math
@@ -169,15 +169,34 @@ def log_out():
@bp.get('/sign-up/')
@redirect_if_logged_in()
def sign_up():
return render_template('users/sign_up.html')
key = request.args.get('key', '')
if not key and current_app.config['DISABLE_SIGNUP']:
return redirect(url_for('topics.all_topics'))
elif key and current_app.config['DISABLE_SIGNUP']:
invite = InviteKeys.find({'key': key})
if not invite:
return redirect(url_for('topics.all_topics'))
inviter = Users.find({'id': invite.created_by})
return render_template('users/sign_up.html', invite=invite, inviter=inviter)
@bp.post('/sign-up/')
@redirect_if_logged_in()
def sign_up_post():
generic_error_page = redirect(url_for('.sign_up', error='The username or password you entered is invalid.'))
invalid_username_error_page = redirect(url_for('.sign_up', error='This username cannot be used. Please pick another.'))
passwords_error_page = redirect(url_for('.sign_up', error='The passwords do not match.'))
args_sans_error = dict(request.args)
args_sans_error.pop('error', '')
generic_error_page = redirect(url_for('.sign_up', error='The username or password you entered is invalid.', **args_sans_error))
invalid_username_error_page = redirect(url_for('.sign_up', error='This username cannot be used. Please pick another.', **args_sans_error))
passwords_error_page = redirect(url_for('.sign_up', error='The passwords do not match.', **args_sans_error))
username = request.form.get('username', default='')
if current_app.config['DISABLE_SIGNUP']:
key = request.form.get('key', '')
if not key:
return generic_error_page
invite = InviteKeys.find({'key': key})
if not invite:
return generic_error_page
if invite.expires_at < time_now():
return generic_error_page
if not username:
return generic_error_page
if request.form.get('password') is None:
@@ -197,12 +216,18 @@ def sign_up_post():
password_hash = digest(request.form.get('password'))
user = Users.create({
user_data = {
'username': username_pair[0],
'password_hash': password_hash,
'permission': PermissionLevel.GUEST.value,
'created_at': int(time.time()),
})
}
if invite:
user_data['invited_by'] = invite.created_by
user_data['permission'] = PermissionLevel.USER.value
invite.delete()
user = Users.create(user_data)
BookmarkCollections.create_default(user.id)
@@ -217,6 +242,7 @@ def sign_up_post():
if session['remember']:
session.permanent = True
flash(f'Welcome to {current_app.config['SITE_NAME']}!', InfoboxKind.INFO)
return redirect(url_for('topics.all_topics'))
@bp.get('/<username>/')
@@ -286,9 +312,11 @@ def comments(username):
def settings(username):
user = get_active_user()
sort_by = session.get('sort_by', 'activity')
invites = InviteKeys.findall({'created_by': user.id})
return render_template(
'users/settings.html', user=user,
sort_by=sort_by
sort_by=sort_by,
invites=invites,
)
@bp.post('/<username>/settings/set-avatar')
@@ -539,3 +567,40 @@ def delete_confirm_post(username):
user.delete()
return redirect(url_for('topics.all_topics'))
@bp.post('/<username>/invite-keys/create/')
@login_required
@redirect_to_own
@csrf_verified
def create_invite_key(username):
user = get_active_user()
if not user.can_invite():
abort(404)
key = token_urlsafe(16)
expires_at = time_now() + 48 * 60 * 60
invite = InviteKeys.create({
'created_by': user.id,
'expires_at': expires_at,
'key': key,
})
return redirect(url_for('.settings', username=username, _anchor='invite'))
@bp.post('/<username>/invite-keys/revoke/')
@login_required
@redirect_to_own
@csrf_verified
def revoke_invite_key(username):
user = get_active_user()
if not user.can_invite():
abort(404)
key = request.form.get('key', '')
invite = InviteKeys.find({'created_by': user.id, 'key': key})
if not invite:
abort(404)
invite.delete()
return redirect(url_for('.settings', username=username, _anchor='invite'))