journal: log user session expiries (#54525)
gitea/authentic/pipeline/head Build started... Details

This commit is contained in:
Paul Marillonnet 2021-07-13 11:34:19 +02:00
parent 6787982330
commit a4a4d7306d
4 changed files with 48 additions and 8 deletions

View File

@ -139,7 +139,10 @@ if settings.SESSION_ENGINE in DB_SESSION_ENGINES:
user.short_description = _('user')
def clear_expired(self, request, queryset):
queryset.filter(expire_date__lt=timezone.now()).delete()
qs = queryset.filter(expire_date__lt=timezone.now())
for session in qs:
request.journal.record('user.session.expiry', session=session)
qs.delete()
clear_expired.short_description = _('clear expired sessions')

View File

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth import SESSION_KEY, get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
@ -346,3 +347,19 @@ class UserEmailChange(EventTypeDefinition):
new_email = event.get_data('email')
old_email = event.get_data('old_email')
return _('email address changed from "{0}" to "{1}"').format(old_email, new_email)
class UserSessionExpiry(EventTypeDefinition):
name = 'user.session.expiry'
label = _('session expiry')
@classmethod
def record(cls, session):
user_id = session.get_decoded().get(SESSION_KEY)
User = get_user_model()
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
# oops, nonexistent user, no need to record that
return
super().record(user=user, session=session)

View File

@ -69,7 +69,7 @@ class OpenedSessionCookieMiddleware(MiddlewareMixin):
domain=domain,
secure=app_settings.A2_OPENED_SESSION_COOKIE_SECURE,
)
elif app_settings.A2_OPENED_SESSION_COOKIE_NAME in request.COOKIES:
elif name in request.COOKIES:
response.delete_cookie(name, domain=domain)
return response

View File

@ -18,8 +18,9 @@ import datetime
from unittest import mock
import pytest
from django.contrib.auth import SESSION_KEY
from django.contrib.sessions.models import Session
from django.utils.timezone import make_aware
from django.utils.timezone import make_aware, now
from authentic2.a2_rbac.models import Role
from authentic2.a2_rbac.utils import get_default_ou
@ -51,6 +52,12 @@ def events(db, freezer):
username="user", email="user@example.com", ou=ou, uuid="1" * 32, first_name='Johnny', last_name='doe'
)
agent = User.objects.create(username="agent", email="agent@example.com", ou=ou, uuid="2" * 32)
# manual (dirty) assignment of session1 to user
session1_data = session1.get_decoded()
session1_data[SESSION_KEY] = user.id
session1.session_data = Session.objects.encode(session1_data)
session1.expire_date = now() + datetime.timedelta(days=1)
session1.save()
role_user = Role.objects.create(name="role1", ou=ou)
role_agent = Role.objects.create(name="role2", ou=ou)
service = Service.objects.create(name="service")
@ -251,6 +258,7 @@ def events(db, freezer):
old_email='old@example.com',
new_email='new@example.com',
)
make('user.session.expiry', session=session1)
make(
'manager.user.deactivation',
target_user=user,
@ -558,20 +566,26 @@ def test_global_journal(app, superuser, events):
'user': 'Johnny doe',
},
{
'message': 'session expiry',
'timestamp': 'Jan. 2, 2020, 5 p.m.',
'type': 'user.session.expiry',
'user': 'Johnny doe',
},
{
'timestamp': 'Jan. 2, 2020, 6 p.m.',
'type': 'manager.user.deactivation',
'user': '-',
'message': 'automatic deactivation of user "Johnny doe" because the associated LDAP account does not exist anymore',
},
{
'timestamp': 'Jan. 2, 2020, 6 p.m.',
'timestamp': 'Jan. 2, 2020, 7 p.m.',
'type': 'manager.user.deactivation',
'user': '-',
'message': 'automatic deactivation of user "Johnny doe" because the associated LDAP source has been deleted',
},
{
'message': 'automatic activation of user "Johnny doe" because the associated LDAP account reappeared',
'timestamp': 'Jan. 2, 2020, 7 p.m.',
'timestamp': 'Jan. 2, 2020, 8 p.m.',
'type': 'manager.user.activation',
'user': '-',
},
@ -761,20 +775,26 @@ def test_user_journal(app, superuser, events):
'user': 'Johnny doe',
},
{
'message': 'session expiry',
'timestamp': 'Jan. 2, 2020, 5 p.m.',
'type': 'user.session.expiry',
'user': 'Johnny doe',
},
{
'timestamp': 'Jan. 2, 2020, 6 p.m.',
'type': 'manager.user.deactivation',
'user': '-',
'message': 'automatic deactivation because the associated LDAP account does not exist anymore',
},
{
'timestamp': 'Jan. 2, 2020, 6 p.m.',
'timestamp': 'Jan. 2, 2020, 7 p.m.',
'type': 'manager.user.deactivation',
'user': '-',
'message': 'automatic deactivation because the associated LDAP source has been deleted',
},
{
'message': 'automatic activation because the associated LDAP account reappeared',
'timestamp': 'Jan. 2, 2020, 7 p.m.',
'timestamp': 'Jan. 2, 2020, 8 p.m.',
'type': 'manager.user.activation',
'user': '-',
},
@ -1001,7 +1021,7 @@ def test_search(app, superuser, events):
response.form.set('search', 'session:1234')
response = response.form.submit()
assert len(response.pyquery('tbody tr')) == 11
assert len(response.pyquery('tbody tr')) == 12
assert all(
text_content(node) == 'Johnny doe'
for node in response.pyquery('tbody tr td.journal-list--user-column')