app/soc/views/helper/access.py
changeset 1135 24d695060863
parent 1122 659984867a9a
child 1142 da2487767ef4
--- a/app/soc/views/helper/access.py	Sat Jan 31 22:32:21 2009 +0000
+++ b/app/soc/views/helper/access.py	Sat Jan 31 22:32:54 2009 +0000
@@ -39,7 +39,9 @@
 
 from soc.logic import accounts
 from soc.logic import dicts
+from soc.logic import rights as rights_logic
 from soc.logic.models.club_admin import logic as club_admin_logic
+from soc.logic.models.club_member import logic as club_member_logic
 from soc.logic.models.document import logic as document_logic
 from soc.logic.models.host import logic as host_logic
 from soc.logic.models.notification import logic as notification_logic
@@ -68,6 +70,10 @@
   ' and <a href="%%(sign_in)s">sign in</a>'
   ' again as %(role)s to view this page.')
 
+DEF_NEED_MEMBERSHIP_MSG_FMT = ugettext(
+  'You need to be in the %(status)s group to %(action)s'
+  ' documents in the %(prefix)s prefix.')
+
 DEF_PAGE_DENIED_MSG = ugettext(
   'Access to this page has been restricted')
 
@@ -134,6 +140,18 @@
   rather then modifying rights directly if so desired.
   """
 
+  MEMBERSHIP = {
+    'anyone': 'allow',
+    'club_admin': 'checkIsClubAdminForScope',
+    'club_member': 'checkIsClubMemberForScope',
+    'host': 'checkHasHostEntity',
+    'org_admin': 'deny',
+    'org_mentor': 'deny',
+    'org_student': 'deny',
+    'user': 'checkIsUser',
+    'user_self': 'checkIsUserSelf',
+    }
+
   def __init__(self, params):
     """Adopts base.rights as rights if base is set.
     """
@@ -273,6 +291,40 @@
     for checker_name, args in self[access_type]:
       self.check(use_cache, checker_name, django_args, args)
 
+  def checkMembership(self, action, prefix, status, django_args):
+    """Checks whether the user has access to the specified status.
+
+    Args:
+      action: the action that was performed (e.g., 'read')
+      prefix: the prefix, determines what access set is used
+      status: the access status (e.g., 'public')
+      django_args: the django args to pass on to the checkers
+    """
+
+    checker = rights_logic.Checker(prefix)
+    roles = checker.getMembership(status)
+
+    message_fmt = DEF_NEED_MEMBERSHIP_MSG_FMT % {
+        'action': action,
+        'prefix': prefix,
+        'status': status,
+        }
+
+    # try to see if they belong to any of the roles, if not, raise an
+    # access violation for the specified action, prefix and status.
+    for role in roles:
+      try:
+        checker_name = self.MEMBERSHIP[role]
+        self.doCheck(checker_name, django_args, [])
+
+        # the check passed, we can stop now
+        break
+      except out_of_band.Error:
+        continue
+    else:
+      raise out_of_band.AccessViolation(message_fmt)
+
+
   def allow(self, django_args):
     """Never raises an alternate HTTP response.  (an access no-op, basically).
 
@@ -356,7 +408,23 @@
         'tos_link': redirects.getToSRedirect(site_logic.getSingleton())}
 
     raise out_of_band.LoginRequest(message_fmt=login_msg_fmt)
