zango 0.2.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.
Files changed (221) hide show
  1. zango/__init__.py +3 -0
  2. zango/api/__init__.py +0 -0
  3. zango/api/app_auth/__init__.py +0 -0
  4. zango/api/app_auth/profile/__init__.py +0 -0
  5. zango/api/app_auth/profile/v1/__init__.py +0 -0
  6. zango/api/app_auth/profile/v1/serializers.py +9 -0
  7. zango/api/app_auth/profile/v1/urls.py +10 -0
  8. zango/api/app_auth/profile/v1/utils.py +149 -0
  9. zango/api/app_auth/profile/v1/views.py +77 -0
  10. zango/api/app_auth/urls.py +7 -0
  11. zango/api/platform/__init__.py +0 -0
  12. zango/api/platform/auditlogs/__init__.py +0 -0
  13. zango/api/platform/auditlogs/v1/__init__.py +0 -0
  14. zango/api/platform/auditlogs/v1/serializers.py +63 -0
  15. zango/api/platform/auditlogs/v1/urls.py +11 -0
  16. zango/api/platform/auditlogs/v1/views.py +182 -0
  17. zango/api/platform/auth/__init__.py +0 -0
  18. zango/api/platform/auth/v1/__init__.py +0 -0
  19. zango/api/platform/auth/v1/serializers.py +20 -0
  20. zango/api/platform/auth/v1/urls.py +22 -0
  21. zango/api/platform/auth/v1/views.py +167 -0
  22. zango/api/platform/codeassist/__init__.py +0 -0
  23. zango/api/platform/codeassist/v1/__init__.py +0 -0
  24. zango/api/platform/codeassist/v1/urls.py +17 -0
  25. zango/api/platform/codeassist/v1/utils.py +9 -0
  26. zango/api/platform/codeassist/v1/views.py +308 -0
  27. zango/api/platform/packages/__init__.py +0 -0
  28. zango/api/platform/packages/v1/__init__.py +0 -0
  29. zango/api/platform/packages/v1/urls.py +11 -0
  30. zango/api/platform/packages/v1/views.py +71 -0
  31. zango/api/platform/permissions/__init__.py +0 -0
  32. zango/api/platform/permissions/v1/__init__.py +0 -0
  33. zango/api/platform/permissions/v1/serializers.py +51 -0
  34. zango/api/platform/permissions/v1/urls.py +22 -0
  35. zango/api/platform/permissions/v1/views.py +173 -0
  36. zango/api/platform/tasks/__init__.py +0 -0
  37. zango/api/platform/tasks/v1/__init__.py +0 -0
  38. zango/api/platform/tasks/v1/serializers.py +44 -0
  39. zango/api/platform/tasks/v1/urls.py +7 -0
  40. zango/api/platform/tasks/v1/views.py +126 -0
  41. zango/api/platform/tenancy/__init__.py +0 -0
  42. zango/api/platform/tenancy/v1/__init__.py +0 -0
  43. zango/api/platform/tenancy/v1/serializers.py +152 -0
  44. zango/api/platform/tenancy/v1/urls.py +63 -0
  45. zango/api/platform/tenancy/v1/views.py +591 -0
  46. zango/api/platform/urls.py +9 -0
  47. zango/apps/__init__.py +13 -0
  48. zango/apps/appauth/__init__.py +0 -0
  49. zango/apps/appauth/admin.py +5 -0
  50. zango/apps/appauth/apps.py +9 -0
  51. zango/apps/appauth/auth_backend.py +30 -0
  52. zango/apps/appauth/migrations/0001_initial.py +205 -0
  53. zango/apps/appauth/migrations/0002_default_user_roles.py +24 -0
  54. zango/apps/appauth/migrations/0003_remove_userrolemodel_temp_field_appusermodel_mobile_and_more.py +40 -0
  55. zango/apps/appauth/migrations/0004_oldpasswords.py +55 -0
  56. zango/apps/appauth/migrations/0005_remove_appusermodel_user.py +16 -0
  57. zango/apps/appauth/migrations/0006_appusermodel_app_objects.py +18 -0
  58. zango/apps/appauth/migrations/__init__.py +0 -0
  59. zango/apps/appauth/models.py +272 -0
  60. zango/apps/appauth/serializers.py +15 -0
  61. zango/apps/appauth/signals.py +15 -0
  62. zango/apps/appauth/templates/app.html +22 -0
  63. zango/apps/appauth/templates/app_login_signup.html +21 -0
  64. zango/apps/appauth/tests.py +3 -0
  65. zango/apps/appauth/urls.py +10 -0
  66. zango/apps/appauth/views.py +22 -0
  67. zango/apps/auditlogs/__init__.py +1 -0
  68. zango/apps/auditlogs/admin.py +59 -0
  69. zango/apps/auditlogs/apps.py +17 -0
  70. zango/apps/auditlogs/cid.py +71 -0
  71. zango/apps/auditlogs/conf.py +47 -0
  72. zango/apps/auditlogs/context.py +94 -0
  73. zango/apps/auditlogs/diff.py +236 -0
  74. zango/apps/auditlogs/filters.py +33 -0
  75. zango/apps/auditlogs/management/__init__.py +0 -0
  76. zango/apps/auditlogs/management/commands/__init__.py +0 -0
  77. zango/apps/auditlogs/management/commands/auditlogflush.py +52 -0
  78. zango/apps/auditlogs/management/commands/auditlogmigratejson.py +138 -0
  79. zango/apps/auditlogs/middleware.py +57 -0
  80. zango/apps/auditlogs/migrations/0001_initial.py +142 -0
  81. zango/apps/auditlogs/migrations/__init__.py +0 -0
  82. zango/apps/auditlogs/mixins.py +169 -0
  83. zango/apps/auditlogs/models.py +624 -0
  84. zango/apps/auditlogs/receivers.py +178 -0
  85. zango/apps/auditlogs/registry.py +370 -0
  86. zango/apps/auditlogs/signals.py +70 -0
  87. zango/apps/dynamic_models/__init__.py +2 -0
  88. zango/apps/dynamic_models/admin.py +3 -0
  89. zango/apps/dynamic_models/apps.py +29 -0
  90. zango/apps/dynamic_models/fields/__init__.py +127 -0
  91. zango/apps/dynamic_models/management/__init__.py +0 -0
  92. zango/apps/dynamic_models/management/commands/__init__.py +0 -0
  93. zango/apps/dynamic_models/management/commands/reload_tenant.py +8 -0
  94. zango/apps/dynamic_models/migrations/__init__.py +0 -0
  95. zango/apps/dynamic_models/models.py +406 -0
  96. zango/apps/dynamic_models/permissions.py +26 -0
  97. zango/apps/dynamic_models/registry.py +23 -0
  98. zango/apps/dynamic_models/signals.py +27 -0
  99. zango/apps/dynamic_models/templates/default_landing.html +198 -0
  100. zango/apps/dynamic_models/tests.py +3 -0
  101. zango/apps/dynamic_models/urls.py +14 -0
  102. zango/apps/dynamic_models/views.py +90 -0
  103. zango/apps/dynamic_models/workspace/__init__.py +0 -0
  104. zango/apps/dynamic_models/workspace/base.py +474 -0
  105. zango/apps/dynamic_models/workspace/lifecycle.py +25 -0
  106. zango/apps/dynamic_models/workspace/wtree.py +34 -0
  107. zango/apps/object_store/__init__.py +0 -0
  108. zango/apps/object_store/admin.py +7 -0
  109. zango/apps/object_store/apps.py +6 -0
  110. zango/apps/object_store/migrations/0001_initial.py +25 -0
  111. zango/apps/object_store/migrations/__init__.py +0 -0
  112. zango/apps/object_store/models.py +62 -0
  113. zango/apps/object_store/tests.py +3 -0
  114. zango/apps/object_store/views.py +3 -0
  115. zango/apps/permissions/__init__.py +0 -0
  116. zango/apps/permissions/admin.py +9 -0
  117. zango/apps/permissions/apps.py +6 -0
  118. zango/apps/permissions/migrations/0001_initial.py +63 -0
  119. zango/apps/permissions/migrations/0002_policymodel_type_alter_policymodel_expiry.py +26 -0
  120. zango/apps/permissions/migrations/0003_default_policy.py +24 -0
  121. zango/apps/permissions/migrations/0004_policymodel_path_alter_policymodel_name_and_more.py +27 -0
  122. zango/apps/permissions/migrations/__init__.py +0 -0
  123. zango/apps/permissions/mixin.py +128 -0
  124. zango/apps/permissions/models.py +116 -0
  125. zango/apps/permissions/tests.py +3 -0
  126. zango/apps/permissions/views.py +3 -0
  127. zango/apps/shared/__init__.py +12 -0
  128. zango/apps/shared/platformauth/__init__.py +0 -0
  129. zango/apps/shared/platformauth/abstract_model.py +76 -0
  130. zango/apps/shared/platformauth/admin.py +6 -0
  131. zango/apps/shared/platformauth/apps.py +10 -0
  132. zango/apps/shared/platformauth/auth_backend.py +28 -0
  133. zango/apps/shared/platformauth/migrations/0001_initial.py +150 -0
  134. zango/apps/shared/platformauth/migrations/0002_platformusermodel_is_superadmin_and_more.py +33 -0
  135. zango/apps/shared/platformauth/migrations/__init__.py +0 -0
  136. zango/apps/shared/platformauth/models.py +200 -0
  137. zango/apps/shared/platformauth/templates/app_panel/app_panel_login.html +131 -0
  138. zango/apps/shared/platformauth/tests.py +3 -0
  139. zango/apps/shared/platformauth/urls.py +13 -0
  140. zango/apps/shared/platformauth/views.py +25 -0
  141. zango/apps/shared/tenancy/__init__.py +0 -0
  142. zango/apps/shared/tenancy/admin.py +8 -0
  143. zango/apps/shared/tenancy/apps.py +6 -0
  144. zango/apps/shared/tenancy/management/__init__.py +0 -0
  145. zango/apps/shared/tenancy/management/commands/__init__.py +0 -0
  146. zango/apps/shared/tenancy/management/commands/sync_static.py +58 -0
  147. zango/apps/shared/tenancy/management/commands/ws_makemigration.py +74 -0
  148. zango/apps/shared/tenancy/management/commands/ws_migrate.py +39 -0
  149. zango/apps/shared/tenancy/migrations/0001_initial.py +969 -0
  150. zango/apps/shared/tenancy/migrations/0002_rename_is_default_themesmodel_is_active.py +17 -0
  151. zango/apps/shared/tenancy/migrations/0003_themesmodel_created_at_themesmodel_created_by_and_more.py +36 -0
  152. zango/apps/shared/tenancy/migrations/0004_tenantmodel_fav_icon_alter_tenantmodel_logo.py +35 -0
  153. zango/apps/shared/tenancy/migrations/__init__.py +0 -0
  154. zango/apps/shared/tenancy/models.py +177 -0
  155. zango/apps/shared/tenancy/tasks.py +65 -0
  156. zango/apps/shared/tenancy/templates/app_panel.html +26 -0
  157. zango/apps/shared/tenancy/templatetags/__init__.py +0 -0
  158. zango/apps/shared/tenancy/templatetags/zstatic.py +14 -0
  159. zango/apps/shared/tenancy/tests.py +3 -0
  160. zango/apps/shared/tenancy/urls.py +5 -0
  161. zango/apps/shared/tenancy/utils.py +54 -0
  162. zango/apps/shared/tenancy/views.py +16 -0
  163. zango/apps/shared/tenancy/workspace_folder_template/cookiecutter.json +3 -0
  164. zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/manifest.json +3 -0
  165. zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/settings.json +6 -0
  166. zango/apps/tasks/__init__.py +0 -0
  167. zango/apps/tasks/apps.py +6 -0
  168. zango/apps/tasks/migrations/0001_initial.py +83 -0
  169. zango/apps/tasks/migrations/__init__.py +0 -0
  170. zango/apps/tasks/models.py +88 -0
  171. zango/apps/tasks/utils.py +105 -0
  172. zango/assets/app_landing/css/styles.css +302 -0
  173. zango/assets/app_panel/css/styles.css +675 -0
  174. zango/assets/app_panel/images/zangoLogo.svg +7 -0
  175. zango/assets/app_panel/js/build.v1.1.2.min.js +2 -0
  176. zango/assets/js/jquery/3.7.1/jquery.min.js +2 -0
  177. zango/cli/__init__.py +16 -0
  178. zango/cli/install_package.py +15 -0
  179. zango/cli/package_info.py +32 -0
  180. zango/cli/project_template/manage.py +22 -0
  181. zango/cli/project_template/project_name/__init__.py +3 -0
  182. zango/cli/project_template/project_name/asgi.py +16 -0
  183. zango/cli/project_template/project_name/settings.py +140 -0
  184. zango/cli/project_template/project_name/urls.py +22 -0
  185. zango/cli/project_template/project_name/urls_public.py +7 -0
  186. zango/cli/project_template/project_name/urls_tenants.py +7 -0
  187. zango/cli/project_template/project_name/wsgi.py +16 -0
  188. zango/cli/start_project.py +246 -0
  189. zango/cli/utils.py +24 -0
  190. zango/config/__init__.py +0 -0
  191. zango/config/celery.py +18 -0
  192. zango/config/settings/__init__.py +0 -0
  193. zango/config/settings/base.py +180 -0
  194. zango/config/urls_public.py +33 -0
  195. zango/config/urls_tenants.py +22 -0
  196. zango/core/__init__.py +1 -0
  197. zango/core/api/__init__.py +8 -0
  198. zango/core/api/base.py +55 -0
  199. zango/core/api/utils.py +28 -0
  200. zango/core/common_utils.py +36 -0
  201. zango/core/custom_pluginbase.py +27 -0
  202. zango/core/decorators.py +33 -0
  203. zango/core/generic_views/__init__.py +0 -0
  204. zango/core/generic_views/base.py +55 -0
  205. zango/core/internal_requests.py +192 -0
  206. zango/core/model_mixins.py +33 -0
  207. zango/core/package_utils.py +188 -0
  208. zango/core/permissions.py +74 -0
  209. zango/core/storage_utils.py +77 -0
  210. zango/core/tasks.py +38 -0
  211. zango/core/template_loader.py +48 -0
  212. zango/core/utils.py +100 -0
  213. zango/middleware/__init__.py +0 -0
  214. zango/middleware/request.py +45 -0
  215. zango/middleware/tenant.py +157 -0
  216. zango-0.2.0.dist-info/LICENSE +193 -0
  217. zango-0.2.0.dist-info/METADATA +128 -0
  218. zango-0.2.0.dist-info/RECORD +221 -0
  219. zango-0.2.0.dist-info/WHEEL +5 -0
  220. zango-0.2.0.dist-info/entry_points.txt +2 -0
  221. zango-0.2.0.dist-info/top_level.txt +1 -0
