prune_captcha 1.10.0__py3-none-any.whl → 1.11.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.
captcha_prune/urls.py CHANGED
@@ -5,6 +5,6 @@ from captcha_prune.views import create_captcha_view, verify_captcha_view
5
5
  app_name = "captcha"
6
6
 
7
7
  urlpatterns = [
8
- path("", create_captcha_view, name="create-captcha"),
9
- path("<str:uuid>/", verify_captcha_view, name="verify-captcha"),
8
+ path("create/", create_captcha_view, name="create-captcha"),
9
+ path("verify/", verify_captcha_view, name="verify-captcha"),
10
10
  ]
captcha_prune/views.py CHANGED
@@ -1,4 +1,9 @@
1
- from django.http import HttpRequest, HttpResponse, JsonResponse
1
+ import os
2
+ import random
3
+
4
+ from django.conf import settings
5
+ from django.contrib import messages
6
+ from django.http import HttpRequest
2
7
  from django.shortcuts import get_object_or_404
3
8
  from django.views.decorators.csrf import csrf_exempt
4
9
  from django.views.decorators.http import require_GET, require_POST
@@ -10,36 +15,49 @@ from commons.decorators import use_payload
10
15
 
11
16
  @require_POST
12
17
  @csrf_exempt
13
- def create_captcha_view(request: HttpRequest) -> HttpResponse:
18
+ def create_captcha_view(request: HttpRequest) -> dict:
14
19
  captcha = Captcha.objects.create()
15
- return JsonResponse(
16
- status=201,
17
- data={
18
- "uuid": str(captcha.uuid),
19
- "width": captcha.width,
20
- "height": captcha.height,
21
- "piece_width": captcha.piece_width,
22
- "piece_height": captcha.piece_height,
23
- "pos_x_solution": captcha.pos_x_solution,
24
- "pos_y_solution": captcha.pos_y_solution,
25
- "piece_pos_x": captcha.piece_pos_x,
26
- "piece_pos_y": captcha.piece_pos_y,
27
- },
28
- )
20
+ request.session["puzzle_uuid"] = captcha.uuid
21
+
22
+ _, _, puzzle_images_path = settings.PUZZLE_IMAGE_STATIC_PATH.rpartition("static/")
23
+ puzzle_images = [
24
+ f
25
+ for f in os.listdir(settings.PUZZLE_IMAGE_STATIC_PATH)
26
+ if f.lower().endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
27
+ ]
28
+ selected_image = random.choice(puzzle_images)
29
+
30
+ return {
31
+ "uuid": captcha.uuid,
32
+ "width": captcha.width,
33
+ "height": captcha.height,
34
+ "piece_width": captcha.piece_width,
35
+ "piece_height": captcha.piece_height,
36
+ "pos_x_solution": captcha.pos_x_solution,
37
+ "pos_y_solution": captcha.pos_y_solution,
38
+ "piece_pos_x": captcha.piece_pos_x,
39
+ "piece_pos_y": captcha.piece_pos_y,
40
+ "image": f"{puzzle_images_path}{selected_image}",
41
+ }
29
42
 
30
43
 
31
44
  @require_GET
32
45
  @use_payload(PuzzleAnswerPayload)
33
- def verify_captcha_view(
34
- request: HttpRequest, uuid: str, payload: PuzzleAnswerPayload
35
- ) -> HttpResponse:
36
- captcha = get_object_or_404(Captcha, uuid=uuid)
46
+ def verify_captcha_view(request: HttpRequest, payload: PuzzleAnswerPayload) -> bool:
37
47
  pos_x_answer = payload.pos_x_answer
38
48
  pos_y_answer = payload.pos_y_answer
49
+
50
+ puzzle_uuid = request.session.get("puzzle_uuid")
51
+ if not puzzle_uuid:
52
+ messages.error(request, "La session a expiré.")
53
+ return False
54
+ captcha = get_object_or_404(Captcha, uuid=puzzle_uuid)
55
+
39
56
  if (
40
57
  abs(captcha.pos_x_solution - pos_x_answer) <= captcha.precision
41
58
  and abs(captcha.pos_y_solution - pos_y_answer) <= captcha.precision
42
59
  ):
43
- return HttpResponse(status=304)
60
+ return True
44
61
  else:
45
- return HttpResponse(status=401)
62
+ messages.error(request, "Captcha incorrect. Veuillez réessayer.")
63
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prune_captcha
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: A tool to protect formulaire from spam.
5
5
  Author-email: Arnout <bastien@prune.sh>
6
6
  Project-URL: Made_by, https://prune.sh/
@@ -68,14 +68,14 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
68
68
 
