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.
- zango/__init__.py +3 -0
- zango/api/__init__.py +0 -0
- zango/api/app_auth/__init__.py +0 -0
- zango/api/app_auth/profile/__init__.py +0 -0
- zango/api/app_auth/profile/v1/__init__.py +0 -0
- zango/api/app_auth/profile/v1/serializers.py +9 -0
- zango/api/app_auth/profile/v1/urls.py +10 -0
- zango/api/app_auth/profile/v1/utils.py +149 -0
- zango/api/app_auth/profile/v1/views.py +77 -0
- zango/api/app_auth/urls.py +7 -0
- zango/api/platform/__init__.py +0 -0
- zango/api/platform/auditlogs/__init__.py +0 -0
- zango/api/platform/auditlogs/v1/__init__.py +0 -0
- zango/api/platform/auditlogs/v1/serializers.py +63 -0
- zango/api/platform/auditlogs/v1/urls.py +11 -0
- zango/api/platform/auditlogs/v1/views.py +182 -0
- zango/api/platform/auth/__init__.py +0 -0
- zango/api/platform/auth/v1/__init__.py +0 -0
- zango/api/platform/auth/v1/serializers.py +20 -0
- zango/api/platform/auth/v1/urls.py +22 -0
- zango/api/platform/auth/v1/views.py +167 -0
- zango/api/platform/codeassist/__init__.py +0 -0
- zango/api/platform/codeassist/v1/__init__.py +0 -0
- zango/api/platform/codeassist/v1/urls.py +17 -0
- zango/api/platform/codeassist/v1/utils.py +9 -0
- zango/api/platform/codeassist/v1/views.py +308 -0
- zango/api/platform/packages/__init__.py +0 -0
- zango/api/platform/packages/v1/__init__.py +0 -0
- zango/api/platform/packages/v1/urls.py +11 -0
- zango/api/platform/packages/v1/views.py +71 -0
- zango/api/platform/permissions/__init__.py +0 -0
- zango/api/platform/permissions/v1/__init__.py +0 -0
- zango/api/platform/permissions/v1/serializers.py +51 -0
- zango/api/platform/permissions/v1/urls.py +22 -0
- zango/api/platform/permissions/v1/views.py +173 -0
- zango/api/platform/tasks/__init__.py +0 -0
- zango/api/platform/tasks/v1/__init__.py +0 -0
- zango/api/platform/tasks/v1/serializers.py +44 -0
- zango/api/platform/tasks/v1/urls.py +7 -0
- zango/api/platform/tasks/v1/views.py +126 -0
- zango/api/platform/tenancy/__init__.py +0 -0
- zango/api/platform/tenancy/v1/__init__.py +0 -0
- zango/api/platform/tenancy/v1/serializers.py +152 -0
- zango/api/platform/tenancy/v1/urls.py +63 -0
- zango/api/platform/tenancy/v1/views.py +591 -0
- zango/api/platform/urls.py +9 -0
- zango/apps/__init__.py +13 -0
- zango/apps/appauth/__init__.py +0 -0
- zango/apps/appauth/admin.py +5 -0
- zango/apps/appauth/apps.py +9 -0
- zango/apps/appauth/auth_backend.py +30 -0
- zango/apps/appauth/migrations/0001_initial.py +205 -0
- zango/apps/appauth/migrations/0002_default_user_roles.py +24 -0
- zango/apps/appauth/migrations/0003_remove_userrolemodel_temp_field_appusermodel_mobile_and_more.py +40 -0
- zango/apps/appauth/migrations/0004_oldpasswords.py +55 -0
- zango/apps/appauth/migrations/0005_remove_appusermodel_user.py +16 -0
- zango/apps/appauth/migrations/0006_appusermodel_app_objects.py +18 -0
- zango/apps/appauth/migrations/__init__.py +0 -0
- zango/apps/appauth/models.py +272 -0
- zango/apps/appauth/serializers.py +15 -0
- zango/apps/appauth/signals.py +15 -0
- zango/apps/appauth/templates/app.html +22 -0
- zango/apps/appauth/templates/app_login_signup.html +21 -0
- zango/apps/appauth/tests.py +3 -0
- zango/apps/appauth/urls.py +10 -0
- zango/apps/appauth/views.py +22 -0
- zango/apps/auditlogs/__init__.py +1 -0
- zango/apps/auditlogs/admin.py +59 -0
- zango/apps/auditlogs/apps.py +17 -0
- zango/apps/auditlogs/cid.py +71 -0
- zango/apps/auditlogs/conf.py +47 -0
- zango/apps/auditlogs/context.py +94 -0
- zango/apps/auditlogs/diff.py +236 -0
- zango/apps/auditlogs/filters.py +33 -0
- zango/apps/auditlogs/management/__init__.py +0 -0
- zango/apps/auditlogs/management/commands/__init__.py +0 -0
- zango/apps/auditlogs/management/commands/auditlogflush.py +52 -0
- zango/apps/auditlogs/management/commands/auditlogmigratejson.py +138 -0
- zango/apps/auditlogs/middleware.py +57 -0
- zango/apps/auditlogs/migrations/0001_initial.py +142 -0
- zango/apps/auditlogs/migrations/__init__.py +0 -0
- zango/apps/auditlogs/mixins.py +169 -0
- zango/apps/auditlogs/models.py +624 -0
- zango/apps/auditlogs/receivers.py +178 -0
- zango/apps/auditlogs/registry.py +370 -0
- zango/apps/auditlogs/signals.py +70 -0
- zango/apps/dynamic_models/__init__.py +2 -0
- zango/apps/dynamic_models/admin.py +3 -0
- zango/apps/dynamic_models/apps.py +29 -0
- zango/apps/dynamic_models/fields/__init__.py +127 -0
- zango/apps/dynamic_models/management/__init__.py +0 -0
- zango/apps/dynamic_models/management/commands/__init__.py +0 -0
- zango/apps/dynamic_models/management/commands/reload_tenant.py +8 -0
- zango/apps/dynamic_models/migrations/__init__.py +0 -0
- zango/apps/dynamic_models/models.py +406 -0
- zango/apps/dynamic_models/permissions.py +26 -0
- zango/apps/dynamic_models/registry.py +23 -0
- zango/apps/dynamic_models/signals.py +27 -0
- zango/apps/dynamic_models/templates/default_landing.html +198 -0
- zango/apps/dynamic_models/tests.py +3 -0
- zango/apps/dynamic_models/urls.py +14 -0
- zango/apps/dynamic_models/views.py +90 -0
- zango/apps/dynamic_models/workspace/__init__.py +0 -0
- zango/apps/dynamic_models/workspace/base.py +474 -0
- zango/apps/dynamic_models/workspace/lifecycle.py +25 -0
- zango/apps/dynamic_models/workspace/wtree.py +34 -0
- zango/apps/object_store/__init__.py +0 -0
- zango/apps/object_store/admin.py +7 -0
- zango/apps/object_store/apps.py +6 -0
- zango/apps/object_store/migrations/0001_initial.py +25 -0
- zango/apps/object_store/migrations/__init__.py +0 -0
- zango/apps/object_store/models.py +62 -0
- zango/apps/object_store/tests.py +3 -0
- zango/apps/object_store/views.py +3 -0
- zango/apps/permissions/__init__.py +0 -0
- zango/apps/permissions/admin.py +9 -0
- zango/apps/permissions/apps.py +6 -0
- zango/apps/permissions/migrations/0001_initial.py +63 -0
- zango/apps/permissions/migrations/0002_policymodel_type_alter_policymodel_expiry.py +26 -0
- zango/apps/permissions/migrations/0003_default_policy.py +24 -0
- zango/apps/permissions/migrations/0004_policymodel_path_alter_policymodel_name_and_more.py +27 -0
- zango/apps/permissions/migrations/__init__.py +0 -0
- zango/apps/permissions/mixin.py +128 -0
- zango/apps/permissions/models.py +116 -0
- zango/apps/permissions/tests.py +3 -0
- zango/apps/permissions/views.py +3 -0
- zango/apps/shared/__init__.py +12 -0
- zango/apps/shared/platformauth/__init__.py +0 -0
- zango/apps/shared/platformauth/abstract_model.py +76 -0
- zango/apps/shared/platformauth/admin.py +6 -0
- zango/apps/shared/platformauth/apps.py +10 -0
- zango/apps/shared/platformauth/auth_backend.py +28 -0
- zango/apps/shared/platformauth/migrations/0001_initial.py +150 -0
- zango/apps/shared/platformauth/migrations/0002_platformusermodel_is_superadmin_and_more.py +33 -0
- zango/apps/shared/platformauth/migrations/__init__.py +0 -0
- zango/apps/shared/platformauth/models.py +200 -0
- zango/apps/shared/platformauth/templates/app_panel/app_panel_login.html +131 -0
- zango/apps/shared/platformauth/tests.py +3 -0
- zango/apps/shared/platformauth/urls.py +13 -0
- zango/apps/shared/platformauth/views.py +25 -0
- zango/apps/shared/tenancy/__init__.py +0 -0
- zango/apps/shared/tenancy/admin.py +8 -0
- zango/apps/shared/tenancy/apps.py +6 -0
- zango/apps/shared/tenancy/management/__init__.py +0 -0
- zango/apps/shared/tenancy/management/commands/__init__.py +0 -0
- zango/apps/shared/tenancy/management/commands/sync_static.py +58 -0
- zango/apps/shared/tenancy/management/commands/ws_makemigration.py +74 -0
- zango/apps/shared/tenancy/management/commands/ws_migrate.py +39 -0
- zango/apps/shared/tenancy/migrations/0001_initial.py +969 -0
- zango/apps/shared/tenancy/migrations/0002_rename_is_default_themesmodel_is_active.py +17 -0
- zango/apps/shared/tenancy/migrations/0003_themesmodel_created_at_themesmodel_created_by_and_more.py +36 -0
- zango/apps/shared/tenancy/migrations/0004_tenantmodel_fav_icon_alter_tenantmodel_logo.py +35 -0
- zango/apps/shared/tenancy/migrations/__init__.py +0 -0
- zango/apps/shared/tenancy/models.py +177 -0
- zango/apps/shared/tenancy/tasks.py +65 -0
- zango/apps/shared/tenancy/templates/app_panel.html +26 -0
- zango/apps/shared/tenancy/templatetags/__init__.py +0 -0
- zango/apps/shared/tenancy/templatetags/zstatic.py +14 -0
- zango/apps/shared/tenancy/tests.py +3 -0
- zango/apps/shared/tenancy/urls.py +5 -0
- zango/apps/shared/tenancy/utils.py +54 -0
- zango/apps/shared/tenancy/views.py +16 -0
- zango/apps/shared/tenancy/workspace_folder_template/cookiecutter.json +3 -0
- zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/manifest.json +3 -0
- zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/settings.json +6 -0
- zango/apps/tasks/__init__.py +0 -0
- zango/apps/tasks/apps.py +6 -0
- zango/apps/tasks/migrations/0001_initial.py +83 -0
- zango/apps/tasks/migrations/__init__.py +0 -0
- zango/apps/tasks/models.py +88 -0
- zango/apps/tasks/utils.py +105 -0
- zango/assets/app_landing/css/styles.css +302 -0
- zango/assets/app_panel/css/styles.css +675 -0
- zango/assets/app_panel/images/zangoLogo.svg +7 -0
- zango/assets/app_panel/js/build.v1.1.2.min.js +2 -0
- zango/assets/js/jquery/3.7.1/jquery.min.js +2 -0
- zango/cli/__init__.py +16 -0
- zango/cli/install_package.py +15 -0
- zango/cli/package_info.py +32 -0
- zango/cli/project_template/manage.py +22 -0
- zango/cli/project_template/project_name/__init__.py +3 -0
- zango/cli/project_template/project_name/asgi.py +16 -0
- zango/cli/project_template/project_name/settings.py +140 -0
- zango/cli/project_template/project_name/urls.py +22 -0
- zango/cli/project_template/project_name/urls_public.py +7 -0
- zango/cli/project_template/project_name/urls_tenants.py +7 -0
- zango/cli/project_template/project_name/wsgi.py +16 -0
- zango/cli/start_project.py +246 -0
- zango/cli/utils.py +24 -0
- zango/config/__init__.py +0 -0
- zango/config/celery.py +18 -0
- zango/config/settings/__init__.py +0 -0
- zango/config/settings/base.py +180 -0
- zango/config/urls_public.py +33 -0
- zango/config/urls_tenants.py +22 -0
- zango/core/__init__.py +1 -0
- zango/core/api/__init__.py +8 -0
- zango/core/api/base.py +55 -0
- zango/core/api/utils.py +28 -0
- zango/core/common_utils.py +36 -0
- zango/core/custom_pluginbase.py +27 -0
- zango/core/decorators.py +33 -0
- zango/core/generic_views/__init__.py +0 -0
- zango/core/generic_views/base.py +55 -0
- zango/core/internal_requests.py +192 -0
- zango/core/model_mixins.py +33 -0
- zango/core/package_utils.py +188 -0
- zango/core/permissions.py +74 -0
- zango/core/storage_utils.py +77 -0
- zango/core/tasks.py +38 -0
- zango/core/template_loader.py +48 -0
- zango/core/utils.py +100 -0
- zango/middleware/__init__.py +0 -0
- zango/middleware/request.py +45 -0
- zango/middleware/tenant.py +157 -0
- zango-0.2.0.dist-info/LICENSE +193 -0
- zango-0.2.0.dist-info/METADATA +128 -0
- zango-0.2.0.dist-info/RECORD +221 -0
- zango-0.2.0.dist-info/WHEEL +5 -0
- zango-0.2.0.dist-info/entry_points.txt +2 -0
- zango-0.2.0.dist-info/top_level.txt +1 -0
zango/__init__.py
ADDED
zango/api/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -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)
|
|
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,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
|
+
]
|