Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/decorators.py: 47%

43 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-16 22:16 +0000

1import typing as t 

2from viur.core.module import Method 

3 

4__all__ = [ 

5 "access", 

6 "exposed", 

7 "force_post", 

8 "force_ssl", 

9 "internal_exposed", 

10 "skey", 

11] 

12 

13 

14def exposed(func: t.Callable) -> Method: 

15 """ 

16 Decorator, which marks a function as exposed. 

17 

18 Only exposed functions are callable by http-requests. 

19 Can optionally receive a dict of language->translated name to make that function 

20 available under different names 

21 """ 

22 if isinstance(func, dict): 22 ↛ 23line 22 didn't jump to line 23 because the condition on line 22 was never true

23 seo_language_map = func 

24 

25 # We received said dictionary: 

26 def expose_with_translations(func: t.Callable) -> Method: 

27 func = Method.ensure(func) 

28 func.exposed = True 

29 func.seo_language_map = seo_language_map 

30 return func 

31 

32 return expose_with_translations 

33 

34 func = Method.ensure(func) 

35 func.exposed = True 

36 return func 

37 

38 

39def internal_exposed(func: t.Callable) -> Method: 

40 """ 

41 Decorator, which marks a function as internal exposed. 

42 """ 

43 func = Method.ensure(func) 

44 func.exposed = False 

45 return func 

46 

47 

48def force_ssl(func: t.Callable) -> Method: 

49 """ 

50 Decorator, which enforces usage of an encrypted channel for a given resource. 

51 Has no effect on development-servers. 

52 """ 

53 func = Method.ensure(func) 

54 func.ssl = True 

55 return func 

56 

57 

58def force_post(func: t.Callable) -> Method: 

59 """ 

60 Decorator, which enforces usage of a http post request. 

61 """ 

62 func = Method.ensure(func) 

63 func.methods = ("POST",) 

64 return func 

65 

66 

67def access( 

68 *access: str | list[str] | tuple[str] | set[str] | t.Callable, 

69 offer_login: bool | str = False, 

70 message: str | None = None, 

71) -> t.Callable: 

72 """ 

73 Decorator, which performs an authentication and authorization check primarily based on the current user's access, 

74 which is defined via the `UserSkel.access`-bone. Additionally, a callable for individual access checking can be 

75 provided. 

76 

77 In case no user is logged in, the decorator enforces to raise an HTTP error 401 - Unauthorized in case no user is 

78 logged in, otherwise it returns an HTTP error 403 - Forbidden when the specified access parameters prohibit to call 

79 the decorated method. 

80 

81 :params access: Access configuration, either names of access rights or a callable for verification. 

82 :params offer_login: Offers a way to login; Either set it to True, to automatically redirect to /user/login, 

83 or set it to any other URL. 

84 :params message: A custom message to be printed when access is denied or unauthorized. 

85 

86 To check on authenticated users with the access "root" or ("admin" and "file-edit") or "maintainer" use the 

87 decorator like this: 

88 

89 .. code-block:: python 

90 from viur.core.decorators import access 

91 @access("root", ["admin", "file-edit"], ["maintainer"]) 

92 def my_method(self): 

93 return "You're allowed!" 

94 

95 Furthermore, instead of a list/tuple/set/str, a callable can be provided which performs custom access checking, 

96 and directly is checked on True for access grant. 

97 """ 

98 access_config = locals() 

99 

100 def decorator(func): 

101 meth = Method.ensure(func) 

102 meth.access = access_config 

103 return meth 

104 

105 return decorator 

106 

107 

108def skey( 

109 func: t.Callable = None, 

110 *, 

111 allow_empty: bool | list[str] | tuple[str] | t.Callable = False, 

112 forward_payload: str | None = None, 

113 message: str = None, 

114 name: str = "skey", 

115 validate: t.Callable | None = None, 

116 **extra_kwargs: dict, 

117) -> Method: 

118 """ 

119 Decorator, which configures an exposed method for requiring a CSRF-security-key. 

120 The decorator enforces a raise of HTTP error 406 - Precondition failed in case the security-key is not provided 

121 or became invalid. 

122 

123 :param allow_empty: Allows to call the method without a security-key when no other parameters where provided. 

124 This can also be a tuple or list of keys which are being ignored, or a callable taking args and kwargs, and 

125 programmatically decide whether security-key is required or not. 

126 :param forward_payload: Forwards the extracted payload of the security-key to the method under the key specified 

127 here as a value in kwargs. 

128 :param message: Allows to specify a custom error message in case a HTTP 406 is raised. 

129 :param name: Defaults to "skey", but allows also for another name passed to the method. 

130 :param validate: Allows to specify a Callable used to further evaluate the payload of the security-key. 

131 Security-keys can be equipped with further data, see the securitykey-module for details. 

132 :param extra_kwargs: Any provided extra_kwargs are being passed to securitykey.validate as kwargs. 

133 """ 

134 skey_config = locals() 

135 

136 def decorator(func): 

137 meth = Method.ensure(func) 

138 meth.skey = skey_config 

139 return meth 

140 

141 if func is None: 141 ↛ 144line 141 didn't jump to line 144 because the condition on line 141 was always true

142 return decorator 

143 

144 return decorator(func)