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 vclib.svn
import cvsdb import cvsdb
import viewvc import viewvc
import copy
from viewvcmagic import ContentMagic from viewvcmagic import ContentMagic
######################################################################### #########################################################################
@ -143,6 +144,8 @@ class Config:
def __init__(self): def __init__(self):
self.__guesser = None self.__guesser = None
self.__root_configs = {}
self.__parent = None
self.root_options_overlayed = 0 self.root_options_overlayed = 0
for section in self._base_sections: for section in self._base_sections:
if section[-1] == '*': if section[-1] == '*':
@ -283,6 +286,34 @@ class Config:
return None 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): def overlay_root_options(self, rootname):
"""Overlay per-root options for ROOTNAME atop the existing option """Overlay per-root options for ROOTNAME atop the existing option
set. This is a destructive change to the configuration.""" set. This is a destructive change to the configuration."""
@ -317,55 +348,6 @@ class Config:
d[option] = parser.get(section, option) d[option] = parser.get(section, option)
return d.items() 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): def get_authorizer_params(self, authorizer=None):
"""Return a dictionary of parameter names and values which belong """Return a dictionary of parameter names and values which belong
to the configured authorizer (or AUTHORIZER, if provided).""" 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 # the base and per-vhost configuration for authorizer and
# authorizer parameters. # authorizer parameters.
if cvsroot_name: if cvsroot_name:
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name) cfg = cfg.get_root_config(cvsroot_name)
else: authorizer = cfg.options.authorizer
authorizer = cfg.options.authorizer params = cfg.get_authorizer_params()
params = cfg.get_authorizer_params()
# If CVSROOT_NAME isn't configured to use an authorizer, nothing # If CVSROOT_NAME isn't configured to use an authorizer, nothing
# is forbidden. If it's configured to use something other than # is forbidden. If it's configured to use something other than
# the 'forbidden' authorizer, complain. Otherwise, check for # the 'forbidden' authorizer, complain. Otherwise, check for

View File

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