-  
+
+  @allowDeveloper
+  def checkIsUserSelf(self, django_args):
+    """Checks whether the specified user is the logged in user
+
+    Args:
+      django_args: the keyword args from django, only scope_path is used
+    """
+
+    if not 'scope_path' in django_args:
+      self.deny(django_args)
+
+    if self.user.link_id == django_args['scope_path']:
+      return
+
+    raise out_of_band.AccessViolation()
+
   def checkIsUnusedAccount(self, django_args):
     """Raises an alternate HTTP response if Google Account has a User entity.
 
@@ -593,6 +661,12 @@
 
     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
 
+  def checkHasHostEntity(self, django_args):
+    """Checks whether the current user has a Host entity.
+    """
+
+    self.checkIsHost({})
+
   @denySidebar
   @allowDeveloper
   def checkIsHostForProgram(self, django_args):
@@ -688,6 +762,65 @@
     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
 
   @allowDeveloper
+  @allowIfCheckPasses('checkIsClubAdminForClub')
+  def checkIsClubMemberForClub(self, django_args):
+    """Returns an alternate HTTP response if Google Account has no Club Member
+       entity for the specified club.
+
+    Args:
+      django_args: a dictionary with django's arguments
+
+     Raises:
+       AccessViolationResponse: if the required authorization is not met
+
+    Returns:
+      None if Club Member exists for the specified club, or a subclass of
+      django.http.HttpResponse which contains the alternate response
+      should be returned by the calling view.
+    """
+
+    self.checkIsUser(django_args)
+
+    if django_args.get('scope_path'):
+      scope_path = django_args['scope_path']
+    else:
+      scope_path = django_args['link_id']
+
+    fields = {'user': self.user,
+              'scope_path': scope_path,
+              'status': 'active'}
+
+    club_member_entity = club_member_logic.getForFields(fields, unique=True)
+
+    if club_member_entity:
+      return
+
+    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
+        'role': 'a Club Member for this Club'}
+
+    raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+
+  def checkIsClubAdminForScope(self, django_args):
+    """Checks whether the current user is a Club Mdmin.
+
+    Args:
+      django_args: the keyword arguments from django, only scope_path is used
+    """
+
+    scope_path = django_args['scope_path']
+    self.checkIsClubAdminForClub({'link_id': scope_path})
+
+  def checkIsClubMemberForScope(self, django_args):
+    """Checks whether the current user is a Club Mdmin.
+
+    Args:
+      django_args: the keyword arguments from django, only scope_path is used
+    """
+
+    scope_path = django_args['scope_path']
+    self.checkIsClubMemberForClub({'link_id': scope_path})
+
+  @allowDeveloper
   def checkIsApplicationAccepted(self, django_args, app_logic):
     """Returns an alternate HTTP response if Google Account has no Club App
        entity for the specified Club.
@@ -883,15 +1016,33 @@
 
   @denySidebar
   @allowDeveloper
-  def checkIsDocumentPublic(self, django_args):
-    """Checks whether a document is public.
+  def checkIsDocumentReadable(self, django_args):
+    """Checks whether a document is readable.
 
     Args:
       django_args: a dictionary with django's arguments
     """
 
     key_fields = document_logic.getKeyFieldsFromFields(django_args)
-    document_logic.getFromKeyFields(key_fields)
+    document = document_logic.getFromKeyFields(key_fields)
+
+    self.checkMembership('read', document.prefix,
+                         document.read_access, django_args)
+
+  @denySidebar
+  @allowDeveloper
+  def checkIsDocumentWritable(self, django_args):
+    """Checks whether a document is writable.
+
+    Args:
+      django_args: a dictionary with django's arguments
+    """
+
+    key_fields = document_logic.getKeyFieldsFromFields(django_args)
+    document = document_logic.getFromKeyFields(key_fields)
+
+    self.checkMembership('write', document.prefix,
+                         document.write_access, django_args)
 
   @allowIfCheckPasses('checkIsHostForProgram')
   def checkIsProgramVisible(self, django_args):
@@ -919,10 +1070,10 @@
     raise out_of_band.AccessViolation(DEF_DEV_LOGOUT_LOGIN_MSG_FMT,
                                       context=context)
 
+  def checkCanEditTimeline(self, django_args):
+    """Checks whether this program's timeline may be edited.
+    """
 
-  def checkCanEditTimeline(self, django_args):
-    """Allows developers and hosts for this program's timeline to edit it.
-    """
     time_line_keyname = django_args['scope_path']
     timeline_entity = timeline_logic.getFromKeyName(time_line_keyname)