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 stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 08 Sep 2009 15:37:46 +0200
branchstable
changeset 3112 873202e181bb
parent 3109 e7e1bb06b716
child 3113 169bbf0b8dec
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	Tue Sep 08 10:46:47 2009 +0200
+++ b/common/mail.py	Tue Sep 08 15:37:46 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')
@@ -183,18 +184,12 @@
         return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
 
     def render_emails(self, **kwargs):
-        """generate and send an email message for this view"""
+        """generate and send emails for this view (one per recipient)"""
         self._kwargs = kwargs
         recipients = self.recipients()
         if not recipients:
             self.info('skipping %s notification, no recipients', self.id)
             return
-        if not isinstance(recipients[0], tuple):
-            from warnings import warn
-            warn('recipients should now return a list of 2-uple (email, language)',
-                 DeprecationWarning, stacklevel=1)
-            lang = self.vreg.property_value('ui.language')
-            recipients = zip(recipients, repeat(lang))
         if self.rset is not None:
             entity = self.entity(self.row or 0, self.col or 0)
             # if the view is using timestamp in message ids, no way to reference
@@ -208,22 +203,32 @@
         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)
 
     def render_and_send(self, **kwargs):
         """generate and send an email message for this view"""
--- a/dbapi.py	Tue Sep 08 10:46:47 2009 +0200
+++ b/dbapi.py	Tue Sep 08 15:37:46 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	Tue Sep 08 10:46:47 2009 +0200
+++ b/server/session.py	Tue Sep 08 15:37:46 2009 +0200
@@ -78,16 +78,51 @@
     def schema(self):
         return self.repo.schema
 
-    def add_relation(self, fromeid, rtype, toeid):
+    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:
-            self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+            cb(self, fromeid, rtype, toeid)
             return
         self.is_super_session = True
         try:
-            self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+            cb(self, fromeid, rtype, toeid)
         finally:
             self.is_super_session = False
 
+    def add_relation(self, fromeid, rtype, toeid):
+        """provide direct access to the repository method to add a relation.
+
+        This is equivalent to the following rql query:
+
+          SET X rtype Y WHERE X eid  fromeid, T eid toeid
+
+        without read security check but also all the burden of rql execution.
+        You may use this in hooks when you know both eids of the relation you
+        want to add.
+        """
+        self._change_relation(self.repo.glob_add_relation,
+                              fromeid, rtype, toeid)
+    def delete_relation(self, fromeid, rtype, toeid):
+        """provide direct access to the repository method to delete a relation.
+
+        This is equivalent to the following rql query:
+
+          DELETE X rtype Y WHERE X eid  fromeid, T eid toeid
+
+        without read security check but also all the burden of rql execution.
+        You may use this in hooks when you know both eids of the relation you
+        want to delete.
+        """
+        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	Tue Sep 08 10:46:47 2009 +0200
+++ b/sobjects/notification.py	Tue Sep 08 15:37:46 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)