diff --git a/buildbot_nix/__init__.py b/buildbot_nix/__init__.py index 463f554..a2edb3a 100644 --- a/buildbot_nix/__init__.py +++ b/buildbot_nix/__init__.py @@ -15,6 +15,7 @@ from buildbot.process import buildstep, logobserver, remotecommand from buildbot.process.project import Project from buildbot.process.properties import Properties from buildbot.process.results import ALL_RESULTS, statusToString +from buildbot.www.auth import AuthBase from buildbot.www.oauth2 import OAuth2Auth from buildbot.changes.gerritchangesource import GerritChangeSource from buildbot.reporters.utils import getURLForBuildrequest @@ -33,14 +34,22 @@ from .binary_cache import S3BinaryCacheConfig log = Logger() -class LixSystemsOAuth2(OAuth2Auth): - name = 'Lix' - faIcon = 'fa-login' - resourceEndpoint = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect" - sslVerify = True - debug = False - authUri = 'https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth' - tokenUri = 'https://identity.lix.systems/realms/lix-project/protocol/openid-connect/token' +@dataclass +class OAuth2Config: + name: str + faIcon: str + resourceEndpoint: str + authUri: str + tokenUri: str + userinfoUri: str + sslVerify: bool = True + debug: bool = False + +class KeycloakOAuth2Auth(OAuth2Auth): + def __init__(self, userinfoUri: str, *args, debug=False, **kwargs): + super().__init__(*args, **kwargs) + self.userinfoUri = userinfoUri + self.debug = debug def createSessionFromToken(self, token): s = requests.Session() @@ -54,15 +63,26 @@ class LixSystemsOAuth2(OAuth2Auth): return s def getUserInfoFromOAuthClient(self, c): - userinfo_resp = c.get("https://identity.lix.systems/realms/lix-project/protocol/openid-connect/userinfo") - log.info("Userinfo request to Lix OAuth2: {}".format(userinfo_resp.status_code)) + userinfo_resp = c.get(self.userinfoUri) + log.info("Userinfo request to OAuth2: {}".format(userinfo_resp.status_code)) if userinfo_resp.status_code != 200: - log.info("Userinfo failure: {}".format(userinfo_resp.headers["www-authenticate"])) + log.error("Userinfo failure: {}".format(userinfo_resp.headers["www-authenticate"])) + userinfo_resp.raise_for_status() userinfo_data = userinfo_resp.json() return { 'groups': userinfo_data['buildbot_roles'] } + +def make_oauth2_method(oauth2_config: OAuth2Config): + """ + This constructs dynamically a class inheriting + an OAuth2 base configured using a dataclass. + """ + return type(f'{oauth2_config.name}DynamicOAuth2', + (KeycloakOAuth2Auth,), + oauth2_config.__dict__) + class BuildbotNixError(Exception): pass @@ -737,6 +757,7 @@ class GerritNixConfigurator(ConfiguratorBase): signing_keyfile: str | None = None, prometheus_config: dict[str, int | str] | None = None, binary_cache_config: dict[str, str] | None = None, + auth_method: AuthBase | None = None, ) -> None: super().__init__() self.gerrit_server = gerrit_server @@ -762,6 +783,8 @@ class GerritNixConfigurator(ConfiguratorBase): self.signing_keyfile = signing_keyfile + self.auth_method = auth_method + def configure(self, config: dict[str, Any]) -> None: worker_config = json.loads(read_secret_file(self.nix_workers_secret_name)) worker_names = [] @@ -841,9 +864,6 @@ class GerritNixConfigurator(ConfiguratorBase): config["www"].setdefault("plugins", {}) - if "auth" not in config["www"]: - config["www"]["auth"] = LixSystemsOAuth2('buildbot', read_secret_file('buildbot-oauth2-secret'), autologin=False) - if "authz" not in config["www"]: config["www"]["authz"] = util.Authz( allowRules=[ @@ -858,3 +878,6 @@ class GerritNixConfigurator(ConfiguratorBase): util.RolesFromOwner(role="owner") ], ) + + if "auth" not in config["www"] and self.auth_method is not None: + config["www"]["auth"] = self.auth_method diff --git a/nix/coordinator.nix b/nix/coordinator.nix index e92a581..698a1d9 100644 --- a/nix/coordinator.nix +++ b/nix/coordinator.nix @@ -158,13 +158,24 @@ in home = "/var/lib/buildbot"; extraImports = '' from datetime import timedelta - from buildbot_nix import GerritNixConfigurator + from buildbot_nix import GerritNixConfigurator, read_secret_file ''; configurators = [ '' util.JanitorConfigurator(logHorizon=timedelta(weeks=4), hour=12, dayOfWeek=6) '' '' + # TODO(raito): make me configurable from the NixOS module. + # how? + LixSystemsOAuth2 = make_oauth2_method(OAuth2Config( + name='Lix', + faIcon='fa-login', + resourceEndpoint='https://identity.lix.systems', + authUri='https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth', + tokenUri='https://identity.lix.systems/realms/lix-project/protocol/openid-connect/token', + userinfoUri='https://identity.lix.systems/realms/lix-project/protocol/openid-connect/userinfo' + ) + GerritNixConfigurator( "${cfg.gerrit.domain}", "${cfg.gerrit.username}", @@ -183,7 +194,11 @@ in binary_cache_config=${if (!cfg.binaryCache.enable) then "None" else builtins.toJSON { inherit (cfg.binaryCache) bucket region endpoint; profile = "default"; - }} + }}, + auth_method=LixSystemsOAuth2('buildbot', + read_secret_file('buildbot-oauth2-secret'), + autologin=True + ) ) '' ];