Skip to content

Commit f3ec1ad

Browse files
Support aliases and groups in allowed_* config options for Koji (#2320)
Support aliases and groups in allowed_* config options for Koji Introduce aliases all_admins and all_committers and also support groups in allowed_pr_authors and allowed_committers config options Fixes packit/packit#2088 Requires packit/ogr#834 TODO: Write new tests or update the old ones to cover new functionality (needs packit/ogr#834) Update or write new documentation in packit/packit.dev. (packit/packit.dev#814) RELEASE NOTES BEGIN allowed_pr_authors and allowed_committers now allow specifying groups and also aliases all_admins and all_committers (corresponding to the access to the repository). RELEASE NOTES END Reviewed-by: Maja Massarini Reviewed-by: Laura Barcziová Reviewed-by: Nikola Forró
2 parents 688c4cf + cea6451 commit f3ec1ad

File tree

3 files changed

+133
-4
lines changed

3 files changed

+133
-4
lines changed

packit_service/constants.py

+5
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,8 @@ def from_number(number: int):
289289
"of the `pull_from_upstream` job. We believe this adjustment will simplify the onboarding "
290290
"process and enhance the overall user experience. "
291291
)
292+
293+
294+
class KojiAllowedAccountsAlias(Enum):
295+
all_admins = "all_admins"
296+
all_committers = "all_committers"

packit_service/worker/checker/distgit.py

+72-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
import logging
55
import re
66

