Major refactor of the access module
authorSverre Rabbelier <srabbelier@gmail.com>
Mon, 26 Jan 2009 23:32:10 +0000
changeset 1007 3b66772d21a5
parent 1006 6abf12b9e646
child 1008 ae1a36ef7cff
Major refactor of the access module The first step to sanity is a leap into the unknown? Create an object to represent the access checks for each module instead of a bunch of loose functions. Converted all views and params.py to use the new access checker. Main differences: * arguments to a checker can be passed by using a tuple * checkers are referenced by string, rather than directly * the Checker constructor handles merging with child views Patch by: Sverre Rabbelier
app/soc/views/helper/access.py
app/soc/views/helper/decorators.py
app/soc/views/helper/params.py
app/soc/views/models/club.py
app/soc/views/models/club_admin.py
app/soc/views/models/club_app.py
app/soc/views/models/club_member.py
app/soc/views/models/document.py
app/soc/views/models/host.py
app/soc/views/models/notification.py
app/soc/views/models/presence.py
app/soc/views/models/program.py
app/soc/views/models/request.py
app/soc/views/models/site.py
app/soc/views/models/sponsor.py
app/soc/views/models/user_self.py
app/soc/views/sitemap/sidebar.py
--- a/app/soc/views/helper/access.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/helper/access.py	Mon Jan 26 23:32:10 2009 +0000
@@ -74,433 +74,475 @@
     'The requested Group can not be found')
 
 
-def checkAccess(access_type, rights, kwargs=None):
-  """Runs all the defined checks for the specified type.
-
-  Args:
-    access_type: the type of request (such as 'list' or 'edit')
-    rights: a dictionary containing access check functions
-    kwargs: a dictionary with django's arguments
-
-  Rights usage: 
-    The rights dictionary is used to check if the current user is allowed 
-    to view the page specified. The functions defined in this dictionary 
-    are always called with the provided kwargs dictionary as argument. On any
-    request, regardless of what type, the functions in the 'any_access' value
-    are called. If the specified type is not in the rights dictionary, all
-    the functions in the 'unspecified' value are called. When the specified
-    type _is_ in the rights dictionary, all the functions in that access_type's
-    value are called.
+def denySidebar(fun):
+  """Decorator that denies access if the sidebar is calling.
   """
 
-  # Call each access checker
-  for check in rights['any_access']:
-    check(kwargs)
-
-  if access_type not in rights:
-    for check in rights['unspecified']:
-      # No checks defined, so do the 'generic' checks and bail out
-      check(kwargs)
-    return
-
-  for check in rights[access_type]:
-    check(kwargs)
-
-
-def allow(kwargs):
-  """Never raises an alternate HTTP response.  (an access no-op, basically).
-
-  Args:
-    kwargs: a dictionary with django's arguments
-  """
-
-  return
-
-
-def deny(kwargs):
-  """Always raises an alternate HTTP response.
+  from functools import wraps
 
