Skip to content

Commit 24f1150

Browse files
MagicRBzowoq
authored andcommitted
Add users option to both GitHub and Gitea restricting project owners
Signed-off-by: magic_rb <richard@brezak.sk>
1 parent eb6a8b4 commit 24f1150

File tree

5 files changed

+106
-12
lines changed

5 files changed

+106
-12
lines changed

buildbot_nix/buildbot_nix/common.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,29 @@ def atomic_write_file(file: Path, data: str) -> None:
114114
Y = TypeVar("Y")
115115

116116

117-
def filter_repos_by_topic(
118-
topic: str | None, repos: list[Y], topics: Callable[[Y], list[str]]
117+
def filter_repos(
118+
repo_allowlist: list[str] | None,
119+
user_allowlist: list[str] | None,
120+
topic: str | None,
121+
repos: list[Y],
122+
repo_name: Callable[[Y], str],
123+
user: Callable[[Y], str],
124+
topics: Callable[[Y], list[str]],
119125
) -> list[Y]:
126+
# This is a bit complicated so let me explain:
127+
# If both `user_allowlist` and `repo_allowlist` are `None` then we want to allow everything,
128+
# however if either are non-`None`, then the one that is non-`None` determines whether to
129+
# allow a repo, or both if both are non-+None`.
130+
120131
return list(
121132
filter(
122-
lambda repo: topic is None or topic in topics(repo),
123-
repos,
133+
lambda repo: (user_allowlist is None and repo_allowlist is None)
134+
or (user_allowlist is not None and user(repo) in user_allowlist)
135+
or (repo_allowlist is not None and repo_name(repo) in repo_allowlist),
136+
filter(
137+
lambda repo: topic is None or topic in topics(repo),
138+
repos,
139+
),
124140
)
125141
)
126142

buildbot_nix/buildbot_nix/gitea_projects.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .common import (
2121
ThreadDeferredBuildStep,
2222
atomic_write_file,
23-
filter_repos_by_topic,
23+
filter_repos,
2424
http_request,
2525
model_dump_project_cache,
2626
model_validate_project_cache,
@@ -185,12 +185,16 @@ def load_projects(self) -> list["GitProject"]:
185185
if not self.config.project_cache_file.exists():
186186
return []
187187

188-
repos: list[RepoData] = filter_repos_by_topic(
188+
repos: list[RepoData] = filter_repos(
189+
self.config.repo_allowlist,
190+
self.config.user_allowlist,
189191
self.config.topic,
190192
sorted(
191193
model_validate_project_cache(RepoData, self.config.project_cache_file),
192194
key=lambda repo: repo.full_name,
193195
),
196+
lambda repo: repo.full_name,
197+
lambda repo: repo.owner.login,
194198
lambda repo: repo.topics,
195199
)
196200
repo_names: list[str] = [repo.owner.login + "/" + repo.name for repo in repos]
@@ -320,9 +324,13 @@ def __init__(
320324
super().__init__(**kwargs)
321325

322326
def run_deferred(self) -> None:
323-
repos: list[RepoData] = filter_repos_by_topic(
327+
repos: list[RepoData] = filter_repos(
328+
self.config.repo_allowlist,
329+
self.config.user_allowlist,
324330
self.config.topic,
325331
refresh_projects(self.config, self.project_cache_file),
332+
lambda repo: repo.full_name,
333+
lambda repo: repo.owner.login,
326334
lambda repo: repo.topics,
327335
)
328336

buildbot_nix/buildbot_nix/github_projects.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from .common import (
2626
ThreadDeferredBuildStep,
2727
atomic_write_file,
28-
filter_repos_by_topic,
28+
filter_repos,
2929
http_request,
3030
model_dump_project_cache,
3131
model_validate_project_cache,
@@ -145,6 +145,8 @@ class ReloadGithubInstallations(ThreadDeferredBuildStep):
145145
installation_token_map_name: Path
146146
project_id_map_name: Path
147147
topic: str | None
148+
user_allowlist: list[str] | None
149+
repo_allowlist: list[str] | None
148150

149151
def __init__(
150152
self,
@@ -153,13 +155,17 @@ def __init__(
153155
installation_token_map_name: Path,
154156
project_id_map_name: Path,
155157
topic: str | None,
158+
user_allowlist: list[str] | None,
159+
repo_allowlist: list[str] | None,
156160
**kwargs: Any,
157161
) -> None:
158162
self.jwt_token = jwt_token
159163
self.installation_token_map_name = installation_token_map_name
160164
self.project_id_map_name = project_id_map_name
161165
self.project_cache_file = project_cache_file
162166
self.topic = topic
167+
self.user_allowlist = user_allowlist
168+
self.repo_allowlist = repo_allowlist
163169
super().__init__(**kwargs)
164170

165171
def run_deferred(self) -> None:
@@ -179,7 +185,9 @@ def run_deferred(self) -> None:
179185
repos = []
180186

181187
for k, v in installation_token_map.items():
182-
new_repos = filter_repos_by_topic(
188+
new_repos = filter_repos(
189+
self.repo_allowlist,
190+
self.user_allowlist,
183191
self.topic,
184192
refresh_projects(
185193
v.get(),
@@ -189,6 +197,8 @@ def run_deferred(self) -> None:
189197
subkey="repositories",
190198
require_admin=False,
191199
),
200+
lambda repo: repo.full_name,
201+
lambda repo: repo.owner.login,
192202
lambda repo: repo.topics,
193203
)
194204

@@ -259,23 +269,33 @@ class ReloadGithubProjects(ThreadDeferredBuildStep):
259269
token: RepoToken
260270
project_cache_file: Path
261271
topic: str | None
272+
user_allowlist: list[str] | None
273+
repo_allowlist: list[str] | None
262274

263275
def __init__(
264276
self,
265277
token: RepoToken,
266278
project_cache_file: Path,
267279
topic: str | None,
280+
user_allowlist: list[str] | None,
281+
repo_allowlist: list[str] | None,
268282
**kwargs: Any,
269283
) -> None:
270284
self.token = token
271285
self.project_cache_file = project_cache_file
272286
self.topic = topic
287+
self.user_allowlist = user_allowlist
288+
self.repo_allowlist = repo_allowlist
273289
super().__init__(**kwargs)
274290

275291
def run_deferred(self) -> None:
276-
repos: list[RepoData] = filter_repos_by_topic(
292+
repos: list[RepoData] = filter_repos(
293+
self.repo_allowlist,
294+
self.user_allowlist,
277295
self.topic,
278296
refresh_projects(self.token.get(), self.project_cache_file),
297+
lambda repo: repo.full_name,
298+
lambda repo: repo.owner.login,
279299
lambda repo: repo.topics,
280300
)
281301

@@ -309,6 +329,8 @@ def create_reload_builder_steps(
309329
webhook_secret: str,
310330
webhook_url: str,
311331
topic: str | None,
332+
user_allowlist: list[str] | None,
333+
repo_allowlist: list[str] | None,
312334
) -> list[BuildStep]:
313335
pass
314336

@@ -349,12 +371,16 @@ def create_reload_builder_steps(
349371
webhook_secret: str,
350372
webhook_url: str,
351373
topic: str | None,
374+
user_allowlist: list[str] | None,
375+
repo_allowlist: list[str] | None,
352376
) -> list[BuildStep]:
353377
return [
354378
ReloadGithubProjects(
355379
token=self.token,
356380
project_cache_file=project_cache_file,
357381
topic=topic,
382+
user_allowlist=user_allowlist,
383+
repo_allowlist=repo_allowlist,
358384
),
359385
CreateGitHubProjectHooks(
360386
token=self.token,
@@ -447,14 +473,18 @@ def create_reload_builder_steps(
447473
webhook_secret: str,
448474
webhook_url: str,
449475
topic: str | None,
476+
user_allowlist: list[str] | None,
477+
repo_allowlist: list[str] | None,
450478
) -> list[BuildStep]:
451479
return [
452480
ReloadGithubInstallations(
453481
self.jwt_token,
454482
project_cache_file,
455483
self.auth_type.installation_token_map_file,
456484
self.auth_type.project_id_map_file,
457-
topic,
485+
topic=topic,
486+
user_allowlist=user_allowlist,
487+
repo_allowlist=repo_allowlist,
458488
),
459489
CreateGitHubInstallationHooks(
460490
self.jwt_token,
@@ -564,6 +594,8 @@ def create_reload_builder(self, worker_names: list[str]) -> BuilderConfig:
564594
self.webhook_secret,
565595
self.webhook_url,
566596
self.config.topic,
597+
self.config.user_allowlist,
598+
self.config.repo_allowlist,
567599
)
568600
for step in steps:
569601
factory.addStep(step)
@@ -610,12 +642,16 @@ def load_projects(self) -> list["GitProject"]:
610642
if not self.config.project_cache_file.exists():
611643
return []
612644

613-
repos: list[RepoData] = filter_repos_by_topic(
645+
repos: list[RepoData] = filter_repos(
646+
self.config.repo_allowlist,
647+
self.config.user_allowlist,
614648
self.config.topic,
615649
sorted(
616650
model_validate_project_cache(RepoData, self.config.project_cache_file),
617651
key=lambda repo: repo.full_name,
618652
),
653+
lambda repo: repo.full_name,
654+
lambda repo: repo.owner.login,
619655
lambda repo: repo.topics,
620656
)
621657

buildbot_nix/buildbot_nix/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class GiteaConfig(BaseModel):
5858
instance_url: str
5959
topic: str | None
6060

61+
user_allowlist: list[str] | None
62+
repo_allowlist: list[str] | None
63+
6164
token_file: Path = Field(default=Path("gitea-token"))
6265
webhook_secret_file: Path = Field(default=Path("gitea-webhook-secret"))
6366
project_cache_file: Path = Field(default=Path("gitea-project-cache.json"))
@@ -148,6 +151,8 @@ class GitHubConfig(BaseModel):
148151
auth_type: GitHubLegacyConfig | GitHubAppConfig
149152
topic: str | None
150153

154+
user_allowlist: list[str] | None
155+
repo_allowlist: list[str] | None
151156
project_cache_file: Path = Field(default=Path("github-project-cache-v1.json"))
152157
webhook_secret_file: Path = Field(default=Path("github-webhook-secret"))
153158

nixosModules/master.nix

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,18 @@ in
282282
default = cfg.authBackend == "gitea";
283283
};
284284

285+
userAllowlist = lib.mkOption {
286+
type = lib.types.nullOr (lib.types.listOf lib.types.str);
287+
default = null;
288+
description = "If non-null, specifies users/organizations that are allowed to use buildbot, i.e. buildbot-nix will ignore any repositories not owned by these users/organizations.";
289+
};
290+
291+
repoAllowlist = lib.mkOption {
292+
type = lib.types.nullOr (lib.types.listOf lib.types.str);
293+
default = null;
294+
description = "If non-null, specifies an explicit set of repositories that are allowed to use buildbot, i.e. buildbot-nix will ignore any repositories not in this list.";
295+
};
296+
285297
tokenFile = lib.mkOption {
286298
type = lib.types.path;
287299
description = "Gitea token file";
@@ -336,6 +348,18 @@ in
336348
default = cfg.authBackend == "github";
337349
};
338350

351+
userAllowlist = lib.mkOption {
352+
type = lib.types.nullOr (lib.types.listOf lib.types.str);
353+
default = null;
354+
description = "If non-null, specifies users/organizations that are allowed to use buildbot, i.e. buildbot-nix will ignore any repositories not owned by these users/organizations.";
355+
};
356+
357+
repoAllowlist = lib.mkOption {
358+
type = lib.types.nullOr (lib.types.listOf lib.types.str);
359+
default = null;
360+
description = "If non-null, specifies an explicit set of repositories that are allowed to use buildbot, i.e. buildbot-nix will ignore any repositories not in this list.";
361+
};
362+
339363
authType = lib.mkOption {
340364
type = lib.types.attrTag {
341365
legacy = lib.mkOption {
@@ -492,6 +516,7 @@ in
492516
default = [ ];
493517
description = "Users that are allowed to login to buildbot, trigger builds and change settings";
494518
};
519+
495520
workersFile = lib.mkOption {
496521
type = lib.types.path;
497522
description = "File containing a list of nix workers";
@@ -712,6 +737,8 @@ in
712737
null
713738
else
714739
{
740+
user_allowlist = cfg.gitea.userAllowlist;
741+
repo_allowlist = cfg.gitea.repoAllowlist;
715742
token_file = "gitea-token";
716743
webhook_secret_file = "gitea-webhook-secret";
717744
project_cache_file = "gitea-project-cache.json";
@@ -727,6 +754,8 @@ in
727754
null
728755
else
729756
{
757+
user_allowlist = cfg.github.userAllowlist;
758+
repo_allowlist = cfg.github.repoAllowlist;
730759
auth_type =
731760
if (cfg.github.authType ? "legacy") then
732761
{ token_file = "github-token"; }

0 commit comments

Comments
 (0)