Skip to content

Commit c39f714

Browse files
authored
Merge pull request #141 from RenaudLN/feature/use-before-request
Auth using before_request decorator
2 parents eea39c6 + d835ede commit c39f714

File tree

9 files changed

+81
-27
lines changed

9 files changed

+81
-27
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: 2.1
22

33
orbs:
4-
browser-tools: circleci/[email protected].1
4+
browser-tools: circleci/[email protected].3
55

66
jobs:
77
python-36: &test-template

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
### Changed
9+
- Uses flask `before_request` to protect all endpoints rather than protecting routes present at instantiation time
10+
711
## [2.0.0] - 2023-03-10
812
### Removed
913
Removed obsolete `PlotlyAuth`. `dash-auth` is now just responsible for `BasicAuth`.

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,21 @@ python -k ba001
2323
```
2424

2525
Note that Python 3.6 or greater is required.
26+
27+
## Usage
28+
29+
### Basic Authentication
30+
31+
To add basic authentication, add the following to your Dash app:
32+
33+
```python
34+
from dash import Dash
35+
from dash_auth import BasicAuth
36+
37+
app = Dash(__name__)
38+
USER_PWD = {
39+
"username": "password",
40+
"user2": "useSomethingMoreSecurePlease",
41+
}
42+
BasicAuth(app, USER_PWD)
43+
```

dash_auth/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
from .basic_auth import BasicAuth # noqa: F401
2-
from .version import __version__ # noqa: F401
1+
from .basic_auth import BasicAuth
2+
from .version import __version__
3+
4+
5+
__all__ = ["BasicAuth", "__version__"]

dash_auth/auth.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
from __future__ import absolute_import
22
from abc import ABC, abstractmethod
33

4+
from dash import Dash
5+
46

57
class Auth(ABC):
6-
def __init__(self, app, authorization_hook=None, _overwrite_index=True):
8+
def __init__(self, app: Dash, **obsolete):
9+
"""Auth base class for authentication in Dash.
10+
11+
:param app: Dash app
12+
"""
13+
14+
# Deprecated arguments
15+
if obsolete:
16+
raise TypeError(
17+
f"Auth got unexpected keyword arguments: {list(obsolete)}"
18+
)
19+
720
self.app = app
8-
self._index_view_name = app.config['routes_pathname_prefix']
9-
if _overwrite_index:
10-
self._overwrite_index()
11-
self._protect_views()
12-
self._index_view_name = app.config['routes_pathname_prefix']
13-
self._auth_hooks = [authorization_hook] if authorization_hook else []
14-
15-
def _overwrite_index(self):
16-
original_index = self.app.server.view_functions[self._index_view_name]
17-
18-
self.app.server.view_functions[self._index_view_name] = \
19-
self.index_auth_wrapper(original_index)
20-
21-
def _protect_views(self):
22-
# TODO - allow users to white list in case they add their own views
23-
for view_name, view_method in self.app.server.view_functions.items():
24-
if view_name != self._index_view_name:
25-
self.app.server.view_functions[view_name] = \
26-
self.auth_wrapper(view_method)
21+
self._protect()
22+
23+
def _protect(self):
24+
"""Add a before_request authentication check on all routes.
25+
26+
The authentication check will pass if the request
27+
is authorised by `Auth.is_authorised`
28+
"""
29+
30+
server = self.app.server
31+
32+
@server.before_request
33+
def before_request_auth():
34+
35+
# Check whether the request is authorised
36+
if self.is_authorized():
37+
return None
38+
39+
# Otherwise, ask the user to log in
40+
return self.login_request()
2741

2842
def is_authorized_hook(self, func):
2943
self._auth_hooks.append(func)

dash_auth/basic_auth.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
from .auth import Auth
21
import base64
2+
from typing import Union
33
import flask
4+
from dash import Dash
5+
6+
from .auth import Auth
47

58

69
class BasicAuth(Auth):
7-
def __init__(self, app, username_password_list):
10+
def __init__(
11+
self,
12+
app: Dash,
13+
username_password_list: Union[list, dict],
14+
):
15+
"""Add basic authentication to Dash.
16+
17+
:param app: Dash app
18+
:param username_password_list: username:password list, either as a
19+
list of tuples or a dict
20+
"""
821
Auth.__init__(self, app)
922
self._users = (
1023
username_password_list

dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
dash[testing]>=2
22
requests[security]
33
flake8
4+
flask

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
description='Dash Authorization Package.',
1818
long_description=long_description,
1919
install_requires=[
20-
'dash>=1.1.1'
20+
'dash>=1.1.1',
21+
"flask",
2122
],
2223
python_requires=">=3.6",
2324
include_package_data=True,

tests/test_basic_auth_integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def update_output(new_value):
3333

3434
def test_failed_views(url):
3535
assert requests.get(url).status_code == 401
36-
assert requests.get(url.strip("/") + "/_dash-layout").status_code == 403
36+
assert requests.get(url.strip("/") + "/_dash-layout").status_code == 401
3737

3838
test_failed_views(base_url)
3939

0 commit comments

Comments
 (0)