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.
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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)