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.
Files changed (247) hide show
  1. zango-0.2.2.dev0/MANIFEST.in +1 -0
  2. {zango-0.2.1b0 → zango-0.2.2.dev0}/PKG-INFO +2 -1
  3. zango-0.2.1b0/src/zango.egg-info/requires.txt → zango-0.2.2.dev0/requirements/base.txt +1 -0
  4. {zango-0.2.1b0 → zango-0.2.2.dev0}/setup.py +1 -1
  5. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/__init__.py +1 -1
  6. zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/serializers.py +47 -0
  7. zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/urls.py +11 -0
  8. zango-0.2.2.dev0/src/zango/api/platform/accesslogs/v1/views.py +183 -0
  9. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/urls.py +2 -0
  10. zango-0.2.2.dev0/src/zango/apps/accesslogs/apps.py +9 -0
  11. zango-0.2.2.dev0/src/zango/apps/accesslogs/migrations/0001_accesslogs.py +86 -0
  12. zango-0.2.2.dev0/src/zango/apps/accesslogs/models.py +18 -0
  13. zango-0.2.2.dev0/src/zango/apps/accesslogs/signals.py +79 -0
  14. zango-0.2.2.dev0/src/zango/apps/accesslogs/utils.py +76 -0
  15. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/auth_backend.py +10 -10
  16. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/views.py +3 -1
  17. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/views.py +2 -0
  18. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/templates/app_panel/app_panel_login.html +8 -18
  19. zango-0.2.2.dev0/src/zango/apps/shared/platformauth/views.py +37 -0
  20. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/templates/app_panel.html +1 -1
  21. zango-0.2.2.dev0/src/zango/apps/shared/tenancy/templatetags/zango_filters.py +38 -0
  22. zango-0.2.2.dev0/src/zango/assets/app_panel/js/build.v0.3.0.min.js +2 -0
  23. zango-0.2.2.dev0/src/zango/assets/error_pages/css/LockedAccountStyle.css +98 -0
  24. zango-0.2.2.dev0/src/zango/assets/error_pages/images/ImgLocked.svg +135 -0
  25. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/settings.py +12 -1
  26. zango-0.2.2.dev0/src/zango/config/__init__.py +0 -0
  27. zango-0.2.2.dev0/src/zango/config/settings/__init__.py +0 -0
  28. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/settings/base.py +27 -3
  29. zango-0.2.2.dev0/src/zango/core/generic_views/__init__.py +0 -0
  30. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/utils.py +8 -1
  31. zango-0.2.2.dev0/src/zango/middleware/__init__.py +0 -0
  32. zango-0.2.2.dev0/src/zango/templates/core/error_pages/account_lockout.html +47 -0
  33. zango-0.2.2.dev0/src/zango/templates/core/error_pages/base.html +40 -0
  34. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/PKG-INFO +2 -1
  35. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/SOURCES.txt +21 -2
  36. zango-0.2.2.dev0/src/zango.egg-info/requires.txt +37 -0
  37. zango-0.2.1b0/src/zango/apps/shared/platformauth/views.py +0 -25
  38. zango-0.2.1b0/src/zango/assets/app_panel/js/build.v2.0.0.min.js +0 -2
  39. {zango-0.2.1b0 → zango-0.2.2.dev0}/LICENSE +0 -0
  40. {zango-0.2.1b0 → zango-0.2.2.dev0}/README.md +0 -0
  41. {zango-0.2.1b0 → zango-0.2.2.dev0}/setup.cfg +0 -0
  42. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/__init__.py +0 -0
  43. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/__init__.py +0 -0
  44. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/__init__.py +0 -0
  45. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/__init__.py +0 -0
  46. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/serializers.py +0 -0
  47. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/urls.py +0 -0
  48. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/utils.py +0 -0
  49. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/profile/v1/views.py +0 -0
  50. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/app_auth/urls.py +0 -0
  51. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/__init__.py +0 -0
  52. {zango-0.2.1b0/src/zango/api/platform/auditlogs → zango-0.2.2.dev0/src/zango/api/platform/accesslogs}/__init__.py +0 -0
  53. {zango-0.2.1b0/src/zango/api/platform/auditlogs → zango-0.2.2.dev0/src/zango/api/platform/accesslogs}/v1/__init__.py +0 -0
  54. {zango-0.2.1b0/src/zango/api/platform/auth → zango-0.2.2.dev0/src/zango/api/platform/auditlogs}/__init__.py +0 -0
  55. {zango-0.2.1b0/src/zango/api/platform/auth → zango-0.2.2.dev0/src/zango/api/platform/auditlogs}/v1/__init__.py +0 -0
  56. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/serializers.py +0 -0
  57. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/urls.py +0 -0
  58. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auditlogs/v1/views.py +0 -0
  59. {zango-0.2.1b0/src/zango/api/platform/codeassist → zango-0.2.2.dev0/src/zango/api/platform/auth}/__init__.py +0 -0
  60. {zango-0.2.1b0/src/zango/api/platform/codeassist → zango-0.2.2.dev0/src/zango/api/platform/auth}/v1/__init__.py +0 -0
  61. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/serializers.py +0 -0
  62. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/urls.py +0 -0
  63. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/auth/v1/views.py +0 -0
  64. {zango-0.2.1b0/src/zango/api/platform/packages → zango-0.2.2.dev0/src/zango/api/platform/codeassist}/__init__.py +0 -0
  65. {zango-0.2.1b0/src/zango/api/platform/packages → zango-0.2.2.dev0/src/zango/api/platform/codeassist}/v1/__init__.py +0 -0
  66. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/urls.py +0 -0
  67. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/utils.py +0 -0
  68. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/codeassist/v1/views.py +0 -0
  69. {zango-0.2.1b0/src/zango/api/platform/permissions → zango-0.2.2.dev0/src/zango/api/platform/packages}/__init__.py +0 -0
  70. {zango-0.2.1b0/src/zango/api/platform/permissions → zango-0.2.2.dev0/src/zango/api/platform/packages}/v1/__init__.py +0 -0
  71. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/packages/v1/urls.py +0 -0
  72. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/packages/v1/views.py +0 -0
  73. {zango-0.2.1b0/src/zango/api/platform/tasks → zango-0.2.2.dev0/src/zango/api/platform/permissions}/__init__.py +0 -0
  74. {zango-0.2.1b0/src/zango/api/platform/tasks → zango-0.2.2.dev0/src/zango/api/platform/permissions}/v1/__init__.py +0 -0
  75. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/serializers.py +0 -0
  76. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/urls.py +0 -0
  77. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/permissions/v1/views.py +0 -0
  78. {zango-0.2.1b0/src/zango/api/platform/tenancy → zango-0.2.2.dev0/src/zango/api/platform/tasks}/__init__.py +0 -0
  79. {zango-0.2.1b0/src/zango/api/platform/tenancy → zango-0.2.2.dev0/src/zango/api/platform/tasks}/v1/__init__.py +0 -0
  80. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/serializers.py +0 -0
  81. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/urls.py +0 -0
  82. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tasks/v1/views.py +0 -0
  83. {zango-0.2.1b0/src/zango/apps/appauth → zango-0.2.2.dev0/src/zango/api/platform/tenancy}/__init__.py +0 -0
  84. {zango-0.2.1b0/src/zango/apps/appauth/migrations → zango-0.2.2.dev0/src/zango/api/platform/tenancy/v1}/__init__.py +0 -0
  85. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/serializers.py +0 -0
  86. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/tenancy/v1/views.py +0 -0
  87. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/api/platform/urls.py +0 -0
  88. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/__init__.py +0 -0
  89. {zango-0.2.1b0/src/zango/apps/auditlogs/management → zango-0.2.2.dev0/src/zango/apps/accesslogs}/__init__.py +0 -0
  90. {zango-0.2.1b0/src/zango/apps/auditlogs/management/commands → zango-0.2.2.dev0/src/zango/apps/accesslogs/migrations}/__init__.py +0 -0
  91. {zango-0.2.1b0/src/zango/apps/auditlogs/migrations → zango-0.2.2.dev0/src/zango/apps/appauth}/__init__.py +0 -0
  92. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/admin.py +0 -0
  93. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/apps.py +0 -0
  94. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0001_initial.py +0 -0
  95. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0002_default_user_roles.py +0 -0
  96. {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
  97. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0004_oldpasswords.py +0 -0
  98. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0005_remove_appusermodel_user.py +0 -0
  99. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/migrations/0006_appusermodel_app_objects.py +0 -0
  100. {zango-0.2.1b0/src/zango/apps/dynamic_models/management → zango-0.2.2.dev0/src/zango/apps/appauth/migrations}/__init__.py +0 -0
  101. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/models.py +0 -0
  102. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/serializers.py +0 -0
  103. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/signals.py +0 -0
  104. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/templates/app.html +0 -0
  105. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/templates/app_login_signup.html +0 -0
  106. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/tests.py +0 -0
  107. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/appauth/urls.py +0 -0
  108. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/__init__.py +0 -0
  109. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/admin.py +0 -0
  110. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/apps.py +0 -0
  111. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/cid.py +0 -0
  112. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/conf.py +0 -0
  113. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/context.py +0 -0
  114. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/diff.py +0 -0
  115. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/filters.py +0 -0
  116. {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
  117. {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
  118. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/management/commands/auditlogflush.py +0 -0
  119. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/management/commands/auditlogmigratejson.py +0 -0
  120. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/middleware.py +0 -0
  121. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/migrations/0001_initial.py +0 -0
  122. {zango-0.2.1b0/src/zango/apps/dynamic_models/workspace → zango-0.2.2.dev0/src/zango/apps/auditlogs/migrations}/__init__.py +0 -0
  123. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/mixins.py +0 -0
  124. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/models.py +0 -0
  125. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/receivers.py +0 -0
  126. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/registry.py +0 -0
  127. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/auditlogs/signals.py +0 -0
  128. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/__init__.py +0 -0
  129. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/admin.py +0 -0
  130. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/apps.py +0 -0
  131. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/fields/__init__.py +0 -0
  132. {zango-0.2.1b0/src/zango/apps/object_store → zango-0.2.2.dev0/src/zango/apps/dynamic_models/management}/__init__.py +0 -0
  133. {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
  134. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/management/commands/reload_tenant.py +0 -0
  135. {zango-0.2.1b0/src/zango/apps/permissions → zango-0.2.2.dev0/src/zango/apps/dynamic_models/migrations}/__init__.py +0 -0
  136. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/models.py +0 -0
  137. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/permissions.py +0 -0
  138. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/registry.py +0 -0
  139. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/signals.py +0 -0
  140. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/templates/default_landing.html +0 -0
  141. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/tests.py +0 -0
  142. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/urls.py +0 -0
  143. {zango-0.2.1b0/src/zango/apps/permissions/migrations → zango-0.2.2.dev0/src/zango/apps/dynamic_models/workspace}/__init__.py +0 -0
  144. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/base.py +0 -0
  145. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/lifecycle.py +0 -0
  146. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/dynamic_models/workspace/wtree.py +0 -0
  147. {zango-0.2.1b0/src/zango/apps/shared/platformauth → zango-0.2.2.dev0/src/zango/apps/object_store}/__init__.py +0 -0
  148. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/admin.py +0 -0
  149. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/apps.py +0 -0
  150. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/migrations/0001_initial.py +0 -0
  151. {zango-0.2.1b0/src/zango/apps/shared/platformauth → zango-0.2.2.dev0/src/zango/apps/object_store}/migrations/__init__.py +0 -0
  152. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/models.py +0 -0
  153. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/tests.py +0 -0
  154. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/object_store/views.py +0 -0
  155. {zango-0.2.1b0/src/zango/apps/shared/tenancy → zango-0.2.2.dev0/src/zango/apps/permissions}/__init__.py +0 -0
  156. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/admin.py +0 -0
  157. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/apps.py +0 -0
  158. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0001_initial.py +0 -0
  159. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0002_policymodel_type_alter_policymodel_expiry.py +0 -0
  160. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/migrations/0003_default_policy.py +0 -0
  161. {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
  162. {zango-0.2.1b0/src/zango/apps/shared/tenancy/management → zango-0.2.2.dev0/src/zango/apps/permissions/migrations}/__init__.py +0 -0
  163. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/mixin.py +0 -0
  164. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/models.py +0 -0
  165. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/tests.py +0 -0
  166. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/permissions/views.py +0 -0
  167. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/__init__.py +0 -0
  168. {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
  169. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/abstract_model.py +0 -0
  170. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/admin.py +0 -0
  171. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/apps.py +0 -0
  172. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/auth_backend.py +0 -0
  173. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/migrations/0001_initial.py +0 -0
  174. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/migrations/0002_platformusermodel_is_superadmin_and_more.py +0 -0
  175. {zango-0.2.1b0/src/zango/apps/shared/tenancy → zango-0.2.2.dev0/src/zango/apps/shared/platformauth}/migrations/__init__.py +0 -0
  176. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/models.py +0 -0
  177. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/tests.py +0 -0
  178. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/platformauth/urls.py +0 -0
  179. {zango-0.2.1b0/src/zango/apps/shared/tenancy/templatetags → zango-0.2.2.dev0/src/zango/apps/shared/tenancy}/__init__.py +0 -0
  180. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/admin.py +0 -0
  181. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/apps.py +0 -0
  182. {zango-0.2.1b0/src/zango/apps/tasks → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/management}/__init__.py +0 -0
  183. {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
  184. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/sync_static.py +0 -0
  185. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/ws_makemigration.py +0 -0
  186. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/management/commands/ws_migrate.py +0 -0
  187. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/migrations/0001_initial.py +0 -0
  188. {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
  189. {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
  190. {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
  191. {zango-0.2.1b0/src/zango/config → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/migrations}/__init__.py +0 -0
  192. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/models.py +0 -0
  193. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/tasks.py +0 -0
  194. {zango-0.2.1b0/src/zango/config/settings → zango-0.2.2.dev0/src/zango/apps/shared/tenancy/templatetags}/__init__.py +0 -0
  195. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/templatetags/zstatic.py +0 -0
  196. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/tests.py +0 -0
  197. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/urls.py +0 -0
  198. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/utils.py +0 -0
  199. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/views.py +0 -0
  200. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/cookiecutter.json +0 -0
  201. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/manifest.json +0 -0
  202. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/shared/tenancy/workspace_folder_template/{{cookiecutter.app_name}}/settings.json +0 -0
  203. {zango-0.2.1b0/src/zango/core/generic_views → zango-0.2.2.dev0/src/zango/apps/tasks}/__init__.py +0 -0
  204. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/apps.py +0 -0
  205. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/migrations/0001_initial.py +0 -0
  206. {zango-0.2.1b0/src/zango/middleware → zango-0.2.2.dev0/src/zango/apps/tasks/migrations}/__init__.py +0 -0
  207. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/models.py +0 -0
  208. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/apps/tasks/utils.py +0 -0
  209. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_landing/css/styles.css +0 -0
  210. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_panel/css/styles.css +0 -0
  211. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/app_panel/images/zangoLogo.svg +0 -0
  212. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/assets/js/jquery/3.7.1/jquery.min.js +0 -0
  213. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/__init__.py +0 -0
  214. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/install_package.py +0 -0
  215. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/package_info.py +0 -0
  216. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/manage.py +0 -0
  217. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/__init__.py +0 -0
  218. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/asgi.py +0 -0
  219. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls.py +0 -0
  220. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls_public.py +0 -0
  221. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/urls_tenants.py +0 -0
  222. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/project_template/project_name/wsgi.py +0 -0
  223. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/start_project.py +0 -0
  224. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/cli/utils.py +0 -0
  225. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/celery.py +0 -0
  226. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/urls_public.py +0 -0
  227. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/config/urls_tenants.py +0 -0
  228. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/__init__.py +0 -0
  229. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/__init__.py +0 -0
  230. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/base.py +0 -0
  231. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/api/utils.py +0 -0
  232. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/common_utils.py +0 -0
  233. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/custom_pluginbase.py +0 -0
  234. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/decorators.py +0 -0
  235. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/generic_views/base.py +0 -0
  236. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/internal_requests.py +0 -0
  237. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/model_mixins.py +0 -0
  238. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/package_utils.py +0 -0
  239. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/permissions.py +0 -0
  240. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/storage_utils.py +0 -0
  241. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/tasks.py +0 -0
  242. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/core/template_loader.py +0 -0
  243. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/middleware/request.py +0 -0
  244. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango/middleware/tenant.py +0 -0
  245. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/dependency_links.txt +0 -0
  246. {zango-0.2.1b0 → zango-0.2.2.dev0}/src/zango.egg-info/entry_points.txt +0 -0
  247. {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.1b0
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">
@@ -34,3 +34,4 @@ python3-saml==1.16.0
34
34
  django-ipware==7.0.1
35
35
  django-decorator-include==3.0
36
36
  setuptools==70.1.1
37
+ django-axes==6.4.0
@@ -8,7 +8,7 @@ REQUIREMENTS_DIR = os.path.join(PROJECT_DIR, "requirements")
8
8
 
9
9
  README = os.path.join(PROJECT_DIR, "README.md")
10
10
 
11
- PLATFORM_VERSION = "0.2.1.beta"
11
+ PLATFORM_VERSION = "0.2.2.dev"
12
12
 
13
13
 
14
14
  def get_requirements(env):
@@ -1,3 +1,3 @@
1
1
  from zango.core import internal_requests
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2.dev"
@@ -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,11 @@
1
+ from django.urls import path
2
+
3
+ from zango.api.platform.accesslogs.v1.views import AccessLogViewAPIV1
4
+
5
+ urlpatterns = [
6
+ path(
7
+ "",
8
+ AccessLogViewAPIV1.as_view(),
9
+ name="accesslog-apiv1-accessloglistview",
10
+ ),
11
+ ]
@@ -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,9 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AccesslogsAppConfig(AppConfig):
5
+
6
+ name = "zango.apps.accesslogs"
7
+
8
+ def ready(self):
9
+ import zango.apps.accesslogs.signals
@@ -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
- try:
14
- user = AppUserModel.objects.get(Q(email=username) | Q(mobile=username))
15
- pwd_valid = user.check_password(password)
16
- if pwd_valid and user.is_active:
17
- return user
18
- return None
19
- except AppUserModel.DoesNotExist:
20
- return None
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"