prune_captcha 1.10.0__tar.gz → 1.11.0__tar.gz

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.
Files changed (29) hide show
  1. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/PKG-INFO +16 -19
  2. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/README.md +15 -18
  3. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/urls.py +2 -2
  4. prune_captcha-1.11.0/captcha_prune/views.py +63 -0
  5. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/prune_captcha.egg-info/PKG-INFO +16 -19
  6. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/prune_captcha.egg-info/SOURCES.txt +0 -1
  7. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/pyproject.toml +1 -1
  8. prune_captcha-1.10.0/captcha_prune/utils.py +0 -68
  9. prune_captcha-1.10.0/captcha_prune/views.py +0 -45
  10. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/__init__.py +0 -0
  11. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/apps.py +0 -0
  12. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/asgi.py +0 -0
  13. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/migrations/0001_initial.py +0 -0
  14. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/migrations/0002_remove_captcha_created_at_remove_captcha_pos_x_and_more.py +0 -0
  15. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/migrations/0003_captcha_created_at_captcha_updated_at.py +0 -0
  16. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/migrations/__init__.py +0 -0
  17. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/models.py +0 -0
  18. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/payloads.py +0 -0
  19. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/settings.py +0 -0
  20. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/captcha_prune/wsgi.py +0 -0
  21. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/commons/base_model.py +0 -0
  22. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/commons/decorators.py +0 -0
  23. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/prune_captcha.egg-info/dependency_links.txt +0 -0
  24. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/prune_captcha.egg-info/requires.txt +0 -0
  25. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/prune_captcha.egg-info/top_level.txt +0 -0
  26. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/setup.cfg +0 -0
  27. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/tests/__init__.py +0 -0
  28. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/tests/captcha_prune/__init__.py +0 -0
  29. {prune_captcha-1.10.0 → prune_captcha-1.11.0}/tests/captcha_prune/test_views.py +0 -0
@@ -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 |
@@ -50,14 +50,14 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
50
50
 
51
51
  - GET request (form display)
52
52
 
53
- - Use create_and_get_puzzle to generate the captcha data:
53
+ - Use create_captcha_view to generate the captcha data:
54
54
 
55
55
  ```python
56
- from captcha_prune.utils import create_and_get_puzzle
56
+ from captcha_prune.views import create_captcha_view
57
57
  ```
58
58
 
59
59
  ```python
60
- puzzle = create_and_get_puzzle(request)
60
+ puzzle = create_captcha_view(request)
61
61
  ```
62
62
 
63
63
  - Passes the data into the context under the puzzle variable:
@@ -78,25 +78,23 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
78
78
 
79
79
  - POST request (form submission)
80
80
 
81
- - Use verify_captcha to validate the captcha:
81
+ - Use verify_captcha_view to validate the captcha:
82
82
 
83
83
  ```python
84
- from captcha_prune.utils import verify_captcha
84
+ from captcha_prune.views import verify_captcha_view
85
85
  ```
86
86
 
87
87
  ```python
88
- response = verify_captcha(
89
- request, redirect("/"), redirect("website:contact-page"), form
90
- )
88
+ response = verify_captcha_view(request)
91
89
  ```
92
90
 
93
- - None if the captcha is correct.
91
+ - True if the captcha is correct, else False.
94
92
 
95
93
  - Redirects in case of expired session or incorrect captcha:
96
94
 
97
95
  ```python
98
- if response is not None:
99
- return response
96
+ if response is False:
97
+ return redirect("website:contact-page")
100
98
  ```
101
99
 
102
100
  ### Example
@@ -106,18 +104,16 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
106
104
  if request.method == "POST":
107
105
  form = ContactForm(request.POST)
108
106
  if form.is_valid():
109
- response = verify_captcha(
110
- request, redirect("/"), redirect("website:contact-page"), form
111
- )
112
- if response is not None:
113
- return response
107
+ response = verify_captcha_view(request)
108
+ if response is False:
109
+ return redirect("website:contact-page")
114
110
  messages.success(request, "Formulaire soumis avec succès.")
