changeset 919:7b267496da1d

server: moved session interfaces to session_iface module + added SATGuestSession
author Goffi <goffi@goffi.org>
date Wed, 29 Mar 2017 19:46:04 +0200
parents 96a56856d357
children 8cea8bf41b03
files src/server/server.py src/server/session_iface.py
diffstat 2 files changed, 113 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/src/server/server.py	Mon Mar 06 00:05:31 2017 +0100
+++ b/src/server/server.py	Wed Mar 29 19:46:04 2017 +0200
@@ -49,8 +49,6 @@
 import uuid
 import urlparse
 import urllib
-import shortuuid
-from zope.interface import Interface, Attribute, implements
 from httplib import HTTPS_PORT
 import libervia
 
@@ -62,25 +60,13 @@
 
 from libervia.server.constants import Const as C
 from libervia.server.blog import MicroBlog
+from libervia.server import session_iface
 
 
 # following value are set from twisted.plugins.libervia_server initialise (see the comment there)
 DATA_DIR_DEFAULT = OPT_PARAMETERS_BOTH = OPT_PARAMETERS_CFG = coerceDataDir = None
 
 
-class ISATSession(Interface):
-    profile = Attribute("Sat profile")
-    jid = Attribute("JID associated with the profile")
-    uuid = Attribute("uuid associated with the profile session")
-
-
-class SATSession(object):
-    implements(ISATSession)
-
-    def __init__(self, session):
-        self.profile = None
-        self.jid = None
-        self.uuid = unicode(shortuuid.uuid())
 
 
 class LiberviaSession(server.Session):
@@ -169,7 +155,7 @@
                     if not item:
                         raise KeyError
                 except (IndexError, KeyError):