zango/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from zango.core import internal_requests
2
+
3
+ __version__ = "0.2.0"
zango/api/__init__.py ADDED
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,9 @@
1
+ from rest_framework import serializers
2
+
3
+ from zango.apps.appauth.models import AppUserModel
4
+
5
+
6
+ class ProfileSerializer(serializers.ModelSerializer):
7
+ class Meta:
8
+ model = AppUserModel
9
+ fields = ["name", "email", "mobile"]
@@ -0,0 +1,10 @@
1
+ from django.urls import re_path
2
+
3
+ from .views import ProfileViewAPIV1, PasswordChangeViewAPIV1
4
+
5
+ urlpatterns = [
6
+ re_path(
7
+ r"change_password", PasswordChangeViewAPIV1.as_view(), name="change_password"
8
+ ),
9
+ re_path(r"", ProfileViewAPIV1.as_view(), name="profile"),
10
+ ]
@@ -0,0 +1,149 @@
1
+ import re
2
+
3
+ from django.conf import settings
4
+ from django.shortcuts import redirect
5
+
6
+
7
+ class PasswordValidationMixin(object):
8
+ MIN_LENGTH = settings.PASSWORD_MIN_LENGTH
9
+ oldpassword_model = None
10
+ num_specialChar_regex = re.compile(r"[!@#$%^&*()_+-/=~]")
11
+ numeric_regex = re.compile(r"\d")
12
+ uppercase_regex = re.compile(r"[ABCDEFGHIJKLMNOPQRSTUVWXYZ]")
13
+ lowercase_regex = re.compile(r"[abcdefghijklmnopqrstuvwqyz]")
14
+
15
+ @staticmethod
16
+ def is_password_matching(password, password2):
17
+ """
18
+ Checks if both passwords are equal
19
+ """
20
+ validation = password == password2
21
+ if not validation:
22
+ msg = "The two passwords didn't match!"
23
+ else:
24
+ msg = None
25
+ return {"validation": validation, "msg": msg}
26
+
27
+ def check_password_length(self, password):
28
+ if len(password) < self.MIN_LENGTH:
29
+ validation = False
30
+ msg = (
31
+ f"The new password must be at least {self.MIN_LENGTH} characters long."
32
+ )
33
+
34
+ else:
35
+ validation = True
36
+ msg = None
37
+ return {"validation": validation, "msg": msg}
38
+
39
+ @staticmethod
40
+ def is_first_alpha(password):
41
+ """
42
+ First character must be alphabet
43
+ """
44
+ validation = password[0].isalpha()
45
+ if not validation:
46
+ msg = "The first letter of your password must be an alphabet!"
47
+ else:
48
+ msg = None
49
+ return {"validation": validation, "msg": msg}
50
+
51
+ def check_uppercase_char(self, password):
52
+ if not self.uppercase_regex.search(password):
53
+ validation = False
54
+ msg = "The new password must contain at least one upper case character"
55
+ else:
56
+ validation = True
57
+ msg = None
58
+ return {"msg": msg, "validation": validation}
59
+
60
+ def check_lowercase_char(self, password):
61
+ if not self.lowercase_regex.search(password):
62
+ validation = False
63
+ msg = "The new password must contain at least one lower case character"
64
+ else:
65
+ validation = True
66
+ msg = None
67
+ return {"msg": msg, "validation": validation}
68
+
69
+ def verify_old_password(self, user, old_password):
70
+ if not user.check_password(old_password):
71
+ msg = "Current password does not match. Please try again!"
72
+ validation = False
73
+ else:
74
+ msg = ""
75
+ validation = True
76
+ return {"validation": validation, "msg": msg}
77
+
78
+ def check_special_character(self, password):
79
+ msg = ""
80
+ validation = True
81
+ if not self.num_specialChar_regex.search(password):
82
+ validation = False
83
+ msg = "The new password must contain at least one numeric and one special character e.g. ! @ # $ %..."
84
+ if not self.numeric_regex.search(password):
85
+ validation = False
86
+ msg = "The new password must contain at least one numeric and one special character e.g. ! @ # $ %..."
87
+ return {"msg": msg, "validation": validation}
88
+
89
+ @staticmethod
90
+ def match_old_password(user, password):
91
+ validation = True
92
+ if user.check_password_validity(password):
93
+ msg = (
94
+ "Sorry, but your new password must not match one of your \
95
+ old passwords from the previous %s days. Please try \
96
+ again!"
97
+ % (settings.PASSWORD_NO_REPEAT_DAYS)
98
+ )
99
+ validation = False
100
+ else:
101
+ msg = ""
102
+ return {"validation": validation, "msg": msg}
103
+
104
+ @staticmethod
105
+ def match_password_username(user, password):
106
+ validation = True
107
+ msg = None
108
+ if password.lower() == user.email.lower():
109
+ validation = False
110
+ if not validation:
111
+ msg = "Your password must be different from your username."
112
+ return {"msg": msg, "validation": validation}
113
+
114
+ def run_all_validations(
115
+ self, user, password, repeat_password=None, old_password=None
116
+ ):
117
+ if repeat_password:
118
+ if not self.is_password_matching(password, repeat_password).get(
119
+ "validation"
120
+ ):
121
+ return self.is_password_matching(password, repeat_password)
122
+
123
+ if old_password:
124
+ if not self.verify_old_password(user, old_password).get("validation"):
125
+ return self.verify_old_password(user, old_password)
126
+
127
+ if not self.check_password_length(password).get("validation"):
128
+ return self.check_password_length(password)
129
+
130
+ elif not self.is_first_alpha(password).get("validation"):
131
+ return self.is_first_alpha(password)
132
+
133
+ elif not self.check_uppercase_char(password).get("validation"):
134
+ return self.check_uppercase_char(password)
135
+
136
+ elif not self.check_lowercase_char(password).get("validation"):
137
+ return self.check_lowercase_char(password)
138
+
139
+ elif not self.check_special_character(password).get("validation"):
140
+ return self.check_special_character(password)
141
+
142
+ elif not self.match_old_password(user, password).get("validation"):
143
+ return self.match_old_password(user, password)
144
+
145
+ elif not self.match_password_username(user, password).get("validation"):
146
+ return self.match_password_username(user, password)
147
+
148
+ else:
149
+ return {"validation": True, "msg": "Password validations passed"}
@@ -0,0 +1,77 @@
1
+ from django.contrib.auth import authenticate
2
+ from django.core.exceptions import ValidationError
3
+
4
+ from zango.core.api import (
5
+ get_api_response,
6
+ ZangoGenericAppAPIView,
7
+ ZangoSessionAppAPIView,
8
+ )
9
+ from zango.api.app_auth.profile.v1.utils import PasswordValidationMixin
10
+ from zango.apps.appauth.models import OldPasswords
11
+
12
+ from .serializers import ProfileSerializer
13
+
14
+
15
+ class ProfileViewAPIV1(ZangoGenericAppAPIView):
16
+ def get(self, request, *args, **kwargs):
17
+ serializer = ProfileSerializer(request.user)
18
+ success = True
19
+ response = {"message": "success", "profile_data": serializer.data}
20
+ status = 200
21
+ return get_api_response(success, response, status)
22
+
23
+ def put(self, request, *args, **kwargs):
24
+ response = request.user.update_user(request.data)
25
+ success = response.pop("success")
26
+ if success:
27
+ status = 200
28
+ else:
29
+ status = 400
30
+ return get_api_response(success, response, status)
31
+
32
+
33
+ class PasswordChangeViewAPIV1(ZangoSessionAppAPIView, PasswordValidationMixin):
34
+ def clean_password(self, email, password):
35
+ """
36
+ Validates that the email is not already in use.
37
+ """
38
+ try:
39
+ user = authenticate(username=email, password=password)
40
+ except:
41
+ raise ValidationError(
42
+ "The current password you have entered is wrong. Please try again!"
43
+ )
44
+
45
+ def clean_password2(self, user, current_password, new_password):
46
+ """method to validate password"""
47
+ password2 = new_password
48
+ validation = self.run_all_validations(
49
+ user, new_password, password2, current_password
50
+ )
51
+ if not validation.get("validation"):
52
+ raise ValidationError(validation.get("msg"))
53
+ return True
54
+
55
+ def put(self, request, *args, **kwargs):
56
+ current_password = request.data.get("current_password")
57
+ new_password = request.data.get("new_password")
58
+ success = False
59
+ try:
60
+ self.clean_password(request.user.email, current_password)
61
+ self.clean_password2(request.user, current_password, new_password)
62
+ request.user.set_password(new_password)
63
+ request.user.save()
64
+ obj = OldPasswords.objects.create(user=request.user)
65
+ obj.setPasswords(request.user.password)
66
+ obj.save()
67
+ success = True
68
+ response = {}
69
+ status = 200
70
+ return get_api_response(success, response, status)
71
+ except ValidationError as e:
72
+ response = {"message": e.message}
73
+ if success:
74
+ status = 200
75
+ else:
76
+ status = 400
77
+ return get_api_response(success, response, status)
@@ -0,0 +1,7 @@
1
+ from django.urls import path, include
2
+
3
+ from .profile.v1 import urls as profile_v1_urls
4
+
5
+ urlpatterns = [
6
+ path("v1/profile/", include(profile_v1_urls)),
7
+ ]
File without changes
File without changes
File without changes
@@ -0,0 +1,63 @@
1
+ import importlib
2
+
3
+ from rest_framework import serializers
4
+
5
+ from zango.api.platform.tenancy.v1.serializers import AppUserModelSerializerModel
6
+ from zango.apps.auditlogs.models import LogEntry
7
+ from zango.core.utils import get_datetime_str_in_tenant_timezone, get_current_request
8
+
9
+
10
+ class AuditLogSerializerModel(serializers.ModelSerializer):
11
+ actor = serializers.SerializerMethodField()
12
+ actor_type = serializers.SerializerMethodField()
13
+ timestamp = serializers.SerializerMethodField()
14
+ action = serializers.SerializerMethodField()
15
+ object_uuid = serializers.SerializerMethodField()
16
+ object_type = serializers.SerializerMethodField()
17
+
18
+ def get_action(self, obj):
19
+ return obj.get_action_display().capitalize()
20
+
21
+ def get_timestamp(self, obj):
22
+ return get_datetime_str_in_tenant_timezone(
23
+ obj.timestamp, self.context["tenant"]
24
+ )
25
+
26
+ def get_actor(self, obj):
27
+ return (
28
+ obj.tenant_actor.name
29
+ if obj.tenant_actor
30
+ else obj.platform_actor.name
31
+ if obj.platform_actor
32
+ else None
33
+ )
34
+
35
+ def get_actor_type(self, obj):
36
+ return (
37
+ "tenant_actor"
38
+ if obj.tenant_actor
39
+ else "platform_actor"
40
+ if obj.platform_actor
41
+ else None
42
+ )
43
+
44
+ def get_object_uuid(self, obj):
45
+ if obj.object_ref is not None:
46
+ return str(obj.object_ref.object_uuid)
47
+
48
+ def get_object_type(self, obj):
49
+ return obj.content_type.model
50
+
51
+ class Meta:
52
+ model = LogEntry
53
+ fields = [
54
+ "id",
55
+ "actor",
56
+ "actor_type",
57
+ "action",
58
+ "object_id",
59
+ "object_uuid",
60
+ "object_type",
61
+ "timestamp",
62
+ "changes",
63
+ ]
@@ -0,0 +1,11 @@
1
+ from django.urls import path
2
+
3
+ from .views import AuditLogViewAPIV1
4
+
5
+ urlpatterns = [
6
+ path(
7
+ "",
8
+ AuditLogViewAPIV1.as_view(),
9
+ name="auditlog-apiv1-auditloglistview",
10
+ ),
11
+ ]
@@ -0,0 +1,182 @@
1
+ import traceback
2
+ import csv
3
+ from datetime import datetime
4
+ import json
5
+ import pytz
6
+
7
+ from django.db.models import Q
8
+ from django.contrib.contenttypes.models import ContentType
9
+ from django.db import connection
10
+ from django.utils.decorators import method_decorator
11
+
12
+ from zango.core.api import get_api_response, ZangoGenericPlatformAPIView
13
+ from zango.core.api.utils import ZangoAPIPagination
14
+ from zango.core.permissions import IsSuperAdminPlatformUser
15
+ from zango.core.utils import get_search_columns
16
+ from zango.apps.shared.tenancy.models import TenantModel
17
+ from zango.apps.auditlogs.models import LogEntry
18
+ from zango.core.common_utils import set_app_schema_path
19
+
20
+ from .serializers import AuditLogSerializerModel
21
+
22
+
23
+ @method_decorator(set_app_schema_path, name="dispatch")
24
+ class AuditLogViewAPIV1(ZangoGenericPlatformAPIView, ZangoAPIPagination):
25
+ pagination_class = ZangoAPIPagination
26
+
27
+ def process_timestamp(self, timestamp, timezone):
28
+ try:
29
+ ts = json.loads(timestamp)
30
+ tz = pytz.timezone(timezone)
31
+ ts["start"] = tz.localize(
32
+ datetime.strptime(ts["start"] + "-" + "00:00", "%Y-%m-%d-%H:%M"),
33
+ is_dst=None,
34
+ )
35
+ ts["end"] = tz.localize(
36
+ datetime.strptime(ts["end"] + "-" + "23:59", "%Y-%m-%d-%H:%M"),
37
+ is_dst=None,
38
+ )
39
+ return ts
40
+ except Exception:
41
+ return None
42
+
43
+ def process_id(self, id):
44
+ try:
45
+ return int(id)
46
+ except ValueError:
47
+ return None
48
+
49
+ def get_queryset(self, search, tenant, columns={}, model_type=None):
50
+ field_name_query_mapping = {
51
+ "tenant_actor": "tenant_actor__name__icontains",
52
+ "platform_actor": "platform_actor__name__icontains",
53
+ "object_id": "object_id",
54
+ "id": "id",
55
+ "object_repr": "object_repr__icontains",
56
+ "changes": "changes__icontains",
57
+ "object_uuid": "object_ref__object_uuid__icontains",
58
+ }
59
+ search_filters = {
60
+ "id": self.process_id,
61
+ "object_id": self.process_id,
62
+ "timestamp": self.process_timestamp,
63
+ }
64
+ if model_type == "dynamic_models":
65
+ records = (
66
+ LogEntry.objects.all()
67
+ .order_by("-id")
68
+ .filter(content_type__app_label=model_type)
69
+ )
70
+ elif model_type == "core_models":
71
+ records = (
72
+ LogEntry.objects.all()
73
+ .order_by("-id")
74
+ .exclude(content_type__app_label="dynamic_models")
75
+ )
76
+ else:
77
+ records = LogEntry.objects.all().order_by("-id")
78
+ if search == "" and columns == {}:
79
+ return records
80
+ filters = Q()
81
+ for field_name, query in field_name_query_mapping.items():
82
+ if search:
83
+ if search_filters.get(field_name, None):
84
+ filters |= Q(**{query: search_filters[field_name](search)})
85
+ else:
86
+ filters |= Q(**{query: search})
87
+ records = records.filter(filters).distinct()
88
+ if columns.get("timestamp"):
89
+ processed = self.process_timestamp(
90
+ columns.get("timestamp"), tenant.timezone
91
+ )
92
+ if processed is not None:
93
+ records = records.filter(
94
+ timestamp__gte=processed["start"], timestamp__lte=processed["end"]
95
+ )
96
+ if columns.get("action"):
97
+ records = records.filter(action=columns.get("action"))
98
+ if columns.get("object_type"):
99
+ records = records.filter(
100
+ content_type=ContentType.objects.get(id=columns.get("object_type"))
101
+ )
102
+ return records
103
+
104
+ def get_dropdown_options(self, model_type=None):
105
+ options = {}
106
+ options["action"] = [
107
+ {
108
+ "id": "0",
109
+ "label": "Create",
110
+ },
111
+ {
112
+ "id": "1",
113
+ "label": "Update",
114
+ },
115
+ {
116
+ "id": "2",
117
+ "label": "Delete",
118
+ },
119
+ ]
120
+ options["object_type"] = []
121
+ if model_type == "dynamic_models":
122
+ object_types = list(
123
+ LogEntry.objects.all()
124
+ .values_list("content_type_id", "content_type__model")
125
+ .order_by("content_type__model")
126
+ .distinct()
127
+ .filter(content_type__app_label__contains="dynamic_models")
128
+ )
129
+ elif model_type == "core_models":
130
+ object_types = list(
131
+ LogEntry.objects.all()
132
+ .values_list("content_type_id", "content_type__model")
133
+ .order_by("content_type__model")
134
+ .distinct()
135
+ .exclude(content_type__app_label__contains="dynamic_models")
136
+ )
137
+ else:
138
+ object_types = list(
139
+ LogEntry.objects.all()
140
+ .values_list("content_type_id", "content_type__model")
141
+ .order_by("content_type__model")
142
+ .distinct()
143
+ )
144
+ for object_type in object_types:
145
+ options["object_type"].append(
146
+ {
147
+ "id": object_type[0],
148
+ "label": object_type[1],
149
+ }
150
+ )
151
+ return options
152
+
153
+ def get(self, request, *args, **kwargs):
154
+ try:
155
+ app_uuid = kwargs.get("app_uuid")
156
+ tenant = TenantModel.objects.get(uuid=app_uuid)
157
+ include_dropdown_options = request.GET.get("include_dropdown_options")
158
+ model_type = request.GET.get("model_type", None)
159
+ search = request.GET.get("search", None)
160
+ columns = get_search_columns(request)
161
+ audit_logs = self.get_queryset(search, tenant, columns, model_type)
162
+ paginated_audit_logs = self.paginate_queryset(
163
+ audit_logs, request, view=self
164
+ )
165
+ serializer = AuditLogSerializerModel(
166
+ paginated_audit_logs, many=True, context={"tenant": tenant}
167
+ )
168
+ auditlogs = self.get_paginated_response_data(serializer.data)
169
+ success = True
170
+ response = {
171
+ "audit_logs": auditlogs,
172
+ "message": "Audit logs fetched successfully",
173
+ }
174
+ if include_dropdown_options:
175
+ response["dropdown_options"] = self.get_dropdown_options(model_type)
176
+ status = 200
177
+ except Exception as e:
178
+ traceback.print_exc()
179
+ success = False
180
+ response = {"message": str(e)}
181
+ status = 500
182
+ return get_api_response(success, response, status)
File without changes
File without changes
@@ -0,0 +1,20 @@
1
+ from rest_framework import serializers
2
+ from zango.apps.shared.platformauth.models import PlatformUserModel
3
+ from zango.api.platform.tenancy.v1.serializers import TenantSerializerModel
4
+
5
+
6
+ class PlatformUserSerializerModel(serializers.ModelSerializer):
7
+ apps = TenantSerializerModel(many=True)
8
+
9
+ class Meta:
10
+ model = PlatformUserModel
11
+ fields = [
12
+ "id",
13
+ "name",
14
+ "email",
15
+ "apps",
16
+ "is_superadmin",
17
+ "is_active",
18
+ "last_login",
19
+ "created_at",
20
+ ]
@@ -0,0 +1,22 @@
1
+ from django.urls import re_path
2
+
3
+ from .views import (
4
+ PlatformUserViewAPIV1,
5
+ PlatformUserDetailViewAPIV1,
6
+ AppPanelDetailsView,
7
+ )
8
+
9
+
10
+ urlpatterns = [
11
+ re_path(
12
+ r"^platform-users/$",
13
+ PlatformUserViewAPIV1.as_view(),
14
+ name="platformauth-apiv1-userview",
15
+ ),
16
+ re_path(
17
+ r"^platform-users/(?P<user_id>[\w-]+)/$",
18
+ PlatformUserDetailViewAPIV1.as_view(),
19
+ name="platformauth-apiv1-userdetailview",
20
+ ),
21
+ re_path("app-initalization-details/", AppPanelDetailsView.as_view()),
22
+ ]