69
69
  - GET request (form display)
70
70
 
71
- - Use create_and_get_puzzle to generate the captcha data:
71
+ - Use create_captcha_view to generate the captcha data:
72
72
 
73
73
  ```python
74
- from captcha_prune.utils import create_and_get_puzzle
74
+ from captcha_prune.views import create_captcha_view
75
75
  ```
76
76
 
77
77
  ```python
78
- puzzle = create_and_get_puzzle(request)
78
+ puzzle = create_captcha_view(request)
79
79
  ```
80
80
 
81
81
  - Passes the data into the context under the puzzle variable:
@@ -96,25 +96,23 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
96
96
 
97
97
  - POST request (form submission)
98
98
 
99
- - Use verify_captcha to validate the captcha:
99
+ - Use verify_captcha_view to validate the captcha:
100
100
 
101
101
  ```python
102
- from captcha_prune.utils import verify_captcha
102
+ from captcha_prune.views import verify_captcha_view
103
103
  ```
104
104
 
105
105
  ```python
106
- response = verify_captcha(
107
- request, redirect("/"), redirect("website:contact-page"), form
108
- )
106
+ response = verify_captcha_view(request)
109
107
  ```
110
108
 
111
- - None if the captcha is correct.
109
+ - True if the captcha is correct, else False.
112
110
 
113
111
  - Redirects in case of expired session or incorrect captcha:
114
112
 
115
113
  ```python
116
- if response is not None:
117
- return response
114
+ if response is False:
115
+ return redirect("website:contact-page")
118
116
  ```
119
117
 
120
118
  ### Example
@@ -124,18 +122,16 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
124
122
  if request.method == "POST":
125
123
  form = ContactForm(request.POST)
126
124
  if form.is_valid():
127
- response = verify_captcha(
128
- request, redirect("/"), redirect("website:contact-page"), form
129
- )
130
- if response is not None:
131
- return response
125
+ response = verify_captcha_view(request)
126
+ if response is False:
127
+ return redirect("website:contact-page")
132
128
  messages.success(request, "Formulaire soumis avec succès.")
133
129
  return redirect("/")
134
130
  else:
135
- puzzle = create_and_get_puzzle(request)
131
+ puzzle = create_captcha_view(request)
136
132
  else:
137
133
  form = ContactForm()
