[open-ils-commits] r160 - in servres/trunk/conifer: . syrup (gfawcett)

svn at svn.open-ils.org svn at svn.open-ils.org
Mon Mar 9 19:49:28 EDT 2009


Author: gfawcett
Date: 2009-03-09 19:49:27 -0400 (Mon, 09 Mar 2009)
New Revision: 160

Modified:
   servres/trunk/conifer/genshi_support.py
   servres/trunk/conifer/syrup/views.py
Log:
added @members_only decorator; templated the 403 responses.


Modified: servres/trunk/conifer/genshi_support.py
===================================================================
--- servres/trunk/conifer/genshi_support.py	2009-03-09 23:49:24 UTC (rev 159)
+++ servres/trunk/conifer/genshi_support.py	2009-03-09 23:49:27 UTC (rev 160)
@@ -40,7 +40,8 @@
 #------------------------------------------------------------
 # main API
 
-def render(tname, **kwargs):
+def render(tname, _django_type=HttpResponse, **kwargs):
     request = get_request()
     _inject_django_things_into_namespace(request, kwargs)
-    return HttpResponse(template(tname).generate(**kwargs).render('xhtml'))
+    return _django_type(template(tname).generate(**kwargs).render('xhtml'))
+

Modified: servres/trunk/conifer/syrup/views.py
===================================================================
--- servres/trunk/conifer/syrup/views.py	2009-03-09 23:49:24 UTC (rev 159)
+++ servres/trunk/conifer/syrup/views.py	2009-03-09 23:49:27 UTC (rev 160)
@@ -19,8 +19,11 @@
 # for Z39.50 support, not sure whether this is the way to go yet but
 # as generic as it gets
 # from PyZ3950 import zoom
+#-----------------------------------------------------------------------------
+def log(level, msg):
+    print >> sys.stderr, '[%s] %s: %s' % (datetime.now(), level.upper(), msg)
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 # Authentication
 
 def auth_handler(request, path):
@@ -36,10 +39,14 @@
             userid, password = request.POST['userid'], request.POST['password']
             next = request.POST['next']
             user = authenticate(username=userid, password=password)
+            def _error_page(msg):
+                return g.render('auth/login.xhtml', err=msg, next=next)
             if user is None:
-                return g.render('auth/login.xhtml', err=_('Invalid username or password. Please try again.'), next=next)
+                return _error_page(
+                    _('Invalid username or password. Please try again.'))
             elif not user.is_active:
-                return g.render('auth/login.xhtml', err=_('Sorry, this account has been disabled.'), next=next)
+                return _error_age(
+                    _('Sorry, this account has been disabled.'))
             else:
                 login(request, user)
                 return HttpResponseRedirect(request.POST.get('next', '/syrup/'))
@@ -49,27 +56,57 @@
     else:
         return HttpResponse('auth_handler: ' + path)
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 # Authorization
 
+def _fast_user_membership_query(user_id, course_id, where=None):
+    # I use a raw SQL query here because I want the lookup to be as
+    # fast as possible. Caching would help too, but let's try this
+    # first. (todo, review later.)
+    query = ('select count(*) from syrup_member '
+             'where user_id=%s and course_id=%s ')
+    if where:
+        query += (' and ' + where)
+    cursor = django.db.connection.cursor()
+    cursor.execute(query, [user_id, int(course_id)])
+    res = cursor.fetchall()
+    cursor.close()
+    allowed = bool(res[0][0])
+    return allowed
+    
+def _access_denied(message):
+    return g.render('simplemessage.xhtml',
+                    title=_(_('Access denied.')), 
+                    content=message,
+                    _django_type=HttpResponseForbidden)
+
+
 def instructors_only(handler):
     def hdlr(request, course_id, *args, **kwargs):
         allowed = request.user.is_superuser
         if not allowed:
-            cursor = django.db.connection.cursor()
-            cursor.execute('select count(*) from syrup_member where user_id=%s and course_id=%s',                       
-                           [request.user.id, int(course_id)])
-            res = cursor.fetchall()
-            cursor.close()
-            allowed = res[0][0]
+            allowed = _fast_user_membership_query(
+                request.user.id, course_id, "role in ('INSTR','PROXY')")
         if allowed:
             return handler(request, course_id, *args, **kwargs)
         else:
-            return HttpResponseForbidden(_('Only instructors may edit courses.'))
+            return _access_denied(_('Only instructors are allowed here.'))
     return hdlr
 
-#------------------------------------------------------------
 
