zango 0.2.1b0__tar.gz → 0.2.2.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zango-0.2.2.dev0/MANIFEST.in +1 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/PKG-INFO +2 -1
- zango-0.2.1b0/src/zango.egg-info/requires.txt → zango-0.2.2.dev0/requirements/base.txt +1 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/setup.py +1 -1
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/__init__.py +1 -1
- zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/serializers.py +47 -0
- zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/urls.py +11 -0
- zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/views.py +183 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/urls.py +2 -0
- zango-0.2.2.dev0/src/zango/apps/accesslogs/apps.py +9 -0
- zango-0.2.2.dev0/src/zango/apps/accesslogs/migrations/0001_accesslogs.py +86 -0
- zango-0.2.2.dev0/src/zango/apps/accesslogs/models.py +18 -0
- zango-0.2.2.dev0/src/zango/apps/accesslogs/signals.py +79 -0
- zango-0.2.2.dev0/src/zango/apps/accesslogs/utils.py +76 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/auth_backend.py +10 -10
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/views.py +3 -1
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/views.py +2 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/templates/app_panel/app_panel_login.html +8 -18
- zango-0.2.2.dev0/src/zango/apps/shared/platformauth/views.py +37 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/templates/app_panel.html +1 -1
- zango-0.2.2.dev0/src/zango/apps/shared/tenancy/templatetags/zango_filters.py +38 -0
- zango-0.2.2.dev0/src/zango/assets/app_panel/js/build.v0.3.0.min.js +2 -0
- zango-0.2.2.dev0/src/zango/assets/error_pages/css/LockedAccountStyle.css +98 -0
- zango-0.2.2.dev0/src/zango/assets/error_pages/images/ImgLocked.svg +135 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/settings.py +12 -1
- zango-0.2.2.dev0/src/zango/config/__init__.py +0 -0
- zango-0.2.2.dev0/src/zango/config/settings/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/settings/base.py +27 -3
- zango-0.2.2.dev0/src/zango/core/generic_views/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/utils.py +8 -1
- zango-0.2.2.dev0/src/zango/middleware/__init__.py +0 -0
- zango-0.2.2.dev0/src/zango/templates/core/error_pages/account_lockout.html +47 -0
- zango-0.2.2.dev0/src/zango/templates/core/error_pages/base.html +40 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/PKG-INFO +2 -1
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/SOURCES.txt +21 -2
- zango-0.2.2.dev0/src/zango.egg-info/requires.txt +37 -0
- zango-0.2.1b0/src/zango/apps/shared/platformauth/views.py +0 -25
- zango-0.2.1b0/src/zango/assets/app_panel/js/build.v2.0.0.min.js +0 -2
- {zango-0.2.1b0 → zango-0.2.2.dev0}/LICENSE +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/README.md +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/setup.cfg +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/views.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/auditlogs → zango-0.2.2.dev0/src/zango/api/platform/accesslogs}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/auditlogs → zango-0.2.2.dev0/src/zango/api/platform/accesslogs}/v1/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/auth → zango-0.2.2.dev0/src/zango/api/platform/auditlogs}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/auth → zango-0.2.2.dev0/src/zango/api/platform/auditlogs}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/codeassist → zango-0.2.2.dev0/src/zango/api/platform/auth}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/codeassist → zango-0.2.2.dev0/src/zango/api/platform/auth}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/packages → zango-0.2.2.dev0/src/zango/api/platform/codeassist}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/packages → zango-0.2.2.dev0/src/zango/api/platform/codeassist}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/permissions → zango-0.2.2.dev0/src/zango/api/platform/packages}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/permissions → zango-0.2.2.dev0/src/zango/api/platform/packages}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/packages/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/packages/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/tasks → zango-0.2.2.dev0/src/zango/api/platform/permissions}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/tasks → zango-0.2.2.dev0/src/zango/api/platform/permissions}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/tenancy → zango-0.2.2.dev0/src/zango/api/platform/tasks}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/api/platform/tenancy → zango-0.2.2.dev0/src/zango/api/platform/tasks}/v1/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/views.py +0 -0
- {zango-0.2.1b0/src/zango/apps/appauth → zango-0.2.2.dev0/src/zango/api/platform/tenancy}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/appauth/migrations → zango-0.2.2.dev0/src/zango/api/platform/tenancy/v1}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/views.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/auditlogs/management → zango-0.2.2.dev0/src/zango/apps/accesslogs}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/auditlogs/management/commands → zango-0.2.2.dev0/src/zango/apps/accesslogs/migrations}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/auditlogs/migrations → zango-0.2.2.dev0/src/zango/apps/appauth}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0002_default_user_roles.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0003_remove_userrolemodel_temp_field_appusermodel_mobile_and_more.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0004_oldpasswords.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0005_remove_appusermodel_user.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0006_appusermodel_app_objects.py +0 -0
- {zango-0.2.1b0/src/zango/apps/dynamic_models/management → zango-0.2.2.dev0/src/zango/apps/appauth/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/serializers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/signals.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/templates/app.html +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/templates/app_login_signup.html +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/cid.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/conf.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/context.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/diff.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/filters.py +0 -0
- {zango-0.2.1b0/src/zango/apps/dynamic_models/management/commands → zango-0.2.2.dev0/src/zango/apps/auditlogs/management}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/dynamic_models/migrations → zango-0.2.2.dev0/src/zango/apps/auditlogs/management/commands}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/management/commands/auditlogflush.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/management/commands/auditlogmigratejson.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/middleware.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0/src/zango/apps/dynamic_models/workspace → zango-0.2.2.dev0/src/zango/apps/auditlogs/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/mixins.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/receivers.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/registry.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/signals.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/fields/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/object_store → zango-0.2.2.dev0/src/zango/apps/dynamic_models/management}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/object_store/migrations → zango-0.2.2.dev0/src/zango/apps/dynamic_models/management/commands}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/management/commands/reload_tenant.py +0 -0
- {zango-0.2.1b0/src/zango/apps/permissions → zango-0.2.2.dev0/src/zango/apps/dynamic_models/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/permissions.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/registry.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/signals.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/templates/default_landing.html +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/urls.py +0 -0
- {zango-0.2.1b0/src/zango/apps/permissions/migrations → zango-0.2.2.dev0/src/zango/apps/dynamic_models/workspace}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/base.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/lifecycle.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/wtree.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/platformauth → zango-0.2.2.dev0/src/zango/apps/object_store}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/platformauth → zango-0.2.2.dev0/src/zango/apps/object_store}/migrations/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/views.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/tenancy → zango-0.2.2.dev0/src/zango/apps/permissions}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0002_policymodel_type_alter_policymodel_expiry.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0003_default_policy.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0004_policymodel_path_alter_policymodel_name_and_more.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/tenancy/management → zango-0.2.2.dev0/src/zango/apps/permissions/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/mixin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/views.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/tenancy/management/commands → zango-0.2.2.dev0/src/zango/apps/shared/platformauth}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/abstract_model.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/auth_backend.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/migrations/0002_platformusermodel_is_superadmin_and_more.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/tenancy → zango-0.2.2.dev0/src/zango/apps/shared/platformauth}/migrations/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/urls.py +0 -0
- {zango-0.2.1b0/src/zango/apps/shared/tenancy/templatetags → zango-0.2.2.dev0/src/zango/apps/shared/tenancy}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/admin.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/apps.py +0 -0
- {zango-0.2.1b0/src/zango/apps/tasks → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/management}/__init__.py +0 -0
- {zango-0.2.1b0/src/zango/apps/tasks/migrations → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/management/commands}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/sync_static.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/ws_makemigration.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/ws_migrate.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/migrations/0002_rename_is_default_themesmodel_is_active.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/migrations/0003_themesmodel_created_at_themesmodel_created_by_and_more.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/migrations/0004_tenantmodel_fav_icon_alter_tenantmodel_logo.py +0 -0
- {zango-0.2.1b0/src/zango/config → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/tasks.py +0 -0
- {zango-0.2.1b0/src/zango/config/settings → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/templatetags}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/templatetags/zstatic.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/tests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/views.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/cookiecutter.json +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/manifest.json +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/settings.json +0 -0
- {zango-0.2.1b0/src/zango/core/generic_views → zango-0.2.2.dev0/src/zango/apps/tasks}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/apps.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/migrations/0001_initial.py +0 -0
- {zango-0.2.1b0/src/zango/middleware → zango-0.2.2.dev0/src/zango/apps/tasks/migrations}/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/models.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_landing/css/styles.css +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_panel/css/styles.css +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_panel/images/zangoLogo.svg +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/js/jquery/3.7.1/jquery.min.js +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/install_package.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/package_info.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/manage.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/asgi.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls_public.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls_tenants.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/wsgi.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/start_project.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/celery.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/urls_public.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/urls_tenants.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/__init__.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/base.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/common_utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/custom_pluginbase.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/decorators.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/generic_views/base.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/internal_requests.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/model_mixins.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/package_utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/permissions.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/storage_utils.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/tasks.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/template_loader.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/middleware/request.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/middleware/tenant.py +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/dependency_links.txt +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/entry_points.txt +0 -0
- {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include requirements/base.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zango
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2.dev0
|
|
4
4
|
Summary: Zango: multi-tenant Django framework for building business apps
|
|
5
5
|
Home-page: https://github.com/Healthlane-Technologies/zelthy3
|
|
6
6
|
Author: Zelthy ("Healthlane Technologies")
|
|
@@ -48,6 +48,7 @@ Requires-Dist: python3-saml==1.16.0
|
|
|
48
48
|
Requires-Dist: django-ipware==7.0.1
|
|
49
49
|
Requires-Dist: django-decorator-include==3.0
|
|
50
50
|
Requires-Dist: setuptools==70.1.1
|
|
51
|
+
Requires-Dist: django-axes==6.4.0
|
|
51
52
|
|
|
52
53
|
<h1 align="center">
|
|
53
54
|
<a target="_blank" href="https://www.zelthy.com/framework?referer=zelthy3-repo-landing">
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
|
|
3
|
+
from zango.apps.accesslogs.models import AppAccessLog
|
|
4
|
+
from zango.core.utils import get_datetime_str_in_tenant_timezone
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AccessLogSerializerModel(serializers.ModelSerializer):
|
|
8
|
+
user = serializers.SerializerMethodField()
|
|
9
|
+
role = serializers.SerializerMethodField()
|
|
10
|
+
attempt_time = serializers.SerializerMethodField()
|
|
11
|
+
session_expired_at = serializers.SerializerMethodField()
|
|
12
|
+
|
|
13
|
+
def get_attempt_time(self, obj):
|
|
14
|
+
if obj.attempt_time:
|
|
15
|
+
return get_datetime_str_in_tenant_timezone(
|
|
16
|
+
obj.attempt_time, self.context["tenant"]
|
|
17
|
+
)
|
|
18
|
+
return "NA"
|
|
19
|
+
|
|
20
|
+
def get_session_expired_at(self, obj):
|
|
21
|
+
if obj.session_expired_at:
|
|
22
|
+
return get_datetime_str_in_tenant_timezone(
|
|
23
|
+
obj.session_expired_at, self.context["tenant"]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return "NA"
|
|
27
|
+
|
|
28
|
+
def get_user(self, obj):
|
|
29
|
+
return f"{obj.user.name} ({obj.user.id})" if obj.user else "NA"
|
|
30
|
+
|
|
31
|
+
def get_role(self, obj):
|
|
32
|
+
return obj.role.name if obj.role else "NA"
|
|
33
|
+
|
|
34
|
+
class Meta:
|
|
35
|
+
model = AppAccessLog
|
|
36
|
+
fields = [
|
|
37
|
+
"id",
|
|
38
|
+
"ip_address",
|
|
39
|
+
"user",
|
|
40
|
+
"username",
|
|
41
|
+
"attempt_type",
|
|
42
|
+
"attempt_time",
|
|
43
|
+
"role",
|
|
44
|
+
"user_agent",
|
|
45
|
+
"is_login_successful",
|
|
46
|
+
"session_expired_at",
|
|
47
|
+
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import json
|
|
3
|
+
import pytz
|
|
4
|
+
import traceback
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from django.db.models import Q
|
|
8
|
+
from django.utils.decorators import method_decorator
|
|
9
|
+
|
|
10
|
+
from zango.core.utils import get_search_columns
|
|
11
|
+
from zango.core.api.utils import ZangoAPIPagination
|
|
12
|
+
from zango.apps.accesslogs.models import AppAccessLog
|
|
13
|
+
from zango.core.common_utils import set_app_schema_path
|
|
14
|
+
from zango.apps.shared.tenancy.models import TenantModel
|
|
15
|
+
from zango.core.api import get_api_response, ZangoGenericPlatformAPIView
|
|
16
|
+
|
|
17
|
+
from zango.api.platform.accesslogs.v1.serializers import AccessLogSerializerModel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@method_decorator(set_app_schema_path, name="dispatch")
|
|
21
|
+
class AccessLogViewAPIV1(ZangoGenericPlatformAPIView, ZangoAPIPagination):
|
|
22
|
+
pagination_class = ZangoAPIPagination
|
|
23
|
+
|
|
24
|
+
def process_timestamp(self, timestamp, timezone):
|
|
25
|
+
try:
|
|
26
|
+
ts = json.loads(timestamp)
|
|
27
|
+
tz = pytz.timezone(timezone)
|
|
28
|
+
ts["start"] = tz.localize(
|
|
29
|
+
datetime.strptime(ts["start"] + "-" + "00:00", "%Y-%m-%d-%H:%M"),
|
|
30
|
+
is_dst=None,
|
|
31
|
+
)
|
|
32
|
+
ts["end"] = tz.localize(
|
|
33
|
+
datetime.strptime(ts["end"] + "-" + "23:59", "%Y-%m-%d-%H:%M"),
|
|
34
|
+
is_dst=None,
|
|
35
|
+
)
|
|
36
|
+
return ts
|
|
37
|
+
except Exception:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
def process_id(self, id):
|
|
41
|
+
try:
|
|
42
|
+
return int(id)
|
|
43
|
+
except ValueError:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def get_queryset(self, search, tenant, columns={}):
|
|
47
|
+
field_name_query_mapping = {
|
|
48
|
+
"id": "id",
|
|
49
|
+
"user": "user__name__icontains",
|
|
50
|
+
"username": "username__icontains",
|
|
51
|
+
"user_agent": "user_agent__icontains",
|
|
52
|
+
"ip_address": "ip_address__icontains",
|
|
53
|
+
"attempt_type": "attempt_type__icontains",
|
|
54
|
+
"role": "role__name__icontains",
|
|
55
|
+
"user_id": "user_id",
|
|
56
|
+
}
|
|
57
|
+
search_filters = {
|
|
58
|
+
"id": self.process_id,
|
|
59
|
+
"attempt_time": self.process_timestamp,
|
|
60
|
+
"session_expired_at": self.process_timestamp,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
records = AppAccessLog.objects.all().order_by("-id")
|
|
64
|
+
|
|
65
|
+
if search == "" and columns == {}:
|
|
66
|
+
return records
|
|
67
|
+
|
|
68
|
+
filters = Q()
|
|
69
|
+
for field_name, query in field_name_query_mapping.items():
|
|
70
|
+
if search:
|
|
71
|
+
if search_filters.get(field_name, None):
|
|
72
|
+
filters |= Q(**{query: search_filters[field_name](search)})
|
|
73
|
+
else:
|
|
74
|
+
if field_name == "user_id":
|
|
75
|
+
try:
|
|
76
|
+
filters |= Q(**{query: int(search)})
|
|
77
|
+
except ValueError:
|
|
78
|
+
pass
|
|
79
|
+
else:
|
|
80
|
+
filters |= Q(**{query: search})
|
|
81
|
+
records = records.filter(filters).distinct()
|
|
82
|
+
|
|
83
|
+
if columns.get("attempt_time"):
|
|
84
|
+
processed = self.process_timestamp(
|
|
85
|
+
columns.get("attempt_time"), tenant.timezone
|
|
86
|
+
)
|
|
87
|
+
if processed is not None:
|
|
88
|
+
records = records.filter(
|
|
89
|
+
attempt_time__gte=processed["start"],
|
|
90
|
+
attempt_time__lte=processed["end"],
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if columns.get("session_expired_at"):
|
|
94
|
+
processed = self.process_timestamp(
|
|
95
|
+
columns.get("session_expired_at"), tenant.timezone
|
|
96
|
+
)
|
|
97
|
+
if processed is not None:
|
|
98
|
+
records = records.filter(
|
|
99
|
+
session_expired_at__gte=processed["start"],
|
|
100
|
+
session_expired_at__lte=processed["end"],
|
|
101
|
+
)
|
|
102
|
+
if columns.get("attempt_type"):
|
|
103
|
+
records = records.filter(attempt_type=columns.get("attempt_type"))
|
|
104
|
+
|
|
105
|
+
if columns.get("is_login_successful") != None:
|
|
106
|
+
if columns.get("is_login_successful") == "successful":
|
|
107
|
+
records = records.filter(is_login_successful=True)
|
|
108
|
+
elif columns.get("is_login_successful") == "failed":
|
|
109
|
+
records = records.filter(is_login_successful=False)
|
|
110
|
+
|
|
111
|
+
if columns.get("role"):
|
|
112
|
+
records = records.filter(role=columns.get("role"))
|
|
113
|
+
|
|
114
|
+
return records
|
|
115
|
+
|
|
116
|
+
def get_dropdown_options(self):
|
|
117
|
+
options = {"role": []}
|
|
118
|
+
options["attempt_type"] = [
|
|
119
|
+
{
|
|
120
|
+
"id": "login",
|
|
121
|
+
"label": "Login",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "switch_role",
|
|
125
|
+
"label": "Switch Role",
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
options["is_login_successful"] = [
|
|
129
|
+
{
|
|
130
|
+
"id": "successful",
|
|
131
|
+
"label": "Successful",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "failed",
|
|
135
|
+
"label": "Failed",
|
|
136
|
+
},
|
|
137
|
+
]
|
|
138
|
+
role_list = list(
|
|
139
|
+
AppAccessLog.objects.all()
|
|
140
|
+
.values_list("role__id", "role__name")
|
|
141
|
+
.order_by("role__name")
|
|
142
|
+
.distinct()
|
|
143
|
+
)
|
|
144
|
+
for user_role in role_list:
|
|
145
|
+
options["role"].append(
|
|
146
|
+
{
|
|
147
|
+
"id": user_role[0],
|
|
148
|
+
"label": user_role[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
|
+
search = request.GET.get("search", None)
|
|
159
|
+
columns = get_search_columns(request)
|
|
160
|
+
access_logs = self.get_queryset(search, tenant, columns)
|
|
161
|
+
paginated_access_logs = self.paginate_queryset(
|
|
162
|
+
access_logs, request, view=self
|
|
163
|
+
)
|
|
164
|
+
serializer = AccessLogSerializerModel(
|
|
165
|
+
paginated_access_logs, many=True, context={"tenant": tenant}
|
|
166
|
+
)
|
|
167
|
+
accesslogs = self.get_paginated_response_data(serializer.data)
|
|
168
|
+
success = True
|
|
169
|
+
response = {
|
|
170
|
+
"access_logs": accesslogs,
|
|
171
|
+
"message": "Access logs fetched successfully",
|
|
172
|
+
}
|
|
173
|
+
if include_dropdown_options:
|
|
174
|
+
response["dropdown_options"] = self.get_dropdown_options()
|
|
175
|
+
|
|
176
|
+
status = 200
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
traceback.print_exc()
|
|
180
|
+
success = False
|
|
181
|
+
response = {"message": str(e)}
|
|
182
|
+
status = 500
|
|
183
|
+
return get_api_response(success, response, status)
|
|
@@ -16,6 +16,7 @@ from zango.api.platform.packages.v1 import urls as packages_v1_urls
|
|
|
16
16
|
from zango.api.platform.tasks.v1 import urls as tasks_v1_urls
|
|
17
17
|
from zango.api.platform.codeassist.v1 import urls as codeassist_v1_urls
|
|
18
18
|
from zango.api.platform.auditlogs.v1 import urls as auditlog_v1_urls
|
|
19
|
+
from zango.api.platform.accesslogs.v1 import urls as accesslog_v1_urls
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
urlpatterns = [
|
|
@@ -59,5 +60,6 @@ urlpatterns = [
|
|
|
59
60
|
re_path(r"^(?P<app_uuid>[\w-]+)/tasks/", include(tasks_v1_urls)),
|
|
60
61
|
re_path(r"^(?P<app_uuid>[\w-]+)/code-assist/", include(codeassist_v1_urls)),
|
|
61
62
|
re_path(r"^(?P<app_uuid>[\w-]+)/auditlog/", include(auditlog_v1_urls)),
|
|
63
|
+
re_path(r"^(?P<app_uuid>[\w-]+)/access-logs/", include(accesslog_v1_urls)),
|
|
62
64
|
path("", include(permissions_v1_urls)),
|
|
63
65
|
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-04-12 09:20
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
("appauth", "0006_appusermodel_app_objects"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.CreateModel(
|
|
17
|
+
name="AppAccessLog",
|
|
18
|
+
fields=[
|
|
19
|
+
(
|
|
20
|
+
"id",
|
|
21
|
+
models.AutoField(
|
|
22
|
+
auto_created=True,
|
|
23
|
+
primary_key=True,
|
|
24
|
+
serialize=False,
|
|
25
|
+
verbose_name="ID",
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
(
|
|
29
|
+
"user_agent",
|
|
30
|
+
models.CharField(
|
|
31
|
+
db_index=True, max_length=255, verbose_name="User Agent"
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"ip_address",
|
|
36
|
+
models.GenericIPAddressField(
|
|
37
|
+
db_index=True, null=True, verbose_name="IP Address"
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
(
|
|
41
|
+
"username",
|
|
42
|
+
models.CharField(
|
|
43
|
+
db_index=True,
|
|
44
|
+
max_length=255,
|
|
45
|
+
null=True,
|
|
46
|
+
verbose_name="Username",
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
(
|
|
50
|
+
"http_accept",
|
|
51
|
+
models.CharField(max_length=1025, verbose_name="HTTP Accept"),
|
|
52
|
+
),
|
|
53
|
+
("path_info", models.CharField(max_length=255, verbose_name="Path")),
|
|
54
|
+
(
|
|
55
|
+
"attempt_time",
|
|
56
|
+
models.DateTimeField(
|
|
57
|
+
auto_now_add=True, verbose_name="Attempt Time"
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
("attempt_type", models.CharField(max_length=20, null=True)),
|
|
61
|
+
("is_login_successful", models.BooleanField(default=False)),
|
|
62
|
+
("session_expired_at", models.DateTimeField(blank=True, null=True)),
|
|
63
|
+
(
|
|
64
|
+
"role",
|
|
65
|
+
models.ForeignKey(
|
|
66
|
+
blank=True,
|
|
67
|
+
null=True,
|
|
68
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
69
|
+
to="appauth.userrolemodel",
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
(
|
|
73
|
+
"user",
|
|
74
|
+
models.ForeignKey(
|
|
75
|
+
null=True,
|
|
76
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
77
|
+
to="appauth.appusermodel",
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
],
|
|
81
|
+
options={
|
|
82
|
+
"ordering": ["-attempt_time"],
|
|
83
|
+
"abstract": False,
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from axes.models import AccessBase
|
|
3
|
+
|
|
4
|
+
from zango.apps.appauth.models import AppUserModel, UserRoleModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AppAccessLog(AccessBase):
|
|
8
|
+
|
|
9
|
+
user = models.ForeignKey(AppUserModel, null=True, on_delete=models.CASCADE)
|
|
10
|
+
role = models.ForeignKey(
|
|
11
|
+
UserRoleModel, null=True, blank=True, on_delete=models.CASCADE
|
|
12
|
+
)
|
|
13
|
+
attempt_type = models.CharField(max_length=20, null=True)
|
|
14
|
+
is_login_successful = models.BooleanField(default=False)
|
|
15
|
+
session_expired_at = models.DateTimeField(null=True, blank=True)
|
|
16
|
+
|
|
17
|
+
class Meta(AccessBase.Meta):
|
|
18
|
+
app_label = "accesslogs"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from django.db import connection
|
|
5
|
+
from ipware import get_client_ip
|
|
6
|
+
from django.dispatch import receiver
|
|
7
|
+
from axes.helpers import get_client_user_agent
|
|
8
|
+
from django.contrib.auth.signals import (
|
|
9
|
+
user_logged_out,
|
|
10
|
+
user_login_failed,
|
|
11
|
+
user_logged_in,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from zango.apps.accesslogs.models import AppAccessLog
|
|
15
|
+
from zango.apps.appauth.models import UserRoleModel
|
|
16
|
+
from zango.core.utils import get_current_request
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@receiver(user_login_failed)
|
|
20
|
+
def login_failure_handler(sender, **kwargs):
|
|
21
|
+
if connection.tenant.tenant_type == "app":
|
|
22
|
+
creds = kwargs.get("credentials", {})
|
|
23
|
+
request = kwargs.get("request", get_current_request())
|
|
24
|
+
client_ip, is_routable = get_client_ip(request)
|
|
25
|
+
access_log = AppAccessLog.objects.create(
|
|
26
|
+
ip_address=client_ip,
|
|
27
|
+
http_accept=request.META.get("HTTP_ACCEPT", "<unknown>"),
|
|
28
|
+
path_info=request.META.get("PATH_INFO", "<unknown>"),
|
|
29
|
+
user_agent=get_client_user_agent(request),
|
|
30
|
+
username=creds.get("username"),
|
|
31
|
+
attempt_time=datetime.now(),
|
|
32
|
+
attempt_type="login",
|
|
33
|
+
is_login_successful=False,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@receiver(user_logged_in)
|
|
38
|
+
def user_logged_in_handler(sender, request, user, **kwargs):
|
|
39
|
+
if connection.tenant.tenant_type == "app":
|
|
40
|
+
try:
|
|
41
|
+
client_ip, is_routable = get_client_ip(request)
|
|
42
|
+
username = request.user.email if request.user.email else request.user.mobile
|
|
43
|
+
user_role = None
|
|
44
|
+
access_log = AppAccessLog.objects.create(
|
|
45
|
+
ip_address=client_ip,
|
|
46
|
+
http_accept=request.META.get("HTTP_ACCEPT", "<unknown>"),
|
|
47
|
+
path_info=request.META.get("PATH_INFO", "<unknown>"),
|
|
48
|
+
username=username,
|
|
49
|
+
user_agent=get_client_user_agent(request),
|
|
50
|
+
attempt_time=datetime.now(),
|
|
51
|
+
attempt_type="login",
|
|
52
|
+
is_login_successful=True,
|
|
53
|
+
user=user,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if getattr(request, "selected_role_id", ""):
|
|
57
|
+
user_role = UserRoleModel.objects.filter(
|
|
58
|
+
id=getattr(request, "selected_role_id")
|
|
59
|
+
).last()
|
|
60
|
+
|
|
61
|
+
if user_role:
|
|
62
|
+
access_log.role = user_role
|
|
63
|
+
access_log.save()
|
|
64
|
+
except:
|
|
65
|
+
import traceback
|
|
66
|
+
|
|
67
|
+
print(traceback.format_exc())
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@receiver(user_logged_out)
|
|
71
|
+
def user_logged_out_handler(sender, user, **kwargs):
|
|
72
|
+
if connection.tenant.tenant_type == "app":
|
|
73
|
+
access_log = AppAccessLog.objects.filter(
|
|
74
|
+
user=user, session_expired_at__isnull=True
|
|
75
|
+
).order_by("-id").first()
|
|
76
|
+
|
|
77
|
+
if access_log:
|
|
78
|
+
access_log.session_expired_at = datetime.now()
|
|
79
|
+
access_log.save()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth import signals
|
|
4
|
+
from django.db import connection
|
|
5
|
+
from axes.handlers.proxy import AxesProxyHandler
|
|
6
|
+
from django.http import HttpResponseForbidden, HttpResponse
|
|
7
|
+
|
|
8
|
+
from zango.apps.appauth.models import AppUserModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def capture_failed_login_attempt(request, credentials):
|
|
12
|
+
"""
|
|
13
|
+
Capture and handle a failed login attempt.
|
|
14
|
+
Parameters:
|
|
15
|
+
- request (HttpRequest): The HTTP request object.
|
|
16
|
+
- credentials (dict): A dictionary containing the login credentials, with the username as the key.
|
|
17
|
+
Returns:
|
|
18
|
+
None
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
|
|
23
|
+
username_type, username = "", credentials.get("username", "")
|
|
24
|
+
if re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", username):
|
|
25
|
+
username_type = "email"
|
|
26
|
+
|
|
27
|
+
elif re.match(r"^\+[1-9]\d{1,14}$", username):
|
|
28
|
+
username_type = "mobile"
|
|
29
|
+
|
|
30
|
+
if username_type:
|
|
31
|
+
filter_query = {username_type: username}
|
|
32
|
+
app_user = AppUserModel.objects.filter(**filter_query).last()
|
|
33
|
+
if app_user:
|
|
34
|
+
signals.user_login_failed.send(
|
|
35
|
+
sender=app_user,
|
|
36
|
+
request=request,
|
|
37
|
+
credentials=credentials,
|
|
38
|
+
)
|
|
39
|
+
except:
|
|
40
|
+
import traceback
|
|
41
|
+
|
|
42
|
+
print(traceback.format_exc())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def user_authentication_failed(request, credentials):
|
|
46
|
+
"""
|
|
47
|
+
This function handles the case when user authentication fails.
|
|
48
|
+
It captures the failed login attempt by calling the 'capture_failed_login_attempt' function with
|
|
49
|
+
the given request and credentials.
|
|
50
|
+
Parameters:
|
|
51
|
+
- request: The HTTP request object.
|
|
52
|
+
- credentials: A dictionary containing the user's credentials.
|
|
53
|
+
Returns:
|
|
54
|
+
If the request is not allowed by the AxesProxyHandler, it returns a dictionary with the following keys:
|
|
55
|
+
- 'is_locked': True
|
|
56
|
+
- 'message': "Account locked"
|
|
57
|
+
- 'status': The HTTP status code for HttpResponseForbidden
|
|
58
|
+
If the request is allowed by the AxesProxyHandler, it returns a dictionary with the following keys:
|
|
59
|
+
- 'is_locked': False
|
|
60
|
+
- 'message': "This account is permitted to log in"
|
|
61
|
+
- 'status': The HTTP status code for HttpResponse
|
|
62
|
+
"""
|
|
63
|
+
capture_failed_login_attempt(request, credentials)
|
|
64
|
+
|
|
65
|
+
if not AxesProxyHandler.is_allowed(request):
|
|
66
|
+
return {
|
|
67
|
+
"is_locked": True,
|
|
68
|
+
"message": "Account locked",
|
|
69
|
+
"status": HttpResponseForbidden.status_code,
|
|
70
|
+
}
|
|
71
|
+
else:
|
|
72
|
+
return {
|
|
73
|
+
"is_locked": False,
|
|
74
|
+
"message": "This account is permitted to log in",
|
|
75
|
+
"status": HttpResponse.status_code,
|
|
76
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Defines which authentication backend for app users."""
|
|
2
|
+
|
|
2
3
|
from django.contrib.auth.backends import ModelBackend
|
|
3
4
|
from django.db.models import Q
|
|
4
5
|
from django.db import connection
|
|
@@ -9,16 +10,15 @@ from django.contrib.auth.models import User
|
|
|
9
10
|
|
|
10
11
|
class AppUserModelBackend(ModelBackend):
|
|
11
12
|
def authenticate(self, request, username=None, password=None):
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
if request and request.tenant.tenant_type == "app":
|
|
14
|
+
try:
|
|
15
|
+
user = AppUserModel.objects.get(Q(email=username) | Q(mobile=username))
|
|
16
|
+
pwd_valid = user.check_password(password)
|
|
17
|
+
if pwd_valid and user.is_active:
|
|
18
|
+
return user
|
|
19
|
+
return None
|
|
20
|
+
except AppUserModel.DoesNotExist:
|
|
21
|
+
return None
|
|
22
22
|
|
|
23
23
|
def get_user(self, user_id):
|
|
24
24
|
"""
|
|
@@ -2,8 +2,10 @@ from django.shortcuts import redirect
|
|
|
2
2
|
from django.views.generic import View
|
|
3
3
|
|
|
4
4
|
from django.contrib.auth import logout
|
|
5
|
+
from django.utils.decorators import method_decorator
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
from axes.decorators import axes_dispatch
|
|
8
|
+
@method_decorator(axes_dispatch, name='dispatch')
|
|
7
9
|
class AppLogoutView(View):
|
|
8
10
|
def add_protocol(self, request, url):
|
|
9
11
|
if request.is_secure():
|
|
@@ -8,6 +8,7 @@ from django.conf import settings
|
|
|
8
8
|
|
|
9
9
|
from django.views.generic import View
|
|
10
10
|
from django.http import Http404
|
|
11
|
+
from axes.decorators import axes_dispatch
|
|
11
12
|
|
|
12
13
|
from zango.core.utils import get_current_role
|
|
13
14
|
from zango.apps.dynamic_models.permissions import is_platform_user
|
|
@@ -44,6 +45,7 @@ def default_landing_view(request):
|
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
48
|
+
@method_decorator(axes_dispatch, name="dispatch")
|
|
47
49
|
class DynamicView(View, PermMixin):
|
|
48
50
|
"""
|
|
49
51
|
this class is responsible for building the
|
|
@@ -76,29 +76,19 @@
|
|
|
76
76
|
maxlength="150" placeholder="Enter Username" class="textinput form-control" required="">
|
|
77
77
|
<input type="password" name="password" autofocus="" autocapitalize="none" autocomplete="password"
|
|
78
78
|
maxlength="150" placeholder="Email Password" class="textinput form-control" required="">
|
|
79
|
+
{% if error_message %}
|
|
80
|
+
<p style="color: red; text-align: left; font-size: 11px;
|
|
81
|
+
color: #d52918;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
line-height: 16px;
|
|
84
|
+
letter-spacing: 0.2px;
|
|
85
|
+
">{{ error_message }}</p>
|
|
86
|
+
{% endif %}
|
|
79
87
|
<button type="submit"
|
|
80
88
|
style="width: 100%; background: rgb(80, 72, 237); border-radius: 4px; color: white; padding: 9px; margin: 12px 0px 0px; border: none; cursor: pointer; font-weight: 700; font-size: 16px; line-height: 24px;">
|
|
81
89
|
Login
|
|
82
90
|
</button>
|
|
83
|
-
|
|
84
|
-
{% comment %} <table>
|
|
85
|
-
{{ wizard.management_form }}
|
|
86
|
-
{% crispy wizard.form %}
|
|
87
|
-
</table> {% endcomment %}
|
|
88
|
-
|
|
89
91
|
</form>
|
|
90
|
-
{% comment %} <div id="messageListContainer" class="messageList" style="display: none;">
|
|
91
|
-
<p style="font-weight: 700; font-size: 14px;">Error</p>
|
|
92
|
-
{% if messages %}
|
|
93
|
-
<div id="list_div" class="display-message" style='text-align: center;'>
|
|
94
|
-
<ul style='list-style: none;'>
|
|
95
|
-
{% for message in messages %}
|
|
96
|
-
<li><span style=" ">{{ message }}</span></li>
|
|
97
|
-
{% endfor %}
|
|
98
|
-
</ul>
|
|
99
|
-
</div>
|
|
100
|
-
{% endif %}
|
|
101
|
-
</div> {% endcomment %}
|
|
102
92
|
</div>
|
|
103
93
|
</div>
|
|
104
94
|
<div class="footer"
|