-  Args:
-    kwargs: a dictionary with django's arguments
-
-  Raises:
-    always raises AccessViolationResponse if called
-  """
-
-  import soc.views.helper.responses
-
-  context = kwargs.get('context', {})
-  context['title'] = 'Access denied'
-
-  raise out_of_band.AccessViolation(DEF_PAGE_DENIED_MSG, context=context)
-
-
-def checkIsLoggedIn(kwargs):
-  """Raises an alternate HTTP response if Google Account is not logged in.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-  Raises:
-    AccessViolationResponse:
-    * if no Google Account is even logged in
-  """
-
-  if users.get_current_user():
-    return
-
-  raise out_of_band.LoginRequest()
+  @wraps(fun)
+  def wrapper(self, django_args, *args, **kwargs):
+    if django_args.get('SIDEBAR_CALLING'):
+      raise out_of_band.Error("Sidebar Calling")
+    return fun(self, django_args, *args, **kwargs)
+  return wrapper
 
 
-def checkNotLoggedIn(kwargs):
-  """Raises an alternate HTTP response if Google Account is logged in.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-  Raises:
-    AccessViolationResponse:
-    * if a Google Account is currently logged in
+class Checker(object):
   """
-  
-  if not users.get_current_user():
-    return
-
-  raise out_of_band.LoginRequest(message_fmt=DEF_LOGOUT_MSG_FMT)
-
-
-def checkIsUser(kwargs):
-  """Raises an alternate HTTP response if Google Account has no User entity.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-  Raises:
-    AccessViolationResponse:
-    * if no User exists for the logged-in Google Account, or
-    * if no Google Account is logged in at all
-  """
-
-  checkIsLoggedIn(kwargs)
-
-  user = user_logic.getForCurrentAccount()
-
-  if user:
-    return
-
-  raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
-
-
-def checkAgreesToSiteToS(kwargs):
-  """Raises an alternate HTTP response if User has not agreed to site-wide ToS.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-  Raises:
-    AccessViolationResponse:
-    * if User has not agreed to the site-wide ToS, or
-    * if no User exists for the logged-in Google Account, or
-    * if no Google Account is logged in at all
+  The __setitem__() and __getitem__() methods are overloaded to DTRT
+  when adding new access rights, and retrieving them, so use these
+  rather then modifying rights directly if so desired.
   """
 
-  checkIsUser(kwargs)
+  def __init__(self, params):
+    """Adopts base.rights as rights if base is set.
+    """
+
+    base = params.get('rights') if params else None
+    self.rights = base.rights if base else {}
+
+  def __setitem__(self, key, value):
+    """Sets a value only if no old value exists.
+    """
+
+    oldvalue = self.rights.get(key)
+    self.rights[key] = oldvalue if oldvalue else value
+
+  def __getitem__(self, key):
+    """Retrieves the right checkers and massages then into a default format.
+
+    The result is guaranteed to be a list of 2-tuples, the first element is a
+    checker (iff there is an checker with the specified name), the second
+    element is a list of arguments that should be passed to the checker when
+    calling it in addition to the standard django_args.
+    """
+
+    result = []
+
+    for i in self.rights.get(key, []):
+      # Be nice an repack so that it is always a list with tuples
+      if isinstance(i, tuple):
+        name, arg = i
+        tmp = (getattr(self, name), (arg if isinstance(arg, list) else [arg]))
+        result.append(tmp)
+      else:
+        tmp = (getattr(self, i), [])
+        result.append(tmp)
+
+    return result
 
-  user = user_logic.getForCurrentAccount()
-  
-  if user_logic.agreesToSiteToS(user):
+  def checkAccess(self, access_type, django_args):
+    """Runs all the defined checks for the specified type.
+
+    Args:
+      access_type: the type of request (such as 'list' or 'edit')
+      rights: a dictionary containing access check functions
+      django_args: a dictionary with django's arguments
+
+    Rights usage:
+      The rights dictionary is used to check if the current user is allowed
+      to view the page specified. The functions defined in this dictionary
+      are always called with the provided django_args dictionary as argument. On any
+      request, regardless of what type, the functions in the 'any_access' value
+      are called. If the specified type is not in the rights dictionary, all
+      the functions in the 'unspecified' value are called. When the specified
+      type _is_ in the rights dictionary, all the functions in that access_type's
+      value are called.
+    """
+
+    self.id = users.get_current_user()
+
+    # Call each access checker
+    for check, args in self['any_access']:
+      check(django_args, *args)
+
+    if access_type not in self.rights:
+      for check, args in self['unspecified']:
+        # No checks defined, so do the 'generic' checks and bail out
+        check(django_args, *args)
+      return
+
+    for check, args in self[access_type]:
+      check(django_args, *args)
+
+  def allow(self, django_args):
+    """Never raises an alternate HTTP response.  (an access no-op, basically).
+
+    Args:
+      django_args: a dictionary with django's arguments
+    """
+
     return
 
-  # Would not reach this point of site-wide ToS did not exist, since
-  # agreesToSiteToS() call above always returns True if no ToS is in effect.
-  login_msg_fmt = DEF_AGREE_TO_TOS_MSG_FMT % {
-      'tos_link': redirects.getToSRedirect(site_logic.getSingleton())}
+  def deny(self, django_args):
+    """Always raises an alternate HTTP response.
+
+    Args:
+      django_args: a dictionary with django's arguments
+
+    Raises:
+      always raises AccessViolationResponse if called
+    """
+
+    context = django_args.get('context', {})
+    context['title'] = 'Access denied'
+
+    raise out_of_band.AccessViolation(DEF_PAGE_DENIED_MSG, context=context)
 
-  raise out_of_band.LoginRequest(message_fmt=login_msg_fmt)
+  def checkIsLoggedIn(self, django_args):
+    """Raises an alternate HTTP response if Google Account is not logged in.
 
+    Args:
+      django_args: a dictionary with django's arguments
 
-def checkIsDeveloper(kwargs):
-  """Raises an alternate HTTP response if Google Account is not a Developer.
+    Raises:
+      AccessViolationResponse:
+      * if no Google Account is even logged in
+    """
+
+    if self.id:
+      return
+
+    raise out_of_band.LoginRequest()
 
-  Args:
-    kwargs: a dictionary with django's arguments
+  def checkNotLoggedIn(self, django_args):
+    """Raises an alternate HTTP response if Google Account is logged in.
+
+    Args:
+      django_args: a dictionary with django's arguments
+
+    Raises:
+      AccessViolationResponse:
+      * if a Google Account is currently logged in
+    """
+
+    if not self.id:
+      return
+
+    raise out_of_band.LoginRequest(message_fmt=DEF_LOGOUT_MSG_FMT)
 
-  Raises:
-    AccessViolationResponse:
-    * if User is not a Developer, or
-    * if no User exists for the logged-in Google Account, or
-    * if no Google Account is logged in at all
-  """
+  def checkIsUser(self, django_args):
+    """Raises an alternate HTTP response if Google Account has no User entity.
+
+    Args:
+      django_args: a dictionary with django's arguments
 
-  checkAgreesToSiteToS(kwargs)
+    Raises:
+      AccessViolationResponse:
+      * if no User exists for the logged-in Google Account, or
+      * if no Google Account is logged in at all
+    """
+
+    self.checkIsLoggedIn(django_args)
+
+    user = user_logic.getForCurrentAccount()
 
-  if accounts.isDeveloper(account=users.get_current_user()):
-    return
+    if user:
+      return
+
+    raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
+
+  def checkAgreesToSiteToS(self, django_args):
+    """Raises an alternate HTTP response if User has not agreed to site-wide ToS.
+
+    Args:
+      django_args: a dictionary with django's arguments
 
-  login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
-      'role': 'a Site Developer '}
+    Raises:
+      AccessViolationResponse:
+      * if User has not agreed to the site-wide ToS, or
+      * if no User exists for the logged-in Google Account, or
+      * if no Google Account is logged in at all
+    """
+
+    self.checkIsUser(django_args)
 
-  raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+    user = user_logic.getForCurrentAccount()
+
+    if user_logic.agreesToSiteToS(user):
+      return
 
+    # Would not reach this point of site-wide ToS did not exist, since
+    # agreesToSiteToS() call above always returns True if no ToS is in effect.
+    login_msg_fmt = DEF_AGREE_TO_TOS_MSG_FMT % {
+        'tos_link': redirects.getToSRedirect(site_logic.getSingleton())}
+
+    raise out_of_band.LoginRequest(message_fmt=login_msg_fmt)
 
-def checkCanMakeRequestToGroup(group_logic):
-  """Raises an alternate HTTP response if the specified group is not in an
-  active state.
-  
-  Note that state hasn't been implemented yet
-  
-  Args:
-    group_logic: Logic module for the type of group which the request is for
-  """
+  def checkIsDeveloper(self, django_args):
+    """Raises an alternate HTTP response if Google Account is not a Developer.
+
+    Args:
+      django_args: a dictionary with django's arguments
+
+    Raises:
+      AccessViolationResponse:
+      * if User is not a Developer, or
+      * if no User exists for the logged-in Google Account, or
+      * if no Google Account is logged in at all
+    """
+
+    self.checkAgreesToSiteToS(django_args)
+
+    if accounts.isDeveloper(account=self.id):
+      return
 
-  def wrapper(kwargs):
+    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
+        'role': 'a Site Developer '}
+
+    raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+
+  def checkCanMakeRequestToGroup(self, django_args, group_logic):
+    """Raises an alternate HTTP response if the specified group is not in an
+    active state.
+
+    Note that state hasn't been implemented yet
+
+    Args:
+      group_logic: Logic module for the type of group which the request is for
+    """
+
     group_entity = role_logic.getGroupEntityFromScopePath(
-        group_logic.logic, kwargs['scope_path'])
+        group_logic.logic, django_args['scope_path'])
 
     if not group_entity:
       raise out_of_band.Error(DEF_GROUP_NOT_FOUND_MSG, status=404)
 
     # TODO(ljvderijk) check if the group is active
     return
-  return wrapper
-
 
-def checkCanCreateFromRequest(role_name):
-  """Raises an alternate HTTP response if the specified request does not exist
-     or if it's state is not group_accepted. 
-  """
+  def checkCanCreateFromRequest(self, django_args, role_name):
+    """Raises an alternate HTTP response if the specified request does not exist
+       or if it's state is not group_accepted.
+    """
 
-  def wrapper(kwargs):
-    checkAgreesToSiteToS(kwargs)
+    self.checkAgreesToSiteToS(django_args)
 
     user_entity = user_logic.getForCurrentAccount()
 
-    if user_entity.link_id != kwargs['link_id']:
-      deny(kwargs)
+    if user_entity.link_id != django_args['link_id']:
+      deny(django_args)
 
-    fields = {'link_id': kwargs['link_id'],
-        'scope_path': kwargs['scope_path'],
+    fields = {'link_id': django_args['link_id'],
+        'scope_path': django_args['scope_path'],
         'role': role_name}
 
     request_entity = request_logic.getFromFieldsOr404(**fields)
 
     if request_entity.state != 'group_accepted':
       # TODO tell the user that this request has not been accepted yet
-      deny(kwargs)
+      deny(django_args)
 
     return
 
-  return wrapper
-
+  def checkCanProcessRequest(self, django_args, role_name):
+    """Raises an alternate HTTP response if the specified request does not exist
+       or if it's state is completed or denied.
+    """
 
-def checkCanProcessRequest(role_name):
-  """Raises an alternate HTTP response if the specified request does not exist
-     or if it's state is completed or denied. 
-  """
-
-  def wrapper(kwargs):
-
-    fields = {'link_id': kwargs['link_id'],
-        'scope_path': kwargs['scope_path'],
+    fields = {'link_id': django_args['link_id'],
+        'scope_path': django_args['scope_path'],
         'role': role_name}
 
     request_entity = request_logic.getFromFieldsOr404(**fields)
 
     if request_entity.state in ['completed', 'denied']:
       # TODO tell the user that this request has been processed
-      deny(kwargs)
-
-    return
-  
-  return wrapper
-
-
-def checkIsMyGroupAcceptedRequest(kwargs):
-  """Raises an alternate HTTP response if the specified request does not exist
-     or if it's state is not group_accepted.
-  """
-
-  checkAgreesToSiteToS(kwargs)
-
-  user_entity = user_logic.getForCurrentAccount()
-
-  if user_entity.link_id != kwargs['link_id']:
-    # not the current user's request
-    return deny(kwargs)
-
-  fields = {'link_id': kwargs['link_id'],
-            'scope_path': kwargs['scope_path'],
-            'role': kwargs['role']}
-
-  request_entity = request_logic.getForFields(fields, unique=True)
-
-  if not request_entity:
-    # TODO return 404
-    return deny(kwargs)
-
-  if request_entity.state != 'group_accepted':
-    return deny(kwargs)
+      deny(django_args)
 
-  return
-
-
-def checkIsHost(kwargs):
-  """Raises an alternate HTTP response if Google Account has no Host entity.
-
-  Args:
-    request: a Django HTTP request
-
-  Raises:
-    AccessViolationResponse:
-    * if User is not already a Host, or
-    * if User has not agreed to the site-wide ToS, or
-    * if no User exists for the logged-in Google Account, or
-    * if the user is not even logged in
-  """
-
-  try:
-    # if the current user is a developer we allow access
-    checkIsDeveloper(kwargs)
-    return
-  except out_of_band.Error:
-    pass
-
-  checkAgreesToSiteToS(kwargs)
-
-  user = user_logic.getForCurrentAccount()
-
-  fields = {'user': user,
-            'state': 'active'}
-
-  host = host_logic.getForFields(fields, unique=True)
-
-  if host:
     return
 
-  login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
-      'role': 'a Program Administrator '}
-
-  raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
-
-
-def checkIsHostForSponsor(kwargs):
-  """Raises an alternate HTTP response if Google Account has no Host entity
-     for the specified Sponsor.
+  def checkIsMyGroupAcceptedRequest(self, django_args):
+    """Raises an alternate HTTP response if the specified request does not exist
+       or if it's state is not group_accepted.
+    """
 
-  Args:
-    request: a Django HTTP request
+    self.checkAgreesToSiteToS(django_args)
+
+    user_entity = user_logic.getForCurrentAccount()
 