+def members_only(handler):
+    def hdlr(request, course_id, *args, **kwargs):
+        allowed = request.user.is_superuser
+        if not allowed:
+            allowed = _fast_user_membership_query(request.user.id, course_id)
+        if allowed:
+            return handler(request, course_id, *args, **kwargs)
+        else:
+            return _access_denied(_('Only course members are allowed here.'))
+    return hdlr
+
+#-----------------------------------------------------------------------------
+
 def welcome(request):
     return g.render('welcome.xhtml')
 
@@ -153,6 +190,7 @@
 def my_courses(request):
     return g.render('my_courses.xhtml')
 
+ at members_only
 def course_detail(request, course_id):
     course = get_object_or_404(models.Course, pk=course_id)
     if course.access != 'ANON' and request.user.is_anonymous():
@@ -161,12 +199,13 @@
         return login_required(lambda *args: None)(request)
     return g.render('course_detail.xhtml', course=course)
 
+ at members_only
 def course_search(request, course_id):
     course = get_object_or_404(models.Course, pk=course_id)
     return search(request, in_course=course)
 
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 # Creating a new course
 
 class NewCourseForm(ModelForm):
@@ -201,15 +240,15 @@
 @login_required
 def add_new_course(request):
     if not request.user.has_perm('add_course'):
-        return HttpResponseForbidden('You are not allowed to create course sites.') # fixme, prettier msg.
-    return add_or_edit_course(request)
+        return _access_denied(_('You are not allowed to create course sites.'))
+    return _add_or_edit_course(request)
 
 @instructors_only
 def edit_course(request, course_id):
     instance = get_object_or_404(models.Course, pk=course_id)
-    return add_or_edit_course(request, instance=instance)
+    return _add_or_edit_course(request, instance=instance)
     
-def add_or_edit_course(request, instance=None):
+def _add_or_edit_course(request, instance=None):
     is_add = (instance is None)
     if is_add:
         instance = models.Course()
@@ -245,6 +284,7 @@
     title = models.course_codes.course_code_lookup_title(course_code)
     return HttpResponse(simplejson.dumps({'title':title}))
 
+ at instructors_only
 def edit_course_permissions(request, course_id):
     course = get_object_or_404(models.Course, pk=course_id)
     choices = [
@@ -348,7 +388,7 @@
     else:
         return HttpResponseRedirect('../')
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 
 @login_required                 # must be, to avoid/audit brute force attacks.
 def course_invitation(request):
@@ -368,8 +408,8 @@
             # invitation failures? They should be captured somehow, I
             # think. Should we temporarily disable accounts after
             # multiple failures?
-            print >> sys.stdout, '[%s] WARN: Invitation failure, user %r gave code %r' % \
-                (datetime.now(), request.user.username, code)
+            log('WARN', 'Invitation failure, user %r gave code %r' % \
+                (datetime.now(), request.user.username, code))
             error = _('The code you provided is not valid.')
             return g.render('course_invitation.xhtml', **locals())
 
@@ -380,7 +420,7 @@
             mbr.save()
         return HttpResponseRedirect(crs.course_url())
         
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 
 def instructor_detail(request, instructor_id):
     page_num = int(request.GET.get('page', 1))
@@ -404,6 +444,7 @@
             page_num=page_num,
             count=count)
 
+ at members_only
 def item_detail(request, course_id, item_id):
     """Display an item (however that makes sense).""" 
     # really, displaying an item will vary based on what type of item
@@ -416,6 +457,7 @@
     else:
         return item_metadata(request, course_id, item_id)
 
+ at members_only
 def item_metadata(request, course_id, item_id):
     """Display a metadata page for the item."""
     item = get_object_or_404(models.Item, pk=item_id, course__id=course_id)
@@ -450,7 +492,7 @@
         course = parent_item.course
 
     if not course.can_edit(request.user):
-        return HttpResponseForbidden(_('not an editor')) # fixme, prettier msg?
+        return _access_denied(_('You are not an editor.'))
 
     item_type = request.GET.get('item_type')
     assert item_type, _('No item_type parameter was provided.')
@@ -546,6 +588,7 @@
         return HttpResponseRedirect(item.parent_url())
         
     
+ at members_only
 def item_download(request, course_id, item_id, filename):
     course = get_object_or_404(models.Course, pk=course_id)
     item = get_object_or_404(models.Item, pk=item_id, course__id=course_id)
@@ -590,7 +633,7 @@
             query = query & or_query
     return query
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 
 def search(request, in_course=None):
     ''' Need to work on this, the basic idea is
@@ -674,7 +717,7 @@
     return g.render('search_results.xhtml', **locals())
 
 
-#------------------------------------------------------------
+#-----------------------------------------------------------------------------
 # administrative options
 
 def admin_index(request):



More information about the open-ils-commits mailing list