openwisp-users 1.3.0__tar.gz → 1.3.2__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.
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/CHANGES.rst +22 -0
- {openwisp_users-1.3.0/openwisp_users.egg-info → openwisp_users-1.3.2}/PKG-INFO +1 -1
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/__init__.py +1 -1
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/static/openwisp-users/accounts/css/non-admin.css +5 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/base/models.py +2 -2
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/js/org-autocomplete.js +3 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tasks.py +5 -2
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_models.py +94 -7
- {openwisp_users-1.3.0 → openwisp_users-1.3.2/openwisp_users.egg-info}/PKG-INFO +1 -1
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/LICENSE +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/MANIFEST.in +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/README.rst +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/adapter.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/locale/de/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/locale/fur/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/locale/it/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/locale/ru/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/locale/sl/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/static/openwisp-users/accounts/css/change-password.css +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/account_expiration_reminder_message.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/account_expiration_reminder_message.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/account_expired_message.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/account_expired_message.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/base_message.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/email_confirmation_message.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/email_confirmation_message.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/email_confirmation_signup_message.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/password_expiration_message.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email/password_expiration_message.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email_confirm.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/email_verification_success.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/login.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/logout_success.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/password_change.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/password_change_success.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/password_not_required.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/allauth/layouts/base.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/urls.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/views.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/admin.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/authentication.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/filters.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/mixins.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/permissions.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/serializers.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/swagger.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/throttling.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/urls.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/api/views.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/apps.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/auth.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/backends.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/base/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/base/forms.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/de/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/fur/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/it/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/ru/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/sl/LC_MESSAGES/django.po +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/management/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/management/commands/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/management/commands/export_users.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/middleware.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0001_initial.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0002_auto_20180508_2017.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0003_default_organization.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0004_default_groups.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0005_user_phone_number.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0006_id_email_index_together.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0007_unique_email.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0008_update_admins_permissions.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0009_create_organization_owners.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0010_allow_admins_change_organization.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0011_user_first_name_150_max_length.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0012_user_location_max_length_256.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0013_user_birth_date.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0014_user_notes.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0015_alter_organization_users_alter_organizationuser_user.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0016_organizationinvitation.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0017_user_language.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0018_allow_operator_view_organisation.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0019_user_password_updated.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0020_populate_password_updated_field.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0021_rename_user_id_email_openwisp_us_id_06c07a_idx.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0022_user_expiration_date.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0023_add_auth_token_permissions_to_admins.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0024_apikey.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0025_alter_organizationuser_is_admin.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0026_user_password_based_token.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/mixins.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/models.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/multitenancy.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/password_validation.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/settings.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/css/admin.css +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/css/change-password.css +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/js/addform.js +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/admin/action_confirmation.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/admin/login.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/admin/openwisp_users/action_confirmation.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/admin/openwisp_users/login.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/admin/openwisp_users/user/change_form.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/custom_password_reset_email.html +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/templates/custom_password_reset_email.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_accounts.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_adapter.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_admin.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/__init__.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_api.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_authentication.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_password_reset.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_throttling.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_urls.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_views.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_auth.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_backends.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_commands.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_middlewares.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/utils.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/utils.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/views.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/widgets.py +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users.egg-info/SOURCES.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users.egg-info/dependency_links.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users.egg-info/not-zip-safe +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users.egg-info/requires.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users.egg-info/top_level.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/pyproject.toml +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/requirements.txt +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/setup.cfg +0 -0
- {openwisp_users-1.3.0 → openwisp_users-1.3.2}/setup.py +0 -0
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
Version 1.3.2 [2026-10-23]
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
Bugfixes
|
|
8
|
+
~~~~~~~~
|
|
9
|
+
|
|
10
|
+
- Restored logo padding on mobile screens
|
|
11
|
+
|
|
12
|
+
Rules in `openwisp.css` removed the logo padding on non-admin screens,
|
|
13
|
+
making it appear cramped.
|
|
14
|
+
|
|
15
|
+
- Corrected password expiration dates and duplicate notifications `#375
|
|
16
|
+
<https://github.com/openwisp/openwisp-users/issues/375>`_
|
|
17
|
+
|
|
18
|
+
Version 1.3.1 [2026-09-09]
|
|
19
|
+
--------------------------
|
|
20
|
+
|
|
21
|
+
Bugfixes
|
|
22
|
+
~~~~~~~~
|
|
23
|
+
|
|
24
|
+
- Prevented autocomplete loop without an organization field
|
|
25
|
+
|
|
4
26
|
Version 1.3.0 [2026-09-02]
|
|
5
27
|
--------------------------
|
|
6
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openwisp-users
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Provides basic multi-tenancy features for OpenWISP (using the Django web-framework)
|
|
5
5
|
Home-page: http://openwisp.org
|
|
6
6
|
Download-URL: https://github.com/openwisp/openwisp-users/releases
|
|
@@ -129,7 +129,7 @@ class AbstractUser(BaseUser):
|
|
|
129
129
|
return str(pk)
|
|
130
130
|
|
|
131
131
|
def set_password(self, *args, **kwargs):
|
|
132
|
-
self.password_updated =
|
|
132
|
+
self.password_updated = localdate()
|
|
133
133
|
return super().set_password(*args, **kwargs)
|
|
134
134
|
|
|
135
135
|
def has_password_expired(self):
|
|
@@ -145,7 +145,7 @@ class AbstractUser(BaseUser):
|
|
|
145
145
|
)
|
|
146
146
|
else:
|
|
147
147
|
return False
|
|
148
|
-
return expiry_date <
|
|
148
|
+
return expiry_date < localdate()
|
|
149
149
|
|
|
150
150
|
def is_member(self, organization):
|
|
151
151
|
return self._get_pk(organization) in self.organizations_dict
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
// Hence, we need to update the value of selected option before
|
|
11
11
|
// submission of form.
|
|
12
12
|
var formElement = $("select#id_organization");
|
|
13
|
+
if (formElement.length === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
13
16
|
while (formElement.prop("tagName") !== "FORM") {
|
|
14
17
|
formElement = formElement.parent();
|
|
15
18
|
}
|
|
@@ -6,7 +6,7 @@ from django.db.models import Q
|
|
|
6
6
|
from django.template.loader import render_to_string
|
|
7
7
|
from django.urls import reverse
|
|
8
8
|
from django.utils import translation
|
|
9
|
-
from django.utils.timezone import
|
|
9
|
+
from django.utils.timezone import localdate, timedelta
|
|
10
10
|
from django.utils.translation import gettext_lazy as _
|
|
11
11
|
from swapper import load_model
|
|
12
12
|
|
|
@@ -30,7 +30,7 @@ def password_expiration_email():
|
|
|
30
30
|
):
|
|
31
31
|
# The password expiration feature is not enabled
|
|
32
32
|
return
|
|
33
|
-
expiry_date =
|
|
33
|
+
expiry_date = localdate() + timedelta(days=7)
|
|
34
34
|
query = Q()
|
|
35
35
|
if app_settings.USER_PASSWORD_EXPIRATION:
|
|
36
36
|
query |= Q(
|
|
@@ -54,6 +54,9 @@ def password_expiration_email():
|
|
|
54
54
|
emailaddress__verified=True,
|
|
55
55
|
)
|
|
56
56
|
.filter(query)
|
|
57
|
+
# a user can own more than one verified email address:
|
|
58
|
+
# without distinct() the join would send one email per address
|
|
59
|
+
.distinct()
|
|
57
60
|
)
|
|
58
61
|
email_count = 0
|
|
59
62
|
for user in qs.iterator():
|
|
@@ -416,7 +416,7 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
416
416
|
|
|
417
417
|
# User.objects.create_user does not call User.set_password.
|
|
418
418
|
# Therefore, we set the password_updated field manually.
|
|
419
|
-
User.objects.update(password_updated=
|
|
419
|
+
User.objects.update(password_updated=localdate())
|
|
420
420
|
staff_user.refresh_from_db()
|
|
421
421
|
end_user.refresh_from_db()
|
|
422
422
|
|
|
@@ -437,7 +437,7 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
437
437
|
self.assertEqual(end_user.has_password_expired(), False)
|
|
438
438
|
|
|
439
439
|
with self.subTest("Test password is expired"):
|
|
440
|
-
User.objects.update(password_updated=
|
|
440
|
+
User.objects.update(password_updated=localdate() - timedelta(days=180))
|
|
441
441
|
staff_user.refresh_from_db()
|
|
442
442
|
end_user.refresh_from_db()
|
|
443
443
|
with (
|
|
@@ -459,10 +459,10 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
459
459
|
@patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30)
|
|
460
460
|
@patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 90)
|
|
461
461
|
def test_password_expiration_mail(self):
|
|
462
|
-
user_expiry_date =
|
|
462
|
+
user_expiry_date = localdate() - timedelta(
|
|
463
463
|
days=(app_settings.USER_PASSWORD_EXPIRATION - 7)
|
|
464
464
|
)
|
|
465
|
-
staff_user_expiry_date =
|
|
465
|
+
staff_user_expiry_date = localdate() - timedelta(
|
|
466
466
|
days=(app_settings.STAFF_USER_PASSWORD_EXPIRATION - 7)
|
|
467
467
|
)
|
|
468
468
|
staff_user = self._create_operator()
|
|
@@ -498,7 +498,7 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
498
498
|
password_updated=user_expiry_date
|
|
499
499
|
)
|
|
500
500
|
password_expiration_email.delay()
|
|
501
|
-
expected_date = localize((
|
|
501
|
+
expected_date = localize(localdate() + timedelta(days=7))
|
|
502
502
|
self.assertEqual(len(mail.outbox), 1)
|
|
503
503
|
email = mail.outbox.pop()
|
|
504
504
|
self.assertEqual(email.to, [verified_email_user.email])
|
|
@@ -525,7 +525,7 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
525
525
|
@patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30)
|
|
526
526
|
@patch("openwisp_users.utils.sleep")
|
|
527
527
|
def test_password_expiration_mail_sleep(self, mocked_sleep):
|
|
528
|
-
user_expiry_date =
|
|
528
|
+
user_expiry_date = localdate() - timedelta(
|
|
529
529
|
days=(app_settings.USER_PASSWORD_EXPIRATION - 7)
|
|
530
530
|
)
|
|
531
531
|
for i in range(10):
|
|
@@ -547,7 +547,7 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
547
547
|
self.assertEqual(app_settings.USER_PASSWORD_EXPIRATION, 0)
|
|
548
548
|
self.assertEqual(app_settings.STAFF_USER_PASSWORD_EXPIRATION, 0)
|
|
549
549
|
self._create_user()
|
|
550
|
-
User.objects.update(password_updated=
|
|
550
|
+
User.objects.update(password_updated=localdate() - timedelta(days=180))
|
|
551
551
|
with patch("openwisp_users.tasks.send_email") as mocked_send_email:
|
|
552
552
|
password_expiration_email.delay()
|
|
553
553
|
mocked_send_email.assert_not_called()
|
|
@@ -1195,3 +1195,90 @@ class TestUsers(TestOrganizationMixin, TestCase):
|
|
|
1195
1195
|
expiration_reminder_email,
|
|
1196
1196
|
None,
|
|
1197
1197
|
)
|
|
1198
|
+
|
|
1199
|
+
@patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30)
|
|
1200
|
+
def test_password_expiration_mail_multiple_verified_emails(self):
|
|
1201
|
+
user = self._create_user()
|
|
1202
|
+
for index in range(2):
|
|
1203
|
+
EmailAddress.objects.create(
|
|
1204
|
+
user=user,
|
|
1205
|
+
email=f"alias{index}@tester.com",
|
|
1206
|
+
verified=True,
|
|
1207
|
+
)
|
|
1208
|
+
User.objects.update(
|
|
1209
|
+
password_updated=localdate()
|
|
1210
|
+
- timedelta(days=app_settings.USER_PASSWORD_EXPIRATION - 7)
|
|
1211
|
+
)
|
|
1212
|
+
password_expiration_email.delay()
|
|
1213
|
+
self.assertEqual(
|
|
1214
|
+
len(mail.outbox),
|
|
1215
|
+
1,
|
|
1216
|
+
msg=(
|
|
1217
|
+
f"Expected 1 password expiry notice, got {len(mail.outbox)}:"
|
|
1218
|
+
" the notice is duplicated once per verified email address"
|
|
1219
|
+
" of the same user"
|
|
1220
|
+
),
|
|
1221
|
+
)
|
|
1222
|
+
self.assertEqual(mail.outbox[0].to, [user.email])
|
|
1223
|
+
|
|
1224
|
+
@freeze_time("2026-03-29 23:30:00")
|
|
1225
|
+
@override_settings(TIME_ZONE="Europe/Rome")
|
|
1226
|
+
@patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30)
|
|
1227
|
+
def test_password_expiration_local_date(self):
|
|
1228
|
+
"""
|
|
1229
|
+
23:30 UTC is already the next day in Europe/Rome (UTC+2), so password
|
|
1230
|
+
expiration dates must follow the local calendar day.
|
|
1231
|
+
"""
|
|
1232
|
+
user = self._create_user()
|
|
1233
|
+
|
|
1234
|
+
with self.subTest("password_updated is stamped with the local date"):
|
|
1235
|
+
user.set_password("tester")
|
|
1236
|
+
self.assertEqual(
|
|
1237
|
+
user.password_updated,
|
|
1238
|
+
localdate(),
|
|
1239
|
+
msg=(
|
|
1240
|
+
f"Expected password_updated {localdate()} (local date),"
|
|
1241
|
+
f" got {user.password_updated} (UTC date)"
|
|
1242
|
+
),
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
expiration = app_settings.USER_PASSWORD_EXPIRATION
|
|
1246
|
+
|
|
1247
|
+
with self.subTest("password is valid on the expiration date"):
|
|
1248
|
+
user.password_updated = localdate() - timedelta(days=expiration)
|
|
1249
|
+
self.assertFalse(
|
|
1250
|
+
user.has_password_expired(),
|
|
1251
|
+
msg="Expected password to remain valid on its expiration date",
|
|
1252
|
+
)
|
|
1253
|
+
|
|
1254
|
+
with self.subTest("password is expired after the expiration date"):
|
|
1255
|
+
user.password_updated = localdate() - timedelta(days=expiration + 1)
|
|
1256
|
+
self.assertTrue(
|
|
1257
|
+
user.has_password_expired(),
|
|
1258
|
+
msg=(
|
|
1259
|
+
"Expected the password to be expired: it expired on"
|
|
1260
|
+
f" {user.password_updated + timedelta(days=expiration)},"
|
|
1261
|
+
f" while the local date is {localdate()}"
|
|
1262
|
+
),
|
|
1263
|
+
)
|
|
1264
|
+
|
|
1265
|
+
@freeze_time("2026-03-29 23:30:00")
|
|
1266
|
+
@override_settings(TIME_ZONE="Europe/Rome")
|
|
1267
|
+
@patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30)
|
|
1268
|
+
def test_password_expiration_mail_local_date(self):
|
|
1269
|
+
self._create_user()
|
|
1270
|
+
User.objects.update(
|
|
1271
|
+
password_updated=localdate()
|
|
1272
|
+
- timedelta(days=app_settings.USER_PASSWORD_EXPIRATION - 7)
|
|
1273
|
+
)
|
|
1274
|
+
password_expiration_email.delay()
|
|
1275
|
+
self.assertEqual(
|
|
1276
|
+
len(mail.outbox),
|
|
1277
|
+
1,
|
|
1278
|
+
msg=(
|
|
1279
|
+
f"Expected 1 password expiry notice, got {len(mail.outbox)}:"
|
|
1280
|
+
" the task looks for passwords expiring 7 days after the UTC"
|
|
1281
|
+
f" date ({now().date()}) instead of the local date"
|
|
1282
|
+
f" ({localdate()})"
|
|
1283
|
+
),
|
|
1284
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openwisp-users
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Provides basic multi-tenancy features for OpenWISP (using the Django web-framework)
|
|
5
5
|
Home-page: http://openwisp.org
|
|
6
6
|
Download-URL: https://github.com/openwisp/openwisp-users/releases
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/accounts/templates/account/login.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/de/LC_MESSAGES/django.po
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/fur/LC_MESSAGES/django.po
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/it/LC_MESSAGES/django.po
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/ru/LC_MESSAGES/django.po
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/locale/sl/LC_MESSAGES/django.po
RENAMED
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/management/commands/__init__.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/management/commands/export_users.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0002_auto_20180508_2017.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0003_default_organization.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0004_default_groups.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0005_user_phone_number.py
RENAMED
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0007_unique_email.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0013_user_birth_date.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0017_user_language.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/migrations/0022_user_expiration_date.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/css/admin.css
RENAMED
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/static/openwisp-users/js/addform.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_authentication.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_password_reset.py
RENAMED
|
File without changes
|
{openwisp_users-1.3.0 → openwisp_users-1.3.2}/openwisp_users/tests/test_api/test_throttling.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|