-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathauth.py
More file actions
97 lines (80 loc) · 3.25 KB
/
auth.py
File metadata and controls
97 lines (80 loc) · 3.25 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
from __future__ import absolute_import
from abc import ABC, abstractmethod
from typing import Optional
from dash import Dash
from flask import request
from .public_routes import (
add_public_routes,
get_public_callbacks,
get_public_routes,
)
class Auth(ABC):
def __init__(
self, app: Dash, public_routes: Optional[list] = None, **obsolete
):
"""Auth base class for authentication in Dash.
:param app: Dash app
:param public_routes: list of public routes, routes should follow the
Flask route syntax
"""
# Deprecated arguments
if obsolete:
raise TypeError(
f"Auth got unexpected keyword arguments: {list(obsolete)}"
)
self.app = app
self._protect()
if public_routes is not None:
add_public_routes(app, public_routes)
def _protect(self):
"""Add a before_request authentication check on all routes.
The authentication check will pass if either
* The endpoint is marked as public via `add_public_routes`
* The request is authorised by `Auth.is_authorised`
"""
server = self.app.server
@server.before_request
def before_request_auth():
public_routes = get_public_routes(self.app)
public_callbacks = get_public_callbacks(self.app)
url_base = (
self.app.config.get("url_base_pathname", "")
or self.app.config.get("requests_pathname_prefix", "")
or self.app.config.get("routes_pathname_prefix", "")
)
# Handle Dash's callback route:
# * Check whether the callback is marked as public
# * Check whether the callback is performed on route change in
# which case the path should be checked against the public routes
callback_path = f"{url_base.rstrip('/')}/_dash-update-component"
if request.path == callback_path:
body = request.get_json()
# Check whether the callback is marked as public
if body["output"] in public_callbacks:
return None
# Check whether the callback has an input using the pathname,
# such a callback will be a routing callback and the pathname
# should be checked against the public routes
pathname = next(
(
inp.get("value")
for inp in body["inputs"]
if isinstance(inp, dict)
and inp.get("property") == "pathname"
),
None,
)
if pathname and public_routes.test(pathname):
return None
# If the route is not a callback route, check whether the path
# matches a public route, or whether the request is authorised
if public_routes.test(request.path) or self.is_authorized():
return None
# Otherwise, ask the user to log in
return self.login_request()
@abstractmethod
def is_authorized(self):
pass
@abstractmethod
def login_request(self):
pass