forked from pallets-eco/flask-security
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathusername_util.py
More file actions
107 lines (82 loc) · 3.22 KB
/
Copy pathusername_util.py
File metadata and controls
107 lines (82 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""
flask_security.username_util
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Utility class providing methods for validating and normalizing usernames.
:copyright: (c) 2020-2025 by J. Christopher Wagner (jwag).
:license: MIT, see LICENSE for more details.
"""
from __future__ import annotations
import typing as t
import unicodedata
from .utils import (
config_value as cv,
get_message,
)
if t.TYPE_CHECKING: # pragma: no cover
import flask
class UsernameUtil:
"""
Utility class providing methods for validating and normalizing usernames.
To provide your own implementation, pass in the class as ``username_util_cls``
at init time. Your class will be instantiated once as part of app initialization.
.. versionadded:: 4.1.0
"""
def __init__(self, app: flask.Flask):
"""Instantiate class.
:param app: The Flask application being initialized.
"""
pass
def check_username(self, username: str) -> str | None:
"""
Given a username - check for allowable character categories.
This is broken out so applications can easily override this method only.
By default, allow letters and numbers (using unicodedata.category).
Returns None if allowed, error message if not allowed.
"""
cats = [unicodedata.category(c)[0] for c in username]
if any([cat not in ["L", "N"] for cat in cats]):
return get_message("USERNAME_DISALLOWED_CHARACTERS")[0]
return None
def normalize(self, username: str) -> str:
"""
Given an input username - return a clean (using nh3) and normalized
(using Python's unicodedata.normalize()) version.
Must be called in app context and uses
:py:data:`SECURITY_USERNAME_NORMALIZE_FORM` config variable.
"""
import nh3
if not username:
return ""
username = nh3.clean(username.strip(), tags=set())
if not username:
return ""
cf = cv("USERNAME_NORMALIZE_FORM")
if cf:
return unicodedata.normalize(cf, username)
return username
def validate(self, username: str) -> tuple[str | None, str | None]:
"""
Username validation.
Called in app/request context.
The username is first validated then normalized.
Input is restricted/validated via a call to check_username.
Return value is a tuple (msg, normalized_username). msg will be None if
properly validated.
It is important that None be returned if data is an empty string since
otherwise DBs will complain since the field is unique/nullable.
"""
import nh3
if not username:
return None, None
uclean = nh3.clean(username.strip(), tags=set())
if uclean != username:
return get_message("USERNAME_ILLEGAL_CHARACTERS")[0], None
msg = self.check_username(uclean)
if msg:
return msg, None
unorm = self.normalize(username)
umin = cv("USERNAME_MIN_LENGTH")
umax = cv("USERNAME_MAX_LENGTH")
if len(unorm) < umin or len(unorm) > umax:
return get_message("USERNAME_INVALID_LENGTH", min=umin, max=umax)[0], unorm
return None, unorm