Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/decorators.py: 43%
49 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-07 19:28 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-07 19:28 +0000
1import typing as t
3from viur.core.module import Method
5__all__ = [
6 "access",
7 "exposed",
8 "force_post",
9 "force_ssl",
10 "internal_exposed",
11 "skey",
12 "cors",
13]
16def exposed(func: t.Callable) -> Method:
17 """
18 Decorator, which marks a function as exposed.
20 Only exposed functions are callable by http-requests.
21 Can optionally receive a dict of language->translated name to make that function
22 available under different names
23 """
24 if isinstance(func, dict): 24 ↛ 25line 24 didn't jump to line 25 because the condition on line 24 was never true
25 seo_language_map = func
27 # We received said dictionary:
28 def expose_with_translations(func: t.Callable) -> Method:
29 func = Method.ensure(func)
30 func.exposed = True
31 func.seo_language_map = seo_language_map
32 return func
34 return expose_with_translations
36 func = Method.ensure(func)
37 func.exposed = True
38 return func
41def internal_exposed(func: t.Callable) -> Method:
42 """
43 Decorator, which marks a function as internal exposed.
44 """
45 func = Method.ensure(func)
46 func.exposed = False
47 return func
50def force_ssl(func: t.Callable) -> Method:
51 """
52 Decorator, which enforces usage of an encrypted channel for a given resource.
53 Has no effect on development-servers.
54 """
55 func = Method.ensure(func)
56 func.ssl = True
57 return func
60def force_post(func: t.Callable) -> Method:
61 """
62 Decorator, which enforces usage of a http post request.
63 """
64 func = Method.ensure(func)
65 func.methods = ("POST",)
66 return func
69def access(
70 *access: str | list[str] | tuple[str] | set[str] | t.Callable,
71 offer_login: bool | str = False,
72 message: str | None = None,
73) -> t.Callable:
74 """
75 Decorator, which performs an authentication and authorization check primarily based on the current user's access,
76 which is defined via the `UserSkel.access`-bone. Additionally, a callable for individual access checking can be
77 provided.
79 In case no user is logged in, the decorator enforces to raise an HTTP error 401 - Unauthorized in case no user is
80 logged in, otherwise it returns an HTTP error 403 - Forbidden when the specified access parameters prohibit to call
81 the decorated method.
83 :params access: Access configuration, either names of access rights or a callable for verification.
84 :params offer_login: Offers a way to login; Either set it to True, to automatically redirect to /user/login,
85 or set it to any other URL.
86 :params message: A custom message to be printed when access is denied or unauthorized.
88 To check on authenticated users with the access "root" or ("admin" and "file-edit") or "maintainer" use the
89 decorator like this:
91 .. code-block:: python
92 from viur.core.decorators import access
93 @access("root", ["admin", "file-edit"], ["maintainer"])
94 def my_method(self):
95 return "You're allowed!"
97 Furthermore, instead of a list/tuple/set/str, a callable can be provided which performs custom access checking,
98 and directly is checked on True for access grant.
99 """
100 access_config = locals()
102 def decorator(func):
103 meth = Method.ensure(func)
104 meth.access = access_config
105 return meth
107 return decorator
110def skey(
111 func: t.Callable = None,
112 *,
113 allow_empty: bool | list[str] | tuple[str] | t.Callable = False,
114 forward_payload: str | None = None,
115 message: str = None,
116 name: str = "skey",
117 validate: t.Callable | None = None,
118 **extra_kwargs: dict,
119) -> Method:
120 """
121 Decorator, which configures an exposed method for requiring a CSRF-security-key.
122 The decorator enforces a raise of HTTP error 406 - Precondition failed in case the security-key is not provided
123 or became invalid.
125 :param allow_empty: Allows to call the method without a security-key when no other parameters where provided.
126 This can also be a tuple or list of keys which are being ignored, or a callable taking args and kwargs, and
127 programmatically decide whether security-key is required or not.
128 :param forward_payload: Forwards the extracted payload of the security-key to the method under the key specified
129 here as a value in kwargs.
130 :param message: Allows to specify a custom error message in case a HTTP 406 is raised.
131 :param name: Defaults to "skey", but allows also for another name passed to the method.
132 :param validate: Allows to specify a Callable used to further evaluate the payload of the security-key.
133 Security-keys can be equipped with further data, see the securitykey-module for details.
134 :param extra_kwargs: Any provided extra_kwargs are being passed to securitykey.validate as kwargs.
135 """
136 skey_config = locals()
138 def decorator(func):
139 meth = Method.ensure(func)
140 meth.skey = skey_config
141 return meth
143 if func is None: 143 ↛ 146line 143 didn't jump to line 146 because the condition on line 143 was always true
144 return decorator
146 return decorator(func)
149def cors(
150 allow_headers: t.Iterable[str] = (),
151) -> t.Callable:
152 """Add additional CORS setting for a decorated :meth:`exposed` method."""
153 def decorator(func):
154 meth = Method.ensure(func)
155 meth.cors_allow_headers = allow_headers
156 return meth
158 return decorator