-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added `LoginHooks` * Update doc-requirements.txt * add `html_short_title` to conf.py * add docs for `LoginHooks` * add methods for running hooks * call hooks * add suggested changes, and change login_success signature Was passing `BaseUser` to `login_success` hook, but could be wasteful because it requires an extra SQL query, and the end user might not even need it (username and user_id might be sufficient). * fix example
- Loading branch information
1 parent
7e24d16
commit 0329afd
Showing
7 changed files
with
281 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
API Reference | ||
============= | ||
|
||
Auth | ||
---- | ||
|
||
.. currentmodule:: piccolo_api.shared.auth.hooks | ||
|
||
.. autoclass:: LoginHooks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from __future__ import annotations | ||
|
||
import dataclasses | ||
import inspect | ||
import logging | ||
import typing as t | ||
|
||
PreLoginHook = t.Union[ | ||
t.Callable[[str], t.Optional[str]], | ||
t.Callable[[str], t.Awaitable[t.Optional[str]]], | ||
] | ||
LoginSuccessHook = t.Union[ | ||
t.Callable[[str, int], t.Optional[str]], | ||
t.Callable[[str, int], t.Awaitable[t.Optional[str]]], | ||
] | ||
LoginFailureHook = t.Union[ | ||
t.Callable[[str], t.Optional[str]], | ||
t.Callable[[str], t.Awaitable[t.Optional[str]]], | ||
] | ||
|
||
|
||
logger = logging.getLogger(__file__) | ||
|
||
|
||
@dataclasses.dataclass | ||
class LoginHooks: | ||
""" | ||
Allows you to run custom logic during login. A hook can be a function or | ||
coroutine. | ||
Here's an example using :class:`session_login <piccolo_api.session_auth.endpoints.session_login>`: | ||
.. code-block:: python | ||
def check_ban_list(username: str, **kwargs): | ||
''' | ||
An example `pre_login` hook. | ||
''' | ||
if username in ('nuisance', 'pest'): | ||
return 'This account has been temporarily suspended'. | ||
async def log_success(username: str, user_id: int, **kwargs): | ||
''' | ||
An example `login_success` hook. | ||
''' | ||
await my_logging_service.record( | ||
f'{username} just logged in' | ||
) | ||
async def log_failure(username: str, **kwargs): | ||
''' | ||
An example `login_failure` hook. | ||
''' | ||
await my_logging_service.record(f'{username} could not login') | ||
return ( | ||
'To reset your password go <a href="/password-reset/">here</a>.' | ||
) | ||
login_endpoint = session_login( | ||
hooks=LoginHooks( | ||
pre_login=[check_ban_list], | ||
login_success=[log_success], | ||
login_failure=[log_failure], | ||
) | ||
) | ||
If any of the hooks return a string, the login process is aborted, and the | ||
login template is shown again, containing the string as a warning message. | ||
The string can contain HTML such as links, and it will be rendered | ||
correctly. | ||
All of the example hooks above accept ``**kwargs`` - this is recommended | ||
just in case more data is passed to the hooks in future Piccolo API | ||
versions. | ||
:param pre_login: | ||
A list of function and / or coroutines, which accept the username as a | ||
string. | ||
:param login_success: | ||
A list of function and / or coroutines, which accept the username as a | ||
string, and the user ID as an integer. If a string is returned, the | ||
login process stops before a session is created. | ||
:param login_failure: | ||
A list of function and / or coroutines, which accept the username as a | ||
string. | ||
""" # noqa: E501 | ||
|
||
pre_login: t.Optional[t.List[PreLoginHook]] = None | ||
login_success: t.Optional[t.List[LoginSuccessHook]] = None | ||
login_failure: t.Optional[t.List[LoginFailureHook]] = None | ||
|
||
async def run_pre_login(self, username: str) -> t.Optional[str]: | ||
if self.pre_login: | ||
for hook in self.pre_login: | ||
response = hook(username) | ||
if inspect.isawaitable(response): | ||
response = t.cast(t.Awaitable, response) | ||
response = await response | ||
|
||
if isinstance(response, str): | ||
return response | ||
|
||
return None | ||
|
||
async def run_login_success( | ||
self, username: str, user_id: int | ||
) -> t.Optional[str]: | ||
if self.login_success: | ||
for hook in self.login_success: | ||
response = hook(username, user_id) | ||
if inspect.isawaitable(response): | ||
response = t.cast(t.Awaitable, response) | ||
response = await response | ||
|
||
if isinstance(response, str): | ||
return response | ||
|
||
return None | ||
|
||
async def run_login_failure(self, username: str) -> t.Optional[str]: | ||
if self.login_failure: | ||
for hook in self.login_failure: | ||
response = hook(username) | ||
if inspect.isawaitable(response): | ||
response = t.cast(t.Awaitable, response) | ||
response = await response | ||
|
||
if isinstance(response, str): | ||
return response | ||
|
||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
Sphinx==4.4.0 | ||
piccolo-theme>=0.5.0 | ||
sphinx-rtd-theme==1.0.0 | ||
Sphinx==4.5.0 | ||
piccolo-theme>=0.9.0 | ||
livereload==2.6.3 |
Oops, something went wrong.