From 47c4b5d70baaea36dbf637bee907a33229cf7a60 Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Fri, 16 Jul 2021 15:33:39 +0000 Subject: [PATCH] kojid: extend SCM.assert_allowed with hub policy This is a simple extention of `SCM.assert_allowed` - `assert_allowed_by_policy` will set the default "use_common" to False which is different to the old behavior - `channel`, `user_id`, `scratch` are passed in the `policy_data` with scminfo right now. This is a prototype for this change, and there are some other solutions could be implemented too - Use a scmpolicy plugin as `postSCMCheckout` callback, the pro is that we can do more checks after the source is initialized on builder, meanwhile, the con is that the source will be downloaded even it is denied by policy. It might be a potential risk? - Do the scm check in hub's `make_task`, this looks straightforward, but may lack some builder's information fixes: #2757 --- builder/kojid | 93 ++++++++++++++++--- builder/kojid.conf | 8 ++ hub/kojixmlrpc.py | 7 ++ koji/daemon.py | 79 +++++++++++++++- koji/tasks.py | 18 ++-- tests/test_scm.py | 226 ++++++++++++++++++++++++++++++++++++++++++++- vm/kojivmd | 17 +++- vm/kojivmd.conf | 8 ++ 8 files changed, 431 insertions(+), 25 deletions(-) diff --git a/builder/kojid b/builder/kojid index ecf8451f..3dfc5009 100755 --- a/builder/kojid +++ b/builder/kojid @@ -1633,7 +1633,17 @@ class BuildMavenTask(BaseBuildTask): self.opts = opts scm = SCM(url) - scm.assert_allowed(self.options.allowed_scms) + scm_policy_opts = { + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': self.opts.get('scratch') + } + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts=scm_policy_opts) repo_id = opts.get('repo_id') if not repo_id: raise koji.BuildError('A repo_id must be provided') @@ -1707,7 +1717,11 @@ class BuildMavenTask(BaseBuildTask): if self.opts.get('patches'): patchlog = self.workdir + '/patches.log' patch_scm = SCM(self.opts.get('patches')) - patch_scm.assert_allowed(self.options.allowed_scms) + patch_scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts=scm_policy_opts) self.run_callbacks('preSCMCheckout', scminfo=patch_scm.get_info(), build_tag=build_tag, scratch=opts.get('scratch'), buildroot=buildroot) @@ -1990,7 +2004,16 @@ class WrapperRPMTask(BaseBuildTask): assert False # pragma: no cover scm = SCM(spec_url) - scm.assert_allowed(self.options.allowed_scms) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': opts.get('scratch') + }) repo_id = opts.get('repo_id') if not repo_id: @@ -2979,7 +3002,16 @@ class ImageTask(BaseTaskHandler): self.logger.debug("ksfile = %s" % ksfile) if self.opts.get('ksurl'): scm = SCM(self.opts['ksurl']) - scm.assert_allowed(self.options.allowed_scms) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': self.opts.get('scratch') + }) logfile = os.path.join(self.workdir, 'checkout.log') self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(), build_tag=build_tag, scratch=self.opts.get('scratch'), @@ -3453,7 +3485,16 @@ class LiveMediaTask(ImageTask): can find the checked out templates. """ scm = SCM(self.opts['lorax_url']) - scm.assert_allowed(self.options.allowed_scms) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': self.opts.get('scratch') + }) logfile = os.path.join(self.workdir, 'lorax-templates-checkout.log') checkout_dir = scm.checkout(build_root.tmpdir(), self.session, self.getUploadDir(), logfile) @@ -3700,7 +3741,16 @@ class OzImageTask(BaseTaskHandler): self.logger.debug("ksfile = %s" % ksfile) if self.opts.get('ksurl'): scm = SCM(self.opts['ksurl']) - scm.assert_allowed(self.options.allowed_scms) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': self.opts.get('scratch') + }) logfile = os.path.join(self.workdir, 'checkout-%s.log' % self.arch) self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(), build_tag=build_tag, scratch=self.opts.get('scratch')) @@ -4527,7 +4577,16 @@ class BuildIndirectionImageTask(OzImageTask): self.logger.debug("filepath = %s" % filepath) if fileurl: scm = SCM(fileurl) - scm.assert_allowed(self.options.allowed_scms) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': self.opts.get('scratch') + }) self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(), build_tag=build_tag, scratch=self.opts.get('scratch')) logfile = os.path.join(self.workdir, 'checkout.log') @@ -4935,12 +4994,20 @@ class BuildSRPMFromSCMTask(BaseBuildTask): return self.checkHostArch(tag, hostdata) def handler(self, url, build_tag, opts=None): - # will throw a BuildError if the url is invalid - scm = SCM(url) - scm.assert_allowed(self.options.allowed_scms) - if opts is None: opts = {} + # will throw a BuildError if the url is invalid + scm = SCM(url) + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': opts.get('scratch') + }) repo_id = opts.get('repo_id') if not repo_id: raise koji.BuildError("A repo id must be provided") @@ -6362,6 +6429,8 @@ def get_options(): 'mock_bootstrap_image': False, 'pkgurl': None, 'allowed_scms': '', + 'allowed_scms_by_config': True, + 'allowed_scms_by_policy': False, 'scm_credentials_dir': None, 'support_rpm_source_layout': True, 'yum_proxy': None, @@ -6390,7 +6459,7 @@ def get_options(): elif name in ['offline_retry', 'use_createrepo_c', 'createrepo_skip_stat', 'createrepo_update', 'use_fast_upload', 'support_rpm_source_layout', 'build_arch_can_fail', 'no_ssl_verify', 'log_timestamps', - 'allow_noverifyssl']: + 'allow_noverifyssl', 'allowed_scms_by_config', 'allowed_scms_by_policy']: defaults[name] = config.getboolean('kojid', name) elif name in ['plugin', 'plugins']: defaults['plugin'] = value.split() diff --git a/builder/kojid.conf b/builder/kojid.conf index 4dc2a87e..777a421f 100644 --- a/builder/kojid.conf +++ b/builder/kojid.conf @@ -77,6 +77,14 @@ topurl=http://hub.example.com/kojifiles ; is run by default. allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*:no +; If use the option allowed_scms above for allowing / denying SCM, default: true +; allowed_scms_use_config = true + +; If use hub policy build_from_scm for allowing / denying SCM, default: false +; notice that if both options are enabled, both assertions will be applied, and user_common and +; source_cmd will be overridden by the policy's result. +; allowed_scms_use_policy = false + ; A directory to bind mount into Source RPM creation so that some ; credentials can be supplied when required to fetch sources, e.g. ; when the place the sources are fetched from requires all accesses to diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py index 468461be..196e4730 100644 --- a/hub/kojixmlrpc.py +++ b/hub/kojixmlrpc.py @@ -524,6 +524,13 @@ _default_policies = { has_perm admin :: allow all :: deny ''', + 'build_from_scm': ''' + has_perm admin :: allow + # match scmtype CVS CVS+SSH && match scmhost scm.example.com && match scmrepository /cvs/example :: allow + # match scmtype GIT GIT+SSH && match scmhost git.example.org && match scmrepository /example :: allow + # match scmtype SVN SVN+SSH && match scmhost svn.example.org && match scmrepository /users/* :: allow + all :: deny + ''', # noqa: E501 'package_list': ''' has_perm admin :: allow has_perm tag :: allow diff --git a/koji/daemon.py b/koji/daemon.py index bcd01b21..3eba1a7e 100644 --- a/koji/daemon.py +++ b/koji/daemon.py @@ -327,7 +327,16 @@ class SCM(object): # return parsed values return (scheme, user, netloc, path, query, fragment) - def assert_allowed(self, allowed): + def assert_allowed(self, allowed='', session=None, by_config=True, by_policy=False, opts=None): + if by_config: + self.assert_allowed_by_config(allowed or '') + if by_policy: + if session is None: + raise koji.ConfigurationError( + 'When allowed SCM assertion is by policy, session must be passed in.') + self.assert_allowed_by_policy(session, **(opts or {})) + + def assert_allowed_by_config(self, allowed): """ Check this scm against allowed list and apply options @@ -396,6 +405,74 @@ class SCM(object): raise koji.BuildError( '%s:%s is not in the list of allowed SCMs' % (self.host, self.repository)) + def assert_allowed_by_policy(self, session, **opts): + """ + Check this scm against hub policy: build_from_scm and apply options + + The policy data is the combination of scminfo with scm prefix and kwargs. + It should at least contain following keys: + + - scmurl + - scmscheme + - scmuser + - scmhost + - scmrepository + - scmmodule + - scmrevision + - scmtype + + More keys could be added in opts. You can pass any reasonable data which could be handled + by policy tests, like: + + - scratch (if the task is scratch) + - channel (which channel the task is assigned) + - user_id (the task owner) + + The format of the action returned from build_from_scm could be one of following forms:: + + allow [use_common] [source_cmd] + deny [reason] + + If use_common is not set, use_common property is False. + If source_cmd is none, it will be parsed as None. If it not set, the default value: + ['make', 'sources'], or the value set by :func:`~koji.daemon.SCM.assert_allowed_by_config` + will be set. + + Policy example: + + build_from_scm = + bool scratch :: allow none + match scmhost scm.example.com :: allow use_common make sources + match scmhost scm2.example.com :: allow + all :: deny + + + :param koji.ClientSession session: the session object to call hub xmlrpc APIs. + It should be a host session. + + :raises koji.BuildError: if the scm is denied. + """ + policy_data = {} + for k, v in six.iteritems(self.get_info()): + if not k.startswith('scm'): + k = 'scm' + k + policy_data[k] = v + policy_data.update(opts) + result = (session.host.evalPolicy('build_from_scm', policy_data) or '').split() + is_allowed = result and result[0].lower() in ('yes', 'true', 'allow', 'allowed') + if not is_allowed: + raise koji.BuildError( + 'SCM: %s:%s is not allowed, reason: %s' % (self.host, self.repository, + ' '.join(result[1:]) or None)) + # Apply options when it's allowed + applied = result[1:] + self.use_common = len(applied) != 0 and applied[0].lower() == 'use_common' + idx = 1 if self.use_common else 0 + self.source_cmd = applied[idx:] or self.source_cmd + if self.source_cmd is not None and len(self.source_cmd) > 0 \ + and self.source_cmd[0].lower() == 'none': + self.source_cmd = None + def checkout(self, scmdir, session=None, uploadpath=None, logfile=None): """ Checkout the module from SCM. Accepts the following parameters: diff --git a/koji/tasks.py b/koji/tasks.py index ca741caa..0a03c725 100644 --- a/koji/tasks.py +++ b/koji/tasks.py @@ -312,6 +312,7 @@ class BaseTaskHandler(object): self.workdir = workdir self.logger = logging.getLogger("koji.build.BaseTaskHandler") self.manager = None + self.taskinfo = None def setManager(self, manager): """Set the manager attribute @@ -597,15 +598,20 @@ class BaseTaskHandler(object): def run_callbacks(self, plugin, *args, **kwargs): if 'taskinfo' not in kwargs: - try: - taskinfo = self.taskinfo - except AttributeError: - self.taskinfo = self.session.getTaskInfo(self.id, request=True) - taskinfo = self.taskinfo - kwargs['taskinfo'] = taskinfo + kwargs['taskinfo'] = self.taskinfo kwargs['session'] = self.session koji.plugin.run_callbacks(plugin, *args, **kwargs) + @property + def taskinfo(self): + if not getattr(self, '_taskinfo', None): + self._taskinfo = self.session.getTaskInfo(self.id, request=True, strict=True) + return self._taskinfo + + @taskinfo.setter + def taskinfo(self, taskinfo): + self._taskinfo = taskinfo + class FakeTask(BaseTaskHandler): Methods = ['someMethod'] diff --git a/tests/test_scm.py b/tests/test_scm.py index 54b26910..2ce67b46 100644 --- a/tests/test_scm.py +++ b/tests/test_scm.py @@ -8,9 +8,40 @@ import unittest import koji import koji.daemon +import koji.policy from koji.daemon import SCM +policy = { + 'one': ''' + match scmhost goodserver :: allow none + match scmhost badserver :: deny + match scmhost maybeserver && match scmrepository /badpath/* :: deny + all :: allow + ''', + 'two': ''' + match scmhost default :: allow + match scmhost nocommon :: allow + match scmhost common :: allow use_common + match scmhost srccmd :: allow fedpkg sources + match scmhost nosrc :: allow none + match scmhost mixed && match scmrepository /foo/* :: allow + match scmhost mixed && match scmrepository /bar/* :: allow use_common + match scmhost mixed && match scmrepository /baz/* :: allow fedpkg sources + match scmhost mixed && match scmrepository /foobar/* :: allow use_common fedpkg sources + match scmhost mixed && match scmrepository /foobaz/* :: allow use_common none + ''' +} + +class FakePolicy(object): + + def __init__(self, rule): + base_tests = koji.policy.findSimpleTests(vars(koji.policy)) + self.ruleset = koji.policy.SimpleRuleSet(rule.splitlines(), base_tests) + + def evalPolicy(self, name, data): + return self.ruleset.apply(data) + class TestSCM(unittest.TestCase): @@ -67,8 +98,22 @@ class TestSCM(unittest.TestCase): self.assertEqual(scm.source_cmd, ['make', 'sources']) self.assertEqual(scm.scmtype, 'GIT') + def test_assert_allowed_basic(self): + scm = SCM("git://scm.example.com/path1#1234") + + # session must be passed + with self.assertRaises(koji.GenericError) as cm: + scm.assert_allowed(session=None, by_config=False, by_policy=True) + self.assertEqual(str(cm.exception), + 'When allowed SCM assertion is by policy, session must be passed in.') + + # allowed could not be None + scm.assert_allowed_by_config = mock.MagicMock() + scm.assert_allowed(allowed=None, by_config=True, by_policy=False) + scm.assert_allowed_by_config.assert_called_once_with('') + @mock.patch('logging.getLogger') - def test_allowed(self, getLogger): + def test_allowed_by_config(self, getLogger): config = ''' goodserver:*:no !badserver:* @@ -104,7 +149,7 @@ class TestSCM(unittest.TestCase): raise AssertionError("allowed bad url: %s" % url) @mock.patch('logging.getLogger') - def test_badrule(self, getLogger): + def test_badrule_by_config(self, getLogger): config = ''' bogus-entry-should-be-ignored goodserver:*:no @@ -115,7 +160,7 @@ class TestSCM(unittest.TestCase): scm.assert_allowed(config) @mock.patch('logging.getLogger') - def test_opts(self, getLogger): + def test_opts_by_config(self, getLogger): config = ''' default:* nocommon:*:no @@ -196,6 +241,181 @@ class TestSCM(unittest.TestCase): with self.assertRaises(koji.BuildError): scm.assert_allowed(config) + def test_allowed_by_policy(self): + good = [ + "git://goodserver/path1#1234", + "git+ssh://maybeserver/path1#1234", + ] + bad = [ + "cvs://badserver/projects/42#ref", + "svn://badserver/projects/42#ref", + "git://maybeserver/badpath/project#1234", + "git://maybeserver//badpath/project#1234", + "git://maybeserver////badpath/project#1234", + "git://maybeserver/./badpath/project#1234", + "git://maybeserver//.//badpath/project#1234", + "git://maybeserver/goodpath/../badpath/project#1234", + "git://maybeserver/goodpath/..//badpath/project#1234", + "git://maybeserver/..//badpath/project#1234", + ] + session = mock.MagicMock() + session.host.evalPolicy.side_effect = FakePolicy(policy['one']).evalPolicy + for url in good: + scm = SCM(url) + scm.assert_allowed(session=session, by_config=False, by_policy=True) + for url in bad: + scm = SCM(url) + with self.assertRaises(koji.BuildError) as cm: + scm.assert_allowed(session=session, by_config=False, by_policy=True) + self.assertRegexpMatches(str(cm.exception), '^SCM: .* is not allowed, reason: None$') + + def test_opts_by_policy(self): + session = mock.MagicMock() + session.host.evalPolicy.side_effect = FakePolicy(policy['two']).evalPolicy + + url = "git://default/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://nocommon/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://common/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://srccmd/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://nosrc/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, None) + + url = "git://mixed/foo/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/bar/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/baz/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://mixed/koji.git#1234" + scm = SCM(url) + with self.assertRaises(koji.BuildError): + scm.assert_allowed_by_policy(session=session) + + url = "git://mixed/foo/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/bar/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/baz/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://mixed/foobar/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://mixed/foobaz/koji.git#1234" + scm = SCM(url) + scm.assert_allowed_by_policy(session=session) + self.assertEqual(scm.use_common, True) + self.assertIsNone(scm.source_cmd) + + url = "git://nomatch/koji.git#1234" + scm = SCM(url) + with self.assertRaises(koji.BuildError): + scm.assert_allowed_by_policy(session=session) + + def test_assert_allowed_by_both(self): + config = ''' + default:*:no: + mixed:/foo/*:yes + mixed:/bar/*:no + mixed:/baz/*:no:centpkg,sources + mixed:/foobar/*:no: + mixed:/foobaz/*:no:centpkg,sources + ''' + + session = mock.MagicMock() + session.host.evalPolicy.side_effect = FakePolicy(policy['two']).evalPolicy + + url = "git://default/koji.git#1234" + scm = SCM(url) + # match scmhost default :: allow + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, False) + self.assertIsNone(scm.source_cmd) + + url = "git://mixed/foo/koji.git#1234" + scm = SCM(url) + # match scmhost mixed && match scmrepository /foo/* :: allow + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/bar/koji.git#1234" + scm = SCM(url) + # match scmhost mixed && match scmrepository /bar/* :: allow use_common + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['make', 'sources']) + + url = "git://mixed/baz/koji.git#1234" + scm = SCM(url) + # match scmhost mixed && match scmrepository /baz/* :: allow fedpkg sources + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, False) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://mixed/foobar/koji.git#1234" + scm = SCM(url) + # match scmhost mixed && match scmrepository /foobar/* :: allow use_common fedpkg sources + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, True) + self.assertEqual(scm.source_cmd, ['fedpkg', 'sources']) + + url = "git://mixed/foobaz/koji.git#1234" + scm = SCM(url) + # match scmhost mixed && match scmrepository /foobaz/* :: allow use_common none + scm.assert_allowed(allowed=config, session=session, by_config=True, by_policy=True) + self.assertEqual(scm.use_common, True) + self.assertIsNone(scm.source_cmd) + class TestSCMCheckouts(unittest.TestCase): diff --git a/vm/kojivmd b/vm/kojivmd index 89e96cf0..75ab017d 100755 --- a/vm/kojivmd +++ b/vm/kojivmd @@ -138,6 +138,8 @@ def get_options(): 'offline_retry': True, 'offline_retry_interval': 120, 'allowed_scms': '', + 'allowed_scms_by_config': True, + 'allowed_scms_by_policy': False, 'cert': None, 'serverca': None} if config.has_section('kojivmd'): @@ -149,7 +151,8 @@ def get_options(): defaults[name] = int(value) except ValueError: quit("value for %s option must be a valid integer" % name) - elif name in ['offline_retry', 'no_ssl_verify']: + elif name in ['offline_retry', 'no_ssl_verify', 'allowed_scms_by_config', + 'allowed_scms_by_policy']: defaults[name] = config.getboolean('kojivmd', name) elif name in ['plugin', 'plugins']: defaults['plugin'] = value.split() @@ -325,8 +328,16 @@ class WinBuildTask(MultiPlatformTask): # verify the urls before passing them to the VM for url in [source_url] + koji.util.to_list(subopts.values()): scm = SCM(url) - scm.assert_allowed(self.options.allowed_scms) - + scm.assert_allowed(allowed=self.options.allowed_scms, + session=self.session, + by_config=self.options.allowed_scms_use_config, + by_policy=self.options.allowed_scms_use_policy, + opts={ + 'user_id': self.taskinfo['owner'], + 'channel': self.session.getChannel(self.taskinfo['channel_id'], + strict=True)['name'], + 'scratch': opts.get('scratch') + }) task_info = self.session.getTaskInfo(self.id) target_info = self.session.getBuildTarget(target) if not target_info: diff --git a/vm/kojivmd.conf b/vm/kojivmd.conf index 78e3f249..e6ae14fd 100644 --- a/vm/kojivmd.conf +++ b/vm/kojivmd.conf @@ -27,6 +27,14 @@ server=http://hub.example.com/kojihub ; dir, and will raise an exception if it cannot. allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*:no +; If use the option allowed_scms above for allowing / denying SCM, default: true +; allowed_scms_use_config = true + +; If use hub policy: build_from_scm for allowing / denying SCM, default: false +; notice that if both options are enabled, both assertions will be applied, and user_common +; will be overridden by the policy's result. +; allowed_scms_use_policy = false + ; The mail host to use for sending email notifications smtphost=example.com