codekingpro/portable-devtools
114k
1"""2 flask_security.username_util3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~4 5 Utility class providing methods for validating and normalizing usernames.6 7 :copyright: (c) 2020-2024 by J. Christopher Wagner (jwag).8 :license: MIT, see LICENSE for more details.9 10"""11 12from __future__ import annotations13 14import typing as t15import unicodedata16 17from .utils import (18 config_value as cv,19 get_message,20)21 22if t.TYPE_CHECKING: # pragma: no cover23 import flask24 25 26class UsernameUtil:27 """28 Utility class providing methods for validating and normalizing usernames.29 30 To provide your own implementation, pass in the class as ``username_util_cls``31 at init time. Your class will be instantiated once as part of app initialization.32 33 .. versionadded:: 4.1.034 """35 36 def __init__(self, app: flask.Flask):37 """Instantiate class.38 39 :param app: The Flask application being initialized.40 """41 pass42 43 def check_username(self, username: str) -> str | None:44 """45 Given a username - check for allowable character categories.46 This is broken out so applications can easily override this method only.47 48 By default allow letters and numbers (using unicodedata.category).49 50 Returns None if allowed, error message if not allowed.51 """52 cats = [unicodedata.category(c)[0] for c in username]53 if any([cat not in ["L", "N"] for cat in cats]):54 return get_message("USERNAME_DISALLOWED_CHARACTERS")[0]55 return None56 57 def normalize(self, username: str) -> str:58 """59 Given an input username - return a clean (using bleach) and normalized60 (using Python's unicodedata.normalize()) version.61 Must be called in app context and uses62 :py:data:`SECURITY_USERNAME_NORMALIZE_FORM` config variable.63 """64 import bleach65 66 if not username:67 return ""68 69 username = bleach.clean(username.strip(), strip=True)70 if not username:71 return ""72 cf = cv("USERNAME_NORMALIZE_FORM")73 if cf:74 return unicodedata.normalize(cf, username)75 return username76 77 def validate(self, username: str) -> tuple[str | None, str | None]:78 """79 Username validation.80 Called in app/request context.81 82 The username is first validated then normalized.83 Input is restricted/validated via a call to check_username.84 Return value is a tuple (msg, normalized_username). msg will be None if85 properly validated.86 87 It is important that None be returned if data is an empty string since88 otherwise DBs will complain since the field is unique/nullable.89 """90 import bleach91 92 if not username:93 return None, None94 uclean = bleach.clean(username.strip(), strip=True)95 if uclean != username:96 return get_message("USERNAME_ILLEGAL_CHARACTERS")[0], None97 98 msg = self.check_username(uclean)99 if msg:100 return msg, None101 102 unorm = self.normalize(username)103 umin = cv("USERNAME_MIN_LENGTH")104 umax = cv("USERNAME_MAX_LENGTH")105 if len(unorm) < umin or len(unorm) > umax:106 return get_message("USERNAME_INVALID_LENGTH", min=umin, max=umax)[0], unorm107 return None, unorm108 