Add user signup (has to be enabled via config)
This commit is contained in:
parent
d68eee7bf4
commit
f92ad205d2
9 changed files with 158 additions and 8 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,5 +1,18 @@
|
||||||
# Matemat Changelog
|
# Matemat Changelog
|
||||||
|
|
||||||
|
<!-- BEGIN RELEASE v0.2.11 -->
|
||||||
|
## Version 0.2.11
|
||||||
|
|
||||||
|
Feature release
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
<!-- BEGIN CHANGES 0.2.11 -->
|
||||||
|
- Feature: Permit user signup
|
||||||
|
<!-- END CHANGES 0.2.11 -->
|
||||||
|
|
||||||
|
<!-- END RELEASE v0.2.11 -->
|
||||||
|
|
||||||
<!-- BEGIN RELEASE v0.2.10 -->
|
<!-- BEGIN RELEASE v0.2.10 -->
|
||||||
## Version 0.2.10
|
## Version 0.2.10
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
|
|
||||||
__version__ = '0.2.10'
|
__version__ = '0.2.11'
|
||||||
|
|
|
@ -7,6 +7,7 @@ A new pagelet function must be imported here to be automatically loaded when the
|
||||||
from .main import main_page
|
from .main import main_page
|
||||||
from .login import login_page
|
from .login import login_page
|
||||||
from .logout import logout
|
from .logout import logout
|
||||||
|
from .signup import signup
|
||||||
from .touchkey import touchkey_page
|
from .touchkey import touchkey_page
|
||||||
from .buy import buy
|
from .buy import buy
|
||||||
from .deposit import deposit
|
from .deposit import deposit
|
||||||
|
|
|
@ -163,14 +163,14 @@ def handle_admin_change(args: FormsDict, files: FormsDict, db: MatematDatabase):
|
||||||
# The user requested to create a new user
|
# The user requested to create a new user
|
||||||
if change == 'newuser':
|
if change == 'newuser':
|
||||||
# Only create a new user if all required properties of the user are present in the request arguments
|
# Only create a new user if all required properties of the user are present in the request arguments
|
||||||
if 'username' not in args or 'email' not in args or 'password' not in args:
|
if 'username' not in args or 'password' not in args:
|
||||||
return
|
return
|
||||||
# Read the properties from the request arguments
|
# Read the properties from the request arguments
|
||||||
username = str(args.username)
|
username = str(args.username)
|
||||||
email = str(args.email)
|
|
||||||
# An empty e-mail field should be interpreted as NULL
|
|
||||||
if len(email) == 0:
|
|
||||||
email = None
|
email = None
|
||||||
|
if 'email' in args and len(args.email) > 0:
|
||||||
|
# An empty e-mail field should be interpreted as NULL
|
||||||
|
email = str(args.email)
|
||||||
password = str(args.password)
|
password = str(args.password)
|
||||||
is_member = 'ismember' in args
|
is_member = 'ismember' in args
|
||||||
is_admin = 'isadmin' in args
|
is_admin = 'isadmin' in args
|
||||||
|
|
|
@ -34,4 +34,5 @@ def main_page():
|
||||||
# If no user is logged in, fetch the list of users and render the userlist template
|
# If no user is logged in, fetch the list of users and render the userlist template
|
||||||
users = db.list_users(with_touchkey=True)
|
users = db.list_users(with_touchkey=True)
|
||||||
return template.render('userlist.html',
|
return template.render('userlist.html',
|
||||||
users=users, setupname=config['InstanceName'])
|
users=users, setupname=config['InstanceName'],
|
||||||
|
signup=(config.get('SignupEnabled', '0') == '1'))
|
||||||
|
|
93
matemat/webserver/pagelets/signup.py
Normal file
93
matemat/webserver/pagelets/signup.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
import os
|
||||||
|
from shutil import copyfile
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
import magic
|
||||||
|
from bottle import get, post, redirect, abort, request, FormsDict
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from matemat.db import MatematDatabase
|
||||||
|
from matemat.db.primitives import User
|
||||||
|
from matemat.webserver import template, session
|
||||||
|
from matemat.webserver.config import get_app_config
|
||||||
|
|
||||||
|
|
||||||
|
def signup_user(args: FormsDict, files: FormsDict, db: MatematDatabase) -> User:
|
||||||
|
config = get_app_config()
|
||||||
|
if 'username' not in args or 'password' not in args or 'password2' not in args:
|
||||||
|
raise ValueError('Required fields missing')
|
||||||
|
username = str(args.username)
|
||||||
|
password = str(args.password)
|
||||||
|
password2 = str(args.password2)
|
||||||
|
if password != password2:
|
||||||
|
raise ValueError('Passwords do not match')
|
||||||
|
email = None
|
||||||
|
if 'email' in args and len(args.email) > 0:
|
||||||
|
email = str(args.email)
|
||||||
|
|
||||||
|
new_user = db.create_user(username, password, email=email, admin=False, member=False)
|
||||||
|
# If a touchkey was provided, set it
|
||||||
|
if 'touchkey' in args and len(args.touchkey) > 0:
|
||||||
|
touchkey = str(args.touchkey)
|
||||||
|
db.change_touchkey(new_user, password, touchkey, verify_password=False)
|
||||||
|
# Finally, set the avatar, if provided
|
||||||
|
if 'avatar' in files:
|
||||||
|
avatar = files.avatar.file.read()
|
||||||
|
if len(avatar) > 0:
|
||||||
|
filemagic: magic.FileMagic = magic.detect_from_content(avatar)
|
||||||
|
if filemagic.mime_type.startswith('image/'):
|
||||||
|
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/users/')
|
||||||
|
os.makedirs(abspath, exist_ok=True)
|
||||||
|
# Parse the image data
|
||||||
|
image: Image = Image.open(BytesIO(avatar))
|
||||||
|
# Resize the image to 150x150
|
||||||
|
image.thumbnail((150, 150), Image.LANCZOS)
|
||||||
|
# Write the image to the file
|
||||||
|
image.save(os.path.join(abspath, f'{new_user.id}.png'), 'PNG')
|
||||||
|
else:
|
||||||
|
# If a default avatar is set, copy it to the user's avatar path
|
||||||
|
# Create the absolute path of the upload directory
|
||||||
|
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/users/')
|
||||||
|
# Derive the individual paths
|
||||||
|
default: str = os.path.join(abspath, 'default.png')
|
||||||
|
userimg: str = os.path.join(abspath, f'{new_user.id}.png')
|
||||||
|
# Copy the default image, if it exists
|
||||||
|
if os.path.exists(default):
|
||||||
|
copyfile(default, userimg, follow_symlinks=True)
|
||||||
|
return new_user
|
||||||
|
|
||||||
|
|
||||||
|
@get('/signup')
|
||||||
|
@post('/signup')
|
||||||
|
def signup():
|
||||||
|
"""
|
||||||
|
The password login mechanism. If called via GET, render the UI template; if called via POST, attempt to log in with
|
||||||
|
the provided credentials (username and passsword).
|
||||||
|
"""
|
||||||
|
config = get_app_config()
|
||||||
|
session_id: str = session.start()
|
||||||
|
# If signup is not enabled, redirect to the main page
|
||||||
|
if config.get('SignupEnabled', '0') != '1':
|
||||||
|
redirect('/')
|
||||||
|
# If a user is already logged in, simply redirect to the main page, showing the product list
|
||||||
|
if session.has(session_id, 'authenticated_user'):
|
||||||
|
redirect('/')
|
||||||
|
# If requested via HTTP POST, read the request arguments and attempt to create a new user
|
||||||
|
if request.method == 'POST':
|
||||||
|
# Connect to the database and create the user
|
||||||
|
with MatematDatabase(config['DatabaseFile']) as db:
|
||||||
|
try:
|
||||||
|
user = signup_user(request.params, request.files, db)
|
||||||
|
except ValueError as e:
|
||||||
|
redirect('/signup')
|
||||||
|
# Set the user ID session variable
|
||||||
|
session.put(session_id, 'authenticated_user', user.id)
|
||||||
|
# Set the authlevel session variable (0 = none, 1 = touchkey, 2 = password login)
|
||||||
|
session.put(session_id, 'authentication_level', 2)
|
||||||
|
# Redirect to the main page, showing the product list
|
||||||
|
redirect('/')
|
||||||
|
elif request.method != 'GET':
|
||||||
|
abort(405, 'Method not allowed')
|
||||||
|
return template.render('signup.html',
|
||||||
|
setupname=config['InstanceName'])
|
|
@ -1,5 +1,5 @@
|
||||||
Package: matemat
|
Package: matemat
|
||||||
Version: 0.2.10
|
Version: 0.2.11
|
||||||
Maintainer: s3lph <account-gitlab-ideynizv@kernelpanic.lol>
|
Maintainer: s3lph <account-gitlab-ideynizv@kernelpanic.lol>
|
||||||
Section: web
|
Section: web
|
||||||
Priority: optional
|
Priority: optional
|
||||||
|
|
39
templates/signup.html
Normal file
39
templates/signup.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1>Signup</h1>
|
||||||
|
{{ super() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
{# Show a username/password signup form #}
|
||||||
|
<form method="post" action="/signup" id="signupform" enctype="multipart/form-data" accept-charset="UTF-8">
|
||||||
|
<label for="signup-username"><b>Username</b>: </label>
|
||||||
|
<input id="signup-username" type="text" name="username" required="required"/><br/>
|
||||||
|
|
||||||
|
<label for="signup-password"><b>Choose a password</b>: </label>
|
||||||
|
<input id="signup-password" type="password" name="password" required="required"/><br/>
|
||||||
|
|
||||||
|
<label for="signup-password2"><b>Repeat password</b>: </label>
|
||||||
|
<input id="signup-password2" type="password" name="password2" required="required"/><br/>
|
||||||
|
|
||||||
|
<label for="signup-avatar">Upload a profile picture: </label>
|
||||||
|
<input id="signup-avatar" type="file" name="avatar" accept="image/*" /><br/>
|
||||||
|
|
||||||
|
<label for="signup-touchkey">Draw a touchkey (touchscreen login pattern)</label>
|
||||||
|
<br/>
|
||||||
|
<svg id="touchkey-svg" width="400" height="400"></svg>
|
||||||
|
<br/>
|
||||||
|
<input id="signup-touchkey" type="hidden" name="touchkey" value="" />
|
||||||
|
|
||||||
|
<input type="submit" value="Create account">
|
||||||
|
</form>
|
||||||
|
<script src="/static/js/touchkey.js" ></script>
|
||||||
|
<script>
|
||||||
|
initTouchkey(true, 'touchkey-svg', null, 'signup-touchkey');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ super() }}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -23,6 +23,9 @@
|
||||||
<br/>
|
<br/>
|
||||||
{# Link to the password login #}
|
{# Link to the password login #}
|
||||||
<a href="/login">Password login</a>
|
<a href="/login">Password login</a>
|
||||||
|
{% if signup %}
|
||||||
|
<a href="/signup">Create account</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue