enhance notification mecanism: recipients may return user entities, which will be used to create a fake session so one can check security during notification if necessary 3.5
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 08 Sep 2009 15:30:14 +0200
branch3.5
changeset 3110 757d36162235
parent 3103 1d09765ee720
child 3111 7b405bb305ab
enhance notification mecanism: recipients may return user entities, which will be used to create a fake session so one can check security during notification if necessary
common/mail.py
dbapi.py
server/session.py
sobjects/notification.py
--- a/common/mail.py	Thu Sep 03 14:08:17 2009 +0200
+++ b/common/mail.py	Tue Sep 08 15:30:14 2009 +0200
@@ -21,6 +21,7 @@
         return 'XXX'
 
 from cubicweb.view import EntityView
+from cubicweb.entity import Entity
 
 def header(ustring):
     return Header(ustring.encode('UTF-8'), 'UTF-8')
@@ -176,28 +177,38 @@
         else:
             refs = ()
             msgid = None
-        userdata = self.req.user_data()
-        origlang = self.req.lang
-        for emailaddr, lang in recipients:
-            self.req.set_language(lang)
+        req = self.req
+        self.user_data = req.user_data()
+        origlang = req.lang
+        for something in recipients:
+            if isinstance(something, Entity):
+                # hi-jack self.req to get a session for the returned user
+                self.req = self.req.hijack_user(something)
+                emailaddr = something.get_email()
+            else:
+                emailaddr, lang = something
+                self.req.set_language(lang)
             # since the same view (eg self) may be called multiple time and we
             # need a fresh stream at each iteration, reset it explicitly
             self.w = None
             # XXX call render before subject to set .row/.col attributes on the
             #     view
-            content = self.render(row=0, col=0, **kwargs)
-            subject = self.subject()
-            msg = format_mail(userdata, [emailaddr], content, subject,
+            try:
+                content = self.render(row=0, col=0, **kwargs)
+                subject = self.subject()
+            except SkipEmail:
+                continue
+            msg = format_mail(self.user_data, [emailaddr], content, subject,
                               config=self.config, msgid=msgid, references=refs)
             yield [emailaddr], msg
         # restore language
-        self.req.set_language(origlang)
+        req.set_language(origlang)
 
     # recipients / email sending ###############################################
 
     def recipients(self):
-        """return a list of 2-uple (email, language) to who this email should be
-        sent
+        """return a list of either 2-uple (email, language) or user entity to
+        who this email should be sent
         """
         finder = self.vreg['components'].select('recipients_finder', self.req,
                                                 rset=self.rset,
@@ -230,7 +241,7 @@
         subject = self.req._(self.message)
         etype = entity.dc_type()
         eid = entity.eid
-        login = self.user_login()
+        login = self.user_data['login']
         return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals()
 
     def context(self, **kwargs):
@@ -238,17 +249,12 @@
         for key, val in kwargs.iteritems():
             if val and isinstance(val, unicode) and val.strip():
                kwargs[key] = self.req._(val)
-        kwargs.update({'user': self.user_login(),
+        kwargs.update({'user': self.user_data['login'],
                        'eid': entity.eid,
                        'etype': entity.dc_type(),
                        'url': entity.absolute_url(),
                        'title': entity.dc_long_title(),})
         return kwargs
 
-    def user_login(self):
-        try:
-            # if req is actually a session (we are on the server side), and we
-            # have to prevent nested internal session
-            return self.req.actual_session().user.login
-        except AttributeError:
-            return self.req.user.login
+class SkipEmail(Exception):
+    """raise this if you decide to skip an email during its generation"""
--- a/dbapi.py	Thu Sep 03 14:08:17 2009 +0200
+++ b/dbapi.py	Tue Sep 08 15:30:14 2009 +0200
@@ -284,6 +284,12 @@
 
     # server session compat layer #############################################
 
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        req = DBAPIRequest(self.vreg)
+        req.set_connection(self.cnx, user)
+        return req
+
     @property
     def user(self):
         if self._user is None and self.cnx:
--- a/server/session.py	Thu Sep 03 14:08:17 2009 +0200
+++ b/server/session.py	Tue Sep 08 15:30:14 2009 +0200
@@ -78,6 +78,12 @@
     def schema(self):
         return self.repo.schema
 
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        session = Session(user, self.repo)
+        session._threaddata = self._threaddata
+        return session
+
     def _change_relation(self, cb, fromeid, rtype, toeid):
         if self.is_super_session:
             cb(self, fromeid, rtype, toeid)
@@ -115,6 +121,8 @@
         self._change_relation(self.repo.glob_delete_relation,
                               fromeid, rtype, toeid)
 
+    # relations cache handling #################################################
+
     def update_rel_cache_add(self, subject, rtype, object, symetric=False):
         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
         if symetric:
--- a/sobjects/notification.py	Thu Sep 03 14:08:17 2009 +0200
+++ b/sobjects/notification.py	Tue Sep 08 15:30:14 2009 +0200
@@ -190,6 +190,6 @@
     def subject(self):
         entity = self.entity(self.row or 0, self.col or 0)
         return  u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
-                                  entity.eid, self.user_login())
+                                  entity.eid, self.user_data['login'])
 
 NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)