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
This commit is contained in:
Yu Ming Zhu 2021-07-16 15:33:39 +00:00
parent ec70d21c41
commit 47c4b5d70b
8 changed files with 431 additions and 25 deletions

View file

@ -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):