Make it possible to list and invalidate active sessions
This commit is contained in:
		| @@ -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(_('Can’t 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')) | ||||||
|   | |||||||
| @@ -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 user’s 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})>' | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								calsocial/templates/account/active-sessions.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								calsocial/templates/account/active-sessions.html
									
									
									
									
									
										Normal 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 %} | ||||||
| @@ -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"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user