7+
from ogr.abstract import AccessLevel
78
from packit.config.aliases import get_branches
8-
from packit_service.constants import MSG_GET_IN_TOUCH
9+
from packit_service.constants import MSG_GET_IN_TOUCH, KojiAllowedAccountsAlias
910
from packit_service.worker.checker.abstract import Checker, ActorChecker
1011
from packit_service.worker.events import (
1112
PushPagureEvent,
@@ -43,6 +44,70 @@ def contains_specfile_change(self):
4344
return False
4445
return True
4546

47+
@staticmethod
48+
def is_koji_allowed_accounts_alias(value: str) -> bool:
49+
return any(value == alias.value for alias in KojiAllowedAccountsAlias)
50+
51+
def check_allowed_accounts(
52+
self, accounts_list: list[str], account_to_check: str
53+
) -> bool:
54+
"""
55+
Check whether the account_to_check matches one of the values in accounts_list
56+
(considering the groups and aliases).
57+
"""
58+
logger.info(f"Checking {account_to_check} in list of accounts: {accounts_list}")
59+
60+
direct_account_names = [
61+
value
62+
for value in accounts_list
63+
if not self.is_koji_allowed_accounts_alias(value)
64+
and not value.startswith("@")
65+
]
66+
67+
# check the direct account names to prevent unneeded API interactions
68+
if account_to_check in direct_account_names:
69+
return True
70+
71+
all_accounts = set()
72+
73+
for value in accounts_list:
74+
if self.is_koji_allowed_accounts_alias(value):
75+
all_accounts.update(self.expand_maintainer_alias(value))
76+
elif value.startswith("@"):
77+
try:
78+
# remove @
79+
group_name = value[1:]
80+
group = self.project.service.get_group(group_name)
81+
all_accounts.update(group.members)
82+
except Exception as ex:
83+
logger.debug(
84+
f"Exception while getting the members of group {value}: {ex!r}"
85+
)
86+
continue
87+
else:
88+
all_accounts.add(value)
89+
90+
logger.debug(f"Expanded accounts list: {all_accounts}")
91+
return account_to_check in all_accounts
92+
93+
def expand_maintainer_alias(self, alias: str) -> set[str]:
94+
"""
95+
Expand the 'all_admins' and 'all_committers' aliases to users.
96+
"""
97+
# see AccessLevel mapping
98+
# https://github.com/packit/ogr/blob/d183a6c6459231c2a60bacd6b827502c92a130ef/ogr/abstract.py#L1079
99+
# all_admins -> Pagure "admin" and "maintainer" access
100+
# all_committers -> on top of that "commit" access
101+
access_levels = [AccessLevel.maintain]
102+
103+
if alias == KojiAllowedAccountsAlias.all_committers.value:
104+
access_levels.extend([AccessLevel.admin, AccessLevel.push])
105+
106+
accounts = self.project.get_users_with_given_access(access_levels)
107+
108+
logger.debug(f"Expanded {alias}: {accounts}")
109+
return accounts
110+
46111
def pre_check(self) -> bool:
47112
if self.data.event_type in (PushPagureEvent.__name__,):
48113
if self.data.git_ref not in (
@@ -71,7 +136,9 @@ def pre_check(self) -> bool:
71136

72137
pr_author = self.get_pr_author()
73138
logger.debug(f"PR author: {pr_author}")
74-
if pr_author not in self.job_config.allowed_pr_authors:
139+
if not self.check_allowed_accounts(
140+
self.job_config.allowed_pr_authors, pr_author
141+
):
75142
logger.info(
76143
f"Push event {self.data.identifier} with corresponding PR created by"
77144
f" {pr_author} that is not allowed in project "
@@ -81,7 +148,9 @@ def pre_check(self) -> bool:
81148
else:
82149
committer = self.data.event_dict["committer"]
83150
logger.debug(f"Committer: {committer}")
84-
if committer not in self.job_config.allowed_committers:
151+
if not self.check_allowed_accounts(
152+
self.job_config.allowed_committers, committer
153+
):
85154
logger.info(
86155
f"Push event {self.data.identifier} done by "
87156
f"{committer} that is not allowed in project "

tests/unit/test_checkers.py

+56-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55

66
from flexmock import flexmock
77

8+
from ogr import PagureService
9+
from ogr.abstract import AccessLevel
10+
from ogr.services.pagure import PagureProject
811
from packit.config import (
912
CommonPackageConfig,
1013
JobType,
1114
JobConfigTriggerType,
1215
JobConfigView,
1316
JobConfig,
17+
PackageConfig,
1418
)
1519

1620
from packit.config.commands import TestCommandConfig
@@ -20,7 +24,10 @@
2024
IsJobConfigTriggerMatching as IsJobConfigTriggerMatchingCopr,
2125
IsPackageMatchingJobView,
2226
)
23-
from packit_service.worker.checker.distgit import IsUpstreamTagMatchingConfig
27+
from packit_service.worker.checker.distgit import (
28+
IsUpstreamTagMatchingConfig,
29+
PermissionOnDistgit,
30+
)
2431
from packit_service.worker.checker.koji import (
2532
IsJobConfigTriggerMatching as IsJobConfigTriggerMatchingKoji,
2633
)
@@ -937,3 +944,51 @@ def test_sync_release_matching_tag(upstream_tag_include, upstream_tag_exclude, r
937944
)
938945

939946
assert checker.pre_check() == result
947+
948+
949+
@pytest.mark.parametrize(
950+
"account, allowed_pr_authors, should_pass",
951+
(
952+
("direct-account", ["all_admins", "direct-account"], True),
953+
("admin-1", ["all_admins"], True),
954+
("admin-2", ["all_admins"], False),
955+
("group-account-1", ["all_admins", "@copr"], True),
956+
("group-account-2", ["all_admins", "@copr"], False),
957+
),
958+
)
959+
def test_koji_check_allowed_accounts(
960+
distgit_push_event,
961+
account,
962+
allowed_pr_authors,
963+
should_pass,
964+
):
965+
jobs = [
966+
JobConfig(
967+
type=JobType.koji_build,
968+
trigger=JobConfigTriggerType.commit,
969+
packages={
970+
"package": CommonPackageConfig(
971+
dist_git_branches=["f36"],
972+
allowed_pr_authors=allowed_pr_authors,
973+
)
974+
},
975+
),
976+
]
977+
978+
package_config = PackageConfig(
979+
jobs=jobs,
980+
packages={"package": CommonPackageConfig()},
981+
)
982+
job_config = jobs[0]
983+
984+
flexmock(PagureProject).should_receive("get_users_with_given_access").with_args(
985+
[AccessLevel.maintain]
986+
).and_return({"admin-1"})
987+
flexmock(PagureService).should_receive("get_group").with_args("copr").and_return(
988+
flexmock(members={"group-account-1"})
989+
)
990+
991+
checker = PermissionOnDistgit(
992+
package_config, job_config, distgit_push_event.get_dict()
993+
)
994+
assert checker.check_allowed_accounts(allowed_pr_authors, account) == should_pass

0 commit comments

Comments
 (0)