app/soc/views/helper/access.py
changeset 1135 24d695060863
parent 1122 659984867a9a
child 1142 da2487767ef4
equal deleted inserted replaced
1134:493e7b5d3ab2 1135:24d695060863
    37 from django.core import urlresolvers
    37 from django.core import urlresolvers
    38 from django.utils.translation import ugettext
    38 from django.utils.translation import ugettext
    39 
    39 
    40 from soc.logic import accounts
    40 from soc.logic import accounts
    41 from soc.logic import dicts
    41 from soc.logic import dicts
       
    42 from soc.logic import rights as rights_logic
    42 from soc.logic.models.club_admin import logic as club_admin_logic
    43 from soc.logic.models.club_admin import logic as club_admin_logic
       
    44 from soc.logic.models.club_member import logic as club_member_logic
    43 from soc.logic.models.document import logic as document_logic
    45 from soc.logic.models.document import logic as document_logic
    44 from soc.logic.models.host import logic as host_logic
    46 from soc.logic.models.host import logic as host_logic
    45 from soc.logic.models.notification import logic as notification_logic
    47 from soc.logic.models.notification import logic as notification_logic
    46 from soc.logic.models.request import logic as request_logic
    48 from soc.logic.models.request import logic as request_logic
    47 from soc.logic.models.role import logic as role_logic
    49 from soc.logic.models.role import logic as role_logic
    66 DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext(
    68 DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext(
    67   'Please <a href="%%(sign_out)s">sign out</a>'
    69   'Please <a href="%%(sign_out)s">sign out</a>'
    68   ' and <a href="%%(sign_in)s">sign in</a>'
    70   ' and <a href="%%(sign_in)s">sign in</a>'
    69   ' again as %(role)s to view this page.')
    71   ' again as %(role)s to view this page.')
    70 
    72 
       
    73 DEF_NEED_MEMBERSHIP_MSG_FMT = ugettext(
       
    74   'You need to be in the %(status)s group to %(action)s'
       
    75   ' documents in the %(prefix)s prefix.')
       
    76 
    71 DEF_PAGE_DENIED_MSG = ugettext(
    77 DEF_PAGE_DENIED_MSG = ugettext(
    72   'Access to this page has been restricted')
    78   'Access to this page has been restricted')
    73 
    79 
    74 DEF_LOGOUT_MSG_FMT = ugettext(
    80 DEF_LOGOUT_MSG_FMT = ugettext(
    75     'Please <a href="%(sign_out)s">sign out</a> in order to view this page')
    81     'Please <a href="%(sign_out)s">sign out</a> in order to view this page')
   132   The __setitem__() and __getitem__() methods are overloaded to DTRT
   138   The __setitem__() and __getitem__() methods are overloaded to DTRT
   133   when adding new access rights, and retrieving them, so use these
   139   when adding new access rights, and retrieving them, so use these
   134   rather then modifying rights directly if so desired.
   140   rather then modifying rights directly if so desired.
   135   """
   141   """
   136 
   142 
       
   143   MEMBERSHIP = {
       
   144     'anyone': 'allow',
       
   145     'club_admin': 'checkIsClubAdminForScope',
       
   146     'club_member': 'checkIsClubMemberForScope',
       
   147     'host': 'checkHasHostEntity',
       
   148     'org_admin': 'deny',
       
   149     'org_mentor': 'deny',
       
   150     'org_student': 'deny',
       
   151     'user': 'checkIsUser',
       
   152     'user_self': 'checkIsUserSelf',
       
   153     }
       
   154 
   137   def __init__(self, params):
   155   def __init__(self, params):
   138     """Adopts base.rights as rights if base is set.
   156     """Adopts base.rights as rights if base is set.
   139     """
   157     """
   140 
   158 
   141     base = params.get('rights') if params else None
   159     base = params.get('rights') if params else None
   271       return
   289       return
   272 
   290 
   273     for checker_name, args in self[access_type]:
   291     for checker_name, args in self[access_type]:
   274       self.check(use_cache, checker_name, django_args, args)
   292       self.check(use_cache, checker_name, django_args, args)
   275 
   293 
       
   294   def checkMembership(self, action, prefix, status, django_args):
       
   295     """Checks whether the user has access to the specified status.
       
   296 
       
   297     Args:
       
   298       action: the action that was performed (e.g., 'read')
       
   299       prefix: the prefix, determines what access set is used
       
   300       status: the access status (e.g., 'public')
       
   301       django_args: the django args to pass on to the checkers
       
   302     """
       
   303 
       
   304     checker = rights_logic.Checker(prefix)
       
   305     roles = checker.getMembership(status)
       
   306 
       
   307     message_fmt = DEF_NEED_MEMBERSHIP_MSG_FMT % {
       
   308         'action': action,
       
   309         'prefix': prefix,
       
   310         'status': status,
       
   311         }
       
   312 
       
   313     # try to see if they belong to any of the roles, if not, raise an
       
   314     # access violation for the specified action, prefix and status.
       
   315     for role in roles:
       
   316       try:
       
   317         checker_name = self.MEMBERSHIP[role]
       
   318         self.doCheck(checker_name, django_args, [])
       
   319 
       
   320         # the check passed, we can stop now
       
   321         break
       
   322       except out_of_band.Error:
       
   323         continue
       
   324     else:
       
   325       raise out_of_band.AccessViolation(message_fmt)
       
   326 
       
   327 
   276   def allow(self, django_args):
   328   def allow(self, django_args):
   277     """Never raises an alternate HTTP response.  (an access no-op, basically).
   329     """Never raises an alternate HTTP response.  (an access no-op, basically).
   278 
   330 
   279     Args:
   331     Args:
   280       django_args: a dictionary with django's arguments
   332       django_args: a dictionary with django's arguments
   354     # agreesToSiteToS() call above always returns True if no ToS is in effect.
   406     # agreesToSiteToS() call above always returns True if no ToS is in effect.
   355     login_msg_fmt = DEF_AGREE_TO_TOS_MSG_FMT % {
   407     login_msg_fmt = DEF_AGREE_TO_TOS_MSG_FMT % {
   356         'tos_link': redirects.getToSRedirect(site_logic.getSingleton())}
   408         'tos_link': redirects.getToSRedirect(site_logic.getSingleton())}
   357 
   409 
   358     raise out_of_band.LoginRequest(message_fmt=login_msg_fmt)
   410     raise out_of_band.LoginRequest(message_fmt=login_msg_fmt)
   359   
   411 
       
   412   @allowDeveloper
       
   413   def checkIsUserSelf(self, django_args):
       
   414     """Checks whether the specified user is the logged in user
       
   415 
       
   416     Args:
       
   417       django_args: the keyword args from django, only scope_path is used
       
   418     """
       
   419 
       
   420     if not 'scope_path' in django_args:
       
   421       self.deny(django_args)
       
   422 
       
   423     if self.user.link_id == django_args['scope_path']:
       
   424       return
       
   425 
       
   426     raise out_of_band.AccessViolation()
       
   427 
   360   def checkIsUnusedAccount(self, django_args):
   428   def checkIsUnusedAccount(self, django_args):
   361     """Raises an alternate HTTP response if Google Account has a User entity.
   429     """Raises an alternate HTTP response if Google Account has a User entity.
   362 
   430 
   363     Args:
   431     Args:
   364       django_args: a dictionary with django's arguments
   432       django_args: a dictionary with django's arguments
   591     login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   659     login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   592         'role': 'a Program Administrator '}
   660         'role': 'a Program Administrator '}
   593 
   661 
   594     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   662     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   595 
   663 
       
   664   def checkHasHostEntity(self, django_args):
       
   665     """Checks whether the current user has a Host entity.
       
   666     """
       
   667 
       
   668     self.checkIsHost({})
       
   669 
   596   @denySidebar
   670   @denySidebar
   597   @allowDeveloper
   671   @allowDeveloper
   598   def checkIsHostForProgram(self, django_args):
   672   def checkIsHostForProgram(self, django_args):
   599     """Checks if the user is a host for the specified program.
   673     """Checks if the user is a host for the specified program.
   600     """
   674     """
   686         'role': 'a Club Admin for this Club'}
   760         'role': 'a Club Admin for this Club'}
   687 
   761 
   688     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   762     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   689 
   763 
   690   @allowDeveloper
   764   @allowDeveloper
       
   765   @allowIfCheckPasses('checkIsClubAdminForClub')
       
   766   def checkIsClubMemberForClub(self, django_args):
       
   767     """Returns an alternate HTTP response if Google Account has no Club Member
       
   768        entity for the specified club.
       
   769 
       
   770     Args:
       
   771       django_args: a dictionary with django's arguments
       
   772 
       
   773      Raises:
       
   774        AccessViolationResponse: if the required authorization is not met
       
   775 
       
   776     Returns:
       
   777       None if Club Member exists for the specified club, or a subclass of
       
   778       django.http.HttpResponse which contains the alternate response
       
   779       should be returned by the calling view.
       
   780     """
       
   781 
       
   782     self.checkIsUser(django_args)
       
   783 
       
   784     if django_args.get('scope_path'):
       
   785       scope_path = django_args['scope_path']
       
   786     else:
       
   787       scope_path = django_args['link_id']
       
   788 
       
   789     fields = {'user': self.user,
       
   790               'scope_path': scope_path,
       
   791               'status': 'active'}
       
   792 
       
   793     club_member_entity = club_member_logic.getForFields(fields, unique=True)
       
   794 
       
   795     if club_member_entity:
       
   796       return
       
   797 
       
   798     login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
       
   799         'role': 'a Club Member for this Club'}
       
   800 
       
   801     raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
       
   802 
       
   803   def checkIsClubAdminForScope(self, django_args):
       
   804     """Checks whether the current user is a Club Mdmin.
       
   805 
       
   806     Args:
       
   807       django_args: the keyword arguments from django, only scope_path is used
       
   808     """
       
   809 
       
   810     scope_path = django_args['scope_path']
       
   811     self.checkIsClubAdminForClub({'link_id': scope_path})
       
   812 
       
   813   def checkIsClubMemberForScope(self, django_args):
       
   814     """Checks whether the current user is a Club Mdmin.
       
   815 
       
   816     Args:
       
   817       django_args: the keyword arguments from django, only scope_path is used
       
   818     """
       
   819 
       
   820     scope_path = django_args['scope_path']
       
   821     self.checkIsClubMemberForClub({'link_id': scope_path})
       
   822 
       
   823   @allowDeveloper
   691   def checkIsApplicationAccepted(self, django_args, app_logic):
   824   def checkIsApplicationAccepted(self, django_args, app_logic):
   692     """Returns an alternate HTTP response if Google Account has no Club App
   825     """Returns an alternate HTTP response if Google Account has no Club App
   693        entity for the specified Club.
   826        entity for the specified Club.
   694 
   827 
   695     Args:
   828     Args:
   881     #TODO(SRabbelier) inform user that return_url and field are required
  1014     #TODO(SRabbelier) inform user that return_url and field are required
   882     self.deny(django_args)
  1015     self.deny(django_args)
   883 
  1016 
   884   @denySidebar
  1017   @denySidebar
   885   @allowDeveloper
  1018   @allowDeveloper
   886   def checkIsDocumentPublic(self, django_args):
  1019   def checkIsDocumentReadable(self, django_args):
   887     """Checks whether a document is public.
  1020     """Checks whether a document is readable.
   888 
  1021 
   889     Args:
  1022     Args:
   890       django_args: a dictionary with django's arguments
  1023       django_args: a dictionary with django's arguments
   891     """
  1024     """
   892 
  1025 
   893     key_fields = document_logic.getKeyFieldsFromFields(django_args)
  1026     key_fields = document_logic.getKeyFieldsFromFields(django_args)
   894     document_logic.getFromKeyFields(key_fields)
  1027     document = document_logic.getFromKeyFields(key_fields)
       
  1028 
       
  1029     self.checkMembership('read', document.prefix,
       
  1030                          document.read_access, django_args)
       
  1031 
       
  1032   @denySidebar
       
  1033   @allowDeveloper
       
  1034   def checkIsDocumentWritable(self, django_args):
       
  1035     """Checks whether a document is writable.
       
  1036 
       
  1037     Args:
       
  1038       django_args: a dictionary with django's arguments
       
  1039     """
       
  1040 
       
  1041     key_fields = document_logic.getKeyFieldsFromFields(django_args)
       
  1042     document = document_logic.getFromKeyFields(key_fields)
       
  1043 
       
  1044     self.checkMembership('write', document.prefix,
       
  1045                          document.write_access, django_args)
   895 
  1046 
   896   @allowIfCheckPasses('checkIsHostForProgram')
  1047   @allowIfCheckPasses('checkIsHostForProgram')
   897   def checkIsProgramVisible(self, django_args):
  1048   def checkIsProgramVisible(self, django_args):
   898     """Checks whether a program is visible.
  1049     """Checks whether a program is visible.
   899     """
  1050     """
   917         'role': ugettext('a Program Administrator')}
  1068         'role': ugettext('a Program Administrator')}
   918 
  1069 
   919     raise out_of_band.AccessViolation(DEF_DEV_LOGOUT_LOGIN_MSG_FMT,
  1070     raise out_of_band.AccessViolation(DEF_DEV_LOGOUT_LOGIN_MSG_FMT,
   920                                       context=context)
  1071                                       context=context)
   921 
  1072 
   922 
       
   923   def checkCanEditTimeline(self, django_args):
  1073   def checkCanEditTimeline(self, django_args):
   924     """Allows developers and hosts for this program's timeline to edit it.
  1074     """Checks whether this program's timeline may be edited.
   925     """
  1075     """
       
  1076 
   926     time_line_keyname = django_args['scope_path']
  1077     time_line_keyname = django_args['scope_path']
   927     timeline_entity = timeline_logic.getFromKeyName(time_line_keyname)
  1078     timeline_entity = timeline_logic.getFromKeyName(time_line_keyname)
   928 
  1079 
   929     if not timeline_entity:
  1080     if not timeline_entity:
   930       # timeline does not exists so deny
  1081       # timeline does not exists so deny