[FR] User sessions

Fixes #2327
This commit is contained in:
Matthias 2021-11-26 23:56:24 +01:00
parent 82d5952ddd
commit db1a434f81
No known key found for this signature in database
GPG Key ID: F50EF5741D33E076
5 changed files with 76 additions and 2 deletions

View File

@ -257,7 +257,7 @@ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'user_sessions', # db user sessions
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.sites', 'django.contrib.sites',
@ -299,7 +299,7 @@ INSTALLED_APPS = [
MIDDLEWARE = CONFIG.get('middleware', [ MIDDLEWARE = CONFIG.get('middleware', [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'user_sessions.middleware.SessionMiddleware', # db user sessions
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
@ -626,6 +626,12 @@ if _cache_host:
# as well # as well
Q_CLUSTER["django_redis"] = "worker" 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 # Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators

View File

@ -38,6 +38,7 @@ from rest_framework.documentation import include_docs_urls
from .views import auth_request from .views import auth_request
from .views import IndexView, SearchView, DatabaseStatsView from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView
from .views import CustomSessionDeleteView, CustomSessionDeleteOtherView
from .views import CurrencyRefreshView from .views import CurrencyRefreshView
from .views import AppearanceSelectView, SettingCategorySelectView from .views import AppearanceSelectView, SettingCategorySelectView
from .views import DynamicJsView from .views import DynamicJsView
@ -156,6 +157,10 @@ urlpatterns = [
url(r'^markdownx/', include('markdownx.urls')), 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<pk>\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ),
# Single Sign On / allauth # Single Sign On / allauth
# overrides of urlpatterns # overrides of urlpatterns
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'), url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),

View File

@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.timezone import now
from django.shortcuts import redirect from django.shortcuts import redirect
from django.conf import settings from django.conf import settings
@ -29,6 +30,7 @@ from allauth.socialaccount.forms import DisconnectForm
from allauth.account.models import EmailAddress from allauth.account.models import EmailAddress
from allauth.account.views import EmailView, PasswordResetFromKeyView from allauth.account.views import EmailView, PasswordResetFromKeyView
from allauth.socialaccount.views import ConnectionsView from allauth.socialaccount.views import ConnectionsView
from user_sessions.views import SessionDeleteView, SessionDeleteOtherView
from common.settings import currency_code_default, currency_codes from common.settings import currency_code_default, currency_codes
@ -733,6 +735,10 @@ class SettingsView(TemplateView):
ctx["request"] = self.request ctx["request"] = self.request
ctx['social_form'] = DisconnectForm(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 return ctx
@ -766,6 +772,20 @@ class CustomPasswordResetFromKeyView(PasswordResetFromKeyView):
success_url = reverse_lazy("account_login") 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): class CurrencyRefreshView(RedirectView):
""" """
POST endpoint to refresh / update exchange rates POST endpoint to refresh / update exchange rates

View File

@ -4,6 +4,7 @@
{% load inventree_extras %} {% load inventree_extras %}
{% load socialaccount %} {% load socialaccount %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load user_sessions i18n %}
{% block label %}account{% endblock %} {% block label %}account{% endblock %}
@ -173,6 +174,47 @@
</div> </div>
<div class='panel-heading'>
<h4>{% trans "Active Sessions" %}</h4>
</div>
<div>
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
{% trans "<em>unknown</em>" as unknown %}
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{% trans "IP Address" %}</th>
<th>{% trans "Device" %}</th>
<th>{% trans "Last Activity" %}</th>
</tr>
</thead>
{% for object in session_list %}
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
<td>{{ object.ip }}</td>
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
<td>
{% 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 %}
</td>
</tr>
{% endfor %}
</table>
{% if session_list.count > 1 %}
<form method="post" action="{% url 'session_delete_other' %}">
{% csrf_token %}
<p>{% blocktrans %}You can also end all other sessions but the current.
This will log you out on all other devices.{% endblocktrans %}
<button type="submit" class="btn btn-sm btn-default btn-danger">{% trans "End All Other Sessions" %}</button></p>
</form>
{% endif %}
</div>
<div class='panel-heading'> <div class='panel-heading'>
<h4>{% trans "Language Settings" %}</h4> <h4>{% trans "Language Settings" %}</h4>
</div> </div>

View File

@ -23,6 +23,7 @@ django-q==1.3.4 # Background task scheduling
django-sql-utils==0.5.0 # Advanced query annotation / aggregation django-sql-utils==0.5.0 # Advanced query annotation / aggregation
django-stdimage==5.1.1 # Advanced ImageField management django-stdimage==5.1.1 # Advanced ImageField management
django-test-migrations==1.1.0 # Unit testing for database migrations 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 django-weasyprint==1.0.1 # django weasyprint integration
djangorestframework==3.12.4 # DRF framework djangorestframework==3.12.4 # DRF framework
flake8==3.8.3 # PEP checking flake8==3.8.3 # PEP checking