From db1a434f81524a98cf5cce2312f544db65e60116 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 26 Nov 2021 23:56:24 +0100 Subject: [PATCH 1/7] [FR] User sessions Fixes #2327 --- InvenTree/InvenTree/settings.py | 10 ++++- InvenTree/InvenTree/urls.py | 5 +++ InvenTree/InvenTree/views.py | 20 +++++++++ .../templates/InvenTree/settings/user.html | 42 +++++++++++++++++++ requirements.txt | 1 + 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 038d07600f..df84ba315e 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -257,7 +257,7 @@ INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', - 'django.contrib.sessions', + 'user_sessions', # db user sessions 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', @@ -299,7 +299,7 @@ INSTALLED_APPS = [ MIDDLEWARE = CONFIG.get('middleware', [ 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', + 'user_sessions.middleware.SessionMiddleware', # db user sessions 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -626,6 +626,12 @@ if _cache_host: # as well Q_CLUSTER["django_redis"] = "worker" +# database user sessions +SESSION_ENGINE = 'user_sessions.backends.db' +LOGOUT_REDIRECT_URL = 'index' +SILENCED_SYSTEM_CHECKS = [ + 'admin.E410', +] # Password validation # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 584403fc84..15ed8d5478 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -38,6 +38,7 @@ from rest_framework.documentation import include_docs_urls from .views import auth_request from .views import IndexView, SearchView, DatabaseStatsView from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView +from .views import CustomSessionDeleteView, CustomSessionDeleteOtherView from .views import CurrencyRefreshView from .views import AppearanceSelectView, SettingCategorySelectView from .views import DynamicJsView @@ -156,6 +157,10 @@ urlpatterns = [ url(r'^markdownx/', include('markdownx.urls')), + # DB user sessions + url(r'^accounts/sessions/other/delete/$', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ), + url(r'^accounts/sessions/(?P\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ), + # Single Sign On / allauth # overrides of urlpatterns url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'), diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 989fb1bc9d..a5c4da48b6 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _ from django.template.loader import render_to_string from django.http import HttpResponse, JsonResponse, HttpResponseRedirect from django.urls import reverse_lazy +from django.utils.timezone import now from django.shortcuts import redirect from django.conf import settings @@ -29,6 +30,7 @@ from allauth.socialaccount.forms import DisconnectForm from allauth.account.models import EmailAddress from allauth.account.views import EmailView, PasswordResetFromKeyView from allauth.socialaccount.views import ConnectionsView +from user_sessions.views import SessionDeleteView, SessionDeleteOtherView from common.settings import currency_code_default, currency_codes @@ -733,6 +735,10 @@ class SettingsView(TemplateView): ctx["request"] = self.request ctx['social_form'] = DisconnectForm(request=self.request) + # user db sessions + ctx['session_key'] = self.request.session.session_key + ctx['session_list'] = self.request.user.session_set.filter(expire_date__gt=now()).order_by('-last_activity') + return ctx @@ -766,6 +772,20 @@ class CustomPasswordResetFromKeyView(PasswordResetFromKeyView): success_url = reverse_lazy("account_login") +class UserSessionOverride(): + """overrides sucessurl to lead to settings""" + def get_success_url(self): + return str(reverse_lazy('settings')) + + +class CustomSessionDeleteView(UserSessionOverride, SessionDeleteView): + pass + + +class CustomSessionDeleteOtherView(UserSessionOverride, SessionDeleteOtherView): + pass + + class CurrencyRefreshView(RedirectView): """ POST endpoint to refresh / update exchange rates diff --git a/InvenTree/templates/InvenTree/settings/user.html b/InvenTree/templates/InvenTree/settings/user.html index a4c12a9bb0..43dee386be 100644 --- a/InvenTree/templates/InvenTree/settings/user.html +++ b/InvenTree/templates/InvenTree/settings/user.html @@ -4,6 +4,7 @@ {% load inventree_extras %} {% load socialaccount %} {% load crispy_forms_tags %} +{% load user_sessions i18n %} {% block label %}account{% endblock %} @@ -173,6 +174,47 @@ +
+

{% trans "Active Sessions" %}

+
+ +
+ {% trans "unknown on unknown" as unknown_on_unknown %} + {% trans "unknown" as unknown %} + + + + + + + + + {% for object in session_list %} + + + + + + {% endfor %} +
{% trans "IP Address" %}{% trans "Device" %}{% trans "Last Activity" %}
{{ object.ip }}{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }} + {% if object.session_key == session_key %} + {% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %} + {% else %} + {% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %} + {% endif %} +
+ + {% if session_list.count > 1 %} +
+ {% csrf_token %} +

{% blocktrans %}You can also end all other sessions but the current. + This will log you out on all other devices.{% endblocktrans %} +

+
+ {% endif %} +
+ +

{% trans "Language Settings" %}

