RFL.web 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- RFL.web-1.0.0.dist-info/METADATA +23 -0
- RFL.web-1.0.0.dist-info/RECORD +6 -0
- RFL.web-1.0.0.dist-info/WHEEL +5 -0
- RFL.web-1.0.0.dist-info/top_level.txt +1 -0
- rfl/web/__init__.py +0 -0
- rfl/web/tokens.py +147 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: RFL.web
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Rackslab Foundation Library: web package
|
|
5
|
+
Author-email: Rémi Palancher <remi@rackslab.io>
|
|
6
|
+
License: GPLv3+
|
|
7
|
+
Project-URL: Homepage, https://github.com/rackslab/RFL
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/rackslab/RFL/issues
|
|
9
|
+
Keywords: utility,basic
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: System :: Systems Administration
|
|
16
|
+
Requires-Python: >=3.6
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: RFL.permissions
|
|
19
|
+
Requires-Dist: RFL.authentication
|
|
20
|
+
|
|
21
|
+
# RFL: web package
|
|
22
|
+
|
|
23
|
+
Predefined Flask application templates.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
rfl/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
rfl/web/tokens.py,sha256=O2XhrFWRpDDvuNhYpy25zdNN_8taoGw4WcglsPyY9zM,4493
|
|
3
|
+
RFL.web-1.0.0.dist-info/METADATA,sha256=vJX9c_J7B3smDDDJG9wnInMi2AoQwATGhMJ73lPZ15Q,834
|
|
4
|
+
RFL.web-1.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
5
|
+
RFL.web-1.0.0.dist-info/top_level.txt,sha256=EIloiMrXuRz8n0vqkCE6NM9Ze1S2mrbCKBcdtWaM_og,4
|
|
6
|
+
RFL.web-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rfl
|
rfl/web/__init__.py
ADDED
|
File without changes
|
rfl/web/tokens.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Copyright (c) 2023 Rackslab
|
|
2
|
+
#
|
|
3
|
+
# This file is part of RFL.
|
|
4
|
+
#
|
|
5
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import sys
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from flask import request, abort, current_app
|
|
13
|
+
|
|
14
|
+
from rfl.authentication.jwt import JWTManager
|
|
15
|
+
from rfl.authentication.errors import JWTDecodeError, JWTPrivateKeyLoaderError
|
|
16
|
+
from rfl.permissions.rbac import RBACPolicyManager
|
|
17
|
+
from rfl.permissions.errors import (
|
|
18
|
+
RBACPolicyDefinitionLoadError,
|
|
19
|
+
RBACPolicyRolesLoadError,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_token_user(request):
|
|
26
|
+
"""Return the user name as decoded in the token found in request autorization
|
|
27
|
+
headers. Raise a HTTP/403 forbidden error if the token cannot be decoded properly.
|
|
28
|
+
Return None if the authorization header is not found, which is assimilated to
|
|
29
|
+
anonymous user."""
|
|
30
|
+
|
|
31
|
+
request.token = None
|
|
32
|
+
request.user = None
|
|
33
|
+
|
|
34
|
+
auth = request.headers.get("Authorization")
|
|
35
|
+
if auth is None:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
if not auth.startswith("Bearer "):
|
|
39
|
+
logger.warning("Malformed authorization header found in request")
|
|
40
|
+
abort(401, "No valid token provided")
|
|
41
|
+
request.token = auth.split(" ", 1)[1]
|
|
42
|
+
try:
|
|
43
|
+
request.user = current_app.jwt.decode(request.token)
|
|
44
|
+
except JWTDecodeError as err:
|
|
45
|
+
abort(401, str(err))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def check_jwt(view):
|
|
49
|
+
"""Decorator for Flask views functions check for valid authentification JWT
|
|
50
|
+
token and permission in policy."""
|
|
51
|
+
|
|
52
|
+
@wraps(view)
|
|
53
|
+
def wrapped(*args, **kwargs):
|
|
54
|
+
_get_token_user(request)
|
|
55
|
+
return view(*args, **kwargs)
|
|
56
|
+
|
|
57
|
+
return wrapped
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def rbac_action(action):
|
|
61
|
+
"""Decorator for Flask views functions check for valid authentification JWT
|
|
62
|
+
token and permission in policy."""
|
|
63
|
+
|
|
64
|
+
def inner_decorator(view):
|
|
65
|
+
@wraps(view)
|
|
66
|
+
def wrapped(*args, **kwargs):
|
|
67
|
+
_get_token_user(request)
|
|
68
|
+
# verify unauthorized anonymous access
|
|
69
|
+
if request.user is None and (
|
|
70
|
+
not current_app.policy.allow_anonymous
|
|
71
|
+
or not current_app.policy.allowed_anonymous_action(action)
|
|
72
|
+
):
|
|
73
|
+
logger.warning("Unauthorized anonymous access to action %s", action)
|
|
74
|
+
abort(
|
|
75
|
+
403,
|
|
76
|
+
f"Anonymous role is not allowed to perform action {action}",
|
|
77
|
+
)
|
|
78
|
+
# verify real user access
|
|
79
|
+
elif (
|
|
80
|
+
request.user is not None
|
|
81
|
+
and not current_app.policy.allowed_user_action(request.user, action)
|
|
82
|
+
):
|
|
83
|
+
logger.warning(
|
|
84
|
+
"Unauthorized access from user %s to action %s",
|
|
85
|
+
request.user,
|
|
86
|
+
action,
|
|
87
|
+
)
|
|
88
|
+
abort(
|
|
89
|
+
403,
|
|
90
|
+
f"user {request.user} is not allowed to perform action "
|
|
91
|
+
f"{action}",
|
|
92
|
+
)
|
|
93
|
+
return view(*args, **kwargs)
|
|
94
|
+
|
|
95
|
+
return wrapped
|
|
96
|
+
|
|
97
|
+
return inner_decorator
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RFLTokenizedWebApp:
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
audience: str,
|
|
104
|
+
algorithm: str,
|
|
105
|
+
key: Path,
|
|
106
|
+
create: bool = False,
|
|
107
|
+
create_parent: bool = False,
|
|
108
|
+
):
|
|
109
|
+
try:
|
|
110
|
+
self.jwt = JWTManager.key(
|
|
111
|
+
audience=audience,
|
|
112
|
+
algorithm=algorithm,
|
|
113
|
+
path=key,
|
|
114
|
+
create=create,
|
|
115
|
+
create_parent=create_parent,
|
|
116
|
+
)
|
|
117
|
+
except JWTPrivateKeyLoaderError as err:
|
|
118
|
+
logger.critical(f"Error while loading JWT private key {key}: {str(err)}")
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class RFLTokenizedRBACWebApp(RFLTokenizedWebApp):
|
|
123
|
+
def __init__(
|
|
124
|
+
self,
|
|
125
|
+
audience: str,
|
|
126
|
+
algorithm: str,
|
|
127
|
+
key: Path,
|
|
128
|
+
policy: Path,
|
|
129
|
+
roles: Path,
|
|
130
|
+
create: bool = False,
|
|
131
|
+
create_parent: bool = False,
|
|
132
|
+
):
|
|
133
|
+
super().__init__(audience, algorithm, key, create, create_parent)
|
|
134
|
+
try:
|
|
135
|
+
self.policy = RBACPolicyManager.yaml_definition_ini_roles(
|
|
136
|
+
definition=policy, roles=roles
|
|
137
|
+
)
|
|
138
|
+
except RBACPolicyDefinitionLoadError as err:
|
|
139
|
+
logger.critical(
|
|
140
|
+
f"Error while loading RBAC policy definition file {policy}: {str(err)}"
|
|
141
|
+
)
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
except RBACPolicyRolesLoadError as err:
|
|
144
|
+
logger.critical(
|
|
145
|
+
f"Error while loading RBAC roles policy file {roles}: {str(err)}"
|
|
146
|
+
)
|
|
147
|
+
sys.exit(1)
|