-  Raises:
-    AccessViolationResponse:
-    * if User is not already a Host for the specified program, or
-    * if User has not agreed to the site-wide ToS, or
-    * if no User exists for the logged-in Google Account, or
-    * if the user is not even logged in
-  """
+    if user_entity.link_id != django_args['link_id']:
+      # not the current user's request
+      return deny(django_args)
 
-  try:
-    # if the current user is a developer we allow access
-    checkIsDeveloper(kwargs)
-    return
-  except out_of_band.Error:
-    pass
+    fields = {'link_id': django_args['link_id'],
+              'scope_path': django_args['scope_path'],
+              'role': django_args['role']}
 
-  checkAgreesToSiteToS(kwargs)
-
-  user = user_logic.getForCurrentAccount()
+    request_entity = request_logic.getForFields(fields, unique=True)
 
-  if kwargs.get('scope_path'):
-    scope_path = kwargs['scope_path']
-  else:
-    scope_path = kwargs['link_id']
+    if not request_entity:
+      # TODO return 404
+      return deny(django_args)
 
-  fields = {'user': user,
-            'scope_path': scope_path,
-            'state': 'active'}
+    if request_entity.state != 'group_accepted':
+      return deny(django_args)
 
-  host = host_logic.getForFields(fields, unique=True)
-
-  if host:
     return
 
-  login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
-      'role': 'a Program Administrator '}
-
-  raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
-
-
-def checkIsClubAdminForClub(kwargs):
-  """Returns an alternate HTTP response if Google Account has no Club Admin
-     entity for the specified club.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-   Raises:
-     AccessViolationResponse: if the required authorization is not met
+  @denySidebar
+  def checkIsHost(self, django_args):
+    """Raises an alternate HTTP response if Google Account has no Host entity.
 
-  Returns:
-    None if Club Admin exists for the specified club, or a subclass of
-    django.http.HttpResponse which contains the alternate response
-    should be returned by the calling view.
-  """
-
-  try:
-    # if the current user is invited to create a host profile we allow access
-    checkIsDeveloper(kwargs)
-    return
-  except out_of_band.Error:
-    pass
-
-  checkAgreesToSiteToS(kwargs)
-
-  user = user_logic.getForCurrentAccount()
+    Args:
+      request: a Django HTTP request
 
-  if kwargs.get('scope_path'):
-    scope_path = kwargs['scope_path']
-  else:
-    scope_path = kwargs['link_id']
-
-  fields = {'user': user,
-            'scope_path': scope_path,
-            'state': 'active'}
-
-  club_admin_entity = club_admin_logic.getForFields(fields, unique=True)
-
-  if club_admin_entity:
-    return
-
-  login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
-      'role': 'a Club Admin for this Club'}
-
-  raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+    Raises:
+      AccessViolationResponse:
+      * if User is not already a Host, or
+      * if User has not agreed to the site-wide ToS, or
+      * if no User exists for the logged-in Google Account, or
+      * if the user is not even logged in
+    """
 
-
-def checkIsApplicationAccepted(app_logic):
-  """Returns an alternate HTTP response if Google Account has no Club App
-     entity for the specified Club.
-
-  Args:
-    kwargs: a dictionary with django's arguments
-
-   Raises:
-     AccessViolationResponse: if the required authorization is not met
-
-  Returns:
-    None if Club App  exists for the specified program, or a subclass
-    of django.http.HttpResponse which contains the alternate response
-    should be returned by the calling view.
-  """
-
-  def wrapper(kwargs):
     try:
       # if the current user is a developer we allow access
-      checkIsDeveloper(kwargs)
+      self.checkIsDeveloper(django_args)
       return
     except out_of_band.Error:
       pass
 
-    checkAgreesToSiteToS(kwargs)
+    self.checkAgreesToSiteToS(django_args)
+
+    user = user_logic.getForCurrentAccount()
+
+    if django_args.get('scope_path'):
+      scope_path = django_args['scope_path']
+    else:
+      scope_path = django_args['link_id']
+
+    fields = {'user': user,
+              'scope_path': scope_path,
+              'state': 'active'}
+
+    host = host_logic.getForFields(fields, unique=True)
+
+    self.checkAgreesToSiteToS(django_args)
+
+    user = user_logic.getForCurrentAccount()
+
+    fields = {'user': user,
+              'state': 'active'}
+
+    host = host_logic.getForFields(fields, unique=True)
+
+    if host:
+      return
+
+    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
+        'role': 'a Program Administrator '}
+
+    raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+
+  def checkIsHostForSponsor(self, django_args):
+    """Raises an alternate HTTP response if Google Account has no Host entity
+       for the specified Sponsor.
+
+    Args:
+      request: a Django HTTP request
+
+    Raises:
+      AccessViolationResponse:
+      * if User is not already a Host for the specified program, or
+      * if User has not agreed to the site-wide ToS, or
+      * if no User exists for the logged-in Google Account, or
+      * if the user is not even logged in
+    """
+
+    self.checkAgreesToSiteToS(django_args)
+
+    user = user_logic.getForCurrentAccount()
+
+    if django_args.get('scope_path'):
+      scope_path = django_args['scope_path']
+    else:
+      scope_path = django_args['link_id']
+
+    fields = {'user': user,
+              'scope_path': scope_path,
+              'state': 'active'}
+
+    host = host_logic.getForFields(fields, unique=True)
+
+    if host:
+      return
+
+    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
+        'role': 'a Program Administrator '}
+
+    raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+
+  def checkIsClubAdminForClub(self, django_args):
+    """Returns an alternate HTTP response if Google Account has no Club Admin
+       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 Admin exists for the specified club, or a subclass of
+      django.http.HttpResponse which contains the alternate response
+      should be returned by the calling view.
+    """
+
+    try:
+      # if the current user is invited to create a host profile we allow access
+      checkIsDeveloper(django_args)
+      return
+    except out_of_band.Error:
+      pass
+
+    self.checkAgreesToSiteToS(django_args)
+
+    user = user_logic.getForCurrentAccount()
+
+    if django_args.get('scope_path'):
+      scope_path = django_args['scope_path']
+    else:
+      scope_path = django_args['link_id']
+
+    fields = {'user': user,
+              'scope_path': scope_path,
+              'state': 'active'}
+
+    club_admin_entity = club_admin_logic.getForFields(fields, unique=True)
+
+    if club_admin_entity:
+      return
+
+    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
+        'role': 'a Club Admin for this Club'}
+
+    raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
+
+  def checkIsApplicationAccepted(self, django_args, app_logic):
+    """Returns an alternate HTTP response if Google Account has no Club App
+       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 App  exists for the specified program, or a subclass
+      of django.http.HttpResponse which contains the alternate response
+      should be returned by the calling view.
+    """
+
+    try:
+      # if the current user is a developer we allow access
+      checkIsDeveloper(django_args)
+      return
+    except out_of_band.Error:
+      pass
+
+    self.checkAgreesToSiteToS(django_args)
 
     user = user_logic.getForCurrentAccount()
 
@@ -515,80 +557,75 @@
       return
 
     # TODO(srabbelier) Make this give a proper error message
-    deny(kwargs)
-
-  return wrapper
-
-
-def checkIsMyNotification(kwargs):
-  """Returns an alternate HTTP response if this request is for 
-     a Notification belonging to the current user.
-
-  Args:
-    kwargs: a dictionary with django's arguments
+    deny(django_args)
 
-   Raises:
-     AccessViolationResponse: if the required authorization is not met
-
-  Returns:
-    None if the current User is allowed to access this Notification.
-  """
-  
-  try:
-    # if the current user is a developer we allow access
-    checkIsDeveloper(kwargs)
-    return
-  except out_of_band.Error:
-    pass
-
-  checkAgreesToSiteToS(kwargs)
+  def checkIsMyNotification(self, django_args):
+    """Returns an alternate HTTP response if this request is for
+       a Notification belonging to the current user.
 
-  properties = dicts.filter(kwargs, ['link_id', 'scope_path'])
-
-  notification = notification_logic.getForFields(properties, unique=True)
-  user = user_logic.getForCurrentAccount()
-
-  # We need to check to see if the key's are equal since the User
-  # objects are different and the default __eq__ method does not check
-  # if the keys are equal (which is what we want).
-  if user.key() == notification.scope.key():
-    return None
-
-  # TODO(ljvderijk) Make this give a proper error message
-  deny(kwargs)
-
+    Args:
+      django_args: a dictionary with django's arguments
 
-def checkIsMyApplication(app_logic):
-  """Returns an alternate HTTP response if this request is for 
-     a Application belonging to the current user.
-
-  Args:
-    request: a Django HTTP request
+     Raises:
+       AccessViolationResponse: if the required authorization is not met
 
-   Raises:
-     AccessViolationResponse: if the required authorization is not met
+    Returns:
+      None if the current User is allowed to access this Notification.
+    """
 
-  Returns:
-    None if the current User is allowed to access this Application.
-  """
-
-  def wrapper(kwargs):
     try:
       # if the current user is a developer we allow access
-      checkIsDeveloper(kwargs)
+      checkIsDeveloper(django_args)
       return
     except out_of_band.Error:
       pass
 
-    checkAgreesToSiteToS(kwargs)
+    self.checkAgreesToSiteToS(django_args)
+
+    properties = dicts.filter(django_args, ['link_id', 'scope_path'])
+
+    notification = notification_logic.getForFields(properties, unique=True)
+    user = user_logic.getForCurrentAccount()
+
+    # We need to check to see if the key's are equal since the User
+    # objects are different and the default __eq__ method does not check
+    # if the keys are equal (which is what we want).
+    if user.key() == notification.scope.key():
+      return None
+
+    # TODO(ljvderijk) Make this give a proper error message
+    deny(django_args)
+
+  def checkIsMyApplication(self, django_args, app_logic):
+    """Returns an alternate HTTP response if this request is for
+       a Application belonging to the current user.
 
-    properties = dicts.filter(kwargs, ['link_id'])
+    Args:
+      request: a Django HTTP request
+
+     Raises:
+       AccessViolationResponse: if the required authorization is not met
+
+    Returns:
+      None if the current User is allowed to access this Application.
+    """
+
+    try:
+      # if the current user is a developer we allow access
+      self.checkIsDeveloper(django_args)
+      return
+    except out_of_band.Error:
+      pass
+
+    self.checkAgreesToSiteToS(django_args)
+
+    properties = dicts.filter(django_args, ['link_id'])
 
     application = app_logic.logic.getForFields(properties, unique=True)
-    
+
     if not application:
-      deny(kwargs)
-    
+      deny(django_args)
+
     user = user_logic.getForCurrentAccount()
 
     # We need to check to see if the key's are equal since the User
@@ -598,84 +635,76 @@
       return None
 
     # TODO(srabbelier) Make this give a proper error message
-    deny(kwargs)
-
-  return wrapper
+    deny(django_args)
 
-
-def checkIsMyActiveRole(role_logic):
-  """Returns an alternate HTTP response if there is no active role found for
-     the current user using the given role_logic.
+  def checkIsMyActiveRole(self, django_args, role_logic):
+    """Returns an alternate HTTP response if there is no active role found for
+       the current user using the given role_logic.
 
-   Raises:
-     AccessViolationResponse: if the required authorization is not met
+     Raises:
+       AccessViolationResponse: if the required authorization is not met
 
-  Returns:
-    None if the current User has no active role for the given role_logic.
-  """
+    Returns:
+      None if the current User has no active role for the given role_logic.
+    """
 
-  def wrapper(kwargs):
     try:
       # if the current user is a developer we allow access
-      checkIsDeveloper(kwargs)
+      checkIsDeveloper(django_args)
       return
     except out_of_band.Error:
       pass
 
     user = user_logic.getForCurrentAccount()
 
-    if not user or user.link_id != kwargs['link_id']:
+    if not user or user.link_id != django_args['link_id']:
       # not my role
-      deny(kwargs)
+      deny(django_args)
 
-    fields = {'link_id': kwargs['link_id'],
-              'scope_path': kwargs['scope_path']
+    fields = {'link_id': django_args['link_id'],
+              'scope_path': django_args['scope_path']
               }
 
     role_entity = role_logic.logic.getForFields(fields, unique=True)
 
     if not role_entity:
       # no role found
-      deny(kwargs)
-      
+      deny(django_args)
+
     if role_entity.state == 'active':
       # this role exist and is active
       return
     else:
       # this role is not active
-      deny(kwargs)
-
-  return wrapper
+      deny(django_args)
 
+  def checkHasPickGetArgs(self, django_args):
+    """Raises an alternate HTTP response if the request misses get args.
 
