Source code for util.sessions

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# Copyright (C) 2010-2014 Glencoe Software, Inc.
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""
Library for managing user sessions.
"""

import omero.constants
from omero.util import get_omero_userdir, make_logname
from omero.rtypes import rlong
from omero_ext.path import path

from urllib.parse import quote, unquote

import logging

"""
 * Track last used
 * provide single library (with lock) which does all of this
   - save session
   - clear session
   - check session # detachOnDestroy
   - list previous sessions
 * Use an environment variable for changing directories

import subprocess, optparse, os, sys
import getpass, pickle
import omero.java
from omero.cli import Arguments, BaseControl, VERSION
from path import path

"""


def _escape_host(host):
    return quote(host, safe='')


[docs] class SessionsStore(object): """ The store is a file-based repository of user sessions. By default, stores use $HOME/omero/sessions as their repository path. Use add() to add items to the repository """ def __init__(self, dir=None): """ """ self.logger = logging.getLogger(make_logname(self)) if dir is None: self.dir = get_omero_userdir() / "sessions" else: self.dir = path(dir) if not self.dir.exists(): self.dir.makedirs() try: self.dir.chmod(0o700) except: print("WARN: failed to chmod %s" % self.dir) # # File-only methods #
[docs] def report(self): """ Simple dump utility """ for host in self.dir.dirs(): print("[%s]" % host) for name in host.dirs(): print(" -> %s : " % name) for sess in name.files(): print(" %s" % sess)
[docs] def add(self, host, name, id, props, sudo=None): """ Stores a file containing the properties at REPO/host/name/id """ props["omero.host"] = host props["omero.user"] = name props["omero.sess"] = id if sudo is not None: props["omero.sudo"] = sudo lines = [] for k, v in list(props.items()): lines.append("%s=%s" % (k, v)) dhn = self.dir / _escape_host(host) / name if not dhn.exists(): dhn.makedirs() (dhn / id).write_lines(lines)
[docs] def conflicts(self, host, name, id, new_props, ignore_nulls=False, check_group=True): """ Compares if the passed properties are compatible with with those for the host, name, id tuple If ignore_nulls is True, then a null in new_props means matches anything. """ conflicts = "" old_props = self.get(host, name, id) default_port = str(omero.constants.GLACIER2PORT) keys = ["omero.port"] if check_group: keys.append("omero.group") for key in keys: old = old_props.get(key, None) new = new_props.get(key, None) if ignore_nulls and new is None: continue elif (key == "omero.port" and set((old, new)) == set((None, default_port))): continue elif old != new: if conflicts != "": conflicts += "; " conflicts += "%s: %s!=%s" % (key, old, new) return conflicts
[docs] def remove(self, host, name, uuid): """ Removes the given session file from the store and removes the sess_file() if it is equal to the session we just removed. """ if uuid is None: self.logger.debug("No uuid provided") return d = self.dir / _escape_host(host) / name if d.exists(): f = d / uuid if f.exists(): f.remove() self.logger.debug("Removed %s" % f) s = self.sess_file(host, name) if s and s.exists() and s.text().strip() == uuid: s.remove() self.logger.debug("Removed %s" % s)
[docs] def exists(self, host, name, uuid): """ Checks if the given file exists. """ d = self.dir for x in (_escape_host(host), name, uuid): d = d / x if not d.exists(): return False return True
[docs] def get(self, host, name, uuid): """ Returns the properties stored in the given session file """ return self.props(self.dir / _escape_host(host) / name / uuid)
[docs] def available(self, host, name): """ Returns the path to property files which are stored. Internal accounting files are not returned. """ d = self.dir / _escape_host(host) / name if not d.exists(): return [] return [x.basename() for x in self.non_dot(d)]
[docs] def set_current(self, host, name=None, uuid=None, props=None): """ Sets the current session, user, and host files These are used as defaults by other methods. """ if host is not None: self.host_file().write_text(host) if props is not None: port = props.get('omero.port', str(omero.constants.GLACIER2PORT)) self.port_file().write_text(port) if name is not None: self.user_file(host).write_text(name) if uuid is not None: self.sess_file(host, name).write_text(uuid)
[docs] def get_current(self): host = None name = None uuid = None if self.host_file().exists(): host = self.host_file().text().strip() if host: try: name = self.user_file(host).text().strip() except IOError: pass if name: try: uuid = self.sess_file(host, name).text().strip() except IOError: pass return (host, name, uuid, self.last_port())
[docs] def last_host(self): """ Prints either the last saved host (see get_current()) or "localhost" """ f = self.host_file() if not f.exists(): return "localhost" text = f.text().strip() if not text: return "localhost" return text
[docs] def last_port(self): """ Prints either the last saved port (see get_current()) or "4064" """ f = self.port_file() if not f.exists(): return str(omero.constants.GLACIER2PORT) port = f.text().strip() if not port: return str(omero.constants.GLACIER2PORT) return port
[docs] def find_name_by_key(self, server, uuid): """ Returns the name of a user for which the session key exists. This value is taken from the path rather than from the properties file since that value may have been overwritten. An exception is raised if there is more than one name since keys should be UUIDs. A None may be returned. """ s = self.dir / server if not s.exists(): return None else: n = [x.basename() for x in s.dirs() if (x / uuid).exists()] if not n: return None elif len(n) == 1: return n[0] else: raise Exception( "Multiple names found for uuid=%s: %s" % (uuid, ", ".join(n)))
[docs] def contents(self): """ Returns a map of maps with all the contents of the store. Internal accounting files are skipped. """ rv = {} Dhosts = self.dir.dirs() for Dhost in Dhosts: host = unquote(str(Dhost.basename())) if host not in rv: rv[host] = {} Dnames = Dhost.dirs() for Dname in Dnames: name = str(Dname.basename()) if name not in rv[host]: rv[host][name] = {} Dids = self.non_dot(Dname) for Did in Dids: id = str(Did.basename()) props = self.props(Did) props["active"] = "unknown" rv[host][name][id] = props return rv
[docs] def count(self, host=None, name=None): """ Returns the sum of all files visited by walk() """ def f(h, n, s): f.i += 1 f.i = 0 self.walk(f, host, name) return f.i
[docs] def walk(self, func, host=None, name=None, sess=None): """ Applies func to all host, name, and session path-objects. """ for h in self.dir.dirs(): if host is None or str(h.basename()) == host: for n in h.dirs(): if name is None or str(n.basename()) == name: for s in self.non_dot(n): if sess is None or str(s.basename()) == sess: func(h, n, s)
# # Server-requiring methods #
[docs] def attach(self, server, name, sess, set_current=True): """ Simple helper. Delegates to create() using the session as both the username and the password. This reproduces the logic of client.joinSession() """ props = self.get(server, name, sess) return self.create(sess, sess, props, new=False, set_current=set_current)
[docs] def create(self, name, pasw, props, new=True, set_current=True, sudo=None): """ Creates a new omero.client object, and returns: (cilent, session_id, timeToIdle, timeToLive) """ import omero.clients props = dict(props) host = props["omero.host"] client = omero.client(props) client.setAgent("OMERO.sessions") try: if sudo is not None: sf = client.createSession(sudo, pasw) principal = omero.sys.Principal() principal.name = name principal.group = props.get("omero.group", None) principal.eventType = "User" # Retrieve the default time to idle value uuid = sf.ice_getIdentity().name sess = sf.getSessionService().getSession(uuid) timeToIdle = sess.getTimeToIdle().getValue() sess = sf.getSessionService().createSessionWithTimeouts( principal, 0, timeToIdle) client.closeSession() sf = client.joinSession(sess.getUuid().getValue()) else: sf = client.createSession(name, pasw) except: client.__del__() raise ec = sf.getAdminService().getEventContext() uuid = sf.ice_getIdentity().name sf.detachOnDestroy() sess = sf.getSessionService().getSession(uuid) timeToIdle = sess.getTimeToIdle().getValue() timeToLive = sess.getTimeToLive().getValue() # Retrieve timeout from properties timeout = None if props.get("omero.timeout", False): timeout = int(props.get("omero.timeout")) * 1000 # Update timeout if timeout and timeout != timeToIdle: req = omero.cmd.UpdateSessionTimeoutRequest() req.session = sf.getAdminService().getEventContext().sessionUuid req.timeToIdle = rlong(timeout) try: cb = client.submit(req) # Response is "OK" cb.close(True) except omero.CmdError as ce: self.logger.warn(str(ce.err)) except: import traceback self.logger.error(traceback.format_exc()) # Reload session sess = sf.getSessionService().getSession(uuid) timeToIdle = sess.getTimeToIdle().getValue() if new: self.add(host, ec.userName, uuid, props, sudo=sudo) if set_current: self.set_current(host, ec.userName, uuid, props) return client, uuid, timeToIdle, timeToLive
[docs] def clear(self, host=None, name=None, sess=None): """ Walks through all sessions and calls killSession. Regardless of exceptions, it will remove the session files from the store. """ removed = [] def f(h, n, s): hS = str(h.basename()) nS = str(n.basename()) sS = str(s.basename()) try: client = self.attach(hS, nS, sS) client.killSession() except Exception as e: self.logger.debug("Exception on killSession: %s" % e) s.remove() removed.append(s) self.walk(f, host, name, sess) return removed
## # Helpers. Do not modify or rely on mutable state. ##
[docs] def host_file(self): """ Returns the path-object which stores the last active host """ return self.dir / "._LASTHOST_"
[docs] def port_file(self): """ Returns the path-object which stores the last active port """ return self.dir / "._LASTPORT_"
[docs] def user_file(self, host): """ Returns the path-object which stores the last active user """ d = self.dir / _escape_host(host) if not d.exists(): d.makedirs() return d / "._LASTUSER_"
[docs] def sess_file(self, host, user): """ Returns the path-object which stores the last active session """ d = self.dir / _escape_host(host) / user if not d.exists(): d.makedirs() return d / "._LASTSESS_"
[docs] def non_dot(self, d): """ Only returns the files (not directories) contained in d that don't start with a dot """ return [f for f in d.files("*") if not str(f.basename()).startswith(".")]
[docs] def props(self, f): """ Parses the path-object into properties """ txt = f.text() lines = txt.split("\n") props = {} for line in lines: if not line: continue parts = line.split("=", 1) if len(parts) == 1: parts.append("") props[parts[0]] = parts[1] return props
if __name__ == "__main__": SessionsStore().report()