From 689effd04bb7f07f0b31db946d27edd4ac89f9d5 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Mon, 28 Oct 2019 18:11:35 +0000 Subject: [PATCH] people: Check if UID is valid and available on registration Signed-off-by: Michael Tremer --- src/backend/accounts.py | 17 ++++++++++ src/templates/auth/register.html | 57 ++++++++++++++++++++++++++++++-- src/web/__init__.py | 3 ++ src/web/auth.py | 21 ++++++++++++ src/web/base.py | 8 +++++ 5 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/backend/accounts.py b/src/backend/accounts.py index c310cf53..bba6a4fa 100644 --- a/src/backend/accounts.py +++ b/src/backend/accounts.py @@ -10,6 +10,7 @@ import ldap.modlist import logging import os import phonenumbers +import re import sshpubkeys import time import tornado.httpclient @@ -125,6 +126,18 @@ class Accounts(Object): for result in results: return result + def uid_is_valid(self, uid): + # UID must be at least four characters + if len(uid) <= 4: + return False + + # https://unix.stackexchange.com/questions/157426/what-is-the-regex-to-validate-linux-users + m = re.match(r"^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$", uid) + if m: + return True + + return False + def uid_exists(self, uid): if self.get_by_uid(uid): return True @@ -190,6 +203,10 @@ class Accounts(Object): # Convert all uids to lowercase uid = uid.lower() + # Check if UID is valid + if not self.uid_is_valid(uid): + raise ValueError("UID is invalid: %s" % uid) + # Check if UID is unique if self.uid_exists(uid): raise ValueError("UID exists: %s" % uid) diff --git a/src/templates/auth/register.html b/src/templates/auth/register.html index bd040fee..80966242 100644 --- a/src/templates/auth/register.html +++ b/src/templates/auth/register.html @@ -17,8 +17,20 @@
- +
+
+ @ +
+ +
+ {{ _("This username is invalid. Please choose a user name in UNIX format starting with a letter, followed by ASCII characters and digits only.") }} +
+
+ {{ _("This username is not available.") }} +
+
@@ -49,3 +61,44 @@
{% end block %} + +{% block javascript %} + +{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 7fff7036..de8ff2b5 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -287,6 +287,9 @@ class Application(tornado.web.Application): # Single-Sign-On for Discourse (r"/sso/discourse", people.SSODiscourse), + + # API + (r"/api/check/uid", auth.APICheckUID), ] + authentication_handlers) # wiki.ipfire.org diff --git a/src/web/auth.py b/src/web/auth.py index 256c4ee8..4a311393 100644 --- a/src/web/auth.py +++ b/src/web/auth.py @@ -137,3 +137,24 @@ class ActivateHandler(AuthenticationMixin, base.BaseHandler): # Redirect to success page self.render("auth/activated.html", account=account) + + +class APICheckUID(base.APIHandler): + @base.ratelimit(minutes=10, requests=100) + def get(self): + uid = self.get_argument("uid") + result = None + + if not uid: + result = "empty" + + # Check if the username is syntactically valid + elif not self.backend.accounts.uid_is_valid(uid): + result = "invalid" + + # Check if the username is already taken + elif self.backend.accounts.uid_exists(uid): + result = "taken" + + # Username seems to be okay + self.finish({ "result" : result or "ok" }) diff --git a/src/web/base.py b/src/web/base.py index 396799bb..437050ac 100644 --- a/src/web/base.py +++ b/src/web/base.py @@ -247,6 +247,14 @@ class BaseHandler(tornado.web.RequestHandler): return self.backend.talk +class APIHandler(BaseHandler): + def check_xsrf_cookie(self): + """ + Do nothing here, because we cannot verify the XSRF token + """ + pass + + class NotFoundHandler(BaseHandler): def prepare(self): # Raises 404 as soon as it is called -- 2.47.2