mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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.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
|
||||
|
@ -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<pk>\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'),
|
||||
|
@ -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
|
||||
|
@ -4,6 +4,7 @@
|
||||
{% load inventree_extras %}
|
||||
{% load socialaccount %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load user_sessions i18n %}
|
||||
|
||||
{% block label %}account{% endblock %}
|
||||
|
||||
@ -174,58 +175,44 @@
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Language Settings" %}</h4>
|
||||
<h4>{% trans "Active Sessions" %}</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>
|
||||
<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 %}
|
||||
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
|
||||
{% 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>
|
||||
</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>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
|
@ -50,4 +50,57 @@
|
||||
</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 %}
|
@ -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,6 +158,7 @@ class RuleSet(models.Model):
|
||||
'error_report_error',
|
||||
'exchange_rate',
|
||||
'exchange_exchangebackend',
|
||||
'user_sessions_session',
|
||||
|
||||
# Django-q
|
||||
'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-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
|
||||
|
2
tasks.py
2
tasks.py
@ -279,7 +279,6 @@ def content_excludes():
|
||||
|
||||
excludes = [
|
||||
"contenttypes",
|
||||
"sessions.session",
|
||||
"auth.permission",
|
||||
"authtoken.token",
|
||||
"error_report.error",
|
||||
@ -291,6 +290,7 @@ def content_excludes():
|
||||
"exchange.rate",
|
||||
"exchange.exchangebackend",
|
||||
"common.notificationentry",
|
||||
"user_sessions.session",
|
||||
]
|
||||
|
||||
output = ""
|
||||
|
Loading…
Reference in New Issue
Block a user