diff --git a/requirements.txt b/requirements.txt index 139942fd80..5f5695ed3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ django-q==1.3.4 # Background task scheduling django-sql-utils==0.5.0 # Advanced query annotation / aggregation django-stdimage==5.1.1 # Advanced ImageField management django-test-migrations==1.1.0 # Unit testing for database migrations +django-user-sessions==1.7.1 # user sessions in DB django-weasyprint==1.0.1 # django weasyprint integration djangorestframework==3.12.4 # DRF framework flake8==3.8.3 # PEP checking From 84f0966e4fd8a3c5d6a6850e9441bef6bb69e2d5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 27 Nov 2021 00:02:39 +0100 Subject: [PATCH 2/7] movelanguage to display settings --- .../templates/InvenTree/settings/user.html | 55 ------------------- .../InvenTree/settings/user_display.html | 53 ++++++++++++++++++ 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/InvenTree/templates/InvenTree/settings/user.html b/InvenTree/templates/InvenTree/settings/user.html index 43dee386be..97ed4805c9 100644 --- a/InvenTree/templates/InvenTree/settings/user.html +++ b/InvenTree/templates/InvenTree/settings/user.html @@ -213,61 +213,6 @@ {% endif %} - - -
-

{% trans "Language Settings" %}

-
- -
-
-
- {% csrf_token %} - - -
- -
- -
-
-

{% trans "Some languages are not complete" %} - {% if ALL_LANG %} - . {% trans "Show only sufficent" %} - {% else %} - {% trans "and hidden." %} {% trans "Show them too" %} - {% endif %} -

-
-
-
-

{% trans "Help the translation efforts!" %}

-

{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is community contributed via crowdin. Contributions are welcomed and encouraged.{% endblocktrans %}

-
-
- {% endblock %} {% block js_ready %} diff --git a/InvenTree/templates/InvenTree/settings/user_display.html b/InvenTree/templates/InvenTree/settings/user_display.html index ae7843df9c..9f5c22f991 100644 --- a/InvenTree/templates/InvenTree/settings/user_display.html +++ b/InvenTree/templates/InvenTree/settings/user_display.html @@ -50,4 +50,57 @@ +
+

{% trans "Language Settings" %}

+
+ +
+
+
+ {% csrf_token %} + + +
+ +
+ +
+
+

{% trans "Some languages are not complete" %} + {% if ALL_LANG %} + . {% trans "Show only sufficent" %} + {% else %} + {% trans "and hidden." %} {% trans "Show them too" %} + {% endif %} +

+
+
+
+

{% trans "Help the translation efforts!" %}

+

{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is community contributed via crowdin. Contributions are welcomed and encouraged.{% endblocktrans %}

+
+
+ {% endblock %} \ No newline at end of file From 6511a774d141d54c7e83854abef778e9f0cc5eb7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 27 Nov 2021 00:04:43 +0100 Subject: [PATCH 3/7] exclude sessions from imort / exports --- tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks.py b/tasks.py index 21f7616d76..a8f9bcaf84 100644 --- a/tasks.py +++ b/tasks.py @@ -291,6 +291,7 @@ def content_excludes(): "exchange.rate", "exchange.exchangebackend", "common.notificationentry", + "sessions.session", ] output = "" From ace4370ee9773292b9493a15780cd5c9caddbe0f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Nov 2021 16:50:34 +0100 Subject: [PATCH 4/7] add ruleset --- InvenTree/users/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 4d1b46ae5d..c9ce20e567 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -159,6 +159,7 @@ class RuleSet(models.Model): 'error_report_error', 'exchange_rate', 'exchange_exchangebackend', + 'django_session', # Django-q 'django_q_ormq', From 5f685f3c2a283604bcefae382754b5c1d7b63a45 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Nov 2021 16:54:11 +0100 Subject: [PATCH 5/7] fix db sessions in import / export --- tasks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index a8f9bcaf84..f56ed44bc8 100644 --- a/tasks.py +++ b/tasks.py @@ -279,7 +279,6 @@ def content_excludes(): excludes = [ "contenttypes", - "sessions.session", "auth.permission", "authtoken.token", "error_report.error", @@ -291,7 +290,7 @@ def content_excludes(): "exchange.rate", "exchange.exchangebackend", "common.notificationentry", - "sessions.session", + "django_session", ] output = "" From 9454b51866bcaec85446416743b25352aace6521 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Nov 2021 17:18:46 +0100 Subject: [PATCH 6/7] fix rules --- InvenTree/users/models.py | 3 +-- tasks.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index c9ce20e567..48644e1889 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -145,7 +145,6 @@ class RuleSet(models.Model): # Core django models (not user configurable) 'admin_logentry', 'contenttypes_contenttype', - 'sessions_session', # Models which currently do not require permissions 'common_colortheme', @@ -159,7 +158,7 @@ class RuleSet(models.Model): 'error_report_error', 'exchange_rate', 'exchange_exchangebackend', - 'django_session', + 'user_sessions_session', # Django-q 'django_q_ormq', diff --git a/tasks.py b/tasks.py index f56ed44bc8..51df809149 100644 --- a/tasks.py +++ b/tasks.py @@ -290,7 +290,7 @@ def content_excludes(): "exchange.rate", "exchange.exchangebackend", "common.notificationentry", - "django_session", + "user_sessions_session", ] output = "" From a06d91d9a3218d5a63b700ddc2a58b81159539a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Nov 2021 17:21:54 +0100 Subject: [PATCH 7/7] fix reference name --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index 51df809149..7408bb40b5 100644 --- a/tasks.py +++ b/tasks.py @@ -290,7 +290,7 @@ def content_excludes(): "exchange.rate", "exchange.exchangebackend", "common.notificationentry", - "user_sessions_session", + "user_sessions.session", ] output = ""