-                    raise NotImplementedError(u"only item for PubSub URI is handler for the moment for url_redirections_dict")
+                    raise NotImplementedError(u"only item for PubSub URI is handled for the moment for url_redirections_dict")
                 location = "/blog/{profile}/{item}".format(
                     profile=urllib.quote(options['url_redirections_profile'].encode('utf-8')),
                     item = urllib.quote_plus(item),
@@ -363,7 +349,7 @@
 
     def render(self, request):
         self.session = request.getSession()
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         if not profile:
             #user is not identified, we return a jsonrpc fault
             parsed = jsonrpclib.loads(request.content.read())
@@ -385,40 +371,40 @@
 
     def jsonrpc_disconnect(self):
         """Disconnect the profile"""
-        sat_session = ISATSession(self.session)
+        sat_session = session_iface.ISATSession(self.session)
         profile = sat_session.profile
         self.sat_host.bridge.disconnect(profile)
 
     def jsonrpc_getContacts(self):
         """Return all passed args."""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getContacts(profile)
 
     def jsonrpc_addContact(self, entity, name, groups):
         """Subscribe to contact presence, and add it to the given groups"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.addContact(entity, profile)
         self.sat_host.bridge.updateContact(entity, name, groups, profile)
 
     def jsonrpc_delContact(self, entity):
         """Remove contact from contacts list"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.delContact(entity, profile)
 
     def jsonrpc_updateContact(self, entity, name, groups):
         """Update contact's roster item"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.updateContact(entity, name, groups, profile)
 
     def jsonrpc_subscription(self, sub_type, entity):
         """Confirm (or infirm) subscription,
         and setup user roster in case of subscription"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.subscription(sub_type, entity, profile)
 
     def jsonrpc_getWaitingSub(self):
         """Return list of room already joined by user"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getWaitingSub(profile)
 
     def jsonrpc_setStatus(self, presence, status):
@@ -426,12 +412,12 @@
         @param presence: value from ("", "chat", "away", "dnd", "xa")
         @param status: any string to describe your status
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.setPresence('', presence, {'': status}, profile)
 
     def jsonrpc_messageSend(self, to_jid, msg, subject, type_, extra={}):
         """send message"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("messageSend", to_jid, msg, subject, type_, extra, profile)
 
     ## PubSub ##
@@ -442,7 +428,7 @@
         @param service (unicode): service jid
         @param node (unicode): node to delete
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("psDeleteNode", service, node, profile)
 
     # def jsonrpc_psRetractItem(self, service, node, item, notify):
@@ -453,7 +439,7 @@
     #     @param items (iterable): id of item to retract
     #     @param notify (bool): True if notification is required
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     return self.asyncBridgeCall("psRetractItem", service, node, item, notify, profile)
 
     # def jsonrpc_psRetractItems(self, service, node, items, notify):
@@ -464,7 +450,7 @@
     #     @param items (iterable): ids of items to retract
     #     @param notify (bool): True if notification is required
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     return self.asyncBridgeCall("psRetractItems", service, node, items, notify, profile)
 
     ## microblogging ##
@@ -477,7 +463,7 @@
         @param mb_data(dict): microblog data
         @return: a deferred
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbSend", service, node, mb_data, profile)
 
     def jsonrpc_mbRetract(self, service, node, items):
@@ -487,7 +473,7 @@
         @param node (unicode): node to delete, empty string for default node
         @param items (iterable): ids of items to retract
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbRetract", service, node, items, profile)
 
     def jsonrpc_mbGet(self, service_jid, node, max_items, item_ids, extra):
@@ -500,7 +486,7 @@
         @param rsm (dict): TODO
         @return: a deferred couple with the list of items and metadatas.
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbGet", service_jid, node, max_items, item_ids, extra, profile)
 
     def jsonrpc_mbGetFromMany(self, publishers_type, publishers, max_items, extra):
@@ -512,7 +498,7 @@
         @param extra (dict): TODO
         @return (str): RT Deferred session id
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.mbGetFromMany(publishers_type, publishers, max_items, extra, profile)
 
     def jsonrpc_mbGetFromManyRTResult(self, rt_session):
@@ -520,7 +506,7 @@
 
         @param rt_session (str): RT Deferred session id
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbGetFromManyRTResult", rt_session, profile)
 
     def jsonrpc_mbGetFromManyWithComments(self, publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict):
@@ -535,7 +521,7 @@
         @param profile_key: profile key
         @return (str): RT Deferred session id
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.mbGetFromManyWithComments(publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict, profile)
 
     def jsonrpc_mbGetFromManyWithCommentsRTResult(self, rt_session):
@@ -543,7 +529,7 @@
 
         @param rt_session (str): RT Deferred session id
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbGetFromManyWithCommentsRTResult", rt_session, profile)
 
 
@@ -553,7 +539,7 @@
     #     @param dest (tuple(unicode)): recipient groups (ignored for "PUBLIC")
     #     @param text (unicode): microblog's text
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     extra['allow_comments'] = 'True'
 
     #     if not type_:  # auto-detect
@@ -576,7 +562,7 @@
     #     @param pub_data: a tuple (service, comment node identifier, item identifier)
     #     @param comments: comments node identifier (for main item) or False
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     return self.sat_host.bridge.deleteGroupBlog(pub_data, comments if comments else '', profile)
 
     # def jsonrpc_updateMblog(self, pub_data, comments, message, extra={}):
@@ -588,7 +574,7 @@
     #         - allow_comments: True to accept an other level of comments, False else (default: False)
     #         - rich: if present, contain rich text in currently selected syntax
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     if comments:
     #         extra['allow_comments'] = 'True'
     #     return self.sat_host.bridge.updateGroupBlog(pub_data, comments if comments else '', message, extra, profile)
@@ -598,7 +584,7 @@
     #     @param node: url of the comments node
     #     @param text: comment
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     if node and text:
     #         return self.sat_host.bridge.sendGroupBlogComment(node, text, extra, profile)
     #     else:
@@ -609,7 +595,7 @@
     #     @param publisher_jid: jid of the publisher
     #     @param item_ids: list of microblogs items IDs
     #     @return list of microblog data (dict)"""
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     d = self.asyncBridgeCall("getGroupBlogs", publisher_jid, item_ids, {'max_': unicode(max_items)}, False, profile)
     #     return d
 
@@ -618,7 +604,7 @@
     #     @param publisher_jid: jid of the publisher
     #     @param item_ids: list of microblogs items IDs
     #     @return list of couple (microblog data, list of microblog data)"""
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     d = self.asyncBridgeCall("getGroupBlogsWithComments", publisher_jid, item_ids, {}, max_comments, profile)
     #     return d
 
@@ -632,7 +618,7 @@
     #         key: publisher's jid
     #         value: list of microblog data (dict)
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     if rsm is None:
     #         rsm = {'max_': unicode(C.RSM_MAX_ITEMS)}
     #     d = self.asyncBridgeCall("getMassiveGroupBlogs", publishers_type, publishers, rsm, profile)
@@ -644,7 +630,7 @@
     #     @param service: jid of the service hosting the node
     #     @param node: comments node
     #     """
-    #     profile = ISATSession(self.session).profile
+    #     profile = session_iface.ISATSession(self.session).profile
     #     if rsm is None:
     #         rsm = {'max_': unicode(C.RSM_MAX_COMMENTS)}
     #     d = self.asyncBridgeCall("getGroupBlogComments", service, node, rsm, profile)
@@ -652,12 +638,12 @@
 
     def jsonrpc_getPresenceStatuses(self):
         """Get Presence information for connected contacts"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getPresenceStatuses(profile)
 
     def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=''):
         """Return history for the from_jid/to_jid couple"""
-        sat_session = ISATSession(self.session)
+        sat_session = session_iface.ISATSession(self.session)
         profile = sat_session.profile
         sat_jid = sat_session.jid
         if not sat_jid:
@@ -685,7 +671,7 @@
         @param room_jid (unicode): room JID or empty string to generate a unique name
         @param nick (unicode): user nick
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         d = self.asyncBridgeCall("joinMUC", room_jid, nick, {}, profile)
         return d
 
@@ -695,14 +681,14 @@
         @param contact_jid (unicode): contact to invite
         @param room_jid (unicode): room JID or empty string to generate a unique name
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         room_id = room_jid.split("@")[0]
         service = room_jid.split("@")[1]
         self.sat_host.bridge.inviteMUC(contact_jid, service, room_id, {}, profile)
 
     def jsonrpc_mucLeave(self, room_jid):
         """Quit a Multi-User Chat room"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         try:
             room_jid = jid.JID(room_jid)
         except:
@@ -712,7 +698,7 @@
 
     def jsonrpc_mucGetRoomsJoined(self):
         """Return list of room already joined by user"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.mucGetRoomsJoined(profile)
 
     def jsonrpc_mucGetDefaultService(self):
@@ -726,7 +712,7 @@
         @param other_players (list[unicode]): JIDs of the players to play with
         @param room_jid (unicode): room JID or empty string to generate a unique name
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.tarotGameLaunch(other_players, room_jid, profile)
 
     def jsonrpc_getTarotCardsPaths(self):
@@ -737,12 +723,12 @@
 
     def jsonrpc_tarotGameReady(self, player, referee):
         """Tell to the server that we are ready to start the game"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.tarotGameReady(player, referee, profile)
 
     def jsonrpc_tarotGamePlayCards(self, player_nick, referee, cards):
         """Tell to the server the cards we want to put on the table"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.tarotGamePlayCards(player_nick, referee, cards, profile)
 
     def jsonrpc_launchRadioCollective(self, invited, room_jid=""):
@@ -751,7 +737,7 @@
         @param invited (list[unicode]): JIDs of the contacts to play with
         @param room_jid (unicode): room JID or empty string to generate a unique name
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.radiocolLaunch(invited, room_jid, profile)
 
     def jsonrpc_getEntitiesData(self, jids, keys):
@@ -762,7 +748,7 @@
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
             raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridge.getEntitiesData(jids, keys, profile)
         except Exception as e:
@@ -776,7 +762,7 @@
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
             raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridge.getEntityData(jid, keys, profile)
         except Exception as e:
@@ -786,12 +772,12 @@
         """Get VCard for entiry
         @param jid_: jid of contact from who we want data
         @return: id to retrieve the profile"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getCard(jid_, profile)
 
     @defer.inlineCallbacks
     def jsonrpc_avatarGet(self, entity, cache_only, hash_only):
-        session_data = ISATSession(self.session)
+        session_data = session_iface.ISATSession(self.session)
         profile = session_data.profile
         # profile_uuid = session_data.uuid
         avatar = yield self.asyncBridgeCall("avatarGet", entity, cache_only, hash_only, profile)
@@ -805,17 +791,17 @@
     def jsonrpc_getAccountDialogUI(self):
         """Get the dialog for managing user account
         @return: XML string of the XMLUI"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getAccountDialogUI(profile)
 
     def jsonrpc_getParamsUI(self):
         """Return the parameters XML for profile"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("getParamsUI", C.SECURITY_LIMIT, C.APP_NAME, profile)
 
     def jsonrpc_asyncGetParamA(self, param, category, attribute="value"):
         """Return the parameter value for profile"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         if category == "Connection":
             # we need to manage the followings params here, else SECURITY_LIMIT would block them
             if param == "JabberID":
@@ -826,13 +812,13 @@
         return d
 
     def jsonrpc_setParam(self, name, value, category):
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.setParam(name, value, category, C.SECURITY_LIMIT, profile)
 
     def jsonrpc_launchAction(self, callback_id, data):
         #FIXME: any action can be launched, this can be a huge security issue if callback_id can be guessed
         #       a security system with authorised callback_id must be implemented, similar to the one for authorised params
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         d = self.asyncBridgeCall("launchAction", callback_id, data, profile)
         return d
 
@@ -840,7 +826,7 @@
         """Call the method to process a "composing" state.
         @param to_jid_s: contact the user is composing to
         """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         self.sat_host.bridge.chatStateComposing(to_jid_s, profile)
 
     def jsonrpc_getNewAccountDomain(self):
@@ -855,22 +841,22 @@
         @param syntax_to: dest syntax (e.g.: "XHTML")
         @param safe: clean resulting XHTML to avoid malicious code if True (forced here)
         @return: converted text """
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.syntaxConvert(text, syntax_from, syntax_to, True, profile)
 
     def jsonrpc_getLastResource(self, jid_s):
         """Get the last active resource of that contact."""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getLastResource(jid_s, profile)
 
     def jsonrpc_getFeatures(self):
         """Return the available features in the backend for profile"""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.getFeatures(profile)
 
     def jsonrpc_skipOTR(self):
         """Tell the backend to leave OTR handling to Libervia."""
-        profile = ISATSession(self.session).profile
+        profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridge.skipOTR(profile)
 
 
@@ -940,7 +926,7 @@
         method = parsed.get("method")  # pylint: disable=E1103
         if  method not in ['getSessionMetadata', 'registerParams', 'menusGet']:
             #if we don't call these methods, we need to be identified
-            profile = ISATSession(_session).profile
+            profile = session_iface.ISATSession(_session).profile
             if not profile:
                 #user is not identified, we return a jsonrpc fault
                 fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
@@ -1121,7 +1107,7 @@
         register_with_ext_jid = self.waiting_profiles.getRegisterWithExtJid(profile)
         self.waiting_profiles.purgeRequest(profile)
         _session = request.getSession()
-        sat_session = ISATSession(_session)
+        sat_session = session_iface.ISATSession(_session)
         if sat_session.profile:
             log.error(_(u'/!\\ Session has already a profile, this should NEVER happen!'))
             request.write(C.SESSION_ACTIVE)
@@ -1154,12 +1140,12 @@
 
     def jsonrpc_isConnected(self):
         _session = self.request.getSession()
-        profile = ISATSession(_session).profile
+        profile = session_iface.ISATSession(_session).profile
         return self.sat_host.bridge.isConnected(profile)
 
     def jsonrpc_connect(self):
         _session = self.request.getSession()
-        profile = ISATSession(_session).profile
+        profile = session_iface.ISATSession(_session).profile
         if self.waiting_profiles.getRequest(profile):
             raise jsonrpclib.Fault(1, C.ALREADY_WAITING)  # FIXME: define some standard error codes for libervia
         self.waiting_profiles.setRequest(self.request, profile)
@@ -1181,7 +1167,7 @@
         """
         metadata = {}
         _session = self.request.getSession()
-        profile = ISATSession(_session).profile
+        profile = session_iface.ISATSession(_session).profile
         if profile:
             metadata["plugged"] = True
         else:
@@ -1235,7 +1221,7 @@
         """Keep the connection alive until a signal is received, then send it
         @return: (signal, *signal_args)"""
         _session = self.request.getSession()
-        profile = ISATSession(_session).profile
+        profile = session_iface.ISATSession(_session).profile
         if profile in self.queue:  # if we have signals to send in queue
             if self.queue[profile]:
                 return self.queue[profile].pop(0)
@@ -1332,7 +1318,7 @@
         """
         _session = request.getSession()
         parsed = jsonrpclib.loads(request.content.read())
-        profile = ISATSession(_session).profile
+        profile = session_iface.ISATSession(_session).profile
         if not profile:
             #user is not identified, we return a jsonrpc fault
             fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
@@ -1412,7 +1398,7 @@
         @return: a tuple with the name of the async bridge method
         to be called followed by its arguments.
         """
-        profile = ISATSession(request.getSession()).profile
+        profile = session_iface.ISATSession(request.getSession()).profile
         return ("radiocolSongAdded", request.args['referee'][0], filepath, profile)
 
 
@@ -1429,7 +1415,7 @@
         @return: a tuple with the name of the async bridge method
         to be called followed by its arguments.
         """
-        profile = ISATSession(request.getSession()).profile
+        profile = session_iface.ISATSession(request.getSession()).profile
         return ("setAvatar", filepath, profile)
 
 
@@ -1785,6 +1771,7 @@
         self.initialised.addCallback(initOk)
 
     ## URLs ##
+
     def putChild(self, path, resource):
         """Add a child to the root resource"""
         # FIXME: check that no information is leaked (c.f. https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html#request-encoders)
@@ -1932,4 +1919,5 @@
         return web_util.redirectTo(url, request)
 
 
-registerAdapter(SATSession, server.Session, ISATSession)
+registerAdapter(session_iface.SATSession, server.Session, session_iface.ISATSession)
+registerAdapter(session_iface.SATGuestSession, server.Session, session_iface.ISATGuestSession)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/session_iface.py	Wed Mar 29 19:46:04 2017 +0200
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Libervia: a SAT frontend
+# Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org)
+
+# 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 by
+# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+# 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/>.
+from zope.interface import Interface, Attribute, implements
+import shortuuid
+
+class ISATSession(Interface):
+    profile = Attribute("Sat profile")
+    jid = Attribute("JID associated with the profile")
+    uuid = Attribute("uuid associated with the profile session")
+
+
+class SATSession(object):
+    implements(ISATSession)
+
+    def __init__(self, session):
+        self.profile = None
+        self.jid = None
+        self.uuid = unicode(shortuuid.uuid())
+
+
+class ISATGuestSession(Interface):
+    id = Attribute("UUID of the guest")
+    data = Attribute("data associated with the guest")
+
+
+class SATGuestSession(object):
+    implements(ISATGuestSession)
+
+    def __init__(self, session):
+        self.id = None
+        self.data = None