Make it possible to list and invalidate active sessions

This commit is contained in:
Gergely Polonkai 2018-07-19 15:14:00 +02:00
parent 8d71edae5e
commit cb9a62cd88
4 changed files with 68 additions and 1 deletions

View File

@ -17,7 +17,7 @@
"""Main module for the Calendar.social app """Main module for the Calendar.social app
""" """
from flask import Blueprint, abort, current_app, redirect, render_template, url_for from flask import Blueprint, abort, current_app, flash, redirect, render_template, session, url_for
from flask_security import current_user, login_required from flask_security import current_user, login_required
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
@ -197,3 +197,38 @@ class AccountBlueprint(Blueprint, RoutedMixin):
db.session.commit() db.session.commit()
return redirect(url_for('account.follow_requests')) return redirect(url_for('account.follow_requests'))
@staticmethod
@RoutedMixin.route('/sessions')
@login_required
def active_sessions():
"""View the list of active sessions
"""
sessions = []
for sid in current_user.active_sessions:
session = current_app.session_interface.load_session(sid)
sessions.append(session)
return render_template('account/active-sessions.html', sessions=sessions)
@staticmethod
@RoutedMixin.route('/sessions/invalidate/<string:sid>')
@login_required
def invalidate_session(sid):
"""View to invalidate a session
"""
sess = current_app.session_interface.load_session(sid)
if not sess or sess.user != current_user:
abort(404)
if sess.sid == session.sid:
flash(_('Cant invalidate your current session'))
else:
current_app.session_interface.delete_session(sid)
current_user.active_sessions = [sess_id for sess_id in current_user.active_sessions if sess_id != sid]
return redirect(url_for('account.active_sessions'))

View File

@ -27,6 +27,7 @@ from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy_utils.types.choice import ChoiceType from sqlalchemy_utils.types.choice import ChoiceType
from .cache import cache
from .utils import force_locale from .utils import force_locale
db = SQLAlchemy() db = SQLAlchemy()
@ -219,6 +220,21 @@ class User(db.Model, UserMixin):
return current_app.timezone return current_app.timezone
@property
def session_list_key(self):
"""The cache key of this users session list
"""
return f'open_sessions:{self.id}'
@property
def active_sessions(self):
return cache.get(self.session_list_key) or []
@active_sessions.setter
def active_sessions(self, value):
cache.set(self.session_list_key, list(value))
def __repr__(self): def __repr__(self):
return f'<User {self.id}({self.username})>' return f'<User {self.id}({self.username})>'

View File

@ -0,0 +1,15 @@
{% extends 'account/settings-base.html' %}
{% block settings_content %}
<h2>{% trans %}Active sessions{% endtrans %}</h2>
<ul>
{% for sess in sessions %}
<li>
{{ sess['ip'] }}
{% if sess.sid != session.sid %}
<a href="{{ url_for('account.invalidate_session', sid=sess.sid) }}">{% trans %}Invalidate{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock settings_content %}

View File

@ -6,6 +6,7 @@
<div class="ui secondary pointing vertical menu"> <div class="ui secondary pointing vertical menu">
<a class="item{% if request.endpoint == 'account.edit_profile' %} active{% endif %}" href="{{ url_for('account.edit_profile') }}">{% trans %}Edit profile{% endtrans %}</a> <a class="item{% if request.endpoint == 'account.edit_profile' %} active{% endif %}" href="{{ url_for('account.edit_profile') }}">{% trans %}Edit profile{% endtrans %}</a>
<a class="item{% if request.endpoint == 'account.settings' %} active{% endif %}" href="{{ url_for('account.settings') }}">{% trans %}Settings{% endtrans %}</a> <a class="item{% if request.endpoint == 'account.settings' %} active{% endif %}" href="{{ url_for('account.settings') }}">{% trans %}Settings{% endtrans %}</a>
<a class="item{% if request.endpoint == 'account.active_sessions' %} active{% endif %}" href="{{ url_for('account.active_sessions') }}">{% trans %}Active sessions{% endtrans %}</a>
</div> </div>
</div> </div>
<div class="twelve wide stretched column"> <div class="twelve wide stretched column">