mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Merge pull request #2371 from matmair/matmair/issue2327
[FR] User sessions
This commit is contained in:
commit
cbc3089525
@ -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
|
||||||
|
@ -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'),
|
||||||
|
@ -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
|
||||||
|
@ -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 %}
|
||||||
|
|
||||||
@ -174,58 +175,44 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='panel-heading'>
|
<div class='panel-heading'>
|
||||||
<h4>{% trans "Language Settings" %}</h4>
|
<h4>{% trans "Active Sessions" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div>
|
||||||
<div class="col">
|
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
|
||||||
<form action="{% url 'set_language' %}" method="post">
|
{% trans "<em>unknown</em>" as unknown %}
|
||||||
{% csrf_token %}
|
<table class="table table-striped table-condensed">
|
||||||
<input name="next" type="hidden" value="{% url 'settings' %}">
|
<thead>
|
||||||
<label for='language' class=' requiredField'>
|
<tr>
|
||||||
{% trans "Select language" %}
|
<th>{% trans "IP Address" %}</th>
|
||||||
</label>
|
<th>{% trans "Device" %}</th>
|
||||||
<div class='form-group input-group mb-3'>
|
<th>{% trans "Last Activity" %}</th>
|
||||||
<select name="language" class="select form-control w-25">
|
</tr>
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
</thead>
|
||||||
{% get_available_languages as LANGUAGES %}
|
{% for object in session_list %}
|
||||||
{% get_language_info_list for LANGUAGES as languages %}
|
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
|
||||||
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
|
<td>{{ object.ip }}</td>
|
||||||
{% for language in languages %}
|
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
|
||||||
{% define language.code as lang_code %}
|
<td>
|
||||||
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
{% if object.session_key == session_key %}
|
||||||
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
|
||||||
{% if ALL_LANG or use_lang %}
|
|
||||||
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
|
||||||
{{ language.name_local }} ({{ lang_code }})
|
|
||||||
{% if lang_translated %}
|
|
||||||
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
|
||||||
{% else %}
|
|
||||||
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</option>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<div class='input-group-append'>
|
|
||||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>{% trans "Some languages are not complete" %}
|
|
||||||
{% if ALL_LANG %}
|
|
||||||
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
|
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</td>
|
||||||
</form>
|
</tr>
|
||||||
</div>
|
{% endfor %}
|
||||||
<div class="col-sm-6">
|
</table>
|
||||||
<h4>{% trans "Help the translation efforts!" %}</h4>
|
|
||||||
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are welcomed and encouraged.{% endblocktrans %}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{% 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>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
|
@ -50,4 +50,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class='panel-heading'>
|
||||||
|
<h4>{% trans "Language Settings" %}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form action="{% url 'set_language' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name="next" type="hidden" value="{% url 'settings' %}">
|
||||||
|
<label for='language' class=' requiredField'>
|
||||||
|
{% trans "Select language" %}
|
||||||
|
</label>
|
||||||
|
<div class='form-group input-group mb-3'>
|
||||||
|
<select name="language" class="select form-control w-25">
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
{% get_available_languages as LANGUAGES %}
|
||||||
|
{% get_language_info_list for LANGUAGES as languages %}
|
||||||
|
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
|
||||||
|
{% for language in languages %}
|
||||||
|
{% define language.code as lang_code %}
|
||||||
|
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
||||||
|
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
||||||
|
{% if ALL_LANG or use_lang %}
|
||||||
|
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
||||||
|
{{ language.name_local }} ({{ lang_code }})
|
||||||
|
{% if lang_translated %}
|
||||||
|
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<div class='input-group-append'>
|
||||||
|
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{% trans "Some languages are not complete" %}
|
||||||
|
{% if ALL_LANG %}
|
||||||
|
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
||||||
|
{% else %}
|
||||||
|
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h4>{% trans "Help the translation efforts!" %}</h4>
|
||||||
|
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are welcomed and encouraged.{% endblocktrans %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -145,7 +145,6 @@ class RuleSet(models.Model):
|
|||||||
# Core django models (not user configurable)
|
# Core django models (not user configurable)
|
||||||
'admin_logentry',
|
'admin_logentry',
|
||||||
'contenttypes_contenttype',
|
'contenttypes_contenttype',
|
||||||
'sessions_session',
|
|
||||||
|
|
||||||
# Models which currently do not require permissions
|
# Models which currently do not require permissions
|
||||||
'common_colortheme',
|
'common_colortheme',
|
||||||
@ -159,6 +158,7 @@ class RuleSet(models.Model):
|
|||||||
'error_report_error',
|
'error_report_error',
|
||||||
'exchange_rate',
|
'exchange_rate',
|
||||||
'exchange_exchangebackend',
|
'exchange_exchangebackend',
|
||||||
|
'user_sessions_session',
|
||||||
|
|
||||||
# Django-q
|
# Django-q
|
||||||
'django_q_ormq',
|
'django_q_ormq',
|
||||||
|
@ -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
|
||||||
|
2
tasks.py
2
tasks.py
@ -279,7 +279,6 @@ def content_excludes():
|
|||||||
|
|
||||||
excludes = [
|
excludes = [
|
||||||
"contenttypes",
|
"contenttypes",
|
||||||
"sessions.session",
|
|
||||||
"auth.permission",
|
"auth.permission",
|
||||||
"authtoken.token",
|
"authtoken.token",
|
||||||
"error_report.error",
|
"error_report.error",
|
||||||
@ -291,6 +290,7 @@ def content_excludes():
|
|||||||
"exchange.rate",
|
"exchange.rate",
|
||||||
"exchange.exchangebackend",
|
"exchange.exchangebackend",
|
||||||
"common.notificationentry",
|
"common.notificationentry",
|
||||||
|
"user_sessions.session",
|
||||||
]
|
]
|
||||||
|
|
||||||
output = ""
|
output = ""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user