prune_captcha 1.12.1__py3-none-any.whl → 1.13.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/utils.py +29 -21
- {prune_captcha-1.12.1.dist-info → prune_captcha-1.13.0.dist-info}/METADATA +14 -10
- prune_captcha-1.13.0.dist-info/RECORD +7 -0
- {prune_captcha-1.12.1.dist-info → prune_captcha-1.13.0.dist-info}/top_level.txt +0 -2
- captcha_prune/asgi.py +0 -16
- captcha_prune/migrations/0001_initial.py +0 -29
- captcha_prune/migrations/0002_remove_captcha_created_at_remove_captcha_pos_x_and_more.py +0 -73
- captcha_prune/migrations/0003_captcha_created_at_captcha_updated_at.py +0 -25
- captcha_prune/migrations/__init__.py +0 -0
- captcha_prune/models.py +0 -34
- captcha_prune/payloads.py +0 -6
- captcha_prune/settings.py +0 -130
- captcha_prune/wsgi.py +0 -16
- commons/base_model.py +0 -9
- prune_captcha-1.12.1.dist-info/RECORD +0 -20
- tests/__init__.py +0 -0
- tests/captcha_prune/__init__.py +0 -0
- tests/captcha_prune/test_views.py +0 -67
- {prune_captcha-1.12.1.dist-info → prune_captcha-1.13.0.dist-info}/WHEEL +0 -0
captcha_prune/utils.py
CHANGED
@@ -2,14 +2,16 @@ import os
|
|
2
2
|
import random
|
3
3
|
|
4
4
|
from django.conf import settings
|
5
|
-
from django.
|
5
|
+
from django.http import HttpRequest
|
6
6
|
|
7
|
-
from captcha_prune.models import Captcha
|
8
|
-
|
9
|
-
|
10
|
-
def create_and_get_captcha() -> dict:
|
11
|
-
captcha = Captcha.objects.create()
|
12
7
|
|
8
|
+
def create_and_get_captcha(
|
9
|
+
request, *, width=350, height=200, piece_width=80, piece_height=50, precision=2
|
10
|
+
) -> dict:
|
11
|
+
pos_x_solution = random.randint(0, width - piece_width)
|
12
|
+
pos_y_solution = random.randint(0, height - piece_height)
|
13
|
+
piece_pos_x = random.randint(0, width - piece_width)
|
14
|
+
piece_pos_y = random.randint(0, height - piece_height)
|
13
15
|
_, _, puzzle_images_path = settings.PUZZLE_IMAGE_STATIC_PATH.rpartition("static/")
|
14
16
|
puzzle_images = [
|
15
17
|
f
|
@@ -17,27 +19,33 @@ def create_and_get_captcha() -> dict:
|
|
17
19
|
if f.lower().endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
|
18
20
|
]
|
19
21
|
selected_image = random.choice(puzzle_images)
|
20
|
-
|
22
|
+
request.session["pos_x_solution"] = pos_x_solution
|
23
|
+
request.session["pos_y_solution"] = pos_y_solution
|
24
|
+
request.session["precision"] = precision
|
21
25
|
return {
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"piece_pos_y": captcha.piece_pos_y,
|
26
|
+
"width": width,
|
27
|
+
"height": height,
|
28
|
+
"piece_width": piece_width,
|
29
|
+
"piece_height": piece_height,
|
30
|
+
"pos_x_solution": pos_x_solution,
|
31
|
+
"pos_y_solution": pos_y_solution,
|
32
|
+
"piece_pos_x": piece_pos_x,
|
33
|
+
"piece_pos_y": piece_pos_y,
|
31
34
|
"image": f"{puzzle_images_path}{selected_image}",
|
32
35
|
}
|
33
36
|
|
34
37
|
|
35
|
-
def verify_captcha(
|
36
|
-
|
37
|
-
|
38
|
+
def verify_captcha(request: HttpRequest) -> bool:
|
39
|
+
pos_x_answer = request.POST.get("pos_x_answer")
|
40
|
+
pos_y_answer = request.POST.get("pos_Y_answer")
|
41
|
+
if pos_x_answer is None or pos_y_answer is None:
|
42
|
+
return False
|
43
|
+
pos_x_solution = request.session.get("pos_x_solution")
|
44
|
+
pos_y_solution = request.session.get("pos_y_solution")
|
45
|
+
precision = request.session.get("precision")
|
38
46
|
if (
|
39
|
-
abs(
|
40
|
-
and abs(
|
47
|
+
abs(pos_x_solution - pos_x_answer) <= precision
|
48
|
+
and abs(pos_y_solution - pos_y_answer) <= precision
|
41
49
|
):
|
42
50
|
return True
|
43
51
|
else:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: prune_captcha
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.13.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/
|
@@ -64,6 +64,8 @@ In `settings.py`, set the path to the images used for the puzzle:
|
|
64
64
|
PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/puzzles/"
|
65
65
|
```
|
66
66
|
|
67
|
+
Important: You must import the static files (css, js) present in "captcha_prune/static/".
|
68
|
+
|
67
69
|
### Utilisation
|
68
70
|
|
69
71
|
- GET request (form display)
|
@@ -96,24 +98,26 @@ PUZZLE_IMAGE_STATIC_PATH = "website/static/website/images/puzzles/"
|
|
96
98
|
|
97
99
|
- POST request (form submission)
|
98
100
|
|
99
|
-
|
101
|
+
- Use verify_captcha to validate the captcha:
|
100
102
|
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
```python
|
104
|
+
from captcha_prune.utils import verify_captcha
|
105
|
+
```
|
104
106
|
|
105
|
-
|
106
|
-
|
107
|
-
|
107
|
+
```python
|
108
|
+
response = verify_captcha(request)
|
109
|
+
```
|
108
110
|
|
109
|
-
|
111
|
+
- True if the captcha is correct, else False.
|
110
112
|
|
111
|
-
|
113
|
+
- Redirects in case of incorrect captcha.
|
112
114
|
|
113
115
|
# Available Versions
|
114
116
|
|
115
117
|
| Version | Date | Notes |
|
116
118
|
| ------- | ---------- | ---------------------------------- |
|
119
|
+
| 1.13.0 | 2025-05-21 | use session, add static |
|
120
|
+
| 1.12.2 | 2025-05-21 | doc fixed |
|
117
121
|
| 1.12.1 | 2025-05-21 | doc fixed |
|
118
122
|
| 1.12.0 | 2025-05-21 | removed views, added utils, ... |
|
119
123
|
| 1.11.0 | 2025-05-21 | removed utils |
|
@@ -0,0 +1,7 @@
|
|
1
|
+
captcha_prune/__init__.py,sha256=JerrqDuZCUa8pj_9pEv68rnlPsiXovRP4biKpCqyThs,61
|
2
|
+
captcha_prune/apps.py,sha256=WVAz3WWaPAgM7-Ojd_Cl2KVdcub1n-03qpnRyu2ToTo,422
|
3
|
+
captcha_prune/utils.py,sha256=0wgRNCfmAAugFh5sw3qk-YqvNMiMZgJRtd6QlpRa-D8,1861
|
4
|
+
prune_captcha-1.13.0.dist-info/METADATA,sha256=4z0bIAlKrXm8__Isw5WjZArvgJKZ_1KpQOtSxwmSI40,3707
|
5
|
+
prune_captcha-1.13.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
6
|
+
prune_captcha-1.13.0.dist-info/top_level.txt,sha256=01HnLL5Bu66yMBCvNpNBzN0L6AxTdG1_1YFSJr3dI2w,14
|
7
|
+
prune_captcha-1.13.0.dist-info/RECORD,,
|
captcha_prune/asgi.py
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
ASGI config for captcha_prune project.
|
3
|
-
|
4
|
-
It exposes the ASGI callable as a module-level variable named ``application``.
|
5
|
-
|
6
|
-
For more information on this file, see
|
7
|
-
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
|
8
|
-
"""
|
9
|
-
|
10
|
-
import os
|
11
|
-
|
12
|
-
from django.core.asgi import get_asgi_application
|
13
|
-
|
14
|
-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'captcha_prune.settings')
|
15
|
-
|
16
|
-
application = get_asgi_application()
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# Generated by Django 5.2 on 2025-04-28 10:19
|
2
|
-
|
3
|
-
import uuid
|
4
|
-
from django.db import migrations, models
|
5
|
-
|
6
|
-
|
7
|
-
class Migration(migrations.Migration):
|
8
|
-
|
9
|
-
initial = True
|
10
|
-
|
11
|
-
dependencies = [
|
12
|
-
]
|
13
|
-
|
14
|
-
operations = [
|
15
|
-
migrations.CreateModel(
|
16
|
-
name='Captcha',
|
17
|
-
fields=[
|
18
|
-
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
19
|
-
('created_at', models.DateTimeField(auto_now_add=True)),
|
20
|
-
('updated_at', models.DateTimeField(auto_now=True)),
|
21
|
-
('uuid', models.UUIDField(default=uuid.uuid4, unique=True)),
|
22
|
-
('pos_x', models.IntegerField()),
|
23
|
-
('pos_y', models.IntegerField()),
|
24
|
-
],
|
25
|
-
options={
|
26
|
-
'abstract': False,
|
27
|
-
},
|
28
|
-
),
|
29
|
-
]
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# Generated by Django 5.2 on 2025-04-29 07:55
|
2
|
-
|
3
|
-
from django.db import migrations, models
|
4
|
-
|
5
|
-
|
6
|
-
class Migration(migrations.Migration):
|
7
|
-
dependencies = [
|
8
|
-
("captcha_prune", "0001_initial"),
|
9
|
-
]
|
10
|
-
|
11
|
-
operations = [
|
12
|
-
migrations.RemoveField(
|
13
|
-
model_name="captcha",
|
14
|
-
name="created_at",
|
15
|
-
),
|
16
|
-
migrations.RemoveField(
|
17
|
-
model_name="captcha",
|
18
|
-
name="pos_x",
|
19
|
-
),
|
20
|
-
migrations.RemoveField(
|
21
|
-
model_name="captcha",
|
22
|
-
name="pos_y",
|
23
|
-
),
|
24
|
-
migrations.RemoveField(
|
25
|
-
model_name="captcha",
|
26
|
-
name="updated_at",
|
27
|
-
),
|
28
|
-
migrations.AddField(
|
29
|
-
model_name="captcha",
|
30
|
-
name="height",
|
31
|
-
field=models.IntegerField(default=200),
|
32
|
-
),
|
33
|
-
migrations.AddField(
|
34
|
-
model_name="captcha",
|
35
|
-
name="piece_height",
|
36
|
-
field=models.IntegerField(default=50),
|
37
|
-
),
|
38
|
-
migrations.AddField(
|
39
|
-
model_name="captcha",
|
40
|
-
name="piece_pos_x",
|
41
|
-
field=models.IntegerField(blank=True, null=True),
|
42
|
-
),
|
43
|
-
migrations.AddField(
|
44
|
-
model_name="captcha",
|
45
|
-
name="piece_pos_y",
|
46
|
-
field=models.IntegerField(blank=True, null=True),
|
47
|
-
),
|
48
|
-
migrations.AddField(
|
49
|
-
model_name="captcha",
|
50
|
-
name="piece_width",
|
51
|
-
field=models.IntegerField(default=80),
|
52
|
-
),
|
53
|
-
migrations.AddField(
|
54
|
-
model_name="captcha",
|
55
|
-
name="pos_x_solution",
|
56
|
-
field=models.IntegerField(blank=True, null=True),
|
57
|
-
),
|
58
|
-
migrations.AddField(
|
59
|
-
model_name="captcha",
|
60
|
-
name="pos_y_solution",
|
61
|
-
field=models.IntegerField(blank=True, null=True),
|
62
|
-
),
|
63
|
-
migrations.AddField(
|
64
|
-
model_name="captcha",
|
65
|
-
name="precision",
|
66
|
-
field=models.IntegerField(default=2),
|
67
|
-
),
|
68
|
-
migrations.AddField(
|
69
|
-
model_name="captcha",
|
70
|
-
name="width",
|
71
|
-
field=models.IntegerField(default=350),
|
72
|
-
),
|
73
|
-
]
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# Generated by Django 5.2 on 2025-05-20 09:12
|
2
|
-
|
3
|
-
import django.utils.timezone
|
4
|
-
from django.db import migrations, models
|
5
|
-
|
6
|
-
|
7
|
-
class Migration(migrations.Migration):
|
8
|
-
|
9
|
-
dependencies = [
|
10
|
-
('captcha_prune', '0002_remove_captcha_created_at_remove_captcha_pos_x_and_more'),
|
11
|
-
]
|
12
|
-
|
13
|
-
operations = [
|
14
|
-
migrations.AddField(
|
15
|
-
model_name='captcha',
|
16
|
-
name='created_at',
|
17
|
-
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
18
|
-
preserve_default=False,
|
19
|
-
),
|
20
|
-
migrations.AddField(
|
21
|
-
model_name='captcha',
|
22
|
-
name='updated_at',
|
23
|
-
field=models.DateTimeField(auto_now=True),
|
24
|
-
),
|
25
|
-
]
|
File without changes
|
captcha_prune/models.py
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
import random
|
2
|
-
import uuid
|
3
|
-
|
4
|
-
from django.db import models
|
5
|
-
|
6
|
-
from commons.base_model import BaseModel
|
7
|
-
|
8
|
-
|
9
|
-
class Captcha(BaseModel):
|
10
|
-
uuid = models.UUIDField(default=uuid.uuid4, unique=True)
|
11
|
-
width = models.IntegerField(default=350)
|
12
|
-
height = models.IntegerField(default=200)
|
13
|
-
pos_x_solution = models.IntegerField(null=True, blank=True)
|
14
|
-
pos_y_solution = models.IntegerField(null=True, blank=True)
|
15
|
-
piece_pos_x = models.IntegerField(null=True, blank=True)
|
16
|
-
piece_pos_y = models.IntegerField(null=True, blank=True)
|
17
|
-
piece_width = models.IntegerField(default=80)
|
18
|
-
piece_height = models.IntegerField(default=50)
|
19
|
-
precision = models.IntegerField(default=2)
|
20
|
-
|
21
|
-
def save(self, *args, **kwargs):
|
22
|
-
if self.pos_x_solution is None:
|
23
|
-
self.pos_x_solution = random.randint(0, self.width - self.piece_width)
|
24
|
-
|
25
|
-
if self.pos_y_solution is None:
|
26
|
-
self.pos_y_solution = random.randint(0, self.height - self.piece_height)
|
27
|
-
|
28
|
-
if self.piece_pos_x is None:
|
29
|
-
self.piece_pos_x = random.randint(0, self.width - self.piece_width)
|
30
|
-
|
31
|
-
if self.piece_pos_y is None:
|
32
|
-
self.piece_pos_y = random.randint(0, self.height - self.piece_height)
|
33
|
-
|
34
|
-
super().save(*args, **kwargs)
|
captcha_prune/payloads.py
DELETED
captcha_prune/settings.py
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
|
3
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
4
|
-
|
5
|
-
|
6
|
-
class EnvSettings(BaseSettings):
|
7
|
-
model_config = SettingsConfigDict(env_file=".env", extra="allow")
|
8
|
-
|
9
|
-
postgres_db: str
|
10
|
-
postgres_user: str
|
11
|
-
postgres_password: str
|
12
|
-
postgres_host: str
|
13
|
-
postgres_port: str
|
14
|
-
|
15
|
-
|
16
|
-
env_settings = EnvSettings()
|
17
|
-
|
18
|
-
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
19
|
-
BASE_DIR = Path(__file__).resolve().parent.parent
|
20
|
-
|
21
|
-
|
22
|
-
# Quick-start development settings - unsuitable for production
|
23
|
-
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
24
|
-
|
25
|
-
# SECURITY WARNING: keep the secret key used in production secret!
|
26
|
-
SECRET_KEY = "django-insecure-dj-w%-kno*r5w=e_&l1sl3)9kl%m25(3ikoc=z_%p5-0d5472%"
|
27
|
-
|
28
|
-
# SECURITY WARNING: don't run with debug turned on in production!
|
29
|
-
DEBUG = True
|
30
|
-
|
31
|
-
ALLOWED_HOSTS = []
|
32
|
-
|
33
|
-
|
34
|
-
# Application definition
|
35
|
-
|
36
|
-
INSTALLED_APPS = [
|
37
|
-
"django.contrib.admin",
|
38
|
-
"django.contrib.auth",
|
39
|
-
"django.contrib.contenttypes",
|
40
|
-
"django.contrib.sessions",
|
41
|
-
"django.contrib.messages",
|
42
|
-
"django.contrib.staticfiles",
|
43
|
-
"captcha_prune",
|
44
|
-
]
|
45
|
-
|
46
|
-
MIDDLEWARE = [
|
47
|
-
"django.middleware.security.SecurityMiddleware",
|
48
|
-
"django.contrib.sessions.middleware.SessionMiddleware",
|
49
|
-
"django.middleware.common.CommonMiddleware",
|
50
|
-
"django.middleware.csrf.CsrfViewMiddleware",
|
51
|
-
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
52
|
-
"django.contrib.messages.middleware.MessageMiddleware",
|
53
|
-
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
54
|
-
]
|
55
|
-
|
56
|
-
ROOT_URLCONF = "captcha_prune.urls"
|
57
|
-
|
58
|
-
TEMPLATES = [
|
59
|
-
{
|
60
|
-
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
61
|
-
"DIRS": [],
|
62
|
-
"APP_DIRS": True,
|
63
|
-
"OPTIONS": {
|
64
|
-
"context_processors": [
|
65
|
-
"django.template.context_processors.request",
|
66
|
-
"django.contrib.auth.context_processors.auth",
|
67
|
-
"django.contrib.messages.context_processors.messages",
|
68
|
-
],
|
69
|
-
},
|
70
|
-
},
|
71
|
-
]
|
72
|
-
|
73
|
-
WSGI_APPLICATION = "captcha_prune.wsgi.application"
|
74
|
-
|
75
|
-
|
76
|
-
# Database
|
77
|
-
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
78
|
-
|
79
|
-
DATABASES = {
|
80
|
-
"default": {
|
81
|
-
"ENGINE": "django.db.backends.postgresql",
|
82
|
-
"NAME": env_settings.postgres_db,
|
83
|
-
"USER": env_settings.postgres_user,
|
84
|
-
"PASSWORD": env_settings.postgres_password,
|
85
|
-
"HOST": env_settings.postgres_host,
|
86
|
-
"PORT": env_settings.postgres_port,
|
87
|
-
}
|
88
|
-
}
|
89
|
-
|
90
|
-
|
91
|
-
# Password validation
|
92
|
-
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
93
|
-
|
94
|
-
AUTH_PASSWORD_VALIDATORS = [
|
95
|
-
{
|
96
|
-
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
97
|
-
},
|
98
|
-
{
|
99
|
-
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
100
|
-
},
|
101
|
-
{
|
102
|
-
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
103
|
-
},
|
104
|
-
{
|
105
|
-
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
106
|
-
},
|
107
|
-
]
|
108
|
-
|
109
|
-
|
110
|
-
# Internationalization
|
111
|
-
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
112
|
-
|
113
|
-
LANGUAGE_CODE = "en-us"
|
114
|
-
|
115
|
-
TIME_ZONE = "UTC"
|
116
|
-
|
117
|
-
USE_I18N = True
|
118
|
-
|
119
|
-
USE_TZ = True
|
120
|
-
|
121
|
-
|
122
|
-
# Static files (CSS, JavaScript, Images)
|
123
|
-
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
124
|
-
|
125
|
-
STATIC_URL = "static/"
|
126
|
-
|
127
|
-
# Default primary key field type
|
128
|
-
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
129
|
-
|
130
|
-
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
captcha_prune/wsgi.py
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
WSGI config for captcha_prune project.
|
3
|
-
|
4
|
-
It exposes the WSGI callable as a module-level variable named ``application``.
|
5
|
-
|
6
|
-
For more information on this file, see
|
7
|
-
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
|
8
|
-
"""
|
9
|
-
|
10
|
-
import os
|
11
|
-
|
12
|
-
from django.core.wsgi import get_wsgi_application
|
13
|
-
|
14
|
-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'captcha_prune.settings')
|
15
|
-
|
16
|
-
application = get_wsgi_application()
|
commons/base_model.py
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
captcha_prune/__init__.py,sha256=JerrqDuZCUa8pj_9pEv68rnlPsiXovRP4biKpCqyThs,61
|
2
|
-
captcha_prune/apps.py,sha256=WVAz3WWaPAgM7-Ojd_Cl2KVdcub1n-03qpnRyu2ToTo,422
|
3
|
-
captcha_prune/asgi.py,sha256=wi2rRLFltXOZyve8mAB_E8udaFyONOf5N42WrWIQX8M,403
|
4
|
-
captcha_prune/models.py,sha256=O8nxhVfPat3oaDGZzd88kQHkGayQIWZ_dOO9uu1R6P4,1240
|
5
|
-
captcha_prune/payloads.py,sha256=PwR48xKg9YEgGgoJmGe7ywwFPCqM4gumomyFunn-Ems,115
|
6
|
-
captcha_prune/settings.py,sha256=YEZHEjYYYK-cH3gSLIR3a7g4Bypnuf_5eMTTcm719CE,3395
|
7
|
-
captcha_prune/utils.py,sha256=bEOMAA4jatS9eAgHEHpq0qNHjkCrywDGHAn81JPwbuo,1352
|
8
|
-
captcha_prune/wsgi.py,sha256=fukA_iiCT4FFzcJ0QX2Zr_HNddqdq-ln3ien2UWcCTA,403
|
9
|
-
captcha_prune/migrations/0001_initial.py,sha256=QMTaeSIxPociSrV4r89zfsbxMwMu4qpL16LT3yPgcaw,831
|
10
|
-
captcha_prune/migrations/0002_remove_captcha_created_at_remove_captcha_pos_x_and_more.py,sha256=xyyE_G3t8kJqFvKuNWFRTyTyyD1kzvfeSgbkTQ9DO8A,2099
|
11
|
-
captcha_prune/migrations/0003_captcha_created_at_captcha_updated_at.py,sha256=qT9cTDe3coiWXYbYfBlK4iEYg71E9B2FGuo74Wy_gbA,698
|
12
|
-
captcha_prune/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
commons/base_model.py,sha256=q1Q7lgtvN_t6ujhEiSsNNXb_ay0qY6FbfK0Zm2Hdgp0,213
|
14
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
tests/captcha_prune/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
tests/captcha_prune/test_views.py,sha256=h-Mrdiprh71s7kIJPYAt6xv4G_S7vp51WCpAzy4CZ6U,2364
|
17
|
-
prune_captcha-1.12.1.dist-info/METADATA,sha256=oZ7iPR5MtlgIcFcwYhH_WvLJVJyTlm4Yqdwpfq9DaYM,3529
|
18
|
-
prune_captcha-1.12.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
19
|
-
prune_captcha-1.12.1.dist-info/top_level.txt,sha256=EB4h0WF_YGF7kAWB0XtqmuloOhkL5pC71CzgEVYam7w,28
|
20
|
-
prune_captcha-1.12.1.dist-info/RECORD,,
|
tests/__init__.py
DELETED
File without changes
|
tests/captcha_prune/__init__.py
DELETED
File without changes
|
@@ -1,67 +0,0 @@
|
|
1
|
-
import uuid
|
2
|
-
|
3
|
-
from django.test import TestCase
|
4
|
-
from django.urls import reverse
|
5
|
-
|
6
|
-
from captcha_prune.models import Captcha
|
7
|
-
|
8
|
-
|
9
|
-
class CreateCaptchaViewTestCase(TestCase):
|
10
|
-
def test_create_captcha_with_GET_method(self):
|
11
|
-
response = self.client.get(reverse("create-captcha"))
|
12
|
-
self.assertEqual(response.status_code, 405)
|
13
|
-
|
14
|
-
def test_create_captcha_with_POST_method(self):
|
15
|
-
response = self.client.post(reverse("create-captcha"))
|
16
|
-
self.assertEqual(response.status_code, 201)
|
17
|
-
|
18
|
-
data = response.json()
|
19
|
-
captcha = Captcha.objects.get(uuid=data["uuid"])
|
20
|
-
self.assertIsNotNone(captcha)
|
21
|
-
|
22
|
-
|
23
|
-
class VerifyCaptchaViewTestCase(TestCase):
|
24
|
-
def setUp(self):
|
25
|
-
self.captcha = Captcha.objects.create()
|
26
|
-
self.pos_x_solution = self.captcha.pos_x_solution
|
27
|
-
self.pos_y_solution = self.captcha.pos_y_solution
|
28
|
-
|
29
|
-
def test_verify_captcha_with_POST_method(self):
|
30
|
-
response = self.client.post(
|
31
|
-
reverse("verify-captcha", kwargs={"uuid": str(uuid.uuid4())}),
|
32
|
-
data={
|
33
|
-
"pos_x_answer": self.pos_x_solution,
|
34
|
-
"pos_y_answer": self.pos_y_solution,
|
35
|
-
},
|
36
|
-
)
|
37
|
-
self.assertEqual(response.status_code, 405)
|
38
|
-
|
39
|
-
def test_verify_captcha_with_bad_uuid(self):
|
40
|
-
response = self.client.get(
|
41
|
-
reverse("verify-captcha", kwargs={"uuid": str(uuid.uuid4())}),
|
42
|
-
data={
|
43
|
-
"pos_x_answer": self.pos_x_solution,
|
44
|
-
"pos_y_answer": self.pos_y_solution,
|
45
|
-
},
|
46
|
-
)
|
47
|
-
self.assertEqual(response.status_code, 404)
|
48
|
-
|
49
|
-
def test_verify_captcha_with_good_uuid_but_bad_answer(self):
|
50
|
-
response = self.client.get(
|
51
|
-
reverse("verify-captcha", kwargs={"uuid": str(self.captcha.uuid)}),
|
52
|
-
data={
|
53
|
-
"pos_x_answer": self.pos_x_solution - 20,
|
54
|
-
"pos_y_answer": self.pos_y_solution - 20,
|
55
|
-
},
|
56
|
-
)
|
57
|
-
self.assertEqual(response.status_code, 401)
|
58
|
-
|
59
|
-
def test_verify_captcha_with_good_uuid_and_good_answer(self):
|
60
|
-
response = self.client.get(
|
61
|
-
reverse("verify-captcha", kwargs={"uuid": str(self.captcha.uuid)}),
|
62
|
-
data={
|
63
|
-
"pos_x_answer": self.pos_x_solution,
|
64
|
-
"pos_y_answer": self.pos_y_solution,
|
65
|
-
},
|
66
|
-
)
|
67
|
-
self.assertEqual(response.status_code, 304)
|
File without changes
|