1. Sylvain Thénault
  2. cubicweb-shareurl

Commits

Sylvain Thénault  committed 5dea8f2

rewrite to use authentication API rather then url publishing hack

rewrite test accordingly, moving to cw 3.19 API on the way

  • Participants
  • Parent commits 9715c64
  • Branches default

Comments (0)

Files changed (4)

File __pkginfo__.py

View file
  • Ignore whitespace
 description = 'share content without requiring to login'
 web = 'http://www.cubicweb.org/project/%s' % distname
 
-__depends__ =  {'cubicweb': '>= 3.16.4',
+__depends__ =  {'cubicweb': '>= 3.19.0',
                 'localperms': None}
 __recommends__ = {}
 

File debian/control

View file
  • Ignore whitespace
 
 Package: cubicweb-shareurl
 Architecture: all
-Depends: cubicweb-common (>= 3.16.4), ${python:Depends}
+Depends: cubicweb-common (>= 3.19.0), ${python:Depends}
 Description: share content without requiring to login
  CubicWeb is a semantic web application framework.
  .

File test/unittest_shareurl.py

View file
  • Ignore whitespace
 from cubicweb.devtools.testlib import CubicWebTC
 
 class ShareURLTC(CubicWebTC):
-    def test(self):
-        req = self.request()
-        thing = req.create_entity('Thing', name=u'the')
-        ishare = thing.cw_adapt_to('IURLShareable')
-        # share_url return None until content has been explicitly shared
-        self.assertEqual(ishare.share_url(), None)
-        # ask for a share url
-        key = ishare.share()
-        self.commit()
-        thing.cw_clear_all_caches()
-        # share url starts with the share key
-        self.assertTrue(ishare.share_url().startswith(req.build_url(key+'/')))
+
+    def test_auth(self):
+        with self.admin_access.client_cnx() as cnx:
+            thing = cnx.create_entity('Thing', name=u'the')
+            cnx.commit()
+            ishare = thing.cw_adapt_to('IURLShareable')
+            # share_url return None until content has been explicitly shared
+            self.assertEqual(ishare.share_url(), None)
+            # ask for a share url
+            key = ishare.share()
+            # share url starts with the share key
+            thing.cw_clear_all_caches()
+            self.assertTrue(ishare.share_url().startswith(cnx.build_url(key+'/')))
         # share key is also a user / password
-        self.login(key)
-        # though this user is somewhat special
-        self.assertEqual(self.user().shared_user, True)
-        self.assertEqual(self.user().name(), 'anonymous')
-        self.assertEqual(self.user().printable_value('login'), 'anonymous')
-        self.restore_connection()
+        with self.new_access(key).web_request() as req:
+            # though this user is somewhat special
+            self.assertEqual(req.user.shared_user, True)
+            self.assertEqual(req.user.name(), 'anonymous')
+            self.assertEqual(req.user.printable_value('login'), 'anonymous')
         # publishing a shared url open a new session with the above user then
         # redirect to the remaining url
-        with self.assertRaises(Redirect) as cm:
-            self.url_publish(ishare.share_url())
-        self.assertEqual(cm.exception.location, thing.absolute_url())
-        #self.assertEqual(self.user().login, key)
-        #self.restore_connection()
+        req = self.requestcls(self.vreg, url=ishare.share_url())
+        result = self.app.handle_request(req, req.relative_path())
+        self.assertEqual(303, req.status_out)
+        self.assertEqual(ishare.share_url().replace(key+'/', ''),
+                         req.headers_out.getHeader('location'))
+        self.assertEqual('', result)
         # share key is deleted when content is deleted
-        thing.cw_delete()
-        self.commit()
-        self.assertFalse(self.execute('ShareKey X'))
+        with self.admin_access.client_cnx() as cnx:
+            cnx.execute('DELETE Thing X')
+            cnx.commit()
+            self.assertFalse(cnx.execute('ShareKey X'))
+
+    def test_no_auth(self):
+        req = self.requestcls(self.vreg, url=self.vreg.config['base-url'])
+        self.app.handle_request(req, '')
+        self.assertEqual(200, req.status_out)
+
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main

File views.py

View file
  • Ignore whitespace
 from cubicweb import view, tags
 from cubicweb.predicates import relation_possible, match_user_groups, adaptable
 from cubicweb.web import Redirect, component
-from cubicweb.web.views import urlpublishing, uicfg, basecomponents
+from cubicweb.web.views import uicfg, authentication, urlpublishing, basecomponents
 
 
 @objectify_predicate
 def is_shared_session(cls, req, *args, **kwargs):
-    if req.session.data.get('shared-url-session'):
+    if req.cnx.get_shared_data('shared-url-session'):
         return 1
     else:
         return 0
             return key
 
 
-class SharedURLPathEvaluator(urlpublishing.URLPathEvaluator):
+class ShareUrlRetriever(authentication.WebAuthInfoRetriever):
+    __regid__ = 'shareurl.authentifier'
+    order = 20
+
+    def authentication_information(self, req):
+        """retreive authentication information from the given request, raise
+        NoAuthInfo if expected information is not found.
+        """
+        parts = [part for part in req.relative_path(False).split('/')
+                 if part != '']
+        if not parts:
+            raise authentication.NoAuthInfo()
+        vreg = req.vreg
+        with vreg.config.repository(vreg).internal_cnx() as cnx:
+            rset = cnx.execute('Any SU WHERE SU _share_key %(k)s',
+                               {'k': parts[0]})
+            if not rset:
+                raise authentication.NoAuthInfo()
+        # login by expecting a user with login == password == share key
+        return parts[0], {'password': parts[0].encode('utf8')}
+
+    def request_has_auth_info(self, req):
+        return False
+
+    def authenticated(self, retriever, req, cnx, login, authinfo):
+        if retriever is not self:
+            return
+        # mark session as shared
+        cnx.set_shared_data('shared-url-session', login)
+
+
+class ShareUrlPathEvaluator(urlpublishing.URLPathEvaluator):
     priority = 0.5 # insert after RawPathEvaluator but before EidPathEvaluator
+
     def evaluate_path(self, req, parts):
-        rset = req.execute('Any SU WHERE SU _share_key %(k)s', {'k': parts[0]})
-        if not rset:
-            raise urlpublishing.PathDontMatch()
-        publisher = req.publisher
-        # login by expecting a user with login == password == share key
-        req.get_authorization = lambda: (parts[0], parts[0].encode('utf8'))
-        publisher.session_handler.open_session(req)
-        # mark session as shared
-        req.session.data['shared-url-session'] = True
-        # then redirecto to the url as if the key part wasn't there
-        raise Redirect(req.build_url('/'.join(parts[1:])))
+        if parts and req.cnx and parts[0] == req.cnx.get_shared_data('shared-url-session'):
+            # redirect to the url as if the key part wasn't there
+            raise Redirect(req.build_url('/'.join(parts[1:])))
+        raise urlpublishing.PathDontMatch()
 
 
 class ShareBox(component.EntityCtxComponent):