app/soc/views/helper/access.py
changeset 872 70e0b6d8ff73
parent 814 25ffebd9fa8f
child 882 267e31f1a0b6
equal deleted inserted replaced
871:0ec74865eb5e 872:70e0b6d8ff73
    61 
    61 
    62 DEF_LOGOUT_MSG_FMT = ugettext_lazy(
    62 DEF_LOGOUT_MSG_FMT = ugettext_lazy(
    63     'Please <a href="%(sign_out)s">sign out</a> in order to view this page')
    63     'Please <a href="%(sign_out)s">sign out</a> in order to view this page')
    64 
    64 
    65 
    65 
    66 def checkAccess(access_type, request, rights):
    66 def checkAccess(access_type, request, rights, args=None, kwargs=None):
    67   """Runs all the defined checks for the specified type.
    67   """Runs all the defined checks for the specified type.
    68 
    68 
    69   Args:
    69   Args:
    70     access_type: the type of request (such as 'list' or 'edit')
    70     access_type: the type of request (such as 'list' or 'edit')
    71     request: the Django request object
    71     request: the Django request object
    87       the response provided by the failed access check.
    87       the response provided by the failed access check.
    88   """
    88   """
    89 
    89 
    90   # Call each access checker
    90   # Call each access checker
    91   for check in rights['any_access']:
    91   for check in rights['any_access']:
    92     check(request)
    92     check(request, args, kwargs)
    93 
    93 
    94   if access_type not in rights:
    94   if access_type not in rights:
    95     for check in rights['unspecified']:
    95     for check in rights['unspecified']:
    96       # No checks defined, so do the 'generic' checks and bail out
    96       # No checks defined, so do the 'generic' checks and bail out
    97       check(request)
    97       check(request, args, kwargs)
    98     return
    98     return
    99 
    99 
   100   for check in rights[access_type]:
   100   for check in rights[access_type]:
   101     check(request)
   101     check(request, args, kwargs)
   102 
   102 
   103 
   103 
   104 def allow(request):
   104 def allow(request, args, kwargs):
   105   """Never returns an alternate HTTP response.
   105   """Never returns an alternate HTTP response.
   106 
   106 
   107   Args:
   107   Args:
   108     request: a Django HTTP request
   108     request: a Django HTTP request
   109   """
   109   """
   110 
   110 
   111   return
   111   return
   112 
   112 
   113 def deny(request):
   113 
       
   114 def deny(request, args, kwargs):
   114   """Returns an alternate HTTP response.
   115   """Returns an alternate HTTP response.
   115 
   116 
   116   Args:
   117   Args:
   117     request: a Django HTTP request
   118     request: a Django HTTP request
   118 
   119 
   125   context['title'] = 'Access denied'
   126   context['title'] = 'Access denied'
   126 
   127 
   127   raise out_of_band.AccessViolation(DEF_PAGE_DENIED_MSG, context=context)
   128   raise out_of_band.AccessViolation(DEF_PAGE_DENIED_MSG, context=context)
   128 
   129 
   129 
   130 
   130 def checkIsLoggedIn(request):
   131 def checkIsLoggedIn(request, args, kwargs):
   131   """Returns an alternate HTTP response if Google Account is not logged in.
   132   """Returns an alternate HTTP response if Google Account is not logged in.
   132 
   133 
   133   Args:
   134   Args:
   134     request: a Django HTTP request
   135     request: a Django HTTP request
   135 
   136 
   146     return
   147     return
   147 
   148 
   148   raise out_of_band.LoginRequest()
   149   raise out_of_band.LoginRequest()
   149 
   150 
   150 
   151 
   151 def checkNotLoggedIn(request):
   152 def checkNotLoggedIn(request, args, kwargs):
   152   """Returns an alternate HTTP response if Google Account is not logged in.
   153   """Returns an alternate HTTP response if Google Account is not logged in.
   153 
   154 
   154   Args:
   155   Args:
   155     request: a Django HTTP request
   156     request: a Django HTTP request
   156 
   157 
   167     return
   168     return
   168 
   169 
   169   raise out_of_band.LoginRequest(message_fmt=DEF_LOGOUT_MSG_FMT)
   170   raise out_of_band.LoginRequest(message_fmt=DEF_LOGOUT_MSG_FMT)
   170 
   171 
   171 
   172 
   172 def checkIsUser(request):
   173 def checkIsUser(request, args, kwargs):
   173   """Returns an alternate HTTP response if Google Account has no User entity.
   174   """Returns an alternate HTTP response if Google Account has no User entity.
   174 
   175 
   175   Args:
   176   Args:
   176     request: a Django HTTP request
   177     request: a Django HTTP request
   177 
   178 
   182     None if User exists for a Google Account, or a subclass of
   183     None if User exists for a Google Account, or a subclass of
   183     django.http.HttpResponse which contains the alternate response
   184     django.http.HttpResponse which contains the alternate response
   184     should be returned by the calling view.
   185     should be returned by the calling view.
   185   """
   186   """
   186 
   187 
   187   checkIsLoggedIn(request)
   188   checkIsLoggedIn(request, args, kwargs)
   188 
   189 
   189   user = user_logic.logic.getForFields(
   190   user = user_logic.logic.getForFields(
   190       {'account': users.get_current_user()}, unique=True)
   191       {'account': users.get_current_user()}, unique=True)
   191 
   192 
   192   if user:
   193   if user:
   193     return
   194     return
   194 
   195 
   195   raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
   196   raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
   196 
   197 
   197 
   198 
   198 def checkIsDeveloper(request):
   199 def checkIsDeveloper(request, args, kwargs):
   199   """Returns an alternate HTTP response if Google Account is not a Developer.
   200   """Returns an alternate HTTP response if Google Account is not a Developer.
   200 
   201 
   201   Args:
   202   Args:
   202     request: a Django HTTP request
   203     request: a Django HTTP request
   203 
   204 
   208     None if Google Account is logged in and logged-in user is a Developer,
   209     None if Google Account is logged in and logged-in user is a Developer,
   209     or a subclass of django.http.HttpResponse which contains the alternate
   210     or a subclass of django.http.HttpResponse which contains the alternate
   210     response should be returned by the calling view.
   211     response should be returned by the calling view.
   211   """
   212   """
   212 
   213 
   213   checkIsUser(request)
   214   checkIsUser(request, args, kwargs)
   214 
   215 
   215   if accounts.isDeveloper(account=users.get_current_user()):
   216   if accounts.isDeveloper(account=users.get_current_user()):
   216     return
   217     return
   217 
   218 
   218   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   219   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   219       'role': 'a Site Developer '}
   220       'role': 'a Site Developer '}
   220 
   221 
   221   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   222   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   222 
   223 
   223 
   224 
   224 def checkIsHost(request):
   225 def checkIsHost(request, args, kwargs):
   225   """Returns an alternate HTTP response if Google Account has no Host entity
   226   """Returns an alternate HTTP response if Google Account has no Host entity
   226      for the specified program.
   227      for the specified program.
   227 
   228 
   228   Args:
   229   Args:
   229     request: a Django HTTP request
   230     request: a Django HTTP request
   237     should be returned by the calling view.
   238     should be returned by the calling view.
   238   """
   239   """
   239 
   240 
   240   try:
   241   try:
   241     # if the current user is invited to create a host profile we allow access
   242     # if the current user is invited to create a host profile we allow access
   242     checkIsInvited(request)
   243     checkIsInvited(request, args, kwargs)
   243     return
   244     return
   244   except out_of_band.Error:
   245   except out_of_band.Error:
   245     pass
   246     pass
   246 
   247 
   247   checkIsUser(request)
   248   checkIsUser(request, args, kwargs)
   248 
   249 
   249   user = user_logic.logic.getForFields(
   250   user = user_logic.logic.getForFields(
   250       {'account': users.get_current_user()}, unique=True)
   251       {'account': users.get_current_user()}, unique=True)
   251 
   252 
   252   host = host_logic.logic.getForFields(
   253   host = host_logic.logic.getForFields(
   259       'role': 'a Program Administrator '}
   260       'role': 'a Program Administrator '}
   260 
   261 
   261   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   262   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   262 
   263 
   263 
   264 
   264 def checkIsClubAdminForClub(request):
   265 def checkIsClubAdminForClub(request, args, kwargs):
   265   """Returns an alternate HTTP response if Google Account has no Club Admin
   266   """Returns an alternate HTTP response if Google Account has no Club Admin
   266      entity for the specified club.
   267      entity for the specified club.
   267 
   268 
   268   Args:
   269   Args:
   269     request: a Django HTTP request
   270     request: a Django HTTP request
   277     should be returned by the calling view.
   278     should be returned by the calling view.
   278   """
   279   """
   279 
   280 
   280   try:
   281   try:
   281     # if the current user is invited to create a host profile we allow access
   282     # if the current user is invited to create a host profile we allow access
   282     checkIsDeveloper(request)
   283     checkIsDeveloper(request, args, kwargs)
   283     return
   284     return
   284   except out_of_band.Error:
   285   except out_of_band.Error:
   285     pass
   286     pass
   286 
   287 
   287   checkIsUser(request)
   288   checkIsUser(request, args, kwargs)
   288 
   289 
   289   # TODO(srabbelier) implement this
   290   # TODO(srabbelier) implement this
   290 
   291 
   291   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   292   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   292       'role': 'a Club Admin for this Club'}
   293       'role': 'a Club Admin for this Club'}
   293 
   294 
   294   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   295   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   295 
   296 
   296 
   297 
   297 def checkIsInvited(request):
   298 def checkIsInvited(request, args, kwargs):
   298   """Returns an alternate HTTP response if Google Account has no Host entity
   299   """Returns an alternate HTTP response if Google Account has no Host entity
   299      for the specified program.
   300      for the specified program.
   300 
   301 
   301   Args:
   302   Args:
   302     request: a Django HTTP request
   303     request: a Django HTTP request
   310     should be returned by the calling view.
   311     should be returned by the calling view.
   311   """
   312   """
   312 
   313 
   313   try:
   314   try:
   314     # if the current user is a developer we allow access
   315     # if the current user is a developer we allow access
   315     checkIsDeveloper(request)
   316     checkIsDeveloper(request, args, kwargs)
   316     return
   317     return
   317   except out_of_band.Error:
   318   except out_of_band.Error:
   318     pass
   319     pass
   319 
   320 
   320   checkIsUser(request)
   321   checkIsUser(request, args, kwargs)
   321 
   322 
   322   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   323   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
   323       'role': 'a Program Administrator for this Program'}
   324       'role': 'a Program Administrator for this Program'}
   324 
   325 
   325   splitpath = request.path.split('/')
   326   splitpath = request.path.split('/')
   326   splitpath = splitpath[1:] # cut off leading ''
   327   splitpath = splitpath[1:] # cut off leading ''
   327 
   328 
   328   if len(splitpath) < 4:
   329   if len(splitpath) < 4:
   329     # TODO: perhaps this needs a better explanation?
   330     # TODO: perhaps this needs a better explanation?
   330     deny(request)
   331     deny(request, args, kwargs)
   331 
   332 
   332   role = splitpath[0]
   333   role = splitpath[0]
   333   group_id = splitpath[2]
   334   group_id = splitpath[2]
   334   user_id = splitpath[3]
   335   user_id = splitpath[3]
   335 
   336 
   336   user = user_logic.logic.getForFields(
   337   user = user_logic.logic.getForFields(
   337       {'account': users.get_current_user()}, unique=True)
   338       {'account': users.get_current_user()}, unique=True)
   338 
   339 
   339   if user_id != user.link_id:
   340   if user_id != user.link_id:
   340     # TODO: perhaps this needs a better explanation?
   341     # TODO: perhaps this needs a better explanation?
   341     deny(request)
   342     deny(request, args, kwargs)
   342 
   343 
   343   properties = {
   344   properties = {
   344       'link_id': user_id,
   345       'link_id': user_id,
   345       'role': role,
   346       'role': role,
   346       'scope_path': group_id,
   347       'scope_path': group_id,
   353     return
   354     return
   354 
   355 
   355   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   356   raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
   356 
   357 
   357 
   358 
   358 def checkIsClubAppAccepted(request):
   359 def checkIsClubAppAccepted(request, args, kwargs):
   359   """Returns an alternate HTTP response if Google Account has no Club App
   360   """Returns an alternate HTTP response if Google Account has no Club App
   360      entity for the specified Club.
   361      entity for the specified Club.
   361 
   362 
   362   Args:
   363   Args:
   363     request: a Django HTTP request
   364     request: a Django HTTP request
   371     should be returned by the calling view.
   372     should be returned by the calling view.
   372   """
   373   """
   373 
   374 
   374   try:
   375   try:
   375     # if the current user is a developer we allow access
   376     # if the current user is a developer we allow access
   376     checkIsDeveloper(request)
   377     checkIsDeveloper(request, args, kwargs)
   377     return
   378     return
   378   except out_of_band.Error:
   379   except out_of_band.Error:
   379     pass
   380     pass
   380 
   381 
   381   checkIsUser(request)
   382   checkIsUser(request, args, kwargs)
   382 
   383 
   383   user = user_logic.logic.getForCurrentAccount()
   384   user = user_logic.logic.getForCurrentAccount()
   384 
   385 
   385   properties = {
   386   properties = {
   386       'applicant': user,
   387       'applicant': user,
   393 
   394 
   394   if group_app:
   395   if group_app:
   395     return
   396     return
   396 
   397 
   397   # TODO(srabbelier) Make this give a proper error message
   398   # TODO(srabbelier) Make this give a proper error message
   398   deny(request)
   399   deny(request, args, kwargs)
   399 
   400 
   400 
   401 
   401 def checkIsMyNotification(request):
   402 def checkIsMyNotification(request, args, kwargs):
   402   """Returns an alternate HTTP response if this request is for a Notification belonging
   403   """Returns an alternate HTTP response if this request is for a Notification belonging
   403      to the current user.
   404      to the current user.
   404 
   405 
   405   Args:
   406   Args:
   406     request: a Django HTTP request
   407     request: a Django HTTP request
   412     None if the current User is allowed to access this Notification.
   413     None if the current User is allowed to access this Notification.
   413   """
   414   """
   414   
   415   
   415   try:
   416   try:
   416     # if the current user is a developer we allow access
   417     # if the current user is a developer we allow access
   417     checkIsDeveloper(request)
   418     checkIsDeveloper(request, args, kwargs)
   418     return
   419     return
   419   except out_of_band.Error:
   420   except out_of_band.Error:
   420     pass
   421     pass
   421 
   422 
   422   checkIsUser(request)
   423   checkIsUser(request, args, kwargs)
   423 
   424 
   424   # Mine the url for params
   425   # Mine the url for params
   425   try:
   426   try:
   426     callback, args, kwargs = urlresolvers.resolve(request.path)
   427     callback, args, kwargs = urlresolvers.resolve(request.path)
   427   except Exception:
   428   except Exception:
   428     deny(request)
   429     deny(request, args, kwargs)
   429 
   430 
   430   properties = dicts.filter(kwargs, ['link_id', 'scope_path'])
   431   properties = dicts.filter(kwargs, ['link_id', 'scope_path'])
   431 
   432 
   432   notification = notification_logic.logic.getForFields(properties, unique=True)
   433   notification = notification_logic.logic.getForFields(properties, unique=True)
   433   user = user_logic.logic.getForCurrentAccount()
   434   user = user_logic.logic.getForCurrentAccount()
   437   # if the keys are equal (which is what we want).
   438   # if the keys are equal (which is what we want).
   438   if user.key() == notification.scope.key():
   439   if user.key() == notification.scope.key():
   439     return None
   440     return None
   440 
   441 
   441   # TODO(ljvderijk) Make this give a proper error message
   442   # TODO(ljvderijk) Make this give a proper error message
   442   deny(request)
   443   deny(request, args, kwargs)
   443 
   444 
   444 def checkIsMyApplication(request):
   445 
       
   446 def checkIsMyApplication(request, args, kwargs):
   445   """Returns an alternate HTTP response if this request is for a Application belonging
   447   """Returns an alternate HTTP response if this request is for a Application belonging
   446      to the current user.
   448      to the current user.
   447 
   449 
   448   Args:
   450   Args:
   449     request: a Django HTTP request
   451     request: a Django HTTP request
   455     None if the current User is allowed to access this Application.
   457     None if the current User is allowed to access this Application.
   456   """
   458   """
   457   
   459   
   458   try:
   460   try:
   459     # if the current user is a developer we allow access
   461     # if the current user is a developer we allow access
   460     checkIsDeveloper(request)
   462     checkIsDeveloper(request, args, kwargs)
   461     return
   463     return
   462   except out_of_band.Error:
   464   except out_of_band.Error:
   463     pass
   465     pass
   464 
   466 
   465   checkIsUser(request)
   467   checkIsUser(request, args, kwargs)
   466 
   468 
   467   # Mine the url for params
   469   # Mine the url for params
   468   try:
   470   try:
   469     callback, args, kwargs = urlresolvers.resolve(request.path)
   471     callback, args, kwargs = urlresolvers.resolve(request.path)
   470   except Exception:
   472   except Exception:
   471     deny(request)
   473     deny(request, args, kwargs)
   472 
   474 
   473   properties = dicts.filter(kwargs, ['link_id'])
   475   properties = dicts.filter(kwargs, ['link_id'])
   474 
   476 
   475   application = group_app_logic.logic.getForFields(properties, unique=True)
   477   application = group_app_logic.logic.getForFields(properties, unique=True)
   476   user = user_logic.logic.getForCurrentAccount()
   478   user = user_logic.logic.getForCurrentAccount()
   480   # if the keys are equal (which is what we want).
   482   # if the keys are equal (which is what we want).
   481   if user.key() == application.applicant.key():
   483   if user.key() == application.applicant.key():
   482     return None
   484     return None
   483 
   485 
   484   # TODO(srabbelier) Make this give a proper error message
   486   # TODO(srabbelier) Make this give a proper error message
   485   deny(request)
   487   deny(request, args, kwargs)
   486 
   488 
   487 
   489 
   488 def checkCanInvite(request):
   490 def checkCanInvite(request, args, kwargs):
   489   """Checks to see if the current user can create an invite.
   491   """Checks to see if the current user can create an invite.
   490 
   492 
   491   Note that if the current url is not in the default 'request' form
   493   Note that if the current url is not in the default 'request' form
   492   this method either deny()s or performs the wrong access check.
   494   this method either deny()s or performs the wrong access check.
   493 
   495 
   495     request: a Django HTTP request
   497     request: a Django HTTP request
   496   """
   498   """
   497 
   499 
   498   try:
   500   try:
   499     # if the current user is a developer we allow access
   501     # if the current user is a developer we allow access
   500     checkIsDeveloper(request)
   502     checkIsDeveloper(request, args, kwargs)
   501     return
   503     return
   502   except out_of_band.Error:
   504   except out_of_band.Error:
   503     pass
   505     pass
   504 
   506 
   505   # Mine the url for params
   507   # Mine the url for params
   506   try:
   508   try:
   507     callback, args, kwargs = urlresolvers.resolve(request.path)
   509     callback, args, kwargs = urlresolvers.resolve(request.path)
   508   except Exception:
   510   except Exception:
   509     deny(request)
   511     deny(request, args, kwargs)
   510 
   512 
   511   # Construct a new url by reshufling the kwargs
   513   # Construct a new url by reshufling the kwargs
   512   order = ['role', 'access_type', 'scope_path', 'link_id']
   514   order = ['role', 'access_type', 'scope_path', 'link_id']
   513   url_params = dicts.unzip(kwargs, order)
   515   url_params = dicts.unzip(kwargs, order)
   514   url = '/'.join([''] + list(url_params))
   516   url = '/'.join([''] + list(url_params))
   515 
   517 
   516   # Mine the reshufled url
   518   # Mine the reshufled url
   517   try:
   519   try:
   518     callback, args, kwargs = urlresolvers.resolve(url)
   520     callback, args, kwargs = urlresolvers.resolve(url)
   519   except Exception:
   521   except Exception:
   520     deny(request)
   522     deny(request, args, kwargs)
   521 
   523 
   522   # Get the everything we need for the access check
   524   # Get the everything we need for the access check
   523   params = callback.im_self.getParams()
   525   params = callback.im_self.getParams()
   524   access_type = kwargs['access_type']
   526   access_type = kwargs['access_type']
   525 
   527 
   526   # Perform the access check
   528   # Perform the access check
   527   helper.access.checkAccess(access_type, request, rights=params['rights'])
   529   checkAccess(access_type, request, rights=params['rights'])
   528 
   530 
   529 def checkIsDocumentPublic(request):
   531 
       
   532 def checkIsDocumentPublic(request, args, kwargs):
   530   """Checks whether a document is public.
   533   """Checks whether a document is public.
   531 
   534 
   532   Args:
   535   Args:
   533     request: a Django HTTP request
   536     request: a Django HTTP request
   534   """
   537   """
   535 
   538 
   536   # TODO(srabbelier): A proper check needs to be done to see if the document
   539   # TODO(srabbelier): A proper check needs to be done to see if the document
   537   # is public or not, probably involving analysing it's scope or such.
   540   # is public or not, probably involving analysing it's scope or such.
   538   allow(request)
   541   allow(request, args, kwargs)