-def checkHasPickGetArgs(kwargs):
-  """Raises an alternate HTTP response if the request misses get args.
+    Args:
+      django_args: a dictionary with django's arguments
 
-  Args:
-    kwargs: a dictionary with django's arguments
+    Raises:
+      AccessViolationResponse:
+      * if continue is not in request.GET
+      * if field is not in request.GET
+    """
 
-  Raises:
-    AccessViolationResponse:
-    * if continue is not in request.GET
-    * if field is not in request.GET
-  """
+    get_args = django_args.get('GET', {})
 
-  get_args = kwargs.get('GET', {})
+    if 'continue' in get_args and 'field' in get_args:
+      return
 
-  if 'continue' in get_args and 'field' in get_args:
-    return
-
-  #TODO(SRabbelier) inform user that return_url and field are required
-  deny(kwargs)
-
+    #TODO(SRabbelier) inform user that return_url and field are required
+    deny(django_args)
 
-def checkIsDocumentPublic(kwargs):
-  """Checks whether a document is public.
+  def checkIsDocumentPublic(self, django_args):
+    """Checks whether a document is public.
 
-  Args:
-    kwargs: a dictionary with django's arguments
-  """
+    Args:
+      django_args: a dictionary with django's arguments
+    """
 
-  # TODO(srabbelier): A proper check needs to be done to see if the document
-  # is public or not, probably involving analysing it's scope or such.
-  allow(kwargs)
+    # TODO(srabbelier): A proper check needs to be done to see if the document
+    # is public or not, probably involving analysing it's scope or such.
+    allow(django_args)
--- a/app/soc/views/helper/decorators.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/helper/decorators.py	Mon Jan 26 23:32:10 2009 +0000
@@ -103,7 +103,7 @@
 
     # Do the access check dance
     try:
-      access.checkAccess(access_type, rights, kwargs=check_kwargs)
+      rights.checkAccess(access_type, check_kwargs)
     except out_of_band.Error, error:
       return helper.responses.errorResponse(error, request)
     return func(self, request, access_type, *args, **kwargs)
--- a/app/soc/views/helper/params.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/helper/params.py	Mon Jan 26 23:32:10 2009 +0000
@@ -70,21 +70,17 @@
 
   logic = params['logic']
 
-  rights = {}
+  rights = access.Checker(params)
   rights['unspecified'] = []
-  rights['any_access'] = [access.checkIsLoggedIn]
-  rights['show'] = [access.checkAgreesToSiteToS]
-  rights['create'] = [access.checkIsDeveloper]
-  rights['edit'] = [access.checkIsDeveloper]
-  rights['delete'] = [access.checkIsDeveloper]
-  rights['list'] = [access.checkIsDeveloper]
-  rights['pick'] = [access.checkHasPickGetArgs]
-
-  if 'rights' in params:
-    rights = dicts.merge(params['rights'], rights)
+  rights['any_access'] = ['checkIsLoggedIn']
+  rights['show'] = ['checkAgreesToSiteToS']
+  rights['create'] = ['checkIsDeveloper']
+  rights['edit'] = ['checkIsDeveloper']
+  rights['delete'] = ['checkIsDeveloper']
+  rights['list'] = ['checkIsDeveloper']
+  rights['pick'] = ['checkHasPickGetArgs']
 
   new_params = {}
-  new_params['rights'] = rights
   new_params['scope_logic'] = logic.getScopeLogic()
 
   if 'name_short' not in params:
@@ -214,6 +210,9 @@
   if not 'key_fields_pattern' in params:
     params['key_fields_pattern'] = getKeyFieldsPattern(params)
 
+  # merge already done by access.Checker
+  params['rights'] = rights
+
   return params
 
 
--- a/app/soc/views/models/club.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/club.py	Mon Jan 26 23:32:10 2009 +0000
@@ -58,16 +58,16 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['create'] = [access.checkIsDeveloper]
-    rights['edit'] = [access.checkIsClubAdminForClub]
-    rights['delete'] = [access.checkIsDeveloper]
-    rights['home'] = [access.allow]
-    rights['list'] = [access.checkIsDeveloper]
-    rights['apply_member'] = [access.checkAgreesToSiteToS]
-    rights['list_requests'] = [access.checkIsClubAdminForClub]
-    rights['list_roles'] = [access.checkIsClubAdminForClub]
-    rights['applicant'] = [access.checkIsApplicationAccepted(club_app_logic)]
+    rights = access.Checker(params)
+    rights['create'] = ['checkIsDeveloper']
+    rights['edit'] = ['checkIsClubAdminForClub']
+    rights['delete'] = ['checkIsDeveloper']
+    rights['home'] = ['allow']
+    rights['list'] = ['checkIsDeveloper']
+    rights['apply_member'] = ['checkAgreesToSiteToS']
+    rights['list_requests'] = ['checkIsClubAdminForClub']
+    rights['list_roles'] = ['checkIsClubAdminForClub']
+    rights['applicant'] = [('checkIsApplicationAccepted', club_app_logic)]
 
     new_params = {}
     new_params['logic'] = soc.logic.models.club.logic
--- a/app/soc/views/models/club_admin.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/club_admin.py	Mon Jan 26 23:32:10 2009 +0000
@@ -49,14 +49,14 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['create'] = [access.checkIsDeveloper]
-    rights['edit'] = [access.checkIsMyActiveRole(soc.logic.models.club_admin)]
-    rights['delete'] = [access.checkIsDeveloper]
-    rights['invite'] = [access.checkIsClubAdminForClub]
-    rights['accept_invite'] = [access.checkCanCreateFromRequest('club_admin')]
-    rights['process_request'] = [access.checkIsClubAdminForClub,
-        access.checkCanProcessRequest('club_admin')]
+    rights = access.Checker(params)
+    rights['create'] = ['checkIsDeveloper']
+    rights['edit'] = [('checkIsMyActiveRole', soc.logic.models.club_admin)]
+    rights['delete'] = ['checkIsDeveloper']
+    rights['invite'] = ['checkIsClubAdminForClub']
+    rights['accept_invite'] = ['checkCanCreateFromRequest', 'club_admin']
+    rights['process_request'] = ['checkIsClubAdminForClub',
+        ('checkCanProcessRequest', 'club_admin')]
 
     new_params = {}
     new_params['logic'] = soc.logic.models.club_admin.logic
--- a/app/soc/views/models/club_app.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/club_app.py	Mon Jan 26 23:32:10 2009 +0000
@@ -55,13 +55,13 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['create'] = [access.checkAgreesToSiteToS]
-    rights['delete'] = [access.checkIsMyApplication(club_app_logic)]
-    rights['edit'] = [access.checkIsMyApplication(club_app_logic)]
-    rights['list'] = [access.checkAgreesToSiteToS]
-    rights['public'] = [access.checkIsMyApplication(club_app_logic)]
-    rights['review'] = [access.checkIsHost]
+    rights = access.Checker(params)
+    rights['create'] = ['checkAgreesToSiteToS']
+    rights['delete'] = [('checkIsMyApplication', club_app_logic)]
+    rights['edit'] = [('checkIsMyApplication', club_app_logic)]
+    rights['list'] = ['checkAgreesToSiteToS']
+    rights['public'] = [('checkIsMyApplication', club_app_logic)]
+    rights['review'] = ['checkIsHost']
 
     new_params = {}
 
--- a/app/soc/views/models/club_member.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/club_member.py	Mon Jan 26 23:32:10 2009 +0000
@@ -48,16 +48,16 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['create'] = [access.checkIsDeveloper]
-    rights['edit'] = [access.checkIsMyActiveRole(soc.logic.models.club_member)]
-    rights['delete'] = [access.checkIsDeveloper]
-    rights['invite'] = [access.checkIsClubAdminForClub]
-    rights['accept_invite'] = [access.checkCanCreateFromRequest('club_member')]
-    rights['request'] = [access.checkAgreesToSiteToS, 
-        access.checkCanMakeRequestToGroup(club_logic)]
-    rights['process_request'] = [access.checkIsClubAdminForClub,
-        access.checkCanProcessRequest('club_member')]
+    rights = access.Checker(params)
+    rights['create'] = ['checkIsDeveloper']
+    rights['edit'] = [('checkIsMyActiveRole',soc.logic.models.club_member)]
+    rights['delete'] = ['checkIsDeveloper']
+    rights['invite'] = ['checkIsClubAdminForClub']
+    rights['accept_invite'] = [('checkCanCreateFromRequest','club_member')]
+    rights['request'] = ['checkAgreesToSiteToS',
+        ('checkCanMakeRequestToGroup', club_logic)]
+    rights['process_request'] = ['checkIsClubAdminForClub',
+        ('checkCanProcessRequest','club_member')]
 
     new_params = {}
     new_params['logic'] = soc.logic.models.club_member.logic
--- a/app/soc/views/models/document.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/document.py	Mon Jan 26 23:32:10 2009 +0000
@@ -96,9 +96,9 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['any_access'] = [access.allow]
-    rights['show'] = [access.checkIsDocumentPublic]
+    rights = access.Checker(params)
+    rights['any_access'] = ['allow']
+    rights['show'] = ['checkIsDocumentPublic']
 
     new_params = {}
     new_params['logic'] = soc.logic.models.document.logic
--- a/app/soc/views/models/host.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/host.py	Mon Jan 26 23:32:10 2009 +0000
@@ -55,14 +55,14 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['create'] = [access.checkIsHost]
-    rights['edit'] = [access.checkIsMyActiveRole(soc.logic.models.host)]
-    rights['invite'] = [access.checkIsHost]
-    rights['list'] = [access.checkIsDeveloper]
-    rights['accept_invite'] = [access.checkCanCreateFromRequest('host')]
-    rights['process_request'] = [access.checkIsHost,
-        access.checkCanProcessRequest('host')]
+    rights = access.Checker(params)
+    rights['create'] = ['checkIsHost']
+    rights['edit'] = [('checkIsMyActiveRole', soc.logic.models.host)]
+    rights['invite'] = ['checkIsHost']
+    rights['list'] = ['checkIsDeveloper']
+    rights['accept_invite'] = [('checkCanCreateFromRequest','host')]
+    rights['process_request'] = ['checkIsHost',
+        ('checkCanProcessRequest','host')]
 
     new_params = {}
     new_params['rights'] = rights
--- a/app/soc/views/models/notification.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/notification.py	Mon Jan 26 23:32:10 2009 +0000
@@ -82,14 +82,14 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['unspecified'] = [access.deny]
-    rights['edit'] = [access.deny]
-    rights['show'] = [access.checkIsMyNotification]
-    rights['delete'] = [access.checkIsMyNotification]
-    rights['list'] = [access.checkAgreesToSiteToS]
+    rights = access.Checker(params)
+    rights['unspecified'] = ['deny']
+    rights['edit'] = ['deny']
+    rights['show'] = ['checkIsMyNotification']
+    rights['delete'] = ['checkIsMyNotification']
+    rights['list'] = ['checkAgreesToSiteToS']
     # create is developer only for the time being to test functionality
-    rights['create'] = [access.checkIsDeveloper]
+    rights['create'] = ['checkIsDeveloper']
 
     new_params = {}
     new_params['logic'] = notification_logic.logic
--- a/app/soc/views/models/presence.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/presence.py	Mon Jan 26 23:32:10 2009 +0000
@@ -56,8 +56,8 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['home'] = [access.allow]
+    rights = access.Checker(params)
+    rights['home'] = ['allow']
 
     new_params = {}
     new_params['rights'] = rights
--- a/app/soc/views/models/program.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/program.py	Mon Jan 26 23:32:10 2009 +0000
@@ -51,9 +51,9 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['any_access'] = [access.allow]
-    rights['show'] = [access.allow]
+    rights = access.Checker(params)
+    rights['any_access'] = ['allow']
+    rights['show'] = ['allow']
 
     new_params = {}
     new_params['logic'] = soc.logic.models.program.logic
--- a/app/soc/views/models/request.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/request.py	Mon Jan 26 23:32:10 2009 +0000
@@ -65,13 +65,13 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['listSelf'] = [access.checkAgreesToSiteToS]
-    rights['create'] = [access.deny]
-    rights['edit'] = [access.checkIsDeveloper]
-    rights['process_invite'] = [access.checkIsMyGroupAcceptedRequest]
-    rights['list'] = [access.checkIsDeveloper]
-    rights['delete'] = [access.checkIsDeveloper]
+    rights = access.Checker(params)
+    rights['listSelf'] = ['checkAgreesToSiteToS']
+    rights['create'] = ['deny']
+    rights['edit'] = ['checkIsDeveloper']
+    rights['process_invite'] = ['checkIsMyGroupAcceptedRequest']
+    rights['list'] = ['checkIsDeveloper']
+    rights['delete'] = ['checkIsDeveloper']
 
     new_params = {}
     new_params['rights'] = rights
--- a/app/soc/views/models/site.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/site.py	Mon Jan 26 23:32:10 2009 +0000
@@ -46,10 +46,10 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['unspecified'] = [access.checkIsDeveloper]
-    rights['any_access'] = [access.allow]
-    rights['show'] = [access.allow]
+    rights = access.Checker(params)
+    rights['unspecified'] = ['checkIsDeveloper']
+    rights['any_access'] = ['allow']
+    rights['show'] = ['allow']
 
     new_params = {}
     new_params['logic'] = soc.logic.models.site.logic
--- a/app/soc/views/models/sponsor.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/sponsor.py	Mon Jan 26 23:32:10 2009 +0000
@@ -45,14 +45,14 @@
       params: a dict with params for this View
     """    
 
-    rights = {}
-    rights['create'] = [access.checkIsDeveloper]
-    rights['edit'] = [access.checkIsHostForSponsor]
-    rights['delete'] = [access.checkIsDeveloper]
-    rights['home'] = [access.checkIsHostForSponsor]
-    rights['list'] = [access.checkIsDeveloper]
-    rights['list_requests'] = [access.checkIsHostForSponsor]
-    rights['list_roles'] = [access.checkIsHostForSponsor]
+    rights = access.Checker(params)
+    rights['create'] = ['checkIsDeveloper']
+    rights['edit'] = ['checkIsHostForSponsor']
+    rights['delete'] = ['checkIsDeveloper']
+    rights['home'] = ['checkIsHostForSponsor']
+    rights['list'] = ['checkIsDeveloper']
+    rights['list_requests'] = ['checkIsHostForSponsor']
+    rights['list_roles'] = ['checkIsHostForSponsor']
 
     new_params = {}
     new_params['logic'] = soc.logic.models.sponsor.logic
--- a/app/soc/views/models/user_self.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/models/user_self.py	Mon Jan 26 23:32:10 2009 +0000
@@ -112,13 +112,13 @@
       params: a dict with params for this View
     """
 
-    rights = {}
-    rights['unspecified'] = [access.deny]
-    rights['any_access'] = [access.allow]
-    rights['edit'] = [access.checkIsLoggedIn]
-    rights['roles'] = [access.checkAgreesToSiteToS]
-    rights['signIn'] = [access.checkNotLoggedIn]
-    rights['notification'] = [access.checkAgreesToSiteToS]
+    rights = access.Checker(params)
+    rights['unspecified'] = ['deny']
+    rights['any_access'] = ['allow']
+    rights['edit'] = ['checkIsLoggedIn']
+    rights['roles'] = ['checkAgreesToSiteToS']
+    rights['signIn'] = ['checkNotLoggedIn']
+    rights['notification'] = ['checkAgreesToSiteToS']
 
     new_params = {}
     new_params['rights'] = rights
--- a/app/soc/views/sitemap/sidebar.py	Mon Jan 26 21:22:53 2009 +0000
+++ b/app/soc/views/sitemap/sidebar.py	Mon Jan 26 23:32:10 2009 +0000
@@ -140,7 +140,7 @@
 
   for url, menu_text, access_type in items:
     try:
-      access.checkAccess(access_type, rights, kwargs)
+      rights.checkAccess(access_type, kwargs)
       submenus.append({'url': url, 'title': menu_text})
     except out_of_band.Error:
       pass