138
- puzzle = create_and_get_puzzle(request)
134
+ puzzle = create_captcha_view(request)
139
135
  return render(
140
136
  request,
141
137
  "website/pages/contact/page.html",
@@ -147,6 +143,7 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
147
143
 
148
144
  | Version | Date | Notes |
149
145
  | ------- | ---------- | ---------------------------------- |
146
+ | 1.11.0 | 2025-05-21 | removed utils |
150
147
  | 1.10.0 | 2025-05-20 | fix documentation, removed ... |
151
148
  | 1.9.0 | 2025-05-20 | puzzle images path fixed |
152
149
  | 1.8.0 | 2025-05-20 | added migrations |
@@ -4,9 +4,8 @@ captcha_prune/asgi.py,sha256=wi2rRLFltXOZyve8mAB_E8udaFyONOf5N42WrWIQX8M,403
4
4
  captcha_prune/models.py,sha256=O8nxhVfPat3oaDGZzd88kQHkGayQIWZ_dOO9uu1R6P4,1240
5
5
  captcha_prune/payloads.py,sha256=PwR48xKg9YEgGgoJmGe7ywwFPCqM4gumomyFunn-Ems,115
6
6
  captcha_prune/settings.py,sha256=YEZHEjYYYK-cH3gSLIR3a7g4Bypnuf_5eMTTcm719CE,3395
7
- captcha_prune/urls.py,sha256=DeCJTjJA0krW27KoHafvh3GBRr7fJ80E2lVHEWYSsW4,271
8
- captcha_prune/utils.py,sha256=3AYKlLxLUdGf9SBGg5Mgzym7LlxBpDhYv4ITFFZ0nAc,2415
9
- captcha_prune/views.py,sha256=BzjQfXLwYx2YSWODrnGpokqU8XeC-9QORivzWKxYofY,1539
7
+ captcha_prune/urls.py,sha256=0G1O0CO53iBW_QT5vlvyN1UzsWxxZiiooqApx6f3MeU,274
8
+ captcha_prune/views.py,sha256=Y4F3zhq5uYpPuHs-0eI8dpn_-02Vbqw-JURK3fTE7tg,2095
10
9
  captcha_prune/wsgi.py,sha256=fukA_iiCT4FFzcJ0QX2Zr_HNddqdq-ln3ien2UWcCTA,403
11
10
  captcha_prune/migrations/0001_initial.py,sha256=QMTaeSIxPociSrV4r89zfsbxMwMu4qpL16LT3yPgcaw,831
12
11
  captcha_prune/migrations/0002_remove_captcha_created_at_remove_captcha_pos_x_and_more.py,sha256=xyyE_G3t8kJqFvKuNWFRTyTyyD1kzvfeSgbkTQ9DO8A,2099
@@ -17,7 +16,7 @@ commons/decorators.py,sha256=nU_3SyxENcWDdxo03VyNYEvoevplC6ig7BFogY8apNs,1306
17
16
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
17
  tests/captcha_prune/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
18
  tests/captcha_prune/test_views.py,sha256=h-Mrdiprh71s7kIJPYAt6xv4G_S7vp51WCpAzy4CZ6U,2364
20
- prune_captcha-1.10.0.dist-info/METADATA,sha256=DVW1Kulk4o16TNlXHBkAxovoGG2XbyS2G9ywg4P-34g,4311
21
- prune_captcha-1.10.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
22
- prune_captcha-1.10.0.dist-info/top_level.txt,sha256=EB4h0WF_YGF7kAWB0XtqmuloOhkL5pC71CzgEVYam7w,28
23
- prune_captcha-1.10.0.dist-info/RECORD,,
19
+ prune_captcha-1.11.0.dist-info/METADATA,sha256=moZ4qiAgMV2a0wkTqUj8EPkISZgck95t6n_hvE6cVFQ,4259
20
+ prune_captcha-1.11.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
21
+ prune_captcha-1.11.0.dist-info/top_level.txt,sha256=EB4h0WF_YGF7kAWB0XtqmuloOhkL5pC71CzgEVYam7w,28
22
+ prune_captcha-1.11.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
captcha_prune/utils.py DELETED
@@ -1,68 +0,0 @@
1
- import os
2
- import random
3
- from urllib.parse import urlencode
4
-
5
- import requests
6
- from django.conf import settings
7
- from django.contrib import messages
8
- from django.http import HttpRequest, HttpResponse
9
- from django.urls import reverse
10
-
11
-
12
- def create_and_get_puzzle(request: HttpRequest) -> HttpResponse | dict:
13
- _, _, puzzle_images_path = settings.PUZZLE_IMAGE_STATIC_PATH.rpartition("static/")
14
- try:
15
- response = requests.post(
16
- request.build_absolute_uri(reverse("captcha:create-captcha"))
17
- ).json()
18
- except requests.RequestException:
19
- return HttpResponse(status=502)
20
- request.session["puzzle_uuid"] = response["uuid"]
21
- puzzle_images = [
22
- f
23
- for f in os.listdir(settings.PUZZLE_IMAGE_STATIC_PATH)
24
- if f.lower().endswith((".jpg", ".jpeg", ".png", ".gif"))
25
- ]
26
- selected_image = random.choice(puzzle_images)
27
- return {
28
- "uuid": response["uuid"],
29
- "width": response["width"],
30
- "height": response["height"],
31
- "piece_width": response["piece_width"],
32
- "piece_height": response["piece_height"],
33
- "pos_x_solution": response["pos_x_solution"],
34
- "pos_y_solution": response["pos_y_solution"],
35
- "piece_pos_x": response["piece_pos_x"],
36
- "piece_pos_y": response["piece_pos_y"],
37
- "image": f"{puzzle_images_path}{selected_image}",
38
- }
39
-
40
-
41
- def verify_captcha(
42
- request: HttpRequest,
43
- session_expired: HttpResponse,
44
- incorrect_captcha: HttpResponse,
45
- form,
46
- ) -> HttpResponse | None:
47
- puzzle_uuid = request.session.get("puzzle_uuid")
48
- if not puzzle_uuid:
49
- messages.error(request, "La session a expiré.")
50
- return session_expired
51
- try:
52
- base_url = request.build_absolute_uri(
53
- reverse("captcha:verify-captcha", kwargs={"uuid": puzzle_uuid}),
54
- )
55
- data = {
56
- "pos_x_answer": request.POST.get("pos_x_answer"),
57
- "pos_y_answer": request.POST.get("pos_y_answer"),
58
- }
59
- query_string = urlencode(data)
60
- full_url = f"{base_url}?{query_string}"
61
- response = requests.get(full_url)
62
- if response.status_code == 401:
63
- messages.error(request, "Captcha incorrect. Veuillez réessayer.")
64
- return incorrect_captcha
65
- del form.fields["pos_x_answer"]
66
- del form.fields["pos_y_answer"]
67
- except requests.RequestException:
68
- return HttpResponse(status=502)