Utilities¶
Cross-cutting helpers: password hashing, email and identifier normalization, client-IP resolution, and display masking.
crudauth.utils.get_password_hash
¶
Hash a plaintext password with bcrypt (random salt per call).
The password is SHA-256 pre-hashed before bcrypt (see [_bcrypt_input][crudauth.utils._bcrypt_input]), so there is no effective length ceiling and no silent truncation.
crudauth.utils.verify_password
¶
Verify a plaintext password against a bcrypt hash.
Returns False (rather than raising) when the stored hash is malformed,
so a corrupted row produces a clean "invalid password" path instead of a
500 - which would both leak information and be a DoS lever.
crudauth.utils.dummy_verify_password
¶
Run a throwaway bcrypt verification and discard the result.
Called on the user-not-found branch of login so the absent-user path pays the same bcrypt cost as the existing-user path; without it, a missing account returns measurably faster and becomes a user-enumeration oracle.
crudauth.utils.make_unusable_password
¶
Return a sentinel that no input can ever verify against.
Used for OAuth-only accounts. The leading ! makes the value an invalid
bcrypt hash, so verify_password always returns False for it. The
random suffix makes every sentinel unique. Mirrors Django's
set_unusable_password.
crudauth.utils.is_unusable_password
¶
Whether hashed_password is the unusable sentinel (or empty).
True means the account has no real password set - an OAuth-only account
(see make_unusable_password, whose
sentinel starts with !, never a valid bcrypt hash).
crudauth.utils.canonical_email
¶
Normalize an email for storage/comparison (trim + lowercase).
Ensures a user created via Google as Foo@x.com can log in by password as
foo@x.com without surprises.
crudauth.utils.canonical_identifier
¶
Normalize a login identifier the same way the user lookup does.
Email identifiers are canonicalized (trim + lowercase) so that case variants
of one address (v@x.com, V@x.com) collapse to a single rate-limit /
lockout key; otherwise an attacker could reset the per-username counter just
by varying the case while still hitting the same account. Usernames (no
@) are left as-is, matching get_by_username which is case-sensitive.
crudauth.utils.mask_email
¶
Mask an email for display: john@example.com -> j***@example.com.
A display helper for shoulder-surfing / casual logs - not a security
control (it's obfuscation, not a guarantee). Returns "***" when there's
no @; keeps only the first local-part character (so a single-char local
part can't leak more than that one character).
crudauth.utils.get_client_ip
¶
Resolve the client IP with a trusted-proxy boundary.
X-Forwarded-For is client-controllable at its left end, so honoring it
blindly lets an attacker forge a fresh IP per request and slip every per-IP
rate limit and lockout. This function only consults the header when the app
declares how many trusted proxies sit in front of it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Request
|
The incoming request. |
required |
trusted_hops
|
int
|
Number of trusted reverse proxies in front of the app.
|
0
|
Returns:
| Type | Description |
|---|---|
str
|
The resolved client IP, or |