Commit 0636d2d9 authored by Pietro Albini's avatar Pietro Albini

Add session expiration

parent 4360efbc
# Source code of the Ubuntu-it website
# Copyright (C) 2015-2016 Pietro Albini <pietroalbini@ubuntu.com>
# Copyright (C) 2015-2018 Pietro Albini <pietroalbini@ubuntu.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
......@@ -25,6 +25,7 @@ from . import pages
from . import utils
from . import download
from . import navbar
from . import utils
def create_app(data_path):
......@@ -51,6 +52,8 @@ def create_app(data_path):
app.config["CACHE_PATH"] = os.path.join(data_path, "cache")
cache.install_cache(app)
utils.prepare_app(app)
app.download = download.Downloads(data_path)
app.register_blueprint(
app.download.prepare_blueprint(app),
......
......@@ -20,11 +20,15 @@ import uuid
import flask
import flask_openid
import pkg_resources
import time
import yaml
from uitwww.third_party import openid_teams
SESSION_EXPIRES_AFTER = 86400
class SessionError(BaseException):
pass
......@@ -40,20 +44,35 @@ class Sessions:
ip = flask.request.remote_addr
self.db.update(
"INSERT INTO auth_sessions (id, nickname, teams, ip) "
"VALUES (?, ?, ?, ?)", id, nickname, ",".join(teams), ip,
"INSERT INTO auth_sessions (id, nickname, teams, ip, expires_at) "
"VALUES (?, ?, ?, ?, ?)", id, nickname, ",".join(teams), ip,
int(time.time()) + SESSION_EXPIRES_AFTER,
)
return id
def check(self, id):
"""Check if a session is valid and return its data"""
data = self.db.query("SELECT nickname, teams, ip FROM auth_sessions WHERE id = ?", id)
data = self.db.query(
"SELECT nickname, teams, ip, expires_at FROM auth_sessions "
"WHERE id = ?", id,
)
if not data:
raise SessionError("La tua sessione è scaduta, accedi di nuovo.")
nickname = data[0][0]
teams = data[0][1].split(",")
ip = data[0][2]
expires_at = data[0][3]
if expires_at is None or expires_at < time.time():
self.delete(id)
raise SessionError("La tua sessione è scaduta, accedi di nuovo.")
else:
# Bump the session every time a new page is visited
self.db.update(
"UPDATE auth_sessions SET expires_at = ? WHERE id = ?",
int(time.time()) + SESSION_EXPIRES_AFTER, id,
)
if ip != flask.request.remote_addr:
raise SessionError("Questa sessione è valida solo su un'altra rete, accedi di nuovo.")
......@@ -73,31 +92,49 @@ class Sessions:
def all(self):
"""Return all the sessions"""
rows = self.db.query("SELECT id, nickname, teams, ip FROM auth_sessions;")
rows = self.db.query("SELECT id, nickname, teams, ip, expires_at FROM auth_sessions;")
now = time.time()
result = []
for row in rows:
# Delete expired sessions
if row[0] is None or row[4] < now:
self.delete(row[0])
continue
result.append({
"id": row[0],
"nickname": row[1],
"teams": row[2].split(","),
"ip": row[3],
"expires_at": row[4],
})
return result
def get(self, id):
"""Return details about a session"""
row = self.db.query("SELECT nickname, teams, ip FROM auth_sessions WHERE id = ?;", id)
row = self.db.query("SELECT nickname, teams, ip, expires_at FROM auth_sessions WHERE id = ?;", id)
if row:
# Delete expired sessions
if row[0][3] is None or row[0][3] < time.time():
self.delete(id)
return
return {
"id": id,
"nickname": row[0][0],
"teams": row[0][1].split(","),
"ip": row[0][2],
"expires_at": row[0][3],
}
def count(self):
"""Return the number of active sessions"""
return self.db.query("SELECT COUNT(*) FROM auth_sessions;")[0][0]
now = time.time()
return self.db.query(
"SELECT COUNT(*) FROM auth_sessions "
"WHERE expires_at IS NOT NULL AND expires_at >= ?;", now,
)[0][0]
class Permissions:
......
......@@ -81,4 +81,8 @@ MIGRATIONS = [
ip TEXT NOT NULL
);
"""),
("add_auth_sessions_expires_at_column", """
ALTER TABLE auth_sessions ADD COLUMN expires_at INTEGER;
"""),
]
......@@ -35,6 +35,10 @@
<td>Indirizzo IP</td>
<td>{{ session.ip }}</td>
</tr>
<tr>
<td>Scadenza</td>
<td>{{ session.expires_at|format_timestamp }}</td>
</tr>
<tr>
<td>Team</td>
<td>
......@@ -64,12 +68,14 @@
<table>
<tr>
<th>Indirizzo IP</th>
<th>Scadenza</th>
<th></th>
<th></th>
</tr>
{% for session in others %}
<tr>
<td>{{ session.ip }}</td>
<td>{{ session.expires_at|format_timestamp }}
<td>
<a href="{{ url_for(".sessions_show", id=session.id) }}">
Dettagli
......
......@@ -40,6 +40,7 @@
<tr>
<th>Nome utente</th>
<th>Indirizzo IP</th>
<th>Scadenza</th>
<th></th>
<th></th>
</tr>
......@@ -47,6 +48,7 @@
<tr>
<td>{{ session.nickname }}</td>
<td>{{ session.ip }}</td>
<td>{{ session.expires_at|format_timestamp }}</td>
<td>
<a href="{{ url_for(".sessions_show", id=session.id) }}">
Dettagli
......
......@@ -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/>.
import datetime
import random
import string
......@@ -100,3 +101,13 @@ class ReverseProxied:
if scheme:
environ['wsgi.url_scheme'] = scheme
return self.app(environ, start_response)
def prepare_app(app):
"""Add some utilities to the app"""
@app.template_filter("format_timestamp")
def format_timestamp(timestamp):
return datetime.datetime.fromtimestamp(
int(timestamp)
).strftime('%d/%m/%Y %H:%M:%S')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment