[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PROPOSAL] AuthzSVNAccessFile for normal web-files

From: Branko Čibej <brane_at_xbc.nu>
Date: 2006-05-11 10:58:38 CEST

C. Michael Pilato wrote:
> Branko ÄŒibej wrote:
>
>> Peter MÃœNSTER wrote:
>>
>>
>>> Hello,
>>>
>>> what do you think about the following feature request:
>>>
>>> I have one <Location /svn> for the repository with path based access
>>> control (AuthzSVNAccessFile), and I would like to have some other
>>> <Location /docs> with a normal directory structure (no svn repository)
>>> using the same AuthzSVNAccessFile.
>>>
>>>
>> [...]
>>
>> I have a mod_python authorization handler that will do the same thing. I
>> can post it here if you like.
>>
>
> Please do!
>
Here it is then. Note that it's not exactly the fastest possible
implementation, as I didn't attempt to cache the authz info between
requests.

-- Brane

#!/usr/bin/python
#
# Copyright 2005 Branko Cibej <brane@xbc.nu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

'''mod_python authorization handler for mod_authz_svn groups.

   This handler reads group definitions from a mod_authz_svn access
   configuration file and does an authz check against that information.

   Supported Require directives:

     - Require valid-user
       Checks if the authenticated user is mentioned in any of the groups.
       Note that this is authorization, not authentication; so, a user may
       have been authenticated correctly, yet still fail this test if
       she is not mentioned in the authz config file.

     - Require group name...
       Check if the authenticated user is a member of any of the named
       groups.

     - Require user name...
       Ignored. The authentication handlers are supposed to check this.

   Configuration:

     <Location ...>
       PythonAuthzHandler authz_svn_group
       PythonOption AuthzSVNGroupFile /path/to/file
       PythonOption AuthzSVNGroupAuthorirative Yes/On/1|No/Off/0
       ...
     </Location>

     AuthzSVNGroupFile: Path to the mod_authz_svn configuration file.
     AuthzSVNGroupAuthoritative: If turned off, authz_svn_group.py will
       return DECLINED rather than HTTP_FORBIDDEN if a Require
       directive is not satisfied.
'''

import os, sys
import ConfigParser
from mod_python import apache

class __authz_info:
  '''Encapsulation of group info from the mod_authz_svn access file.'''

  def __init__(self, authz_file):
    '''Parse the SVN access file.'''
    self.__groups = {}
    self.__users = {}
    cfg = ConfigParser.ConfigParser()
    cfg.read(authz_file)
    if cfg.has_section('groups'):
      self.__init_groups(cfg)

  def __init_groups(self, cfg):
    '''Compute user and group membership.'''
    group_list = cfg.options('groups')
    group_map = {}
    for group in group_list:
      names = map(lambda x: x.strip(),
                  cfg.get('groups', group).split(','))
      group_map[group] = names
      for name in names:
        if not name.startswith('@'):
          self.__users[name] = None
    for group in group_list:
      self.__groups[group] = self.__expand_group_users(group, group_map)

  def __expand_group_users(self, group, group_map):
    '''Return the complete (recursive) list of users that belong to
    a particular group, as a map.'''
    users = {}
    for name in group_map[group]:
      if not name.startswith('@'):
        users[name] = None
      else:
        users.update(self.__expand_group_users(name[1:], group_map))
    return users

  def is_valid_user(self, user):
    '''Return True if the user is valid.'''
    return self.__users.has_key(user)

  def is_user_in_group(self, user, group):
    '''Return True if the user is in a particular group.'''
    return (self.__groups.has_key(group)
            and self.__groups[group].has_key(user))

class __config:
  '''Handler configuration'''

  GROUP_FILE = 'AuthzSVNGroupFile'
  AUTHORITATIVE = 'AuthzSVNGroupAuthoritative'

  def __init__(self, req):
    self.__authz_file = None
    self.__authorative = True
    cfg = req.get_options()

    if cfg.has_key(self.GROUP_FILE):
      self.__authz_file = cfg[self.GROUP_FILE]
      if not os.path.exists(self.__authz_file):
        req.log_error(('%s: "%s" not found'
                       % (self.GROUP_FILE, self.__authz_file)),
                      apache.APLOG_ERR)
        raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR

    if cfg.has_key(self.AUTHORITATIVE):
      authcfg = cfg[self.AUTHORITATIVE].lower()
      if authcfg in ['yes', 'on', '1']:
        self.__authorative = True
      elif authcfg in ['no', 'off', '0']:
        self.__authorative = False
      else:
        req.log_error(('%s: invalid value "%s"'
                       % (self.AUTHORITATIVE, cfg[self.AUTHORITATIVE])),
                      apache.APLOG_ERR)
        raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR
    pass

  def authz_file(self):
    return self.__authz_file

  def authorative(self):
    return self.__authorative

def __init_authz_info(req, cfg):
  '''Initialize the global authz info if it is not available yet.
  Return False if this module is disabled.'''
  if not globals().has_key('__authz_svn_group_info'):
    if cfg.authz_file() is None:
      return False
    global __authz_svn_group_info
    __authz_svn_group_info = __authz_info(cfg.authz_file())
  return True

def authzhandler(req):
  cfg = __config(req)
  if not __init_authz_info(req, cfg):
    return apache.DECLINED

  if cfg.authorative():
    unauthorized = apache.HTTP_FORBIDDEN
  else:
    unauthorized = apache.DECLINED

  req.get_basic_auth_pw()
  for authz in req.requires():
    if authz == 'valid-user':
      if not __authz_svn_group_info.is_valid_user(req.user):
        return unauthorized
    elif authz.startswith('group '):
      for group in authz.split()[1:]:
        if __authz_svn_group_info.is_user_in_group(req.user, group):
          break
      else:
        return unauthorized
    elif authz.startswith('user '):
      return apache.DECLINED # Handled by the authen handler
    else:
      req.log_error('Unknown directive "Require %s"' % authz, apache.APLOG_ERR)
      return apache.HTTP_INTERNAL_SERVER_ERROR

  return apache.OK

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu May 11 10:59:23 2006

This is an archived mail posted to the Subversion Dev mailing list.