diff --git a/hub/kojihub.py b/hub/kojihub.py index 5932415d..6b32e318 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -990,8 +990,11 @@ def _direct_pkglist_add(taginfo, pkginfo, owner, block, extra_arches, force, action = 'block' if policy: context.session.assertLogin() - policy_data = {'tag': tag_id, 'action': action, 'package': pkginfo, 'force': force} - assert_policy('package_list', policy_data, force=force) + policy_data = {'tag': tag_id, 'action': action, 'package': pkginfo, + 'force' : force, 'extra': tag['extra']} + # don't check policy for admins using force + if not (force and context.session.hasPerm('admin')): + assert_policy('package_list', policy_data) if not pkg: pkg = lookup_package(pkginfo, create=True) # validate arches before running callbacks @@ -1068,7 +1071,8 @@ def _direct_pkglist_remove(taginfo, pkginfo, force=False, policy=False): pkg = lookup_package(pkginfo, strict=True) if policy: context.session.assertLogin() - policy_data = {'tag': tag['id'], 'action': 'remove', 'package': pkg['id'], 'force': force} + policy_data = {'tag': tag['id'], 'action': 'remove', 'package': pkg['id'], + 'force' : force, 'extra': tag['extra']} # don't check policy for admins using force assert_policy('package_list', policy_data, force=force) @@ -1100,7 +1104,8 @@ def pkglist_unblock(taginfo, pkginfo, force=False): tag = get_tag(taginfo, strict=True) pkg = lookup_package(pkginfo, strict=True) context.session.assertLogin() - policy_data = {'tag': tag['id'], 'action': 'unblock', 'package': pkg['id'], 'force': force} + policy_data = {'tag': tag['id'], 'action': 'unblock', 'package': pkg['id'], + 'force' : force, 'extra': tag['extra']} # don't check policy for admins using force assert_policy('package_list', policy_data, force=force) user = get_user(context.session.user_id) diff --git a/plugins/cli/sidetag_cli.py b/plugins/cli/sidetag_cli.py index cc551573..6ec5a9d9 100644 --- a/plugins/cli/sidetag_cli.py +++ b/plugins/cli/sidetag_cli.py @@ -93,3 +93,29 @@ def handle_list_sidetags(options, session, args): for tag in session.listSideTags(basetag=opts.basetag, user=user): print(tag["name"]) + + +@export_cli +def handle_edit_sidetag(options, session, args): + "Edit sidetag" + usage = _("usage: %(prog)s edit-sidetag [options]") + usage += _("\n(Specify the --help global option for a list of other help options)") + parser = ArgumentParser(usage=usage) + parser.add_argument("sidetag", help="name of sidetag") + parser.add_argument("--debuginfo", action="store_true", default=None, + help=_("Generate debuginfo repository")) + parser.add_argument("--no-debuginfo", action="store_false", dest="debuginfo") + parser.add_argument("-b", "--block", action="append", help="block package") + parser.add_argument("-u", "--unblock", action="append", help="unblock package") + + opts = parser.parse_args(args) + + activate_session(session, options) + + kwargs = { + 'block_pkgs': opts.block, + 'unblock_pkgs': opts.unblock, + } + if opts.debuginfo is not None: + kwargs['debuginfo'] = opts.debuginfo + session.editSideTag(opts.sidetag, **kwargs) diff --git a/plugins/hub/sidetag_hub.py b/plugins/hub/sidetag_hub.py index 6b29e1bc..5c90e48c 100644 --- a/plugins/hub/sidetag_hub.py +++ b/plugins/hub/sidetag_hub.py @@ -18,6 +18,7 @@ from kojihub import ( # noqa: F402 get_tag, get_user, nextval + _edit_tag, ) CONFIG_FILE = "/etc/koji-hub/plugins/sidetag.conf" @@ -170,6 +171,49 @@ def listSideTags(basetag=None, user=None, queryOpts=None): return query.execute() +@export +def editSideTag(sidetag, debuginfo=None, block_pkgs=None, unblock_pkgs=None): + """Restricted ability to modify sidetags, parent tag must have: + sidetag_debuginfo_allowed: 1 + sidetag_package_list_allowed: 1 + in extra, if modifying functions should work. For blocking/unblocking + further policy must be compatible with these operations. + + :param sidetag: sidetag id or name + :type sidetag: int or str + :param debuginfo: set or disable debuginfo repo generation + :type debuginfo: bool + :param block_pkgs: package names to be blocked in sidetag + :type block_pkgs: list of str + :param unblock_pkgs: package names to be unblocked in sidetag + :type unblock_pkgs: list of str + """ + + context.session.assertLogin() + user = get_user(context.session.user_id, strict=True) + tag = get_tag(sidetag, strict=True) + + if not sidetag["extra"].get("sidetag"): + raise koji.GenericError("Not a sidetag: %(name)s" % sidetag) + if sidetag["extra"].get("sidetag_user_id") != user["id"]: + if not context.session.hasPerm("admin"): + raise koji.ActionNotAllowed("This is not your sidetag") + + parent_id = getInheritanceData(sidetag)[0]['parent_id'] + parent = get_tag(parent_id) + + if debuginfo is not None and not parent['extra'].get('sidetag_debuginfo_allowed'): + raise koji.GenericError("Debuginfo setting is not allowed in parent tag.") + if (block_pkgs or unblock_pkgs) and not parent['extra'].get('sidetag_package_list_allowed'): + raise koji.GenericError("Package un/blocking is not allowed in parent tag.") + + if debuginfo is not None: + _edit_tag(sidetag, extra={'with_debuginfo': bool(debuginfo)}) + for pkg in block_pkgs: + pkglist_block(sidetag, pkg) + for pkg in unblock_pkgs: + pkglist_unblock(sidetag, pkg) + def handle_sidetag_untag(cbtype, *args, **kws): """Remove a side tag when its last build is untagged diff --git a/tests/test_hub/test_pkglist.py b/tests/test_hub/test_pkglist.py index f9f6c2aa..7ab2642e 100644 --- a/tests/test_hub/test_pkglist.py +++ b/tests/test_hub/test_pkglist.py @@ -58,7 +58,7 @@ class TestPkglistBlock(unittest.TestCase): @mock.patch('kojihub.lookup_package') def test_pkglist_unblock(self, lookup_package, get_tag, assert_policy, readPackageList, _pkglist_add, _pkglist_remove): - tag = {'id': 1, 'name': 'tag'} + tag = {'id': 1, 'name': 'tag', 'extra': {}} pkg = {'id': 2, 'name': 'package', 'owner_id': 3} get_tag.return_value = tag lookup_package.return_value = pkg @@ -73,7 +73,7 @@ class TestPkglistBlock(unittest.TestCase): get_tag.assert_called_once_with('tag', strict=True) lookup_package.assert_called_once_with('pkg', strict=True) assert_policy.assert_called_once_with('package_list', {'tag': tag['id'], - 'action': 'unblock', 'package': pkg['id'], 'force': False}, force=False) + 'action': 'unblock', 'package': pkg['id'], 'force': False, 'extra': {}}, force=False) self.assertEqual(readPackageList.call_count, 2) readPackageList.assert_has_calls([ mock.call(tag['id'], pkgID=pkg['id'], inherit=True), @@ -96,7 +96,7 @@ class TestPkglistBlock(unittest.TestCase): def test_pkglist_unblock_inherited(self, lookup_package, get_tag, assert_policy, readPackageList, _pkglist_add, _pkglist_remove): tag_id, pkg_id, owner_id = 1, 2, 3 - get_tag.return_value = {'id': tag_id, 'name': 'tag'} + get_tag.return_value = {'id': tag_id, 'name': 'tag', 'extra': {}} lookup_package.return_value = {'id': pkg_id, 'name': 'pkg'} readPackageList.return_value = {pkg_id: { 'blocked': True, @@ -109,7 +109,7 @@ class TestPkglistBlock(unittest.TestCase): get_tag.assert_called_once_with('tag', strict=True) lookup_package.assert_called_once_with('pkg', strict=True) assert_policy.assert_called_once_with('package_list', {'tag': tag_id, - 'action': 'unblock', 'package': pkg_id, 'force': False}, force=False) + 'action': 'unblock', 'package': pkg_id, 'force': False, 'extra': {}}, force=False) readPackageList.assert_called_once_with(tag_id, pkgID=pkg_id, inherit=True) _pkglist_add.assert_called_once_with(tag_id, pkg_id, owner_id, False, '') _pkglist_remove.assert_not_called() @@ -123,7 +123,7 @@ class TestPkglistBlock(unittest.TestCase): def test_pkglist_unblock_not_present(self, lookup_package, get_tag, assert_policy, readPackageList, _pkglist_add, _pkglist_remove): tag_id, pkg_id = 1, 2 - get_tag.return_value = {'id': tag_id, 'name': 'tag'} + get_tag.return_value = {'id': tag_id, 'name': 'tag', 'extra': {}} lookup_package.return_value = {'id': pkg_id, 'name': 'pkg'} readPackageList.return_value = {} @@ -133,7 +133,7 @@ class TestPkglistBlock(unittest.TestCase): get_tag.assert_called_once_with('tag', strict=True) lookup_package.assert_called_once_with('pkg', strict=True) assert_policy.assert_called_once_with('package_list', {'tag': tag_id, - 'action': 'unblock', 'package': pkg_id, 'force': False}, force=False) + 'action': 'unblock', 'package': pkg_id, 'force': False, 'extra': {}}, force=False) readPackageList.assert_called_once_with(tag_id, pkgID=pkg_id, inherit=True) _pkglist_add.assert_not_called() _pkglist_remove.assert_not_called() @@ -147,7 +147,7 @@ class TestPkglistBlock(unittest.TestCase): def test_pkglist_unblock_not_blocked(self, lookup_package, get_tag, assert_policy, readPackageList, _pkglist_add, _pkglist_remove): tag_id, pkg_id, owner_id = 1, 2, 3 - get_tag.return_value = {'id': tag_id, 'name': 'tag'} + get_tag.return_value = {'id': tag_id, 'name': 'tag', 'extra': {}} lookup_package.return_value = {'id': pkg_id, 'name': 'pkg'} readPackageList.return_value = {pkg_id: { 'blocked': False, @@ -162,7 +162,7 @@ class TestPkglistBlock(unittest.TestCase): get_tag.assert_called_once_with('tag', strict=True) lookup_package.assert_called_once_with('pkg', strict=True) assert_policy.assert_called_once_with('package_list', {'tag': tag_id, - 'action': 'unblock', 'package': pkg_id, 'force': False}, force=False) + 'action': 'unblock', 'package': pkg_id, 'force': False, 'extra': {}}, force=False) readPackageList.assert_called_once_with(tag_id, pkgID=pkg_id, inherit=True) _pkglist_add.assert_not_called() _pkglist_remove.assert_not_called() @@ -200,7 +200,7 @@ class TestPkglistBlock(unittest.TestCase): force=False update=False policy=True - tag = {'id': 1, 'name': 'tag'} + tag = {'id': 1, 'name': 'tag', 'extra': {}} pkg = {'id': 2, 'name': 'pkg', 'owner_id': 3} users = [ {'id': 3, 'name': 'user'}, @@ -224,7 +224,7 @@ class TestPkglistBlock(unittest.TestCase): mock.call(112233), ]) assert_policy.assert_called_once_with('package_list', {'tag': tag['id'], - 'action': 'add', 'package': pkg['name'], 'force': False}, force=False) + 'action': 'add', 'package': pkg['name'], 'force': False, 'extra': {}}, force=False) self.assertEqual(self.run_callbacks.call_count, 2) self.run_callbacks.assert_has_calls([ mock.call('prePackageListChange', action='add', tag=tag, @@ -313,7 +313,7 @@ class TestPkglistBlock(unittest.TestCase): force=False update=False policy=True - tag = {'id': 1, 'name': 'tag'} + tag = {'id': 1, 'name': 'tag', 'extra': {}} pkg = {'id': 2, 'name': 'pkg', 'owner_id': 3} users = [ {'id': 3, 'name': 'user'}, @@ -341,7 +341,7 @@ class TestPkglistBlock(unittest.TestCase): mock.call(112233), ]) assert_policy.assert_called_once_with('package_list', {'tag': tag['id'], - 'action': 'add', 'package': pkg['name'], 'force': False}, force=False) + 'action': 'add', 'package': pkg['name'], 'force': False, 'extra': {}}, force=False) self.assertEqual(self.run_callbacks.call_count, 2) self.run_callbacks.assert_has_calls([ mock.call('prePackageListChange', action='add', tag=tag, @@ -370,7 +370,7 @@ class TestPkglistBlock(unittest.TestCase): force=False update=False policy=True - tag = {'id': 1, 'name': 'tag'} + tag = {'id': 1, 'name': 'tag', 'extra': {}} pkg = {'id': 2, 'name': 'pkg', 'owner_id': 3} users = [ {'id': 3, 'name': 'user',}, @@ -398,7 +398,7 @@ class TestPkglistBlock(unittest.TestCase): mock.call(112233), ]) assert_policy.assert_called_once_with('package_list', {'tag': tag['id'], - 'action': 'add', 'package': pkg['name'], 'force': False}, force=False) + 'action': 'add', 'package': pkg['name'], 'force': False, 'extra': {}}, force=False) self.run_callbacks.assert_called_once_with( 'prePackageListChange', action='add', tag=tag, package=pkg, owner=user['id'], block=block, @@ -420,7 +420,7 @@ class TestPkglistBlock(unittest.TestCase): force=True update=False policy=True - tag = {'id': 1, 'name': 'tag'} + tag = {'id': 1, 'name': 'tag', 'extra': {}} pkg = {'id': 2, 'name': 'pkg', 'owner_id': 3} users = [ {'id': 3, 'name': 'user',},