From: Michael Tremer Date: Sun, 9 Dec 2012 11:33:40 +0000 (+0100) Subject: Add a page on which admins can see all active sessions. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=20d7f5eb3c2d3d0b5db73135d498cd4f8b19271e;p=pbs.git Add a page on which admins can see all active sessions. --- diff --git a/backend/main.py b/backend/main.py index e7aaf41a..be24867d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -19,6 +19,7 @@ import mirrors import packages import repository import settings +import sessions import sources import updates import uploads @@ -50,6 +51,7 @@ class Pakfire(object): self.mirrors = mirrors.Mirrors(self) self.packages = packages.Packages(self) self.repos = repository.Repositories(self) + self.sessions = sessions.Sessions(self) self.sources = sources.Sources(self) self.updates = updates.Updates(self) self.uploads = uploads.Uploads(self) diff --git a/backend/managers.py b/backend/managers.py index 79f89189..eeeb6829 100644 --- a/backend/managers.py +++ b/backend/managers.py @@ -922,3 +922,19 @@ class DeleteManager(Manager): managers.append(DeleteManager) + +class SessionsManager(Manager): + """ + Cleans up sessions that are not valid anymore. + Keeps the database smaller. + """ + + @property + def timeout(self): + return 3600 + + def do(self): + self.pakfire.sessions.cleanup() + + +managers.append(SessionsManager) diff --git a/backend/sessions.py b/backend/sessions.py index 7c8260c3..73d7730e 100644 --- a/backend/sessions.py +++ b/backend/sessions.py @@ -14,6 +14,21 @@ class Sessions(base.Object): return session + def get_all(self): + query = "SELECT session_id FROM sessions WHERE valid_until >= NOW() \ + ORDER BY valid_until DESC" + + sessions = [] + for s in self.db.query(query): + s = Session(self.pakfire, s.session_id) + sessions.append(s) + + return sessions + + def cleanup(self): + # Delete all sessions that are not valid any more. + self.db.execute("DELETE FROM sessions WHERE valid_until < NOW()") + class Session(base.Object): def __init__(self, pakfire, session_id): @@ -32,9 +47,6 @@ class Session(base.Object): self._user = None self._impersonated_user = None - # Update the valid time of the session. - #self.update() - @staticmethod def has_session(pakfire, session_id): if self.db.get("SELECT session_id FROM sessions WHERE session_id = %s \ @@ -43,25 +55,34 @@ class Session(base.Object): return False - def refresh(self): - self.db.execute("UPDATE sessions SET valid_until = DATE_ADD(NOW(), INTERVAL 3 DAY) \ - WHERE session_id = %s", self.id) + def refresh(self, address=None): + self.db.execute("UPDATE sessions \ + SET valid_until = DATE_ADD(NOW(), INTERVAL 3 DAY), from_address = %s \ + WHERE session_id = %s", address, self.id) def destroy(self): self.db.execute("DELETE FROM sessions WHERE session_id = %s", self.id) - @staticmethod - def cleanup(pakfire): - # Remove all sessions that are no more valid. - pakfire.db.execute("DELETE FROM sessions WHERE valid_until < NOW()") - @property def user(self): if self._user is None: self._user = users.User(self.pakfire, self.data.user_id) + self._user.session = self return self._user + @property + def creation_time(self): + return self.data.creation_time + + @property + def valid_until(self): + return self.data.valid_until + + @property + def from_address(self): + return self.data.from_address + @property def impersonated_user(self): if not self.data.impersonated_user_id: @@ -70,6 +91,7 @@ class Session(base.Object): if self._impersonated_user is None: self._impersonated_user = \ users.User(self.pakfire, self.data.impersonated_user_id) + self._impersonated_user.session = self return self._impersonated_user diff --git a/backend/users.py b/backend/users.py index 01a29b4d..8a32f11c 100644 --- a/backend/users.py +++ b/backend/users.py @@ -220,11 +220,20 @@ class User(base.Object): base.Object.__init__(self, pakfire) self.id = id + # A valid session of the user. + self.session = None + # Cache. self._data = None self._emails = None self._perms = None + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, self.realname) + + def __hash__(self): + return hash(self.id) + def __cmp__(self, other): if other is None: return 1 diff --git a/data/templates/base.html b/data/templates/base.html index ac7e85ae..b164168e 100644 --- a/data/templates/base.html +++ b/data/templates/base.html @@ -82,7 +82,7 @@ {% if current_user %}
  • - + {{ _("Users") }}
  • @@ -100,6 +100,12 @@ +
  • + + + {{ _("Sessions") }} + +
  • diff --git a/data/templates/sessions/index.html b/data/templates/sessions/index.html new file mode 100644 index 00000000..ec388ec8 --- /dev/null +++ b/data/templates/sessions/index.html @@ -0,0 +1,61 @@ +{% extends "../base.html" %} + +{% block title %}{{ _("Sessions") }}{% end block %} + +{% block body %} + + + + + + + {% for user, user_sessions in sessions %} + + + + + {% end %} + +
    + + + + + + + + + + + + {% for s in user_sessions %} + + + + + + {% end %} + +
    {{ _("Started") }}{{ _("Valid until") }}{{ _("Last seen at") }}
    + {{ format_date(s.creation_time) }} + + {{ format_date(s.valid_until) }} + + {{ s.from_address or _("N/A") }} +
    +
    +{% end block %} diff --git a/web/__init__.py b/web/__init__.py index 627dd8ea..d8aa31a6 100644 --- a/web/__init__.py +++ b/web/__init__.py @@ -226,6 +226,9 @@ class Application(tornado.web.Application): # Log (r"/log", LogHandler), + # Sessions + (r"/sessions", SessionsHandler), + ] + static_handlers + [ # Everything else is catched by the 404 handler. diff --git a/web/handlers.py b/web/handlers.py index 638a0f3e..d10fe2e9 100644 --- a/web/handlers.py +++ b/web/handlers.py @@ -106,6 +106,30 @@ class LogHandler(BaseHandler): self.render("log.html", log=self.pakfire.log) +class SessionsHandler(BaseHandler): + def prepare(self): + # This is only accessible for administrators. + if not self.current_user.is_admin(): + raise tornado.web.HTTPError(403) + + @tornado.web.authenticated + def get(self): + sessions = self.pakfire.sessions.get_all() + + # Sort the sessions by user. + users = {} + + for s in sessions: + try: + users[s.user].append(s) + except KeyError: + users[s.user] = [s] + + sessions = sorted(users.items()) + + self.render("sessions/index.html", sessions=sessions) + + class RepositoryDetailHandler(BaseHandler): def get(self, distro, repo): distro = self.pakfire.distros.get_by_name(distro) diff --git a/web/handlers_auth.py b/web/handlers_auth.py index 541311bd..822da85c 100644 --- a/web/handlers_auth.py +++ b/web/handlers_auth.py @@ -28,7 +28,7 @@ class LoginHandler(BaseHandler): session = backend.sessions.Session.create(self.pakfire, user) # Set a cookie and update the current user. - self.set_cookie("session_id", session.id) + self.set_cookie("session_id", session.id, expires=session.valid_until) self._current_user = user # If there is "next" given, we redirect the user accordingly. @@ -117,7 +117,7 @@ class ActivationHandler(BaseHandler): session = backend.sessions.Session.create(self.pakfire, user) # Set a cookie and update the current user. - self.set_cookie("session_id", session.id) + self.set_cookie("session_id", session.id, expires=session.valid_until) self._current_user = user self.render("register-activation-success.html", user=user) diff --git a/web/handlers_base.py b/web/handlers_base.py index 9b8c5ae4..b2d7f768 100644 --- a/web/handlers_base.py +++ b/web/handlers_base.py @@ -12,7 +12,6 @@ import traceback import backend import backend.misc -import backend.sessions class BaseHandler(tornado.web.RequestHandler): @property @@ -24,21 +23,23 @@ class BaseHandler(tornado.web.RequestHandler): if not session_id: return - try: - self.session = backend.sessions.Session(self.pakfire, session_id) - except: + # Get the session from the database. + session = self.pakfire.sessions.get(session_id) + + # Return nothing, if no session was found. + if not session: return # Update the session lifetime. - # XXX refresh cookie, too - self.session.refresh() + session.refresh(self.request.remote_ip) + self.set_cookie("session_id", session.id, expires=session.valid_until) # If the session impersonated a user, we return that one. - if self.session.impersonated_user: - return self.session.impersonated_user + if session.impersonated_user: + return session.impersonated_user # By default, we return the user of this session. - return self.session.user + return session.user def get_user_locale(self): DEFAULT_LOCALE = tornado.locale.get("en_US") @@ -93,6 +94,10 @@ class BaseHandler(tornado.web.RequestHandler): @property def render_args(self): + session = None + if self.current_user: + session = self.current_user.session + ret = { "bugtracker" : self.pakfire.bugzilla, "hostname" : self.request.host, @@ -103,12 +108,10 @@ class BaseHandler(tornado.web.RequestHandler): "format_filemode" : backend.misc.format_filemode, "lang" : self.locale.code[:2], "pakfire_version" : pakfire.__version__, + "session" : session, "year" : time.strftime("%Y"), } - # Add session. - ret["session"] = getattr(self, "session", None) - return ret def render(self, *args, **kwargs): diff --git a/web/handlers_users.py b/web/handlers_users.py index a98c6cb4..7c1f131e 100644 --- a/web/handlers_users.py +++ b/web/handlers_users.py @@ -29,7 +29,8 @@ class UserImpersonateHandler(BaseHandler): action = self.get_argument("action", "start") if action == "stop": - self.session.stop_impersonation() + if self.current_user.session: + self.current_user.session.stop_impersonation() self.redirect("/") return @@ -55,7 +56,8 @@ class UserImpersonateHandler(BaseHandler): if not user: raise tornado.web.HTTPError(404, "User does not exist: %s" % username) - self.session.start_impersonation(user) + if self.current_user.session: + self.current_user.session.start_impersonation(user) # Redirect to start page. self.redirect("/")