115
111
  return redirect("/")
116
112
  else:
117
- puzzle = create_and_get_puzzle(request)
113
+ puzzle = create_captcha_view(request)
118
114
  else:
119
115
  form = ContactForm()
120
- puzzle = create_and_get_puzzle(request)
116
+ puzzle = create_captcha_view(request)
121
117
  return render(
122
118
  request,
123
119
  "website/pages/contact/page.html",
@@ -129,6 +125,7 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/"
129
125
 
130
126
  | Version | Date | Notes |
131
127
  | ------- | ---------- | ---------------------------------- |
128
+ | 1.11.0 | 2025-05-21 | removed utils |
132
129
  | 1.10.0 | 2025-05-20 | fix documentation, removed ... |
133
130
  | 1.9.0 | 2025-05-20 | puzzle images path fixed |
134
131
  | 1.8.0 | 2025-05-20 | added migrations |
@@ -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
  ]
@@ -0,0 +1,63 @@
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
7
+ from django.shortcuts import get_object_or_404
8
+ from django.views.decorators.csrf import csrf_exempt
9
+ from django.views.decorators.http import require_GET, require_POST
10
+
11
+ from captcha_prune.models import Captcha
12
+ from captcha_prune.payloads import PuzzleAnswerPayload
13
+ from commons.decorators import use_payload
14
+
15
+
16
+ @require_POST
17
+ @csrf_exempt
18
+ def create_captcha_view(request: HttpRequest) -> dict:
19
+ captcha = Captcha.objects.create()
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
+ }
42
+
43
+
44
+ @require_GET
45
+ @use_payload(PuzzleAnswerPayload)
46
+ def verify_captcha_view(request: HttpRequest, payload: PuzzleAnswerPayload) -> bool:
47
+ pos_x_answer = payload.pos_x_answer
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
+
56
+ if (
57
+ abs(captcha.pos_x_solution - pos_x_answer) <= captcha.precision
58
+ and abs(captcha.pos_y_solution - pos_y_answer) <= captcha.precision
59
+ ):
60
+ return True
61
+ else:
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 |
@@ -7,7 +7,6 @@ captcha_prune/models.py
7
7
  captcha_prune/payloads.py
8
8
  captcha_prune/settings.py
9
9
  captcha_prune/urls.py
10
- captcha_prune/utils.py
11
10
  captcha_prune/views.py
12
11
  captcha_prune/wsgi.py
13
12
  captcha_prune/migrations/0001_initial.py
@@ -20,7 +20,7 @@ classifiers = [
20
20
  keywords = ["captcha", "django", "code-quality"]
21
21
  urls = {Made_by = "https://prune.sh/"}
22
22
  name = "prune_captcha"
23
- version = "1.10.0"
23
+ version = "1.11.0"
24
24
  description = "A tool to protect formulaire from spam."
25
25
  readme = "README.md"
26
26
  dependencies = [
@@ -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)
@@ -1,45 +0,0 @@
1
- from django.http import HttpRequest, HttpResponse, JsonResponse
2
- from django.shortcuts import get_object_or_404
3
- from django.views.decorators.csrf import csrf_exempt
4
- from django.views.decorators.http import require_GET, require_POST
5
-
6
- from captcha_prune.models import Captcha
7
- from captcha_prune.payloads import PuzzleAnswerPayload
8
- from commons.decorators import use_payload
9
-
10
-
11
- @require_POST
12
- @csrf_exempt
13
- def create_captcha_view(request: HttpRequest) -> HttpResponse:
14
- 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
- )
29
-
30
-
31
- @require_GET
32
- @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)
37
- pos_x_answer = payload.pos_x_answer
38
- pos_y_answer = payload.pos_y_answer
39
- if (
40
- abs(captcha.pos_x_solution - pos_x_answer) <= captcha.precision
41
- and abs(captcha.pos_y_solution - pos_y_answer) <= captcha.precision
42
- ):
43
- return HttpResponse(status=304)
44
- else:
45
- return HttpResponse(status=401)
File without changes