sanic-security 1.11.6__py3-none-any.whl → 1.16.6__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,42 @@
1
1
  import functools
2
2
  from contextlib import suppress
3
3
 
4
+ from sanic.log import logger
4
5
  from sanic.request import Request
5
6
 
6
7
  from sanic_security.exceptions import (
7
8
  JWTDecodeError,
8
9
  NotFoundError,
9
10
  VerifiedError,
11
+ MaxedOutChallengeError,
10
12
  )
11
13
  from sanic_security.models import (
12
14
  Account,
13
15
  TwoStepSession,
14
16
  CaptchaSession,
15
17
  )
18
+ from sanic_security.utils import get_ip
16
19
 
17
20
  """
18
- An effective, simple, and async security library for the Sanic framework.
19
- Copyright (C) 2020-present Aidan Stewart
20
-
21
- This program is free software: you can redistribute it and/or modify
22
- it under the terms of the GNU Affero General Public License as published
23
- by the Free Software Foundation, either version 3 of the License, or
24
- (at your option) any later version.
25
-
26
- This program is distributed in the hope that it will be useful,
27
- but WITHOUT ANY WARRANTY; without even the implied warranty of
28
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
- GNU Affero General Public License for more details.
30
-
31
- You should have received a copy of the GNU Affero General Public License
32
- along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+ Copyright (c) 2020-present Nicholas Aidan Stewart
22
+
23
+ Permission is hereby granted, free of charge, to any person obtaining a copy
24
+ of this software and associated documentation files (the "Software"), to deal
25
+ in the Software without restriction, including without limitation the rights
26
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27
+ copies of the Software, and to permit persons to whom the Software is
28
+ furnished to do so, subject to the following conditions:
29
+
30
+ The above copyright notice and this permission notice shall be included in all
31
+ copies or substantial portions of the Software.
32
+
33
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39
+ SOFTWARE.
33
40
  """
34
41
 
35
42
 
@@ -58,6 +65,7 @@ async def request_two_step_verification(
58
65
  if request.form.get("email") or not account:
59
66
  account = await Account.get_via_email(request.form.get("email"))
60
67
  two_step_session = await TwoStepSession.new(request, account)
68
+ request.ctx.session = two_step_session
61
69
  return two_step_session
62
70
 
63
71
 
@@ -85,7 +93,16 @@ async def two_step_verification(request: Request) -> TwoStepSession:
85
93
  two_step_session = await TwoStepSession.decode(request)
86
94
  two_step_session.validate()
87
95
  two_step_session.bearer.validate()
88
- await two_step_session.check_code(request, request.form.get("code"))
96
+ try:
97
+ await two_step_session.check_code(request.form.get("code"))
98
+ except MaxedOutChallengeError as e:
99
+ logger.warning(
100
+ f"Client {get_ip(request)} has exceeded maximum two-step session {two_step_session.id} challenge attempts."
101
+ )
102
+ raise e
103
+ logger.info(
104
+ f"Client {get_ip(request)} has completed two-step session {two_step_session.id} challenge."
105
+ )
89
106
  return two_step_session
90
107
 
91
108
 
@@ -117,15 +134,12 @@ def requires_two_step_verification(arg=None):
117
134
  def decorator(func):
118
135
  @functools.wraps(func)
119
136
  async def wrapper(request, *args, **kwargs):
120
- request.ctx.two_step_session = await two_step_verification(request)
137
+ await two_step_verification(request)
121
138
  return await func(request, *args, **kwargs)
122
139
 
123
140
  return wrapper
124
141
 
125
- if callable(arg):
126
- return decorator(arg)
127
- else:
128
- return decorator
142
+ return decorator(arg) if callable(arg) else decorator
129
143
 
130
144
 
131
145
  async def verify_account(request: Request) -> TwoStepSession:
@@ -150,31 +164,24 @@ async def verify_account(request: Request) -> TwoStepSession:
150
164
  """
151
165
  two_step_session = await TwoStepSession.decode(request)
152
166
  if two_step_session.bearer.verified:
153
- raise VerifiedError()
167
+ raise VerifiedError
154
168
  two_step_session.validate()
155
- await two_step_session.check_code(request, request.form.get("code"))
169
+ try:
170
+ await two_step_session.check_code(request.form.get("code"))
171
+ except MaxedOutChallengeError as e:
172
+ logger.warning(
173
+ f"Client {get_ip(request)} has exceeded maximum two-step session {two_step_session.id} challenge attempts "
174
+ "during account verification."
175
+ )
176
+ raise e
156
177
  two_step_session.bearer.verified = True
157
178
  await two_step_session.bearer.save(update_fields=["verified"])
179
+ logger.info(
180
+ f"Client {get_ip(request)} has verified account {two_step_session.bearer.id}."
181
+ )
158
182
  return two_step_session
159
183
 
160
184
 
161
- async def request_captcha(request: Request) -> CaptchaSession:
162
- """
163
- Creates a captcha session and deactivates the client's current captcha session if found.
164
-
165
- Args:
166
- request (Request): Sanic request parameter.
167
-
168
- Returns:
169
- captcha_session
170
- """
171
- with suppress(NotFoundError, JWTDecodeError):
172
- captcha_session = await CaptchaSession.decode(request)
173
- if captcha_session.active:
174
- await captcha_session.deactivate()
175
- return await CaptchaSession.new(request)
176
-
177
-
178
185
  async def captcha(request: Request) -> CaptchaSession:
179
186
  """
180
187
  Validates a captcha challenge attempt.
@@ -196,7 +203,16 @@ async def captcha(request: Request) -> CaptchaSession:
196
203
  """
197
204
  captcha_session = await CaptchaSession.decode(request)
198
205
  captcha_session.validate()
199
- await captcha_session.check_code(request, request.form.get("captcha"))
206
+ try:
207
+ await captcha_session.check_code(request.form.get("captcha"))
208
+ except MaxedOutChallengeError as e:
209
+ logger.warning(
210
+ f"Client {get_ip(request)} has exceeded maximum captcha session {captcha_session.id} challenge attempts."
211
+ )
212
+ raise e
213
+ logger.info(
214
+ f"Client {get_ip(request)} has completed captcha session {captcha_session.id} challenge."
215
+ )
200
216
  return captcha_session
201
217
 
202
218
 
@@ -225,12 +241,9 @@ def requires_captcha(arg=None):
225
241
  def decorator(func):
226
242
  @functools.wraps(func)
227
243
  async def wrapper(request, *args, **kwargs):
228
- request.ctx.captcha_session = await captcha(request)
244
+ await captcha(request)
229
245
  return await func(request, *args, **kwargs)
230
246
 
231
247
  return wrapper
232
248
 
233
- if callable(arg):
234
- return decorator(arg)
235
- else:
236
- return decorator
249
+ return decorator(arg) if callable(arg) else decorator
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Nicholas Aidan Stewart
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.