Use non-destructive subconfigs instead of in-place per-root patching of config object

custis
Vitaliy Filippov 2013-11-20 17:03:13 +04:00
parent 44c5f2bb1a
commit 8b3d3363f2
3 changed files with 45 additions and 62 deletions

View File

@ -23,6 +23,7 @@ import vclib.ccvs
import vclib.svn
import cvsdb
import viewvc
import copy
from viewvcmagic import ContentMagic
#########################################################################
@ -143,6 +144,8 @@ class Config:
def __init__(self):
self.__guesser = None
self.__root_configs = {}
self.__parent = None
self.root_options_overlayed = 0
for section in self._base_sections:
if section[-1] == '*':
@ -283,6 +286,34 @@ class Config:
return None
def get_root_config(self, rootname):
"""Get configuration object with per-root overrides for 'rootname'"""
if self.__parent:
return self.__parent.get_root_config(rootname)
elif rootname in self.__root_configs:
return self.__root_configs[rootname]
__guesser = self.__guesser
__root_configs = self.__root_configs
parser = self.parser
self.parser = None
self.__guesser = None
self.__root_configs = None
sub = copy.deepcopy(self)
sub.parser = parser
self.parser = parser
self.__guesser = __guesser
self.__root_configs = __root_configs
self.__root_configs[rootname] = sub
sub.__parent = self
sub.overlay_root_options(rootname)
return sub
def get_parent_config(self):
"""Get the parent non-overridden config."""
if self.__parent:
return self.__parent
return self
def overlay_root_options(self, rootname):
"""Overlay per-root options for ROOTNAME atop the existing option
set. This is a destructive change to the configuration."""
@ -317,55 +348,6 @@ class Config:
d[option] = parser.get(section, option)
return d.items()
def get_authorizer_and_params_hack(self, rootname):
"""Return a 2-tuple containing the name and parameters of the
authorizer configured for use with ROOTNAME.
### FIXME: This whole thing is a hack caused by our not being able
### to non-destructively overlay root options when trying to do
### something like a root listing (which might need to get
### different authorizer bits for each and every root in the list).
### Until we have a good way to do that, we expose this function,
### which assumes that base and per-vhost configuration has been
### absorbed into this object and that per-root options have *not*
### been overlayed. See issue #371."""
# We assume that per-root options have *not* been overlayed.
assert(self.root_options_overlayed == 0)
if not self.conf_path:
return None, {}
# Figure out the authorizer by searching first for a per-root
# override, then falling back to the base/vhost configuration.
authorizer = None
root_options_section = 'root-%s/options' % (rootname)
if self.parser.has_section(root_options_section) \
and self.parser.has_option(root_options_section, 'authorizer'):
authorizer = self.parser.get(root_options_section, 'authorizer')
if not authorizer:
authorizer = self.options.authorizer
# No authorizer? Get outta here.
if not authorizer:
return None, {}
# Dig up the parameters for the authorizer, starting with the
# base/vhost items, then overlaying any root-specific ones we find.
params = {}
authz_section = 'authz-%s' % (authorizer)
if hasattr(self, authz_section):
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
for section in self.parser.sections():
if section == root_authz_section:
for key, value in self._get_parser_items(self.parser, section):
params[key] = value
params['__config'] = self
return authorizer, params
def get_authorizer_params(self, authorizer=None):
"""Return a dictionary of parameter names and values which belong
to the configured authorizer (or AUTHORIZER, if provided)."""

View File

@ -300,11 +300,10 @@ def is_forbidden(cfg, cvsroot_name, module):
# the base and per-vhost configuration for authorizer and
# authorizer parameters.
if cvsroot_name:
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name)
else:
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
cfg = cfg.get_root_config(cvsroot_name)
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
# If CVSROOT_NAME isn't configured to use an authorizer, nothing
# is forbidden. If it's configured to use something other than
# the 'forbidden' authorizer, complain. Otherwise, check for

View File

@ -296,9 +296,11 @@ class Request:
self.repos = rcr.repos
self.rootpath = rcr.rootpath
self.auth = rcr.auth
# Overlay root-specific options.
cfg.overlay_root_options(self.rootname)
cfg = cfg.get_root_config(self)
self.cfg = cfg
# Setup an Authorizer for this rootname and username
debug.t_start('setup-authorizer')
self.auth = setup_authorizer(cfg, self.username)
@ -896,12 +898,12 @@ def setup_authorizer(cfg, username, rootname=None):
"""Setup the authorizer. If ROOTNAME is provided, assume that
per-root options have not been overlayed. Otherwise, assume they
have (and fetch the authorizer for the configured root)."""
if rootname is None:
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
else:
authorizer, params = cfg.get_authorizer_and_params_hack(rootname)
if rootname is not None:
cfg = cfg.get_root_config(rootname)
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
# No configured authorizer? No problem.
if not authorizer: