djgentelella 0.5.5__py3-none-any.whl → 0.5.6__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.
- djgentelella/__init__.py +1 -1
- djgentelella/fields/secure.py +109 -0
- djgentelella/models_utils.py +87 -0
- djgentelella/permission_management/__init__.py +7 -0
- djgentelella/serializers/__init__.py +14 -0
- djgentelella/serializers/secure.py +8 -0
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/METADATA +2 -1
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/RECORD +12 -9
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/WHEEL +1 -1
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/AUTHORS +0 -0
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/LICENSE.txt +0 -0
- {djgentelella-0.5.5.dist-info → djgentelella-0.5.6.dist-info}/top_level.txt +0 -0
djgentelella/__init__.py
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import io
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from base64 import b64encode, b64decode
|
|
6
|
+
|
|
7
|
+
from Crypto.Cipher import AES
|
|
8
|
+
from django.conf import settings
|
|
9
|
+
from django.db import models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_key(size=32):
|
|
13
|
+
key = os.urandom(size)
|
|
14
|
+
base64_encoded = base64.b64encode(key)
|
|
15
|
+
return base64_encoded.decode("utf-8")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_salt_session(size=16):
|
|
19
|
+
key = settings.SECRET_KEY.encode()
|
|
20
|
+
if len(key) > size:
|
|
21
|
+
return key[:size]
|
|
22
|
+
return key
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def salt_encrypt(message, session_key=None):
|
|
26
|
+
if type(message) == str:
|
|
27
|
+
message = message.encode()
|
|
28
|
+
session_key = get_salt_session()
|
|
29
|
+
file_out = io.BytesIO()
|
|
30
|
+
cipher_aes = AES.new(session_key, AES.MODE_EAX)
|
|
31
|
+
ciphertext, tag = cipher_aes.encrypt_and_digest(message)
|
|
32
|
+
[file_out.write(x) for x in (cipher_aes.nonce, tag, ciphertext)]
|
|
33
|
+
file_out.seek(0)
|
|
34
|
+
return b64encode(file_out.read())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def salt_decrypt(message):
|
|
38
|
+
if message is None:
|
|
39
|
+
return None
|
|
40
|
+
raw_cipher_data = b64decode(message)
|
|
41
|
+
file_in = io.BytesIO(raw_cipher_data)
|
|
42
|
+
file_in.seek(0)
|
|
43
|
+
|
|
44
|
+
nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
|
|
45
|
+
session_key = get_salt_session()
|
|
46
|
+
cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
|
|
47
|
+
decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag)
|
|
48
|
+
return decrypted
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class GTEncryptedText(models.TextField):
|
|
52
|
+
"""
|
|
53
|
+
Encrypt data using AES and secure django key use in models like:
|
|
54
|
+
in models.py
|
|
55
|
+
|
|
56
|
+
class MyModel(models.Model):
|
|
57
|
+
my_secret = GTEncryptedText(null=True, blank=True)
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def from_db_value(self, value, expression, connection):
|
|
62
|
+
if value is None:
|
|
63
|
+
return value
|
|
64
|
+
if isinstance(value, str):
|
|
65
|
+
value = value.encode()
|
|
66
|
+
return salt_decrypt(value)
|
|
67
|
+
|
|
68
|
+
def pre_save(self, model_instance, add):
|
|
69
|
+
field = getattr(model_instance, self.attname)
|
|
70
|
+
if field is None:
|
|
71
|
+
return None
|
|
72
|
+
dev = salt_encrypt(field)
|
|
73
|
+
if type(dev) == bytes:
|
|
74
|
+
dev = dev.decode()
|
|
75
|
+
return dev
|
|
76
|
+
|
|
77
|
+
def value_from_object(self, obj):
|
|
78
|
+
dev = super(GTEncryptedText, self).value_from_object(obj)
|
|
79
|
+
return dev.decode() if dev is not None else None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class GTEncryptedJSONField(models.JSONField):
|
|
83
|
+
"""
|
|
84
|
+
Encrypt data using AES and secure django key use in models can store JSON objects:
|
|
85
|
+
in models.py
|
|
86
|
+
|
|
87
|
+
class MyModel(models.Model):
|
|
88
|
+
my_secret = GTEncryptedJSONField(null=True, blank=True)
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def get_prep_value(self, value):
|
|
93
|
+
# encrypt the JSON before save to the database
|
|
94
|
+
if value is not None:
|
|
95
|
+
value = json.dumps(value)
|
|
96
|
+
encrypted_value = salt_encrypt(
|
|
97
|
+
super().get_prep_value(value).encode("utf-8")
|
|
98
|
+
)
|
|
99
|
+
return encrypted_value.decode("utf-8") # Store as text in DB
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
def from_db_value(self, value, expression, connection):
|
|
103
|
+
# decrypt when loading from the database
|
|
104
|
+
if value is not None:
|
|
105
|
+
decrypted_value = salt_decrypt(value.encode("utf-8"))
|
|
106
|
+
return super().from_db_value(
|
|
107
|
+
decrypted_value.decode("utf-8"), expression, connection
|
|
108
|
+
)
|
|
109
|
+
return value
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from django.utils.text import slugify
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("djgentelella")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_file_name(instance, name):
|
|
12
|
+
model_name = str(type(instance).__name__).lower()
|
|
13
|
+
|
|
14
|
+
return "%s-%s" % (
|
|
15
|
+
model_name,
|
|
16
|
+
name
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def upload_files_by_model_and_dates(instance, filename):
|
|
21
|
+
"""
|
|
22
|
+
Create a directory structure of the type model/date/file. Usage:
|
|
23
|
+
|
|
24
|
+
myfile = models.FileField(upload_to=upload_files_by_model_and_dates)
|
|
25
|
+
"""
|
|
26
|
+
date = int(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
|
|
27
|
+
path = Path(filename)
|
|
28
|
+
extension = path.suffix
|
|
29
|
+
|
|
30
|
+
if extension == ".zip":
|
|
31
|
+
name = path.stem
|
|
32
|
+
else:
|
|
33
|
+
name = get_file_name(instance, slugify(path.stem))
|
|
34
|
+
|
|
35
|
+
model_name = str(type(instance).__name__).lower()
|
|
36
|
+
return f"{model_name}/{date}/{name}{extension}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def upload_files_by_model_and_month(instance, filename):
|
|
40
|
+
"""
|
|
41
|
+
Create a directory structure of the type model/yearmonth/file. Usage:
|
|
42
|
+
|
|
43
|
+
myfile = models.FileField(upload_to=upload_files_by_model_and_dates)
|
|
44
|
+
"""
|
|
45
|
+
dates = datetime.datetime.now().strftime("%Y%m")
|
|
46
|
+
path = Path(filename)
|
|
47
|
+
extension = path.suffix
|
|
48
|
+
|
|
49
|
+
if extension == ".zip":
|
|
50
|
+
name = path.stem
|
|
51
|
+
else:
|
|
52
|
+
name = get_file_name(instance, slugify(path.stem))
|
|
53
|
+
|
|
54
|
+
model_name = str(type(instance).__name__).lower()
|
|
55
|
+
return f"{model_name}/{dates}/{name}{extension}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def delete_file_and_folder(file_field):
|
|
59
|
+
"""
|
|
60
|
+
Delete the file associated with file_field and, if the containing folder is empty,
|
|
61
|
+
delete it.
|
|
62
|
+
"""
|
|
63
|
+
if not file_field:
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
# Obtener la ruta absoluta del archivo
|
|
67
|
+
file_path = file_field.path
|
|
68
|
+
if os.path.exists(file_path):
|
|
69
|
+
try:
|
|
70
|
+
os.remove(file_path)
|
|
71
|
+
logger.info(f"Deleted file: {file_path}")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.error(f"Error deleting file: {file_path}", exc_info=e)
|
|
74
|
+
|
|
75
|
+
# Obtener la carpeta contenedora del archivo
|
|
76
|
+
directory = os.path.dirname(file_path)
|
|
77
|
+
|
|
78
|
+
# Intentar eliminar la carpeta, si está vacía.
|
|
79
|
+
if os.path.exists(directory):
|
|
80
|
+
# Listar el contenido de la carpeta
|
|
81
|
+
files = os.listdir(directory)
|
|
82
|
+
if not files:
|
|
83
|
+
try:
|
|
84
|
+
os.rmdir(directory)
|
|
85
|
+
logger.info(f"Folder created: {directory}")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.error(f"Error deleting folder: {directory}", exc_info=e)
|
|
@@ -69,3 +69,10 @@ class AnyPermissionByAction(BasePermission):
|
|
|
69
69
|
return False
|
|
70
70
|
|
|
71
71
|
return any_permission(request.user, perms)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_actions_by_perms(user, actions_list):
|
|
75
|
+
actions = {}
|
|
76
|
+
for action, perms in actions_list.items():
|
|
77
|
+
actions[action] = all_permission(user, perms)
|
|
78
|
+
return actions
|
|
@@ -31,3 +31,17 @@ class GTDateTimeField(DateTimeField):
|
|
|
31
31
|
if not value and self.allow_empty_str:
|
|
32
32
|
return None
|
|
33
33
|
return super().to_internal_value(value)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DateFieldWithEmptyString(DateField):
|
|
37
|
+
def to_internal_value(self, value):
|
|
38
|
+
if not value:
|
|
39
|
+
return None
|
|
40
|
+
return super(DateFieldWithEmptyString, self).to_internal_value(value)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DateTimeFieldFieldWithEmptyString(DateTimeField):
|
|
44
|
+
def to_internal_value(self, value):
|
|
45
|
+
if not value:
|
|
46
|
+
return None
|
|
47
|
+
return super(DateTimeFieldFieldWithEmptyString, self).to_internal_value(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: djgentelella
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.6
|
|
4
4
|
Summary: Help building extra widgets for forms and speciall methods to render forms in templates
|
|
5
5
|
Author-email: Solvosoft <info@solvosoft.com>
|
|
6
6
|
Maintainer-email: Luis Zarate Montero <luis.zarate@solvosoft.com>
|
|
@@ -377,6 +377,7 @@ Requires-Dist: Pillow
|
|
|
377
377
|
Requires-Dist: djangorestframework>=3.13
|
|
378
378
|
Requires-Dist: django>=4.2
|
|
379
379
|
Requires-Dist: django-filter>=22.1
|
|
380
|
+
Requires-Dist: pycryptodome>=3.23.0
|
|
380
381
|
Provides-Extra: dev
|
|
381
382
|
Requires-Dist: pylp; extra == "dev"
|
|
382
383
|
Requires-Dist: pylpconcat; extra == "dev"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
djgentelella/__init__.py,sha256=
|
|
1
|
+
djgentelella/__init__.py,sha256=CGHA797Gml3kD_XQHsmIsfocnbo03NlW5CDNC3lqSZs,73
|
|
2
2
|
djgentelella/admin.py,sha256=1LhKD8iGGl-eHDVvZxH2JPdkSEhQEfT9trDYVaJkIe4,2999
|
|
3
3
|
djgentelella/apps.py,sha256=5VEbpUv4b1Jh7gCRAQCe5TvpakRyndKwafjVj0z-Xfo,153
|
|
4
4
|
djgentelella/chartjs.py,sha256=iNYoFnaIQoXsYInJFbSE2quWpjmDz5CuMKpVsvt4pRM,8572
|
|
@@ -8,6 +8,7 @@ djgentelella/groute.py,sha256=FPKKZBfm0UYAfMKmeLGhPBWqPXqazADjt7iKMruVmcE,799
|
|
|
8
8
|
djgentelella/gtselects.py,sha256=4HiQGmV0OgdT4Eky0bsgudEEWBa3S0M8scP0RN-X4Mk,654
|
|
9
9
|
djgentelella/models.py,sha256=TeDZhFca6Vdcfo0iU_AYMTFUShoScOPvMwX3c7XWwwA,7535
|
|
10
10
|
djgentelella/models_manager.py,sha256=cpiL90mNBVO_BQTmrIUq5V1JQ6AedUwYKZSz1eZ0Eyg,1036
|
|
11
|
+
djgentelella/models_utils.py,sha256=KryCHnpZbSyZUB4BKNV4DECQZ7gv_Fi1KvsPssRIQgk,2473
|
|
11
12
|
djgentelella/objectmanagement.py,sha256=y_dkSKIOfKKK9t1hZ8mwcM5RNxj0bChpKYvUn50mVzs,3121
|
|
12
13
|
djgentelella/settings.py,sha256=7x7R6182YDd9SjGDNDceSwRcwYTVJgEIqU1hNtBmPZE,3298
|
|
13
14
|
djgentelella/urls.py,sha256=Ht4AhWLYlY64hR-_D4N13vBmrD_7STi08V7z7c1Z-3Y,5427
|
|
@@ -54,6 +55,7 @@ djgentelella/fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
54
55
|
djgentelella/fields/catalog.py,sha256=-9THXpjIrcMFy8M3uvPzxoJuwWmldOJhZEeRZ71GLPk,2551
|
|
55
56
|
djgentelella/fields/drfdatetime.py,sha256=CpN5wT-NmaG8q8cKjwEuylhnlNHRqx-aIwdWqH9ngWw,2360
|
|
56
57
|
djgentelella/fields/files.py,sha256=pezlWl7WYtSFE0DSBcOeVf4zZo9MUkm_TxQZYYN6TZ4,5538
|
|
58
|
+
djgentelella/fields/secure.py,sha256=osoFbA7P9Ae0y9mm7YMRppzqCz63LOs5MIdeIDJ2i2M,3195
|
|
57
59
|
djgentelella/fields/tree.py,sha256=I_XxsnxXuXiJbeQSEEyni_AzoiYkNJuPE7LBO6mqrXs,2209
|
|
58
60
|
djgentelella/firmador_digital/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
61
|
djgentelella/firmador_digital/forms.py,sha256=OGsDG7csv-8CnEDy1dZUoFf5BBWEMlX46qtq4MhIReA,5347
|
|
@@ -116,15 +118,16 @@ djgentelella/notification/__init__.py,sha256=Xz_RootxxW07E9GVvn6Rqz9x-P_M7ZU1-Ww
|
|
|
116
118
|
djgentelella/notification/base.py,sha256=VRxfoQwAgMVsWRBNAdh42JboRUQllXDfhqhMA89uMAQ,2760
|
|
117
119
|
djgentelella/notification/serializer.py,sha256=ehLL7YO8vVehqk2xMQelHxuQsKWYVr1piYB7-oGeSnY,1989
|
|
118
120
|
djgentelella/notification/widgets.py,sha256=EjmF6tXiHjme4ersZvUGbN79qoFwzSYGY_rgdvmAGiU,1752
|
|
119
|
-
djgentelella/permission_management/__init__.py,sha256=
|
|
121
|
+
djgentelella/permission_management/__init__.py,sha256=8niXk7Xdde3zAItxdI9D40T-uvxjsRu6BdxuJdF6oRc,2233
|
|
120
122
|
djgentelella/permission_management/forms.py,sha256=HfFUXcsMqr11ioIql67OOXcT87MxcWbOv4fRylib90k,1627
|
|
121
123
|
djgentelella/permission_management/objinterface.py,sha256=b4a_Fa2NsQUNVtQs_2Vbe_HEB0tIMdQt5_ZMvl9F47Q,4926
|
|
122
124
|
djgentelella/permission_management/views.py,sha256=XWaBD3i5kN7ADcbSYz4_Bzq2n0gSaJ7eQ_i9nKLXE_c,2104
|
|
123
|
-
djgentelella/serializers/__init__.py,sha256=
|
|
125
|
+
djgentelella/serializers/__init__.py,sha256=Aboh7mxPBZmg2tGqoZ_m9W8Rm1FAI38msAS2thYad7g,1747
|
|
124
126
|
djgentelella/serializers/calendar.py,sha256=57A9QOcBF6twCLJ4yC2uxhuFtolhDENpZ9Dv0sVWYQs,3461
|
|
125
127
|
djgentelella/serializers/firmador_digital.py,sha256=YRPwV85gkggJC4uudvLZ-x_UVUb8l42ih4ckj5JNnS4,3870
|
|
126
128
|
djgentelella/serializers/helper.py,sha256=xfBZDhI4sE0jRrtcfRFnnNtqtmMpagL2Q2uMkpFvyPQ,248
|
|
127
129
|
djgentelella/serializers/paginators.py,sha256=1KOC9uHBguc6mT9g-QVRZolE6hIxRm9T4hUhOSa0E8Q,501
|
|
130
|
+
djgentelella/serializers/secure.py,sha256=rnTIJhPlzUlPgqoS_msNAxhlqdAYRQuHWp6PoAT8fhw,211
|
|
128
131
|
djgentelella/serializers/selects.py,sha256=91zuRmtCy-ogSuFA_hni1E2U56wnlru8RuHch074llo,1360
|
|
129
132
|
djgentelella/serializers/storyline.py,sha256=4RtDjlYEYbw69ibGexqOdsaIvyje3JmYWKFYQz8EFNM,1255
|
|
130
133
|
djgentelella/serializers/storymap.py,sha256=HMoyoRxZocoO5S3BGQQ9SwuHtq32zaI2BJsUa9-V_G8,2777
|
|
@@ -1047,9 +1050,9 @@ djgentelella/widgets/trees.py,sha256=bV6s-w1cgYahS0aNU6xBrx-aISdlzDZ23BFt3_3hAu4
|
|
|
1047
1050
|
djgentelella/widgets/wysiwyg.py,sha256=wHeMyYNVE8-lKJ06A-0DLDAcRNv3TOAOG_Wl9DYZqS0,609
|
|
1048
1051
|
djgentelella/wysiwyg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1049
1052
|
djgentelella/wysiwyg/views.py,sha256=tI7LjLhCnohdvIEOEEhB2Cu1zxRmdcOhYJJX4LBIZaA,1159
|
|
1050
|
-
djgentelella-0.5.
|
|
1051
|
-
djgentelella-0.5.
|
|
1052
|
-
djgentelella-0.5.
|
|
1053
|
-
djgentelella-0.5.
|
|
1054
|
-
djgentelella-0.5.
|
|
1055
|
-
djgentelella-0.5.
|
|
1053
|
+
djgentelella-0.5.6.dist-info/AUTHORS,sha256=HyQoO-q7oXtavpNm7jaBVv8Vxx3c7yo33xkFkt4blkY,118
|
|
1054
|
+
djgentelella-0.5.6.dist-info/LICENSE.txt,sha256=wDzqAntLQORAL6vQhVdzZyfsPVvFStZKtkct5DIZjK0,18047
|
|
1055
|
+
djgentelella-0.5.6.dist-info/METADATA,sha256=gOpNj-hMQBjMNiXymMZi2tOgM29J79dOTVfaqR6Mlko,27580
|
|
1056
|
+
djgentelella-0.5.6.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
|
|
1057
|
+
djgentelella-0.5.6.dist-info/top_level.txt,sha256=88JbODVPV-P5q7ic25yMHt_FuuizaLzMyIaegpFa7Qk,13
|
|
1058
|
+
djgentelella-0.5.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|