fix bug + add captcha

This commit is contained in:
Meewan 2024-11-09 12:03:55 +01:00
parent 979d0079d7
commit c96c84b836
3 changed files with 100 additions and 8 deletions

View File

@ -47,3 +47,6 @@ mandatory_email = yes
# whether or not the user must give an password to subscribe # whether or not the user must give an password to subscribe
# at least one of email and password is mandatory # at least one of email and password is mandatory
mandatory_password = yes mandatory_password = yes
# path to db managing the captcha
db_path=nextcloudregister.sqli
captcha_timout=3600

View File

@ -42,6 +42,8 @@
<input type="password" name="password1" placeholder="Mot de passe" {% if disable %} disabled="disabled" {% endif %}/> <input type="password" name="password1" placeholder="Mot de passe" {% if disable %} disabled="disabled" {% endif %}/>
<input type="password" name="password2" placeholder="Confirmez le mot de passe" {% if disable %} disabled="disabled" {% endif %}/> <input type="password" name="password2" placeholder="Confirmez le mot de passe" {% if disable %} disabled="disabled" {% endif %}/>
{% endif %} {% endif %}
<input type="hidden" name="token" value="{{ token }}"/>
<input type="text" name="answer" placeholder="Combien font {{first_number}} {{op_text}} {{second_number}} ?" {% if disable %} disabled="disabled" {% endif %}/>
{% if eula %} {% if eula %}
<div class="eula"> <div class="eula">
En m'inscrivant à ce service, j'accepte ses <a href="{{ eula }}">Conditions Générales d'utilisation</a> En m'inscrivant à ce service, j'accepte ses <a href="{{ eula }}">Conditions Générales d'utilisation</a>

101
webapp.py
View File

@ -1,4 +1,9 @@
from configparser import ConfigParser from configparser import ConfigParser
import operator
from os.path import exists
import random
from secrets import token_urlsafe
from time import time
from flask import ( from flask import (
Flask, Flask,
@ -6,6 +11,8 @@ from flask import (
render_template, render_template,
Response, Response,
) )
import sqlalchemy
import sqlalchemy.orm
from nextcloudregister.lib import ( from nextcloudregister.lib import (
CONFIG_PATH, CONFIG_PATH,
@ -24,16 +31,73 @@ config.read(CONFIG_PATH)
app = Flask(__name__) app = Flask(__name__)
base_uri = config.get("web", "base_uri", fallback="") base_uri = config.get("web", "base_uri", fallback="")
base_uri = base_uri + ("" if base_uri.endswith("/") else "/") base_uri = base_uri + ("" if base_uri.endswith("/") else "/")
db_path = config.get("rules", "db_path", fallback="db.sqlite")
Base = sqlalchemy.orm.declarative_base()
OPERATORS = (
('+', operator.add),
('×', operator.mul),
('-', operator.sub),
)
@app.route(base_uri, methods=["GET", "POST"]) class Captcha(Base):
def form_manager(): __tablename__ = "captcha"
if request.method == "POST":
return form_post() token = sqlalchemy.Column(sqlalchemy.String(90), primary_key=True)
else: answer = sqlalchemy.Column(sqlalchemy.String(3))
return form_get() expiration = sqlalchemy.Column(sqlalchemy.Integer())
def get_session():
engine = sqlalchemy.create_engine(f"sqlite:///{db_path}")
if not exists(db_path):
Base.metadata.create_all(engine)
session_factory = sqlalchemy.orm.sessionmaker(engine)
return session_factory()
def clean_db(session):
session.query(Captcha).filter(Captcha.expiration < int(time())).delete()
def generate_captacha():
session = get_session()
clean_db(session)
first_number = random.randrange(10)
second_number = random.randrange(10)
op_text, op_func = random.choice(OPERATORS)
if op_text == "-" and first_number < second_number:
first_number, second_number = second_number, first_number
result = op_func(first_number, second_number)
captcha = Captcha(
token=token_urlsafe(67),
answer=str(result),
expiration=int(time()) + config.getint("rules", "captcha_timout", fallback=3600),
)
session.add(captcha)
session.commit()
return {
"first_number": first_number,
"second_number": second_number,
"op_text": op_text,
"token": captcha.token,
}
def validate_captcha(token, value):
result = False
session = get_session()
clean_db(session)
captcha = session.query(Captcha).filter(Captcha.token == token).one_or_none()
if captcha:
result = value == captcha.answer
session.query(Captcha).filter(Captcha.token == token).delete()
session.commit()
return result
@app.route(base_uri, methods=["GET"])
def form_get(data=None, error=None, info=None, success=False): def form_get(data=None, error=None, info=None, success=False):
context = { context = {
"base_uri": base_uri, "base_uri": base_uri,
@ -63,18 +127,21 @@ def form_get(data=None, error=None, info=None, success=False):
try: try:
count_accounts = api.count_accounts() count_accounts = api.count_accounts()
except NextcloudApiException: except NextcloudApiException:
context["max_accounts"] = 0 context["count_accounts"] = context["max_accounts"]
if count_accounts >= context["max_accounts"] and not success: if count_accounts >= context["max_accounts"] and not success:
context["disable"] = True context["disable"] = True
context["error"] = ( context["error"] = (
"Tous les comptes disponibles sur cette instance ont deja été " "Tous les comptes disponibles sur cette instance ont deja été "
"distribués." "distribués."
) )
context["count_accounts"] = 0
else: else:
context["count_accounts"] = context["max_accounts"] - count_accounts context["count_accounts"] = context["max_accounts"] - count_accounts
context |= generate_captacha()
return render_template("form.html", **context) return render_template("form.html", **context)
@app.route(base_uri, methods=["POST"])
def form_post(): def form_post():
mandatory_email = config.getboolean( mandatory_email = config.getboolean(
"rules", "rules",
@ -86,6 +153,13 @@ def form_post():
"mandatory_password", "mandatory_password",
fallback=False, fallback=False,
) )
# validate captcha
if not validate_captcha(token=request.form.get("token"), value=request.form.get("answer")):
return form_get(
data=request.form,
error="Le captcha est invalide."
)
# validate mandatory fields # validate mandatory fields
if not request.form.get("username"): if not request.form.get("username"):
return form_get( return form_get(
@ -115,6 +189,19 @@ def form_post():
) )
api = NextcloudApi() api = NextcloudApi()
try:
if api.count_accounts() >= config.getint("rules", "max_accounts", fallback=0):
return form_get(
data=request.form,
error="Tous les comptes disponibles sur cette instance ont deja été distribués."
)
except NextcloudApiException:
return form_get(
data=request.form,
error="Tous les comptes disponibles sur cette instance ont deja été distribués."
)
try: try:
api.create_account( api.create_account(
username=request.form.get("username", "").strip(), username=request.form.get("username", "").strip(),