File authentik.spec of Package authentik
#
# spec file for package authentik
#
# Copyright (c) 2026 SUSE LLC and contributors
# Author: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%define upstream_version 2025.12.4
%define suse_version 0.6
%define python_subpackage_only 1
# only build against one Python flavor, Authentik mandates Python 3.13
%define pythons %{primary_python}
%define ak_python %{pythons}
%define ak_description "Authentik is an open-source Identity Provider (IdP) for modern SSO. It supports SAML, OAuth2/OIDC, LDAP, RADIUS, and more, designed for self-hosting from small labs to large production clusters."
%define ak_conf_dir %{_sysconfdir}/authentik
%define ak_data_dir %{_datadir}/authentik
%define ak_home_dir %{_sharedstatedir}/authentik
%define ak_geoip_dir %{ak_home_dir}/geoip
%define ak_run_dir %{_rundir}/authentik
%define ak_blueprints_dir %{ak_data_dir}/blueprints
%define ak_web_dir %{ak_data_dir}/web
%define ak_user _authentik
%define ak_group %{ak_user}
%global component @BUILD_FLAVOR@%{nil}
%if "%{component}" == "frontend"
%bcond_without frontend
%else
%bcond_with frontend
%endif
%if "%{component}" == "python-authentik"
%bcond_without python_authentik
%else
%bcond_with python_authentik
%endif
%if "%{component}" == "python-ak-guardian"
%bcond_without python_ak_guardian
%else
%bcond_with python_ak_guardian
%endif
%if "%{component}" == "python-django-channels-postgres"
%bcond_without python_django_channels_postgres
%else
%bcond_with python_django_channels_postgres
%endif
%if "%{component}" == "python-django-dramatiq-postgres"
%bcond_without python_django_dramatiq_postgres
%else
%bcond_with python_django_dramatiq_postgres
%endif
%if "%{component}" == "python-django-postgres-cache"
%bcond_without python_django_postgres_cache
%else
%bcond_with python_django_postgres_cache
%endif
%if "%{component}" == "server"
%bcond_without server
%else
%bcond_with server
%endif
%if "%{component}" == "worker"
%bcond_without worker
%else
%bcond_with worker
%endif
%if "%{component}" == "outposts"
%bcond_without outposts
%define ak_outposts ldap proxy rac radius
%else
%bcond_with outposts
%endif
# all the possible bconds here
%if !%{with frontend} && !%{with python_authentik} && !%{with python_ak_guardian} && !%{with python_django_channels_postgres} && !%{with python_django_dramatiq_postgres} && !%{with python_django_postgres_cache} && !%{with server} && !%{with worker} && !%{with outposts}
%bcond_without main
%else
%bcond_with main
%endif
# we do not support legacy update-alternatives, but macros require it to be enabled this way
%bcond_without libalternatives
# allow skipping of tests for quicker development
%bcond_without checks
Name: authentik
Version: %{upstream_version}+suse%{suse_version}
Release: 0
Summary: Identity Provider
License: MIT
URL: https://goauthentik.io/
Source0: %{name}-%{version}.tar.zst
Source1: vendor_go.tar.zst
Source2: vendor_node.tar.zst
Source3: %{name}.sysusers
Source4: %{name}.tmpfiles
# allow path to shell script to be set dynamically during prep
Patch0: authentik-ak-libexecdir.patch
# allow path to mode/pid files to be set during prep and built in instead of defaulting to different unsafe places if tmpdir wasn't set
Patch2: authentik-homedir-instead-of-tmpdir.patch
# run server with the binary name we install it as (we give it a more meaningful name and do not want the go run fallback)
Patch3: authentik-ak-server.patch
# allow path to static files to be set during prep
Patch4: authentik-django-staticfiles-webdir.patch
# allow path to the directory containing system_migrations to be set during prep
Patch5: authentik-django-migrate-datadir.patch
# remove parts of tests using online providers (github.com, login.microsoftonline.com)
# (note tests exclusively testing online services are skipped in PYTEST_ADDOPTS => -k later on)
Patch10: authentik-test-conditional-fields-no-azuread.patch
Patch11: authentik-test-import-yaml-tags-no-github.patch
BuildRequires: %{pythons}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
BuildRequires: zstd
# main package is technically noarch but then the server subpackage couldn't be arch specific ("Only noarch subpackages are supported")
#BuildArch: noarch
%if %{with main}
BuildRequires: sysuser-tools
# TODO, how to do this?
%dnl Requires: ( %{name}-server = %{version} OR %{name}-worker = %{version} )
%sysusers_requires
%endif
%python_subpackages
%description
%{ak_description}
Main package.
%if %{with frontend}
%package frontend
Summary: Authentik - Frontend
# upstream uses nodejs24, but the nodejs22 we have in 16 works just as well
BuildRequires: nodejs
BuildRequires: npm
BuildRequires: rsync
BuildArch: noarch
%description frontend
%{ak_description}
Frontend/Website subpackage.
%endif
%if %{with python_authentik}
%package -n %{python_flavor}-authentik
Summary: Authentik - Python Authentik Core
BuildArch: noarch
# SECTION setup requirements
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
# /SECTION
# SECTION app requirements
BuildRequires: %{python_module PyJWT == 2.10.1}
# PyYAML: upstream has == 6.0.2, we have 6.0.3, no breaking changes
BuildRequires: %{python_module PyYAML == 6.0.3}
BuildRequires: %{python_module Unidecode == 1.4.0}
BuildRequires: %{python_module ak-guardian == 3.2.0}
BuildRequires: %{python_module argon2-cffi == 25.1.0}
BuildRequires: %{python_module cachetools == 6.2.4}
BuildRequires: %{python_module channels == 4.3.1}
# cryptography: upstream has == 45.0.5, we have 46.x.x, it should be compatible
BuildRequires: %{python_module cryptography == 46.0.5}
BuildRequires: %{python_module dacite == 1.9.2}
BuildRequires: %{python_module deepmerge == 2.0}
BuildRequires: %{python_module defusedxml == 0.7.1}
BuildRequires: %{python_module django == 5.2.11}
BuildRequires: %{python_module django-channels-postgres}
BuildRequires: %{python_module django-countries == 7.6.1}
BuildRequires: %{python_module django-cte == 2.0.0}
BuildRequires: %{python_module django-dramatiq-postgres}
BuildRequires: %{python_module django-filter == 25.2}
BuildRequires: %{python_module django-model-utils == 5.0.0}
BuildRequires: %{python_module django-pglock == 1.8.0}
BuildRequires: %{python_module django-pgtrigger == 4.17.0}
BuildRequires: %{python_module django-postgres-cache}
BuildRequires: %{python_module django-postgres-extra == 2.0.9}
BuildRequires: %{python_module django-prometheus == 2.4.1}
BuildRequires: %{python_module django-storages == 1.14.6}
BuildRequires: %{python_module django-tenants == 3.9.0}
BuildRequires: %{python_module djangoql == 0.19.1}
BuildRequires: %{python_module djangorestframework == 3.16.1}
BuildRequires: %{python_module docker == 7.1.0}
BuildRequires: %{python_module drf-orjson-renderer == 1.7.3}
BuildRequires: %{python_module drf-spectacular == 0.28.0}
BuildRequires: %{python_module duo-client == 5.5.0}
BuildRequires: %{python_module fido2 == 2.0.0}
BuildRequires: %{python_module geoip2 == 5.2.0}
BuildRequires: %{python_module geopy == 2.4.1}
# 53c376e4e9e999f02be9a93a299456029983a9ce
BuildRequires: %{python_module google-api-python-client == 2.190.0}
BuildRequires: %{python_module gssapi == 1.10.1}
BuildRequires: %{python_module gunicorn == 23.0.0}
BuildRequires: %{python_module jsonpatch == 1.33}
BuildRequires: %{python_module jwcrypto == 1.5.6}
BuildRequires: %{python_module kubernetes == 33.1.0}
BuildRequires: %{python_module ldap3 == 2.9.1}
BuildRequires: %{python_module lxml == 6.0.2}
BuildRequires: %{python_module msgraph-sdk == 1.39.0}
BuildRequires: %{python_module opencontainers == 0.0.15}
BuildRequires: %{python_module packaging == 26.0}
BuildRequires: %{python_module paramiko == 3.5.1}
BuildRequires: %{python_module psycopg == 3.2.9}
BuildRequires: %{python_module pydantic == 2.11.7}
BuildRequires: %{python_module pydantic-scim == 0.0.8}
BuildRequires: %{python_module pyrad == 2.4}
BuildRequires: %{python_module python-kadmin-rs == 0.7.0}
BuildRequires: %{python_module requests-oauthlib == 2.0.0}
BuildRequires: %{python_module scim2-filter-parser == 0.7.0}
BuildRequires: %{python_module sentry-sdk == 2.33.2}
BuildRequires: %{python_module service_identity == 24.2.0}
BuildRequires: %{python_module setproctitle == 1.3.7}
BuildRequires: %{python_module structlog == 25.4.0}
BuildRequires: %{python_module swagger-spec-validator == 3.0.4}
# twilio: upstream has == 9.7.0, we have 9.10.0, potentially DOES have relevant changes, but not investigated as we do not use the feature
BuildRequires: %{python_module twilio == 9.10.0}
BuildRequires: %{python_module ua-parser == 1.0.1}
BuildRequires: %{python_module urllib3 < 3}
# uvicorn: upstream has == 0.35.0, we have 0.36.0, it does not change any existing functionality
BuildRequires: %{python_module uvicorn == 0.36.0}
BuildRequires: %{python_module watchdog == 6.0.0}
# webauthn: upstream has == 2.6.0, we have 2.7.0, it does not change any existing functionality
BuildRequires: %{python_module webauthn == 2.7.0}
BuildRequires: %{python_module wsproto == 1.2.0}
BuildRequires: %{python_module xmlsec == 1.3.16}
BuildRequires: %{python_module zxcvbn == 4.5.0}
BuildRequires: alts
# need a backend for xmlsec, pick openssl as it's recommended and because openssl is installed by default
BuildRequires: libxmlsec1-openssl1
# /SECTION
# SECTION app requirements (runtime, should always be aligned with app build requirements above)
Requires: alts
Requires: libxmlsec1-openssl1
%requires_eq %{ak_python}-Django
%requires_eq %{ak_python}-PyJWT
%requires_eq %{ak_python}-PyYAML
%requires_eq %{ak_python}-Unidecode
%requires_eq %{ak_python}-ak-guardian
%requires_eq %{ak_python}-argon2-cffi
%requires_eq %{ak_python}-channels
%requires_eq %{ak_python}-cryptography
%requires_eq %{ak_python}-dacite
%requires_eq %{ak_python}-deepmerge
%requires_eq %{ak_python}-defusedxml
%requires_eq %{ak_python}-django-channels-postgres
%requires_eq %{ak_python}-django-countries
%requires_eq %{ak_python}-django-cte
%requires_eq %{ak_python}-django-dramatiq-postgres
%requires_eq %{ak_python}-django-filter
%requires_eq %{ak_python}-django-model-utils
%requires_eq %{ak_python}-django-pglock
%requires_eq %{ak_python}-django-pgtrigger
%requires_eq %{ak_python}-django-postgres-cache
%requires_eq %{ak_python}-django-postgres-extra
%requires_eq %{ak_python}-django-prometheus
%requires_eq %{ak_python}-django-storages
%requires_eq %{ak_python}-django-tenants
%requires_eq %{ak_python}-djangoql
%requires_eq %{ak_python}-djangorestframework
%requires_eq %{ak_python}-docker
%requires_eq %{ak_python}-drf-orjson-renderer
%requires_eq %{ak_python}-drf-spectacular
%requires_eq %{ak_python}-duo-client
%requires_eq %{ak_python}-fido2
%requires_eq %{ak_python}-geoip2
%requires_eq %{ak_python}-geopy
%requires_eq %{ak_python}-google-api-python-client
%requires_eq %{ak_python}-gssapi
%requires_eq %{ak_python}-gunicorn
%requires_eq %{ak_python}-jsonpatch
%requires_eq %{ak_python}-jwcrypto
%requires_eq %{ak_python}-kadmin-rs
%requires_eq %{ak_python}-kubernetes
%requires_eq %{ak_python}-ldap3
%requires_eq %{ak_python}-lxml
%requires_eq %{ak_python}-msgraph-sdk
%requires_eq %{ak_python}-opencontainers
%requires_eq %{ak_python}-packaging
%requires_eq %{ak_python}-paramiko
%requires_eq %{ak_python}-psycopg
%requires_eq %{ak_python}-pydantic
%requires_eq %{ak_python}-pydantic-scim
%requires_eq %{ak_python}-pyrad
%requires_eq %{ak_python}-requests-oauthlib
%requires_eq %{ak_python}-scim2-filter-parser
%requires_eq %{ak_python}-sentry-sdk
%requires_eq %{ak_python}-service_identity
%requires_eq %{ak_python}-setproctitle
%requires_eq %{ak_python}-structlog
%requires_eq %{ak_python}-swagger-spec-validator
%requires_eq %{ak_python}-twilio
%requires_eq %{ak_python}-ua-parser
%requires_eq %{ak_python}-urllib3
%requires_eq %{ak_python}-uvicorn
%requires_eq %{ak_python}-watchdog
%requires_eq %{ak_python}-webauthn
%requires_eq %{ak_python}-wsproto
%requires_eq %{ak_python}-xmlsec
%requires_eq %{ak_python}-zxcvbn
# /SECTION
# SECTION test requirements
BuildRequires: %{python_module daphne}
BuildRequires: %{python_module freezegun}
BuildRequires: %{python_module k5test}
BuildRequires: %{python_module pytest-django}
BuildRequires: %{python_module pytest-randomly}
BuildRequires: %{python_module pytest-timeout}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module requests-mock}
BuildRequires: postgresql-server
# for krb5-config, TODO: should this be set as a Require in python-krb5test instead?
BuildRequires: pkgconfig(krb5)
# /SECTION
# SECTION app requirements not listed in pyproject, but imported implicitly and needed to be listed here as not pulled in as dependencies
BuildRequires: %{python_module boto3}
# for pydantic.networks, should be aligned with the recommends in python-pydantic
BuildRequires: %{python_module email-validator >= 2.0}
# for the uvicorn worker, should be aligned with the recommends in python-uvicorn
BuildRequires: %{python_module httptools >= 0.4.0}
BuildRequires: %{python_module uvloop}
BuildRequires: %{python_module websockets}
%requires_eq %{ak_python}-boto3
%requires_eq %{ak_python}-cachetools
%requires_eq %{ak_python}-email-validator
%requires_eq %{ak_python}-httptools
%requires_eq %{ak_python}-uvloop
%requires_eq %{ak_python}-websockets
# /SECTION
%description -n %{python_flavor}-authentik
%{ak_description}
Subpackage: Python library "authentik".
%endif # end with python_authentik
%if %{with python_ak_guardian}
%package -n %{python_flavor}-ak-guardian
Version: 3.2.0
Summary: Authentik - Python Authentik Guardian
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module typing_extensions >= 4.12}
BuildRequires: ( %{python_module django < 6.0} with %{python_module django >= 5.2} )
%requires_eq %{ak_python}-Django
%requires_eq %{ak_python}-typing_extensions
BuildArch: noarch
%description -n %{python_flavor}-ak-guardian
%{ak_description}
Subpackage: Python library "ak-guardian".
Model and object permissions for Django
This is a fork of django-guardian.
%endif # end with python_ak_guardian
%if %{with python_django_channels_postgres}
%package -n %{python_flavor}-django-channels-postgres
Version: 0.1.0
Summary: Authentik - Python Django Channels
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
BuildRequires: ( %{python_module channels < 4.4} with %{python_module channels >= 4.3} )
BuildRequires: ( %{python_module django < 6.0} with %{python_module django >= 4.2} )
BuildRequires: ( %{python_module django-pgtrigger < 5} with %{python_module django-pgtrigger >= 4} )
BuildRequires: ( %{python_module msgpack < 2} with %{python_module msgpack >= 1} )
BuildRequires: ( %{python_module psycopg < 4} with %{python_module psycopg >= 3} )
BuildRequires: ( %{python_module psycopg-pool < 4} with %{python_module psycopg-pool >= 3} )
BuildRequires: ( %{python_module structlog < 26} with %{python_module structlog >= 25} )
%requires_eq %{ak_python}-Django
%requires_eq %{ak_python}-channels
%requires_eq %{ak_python}-django-pgtrigger
%requires_eq %{ak_python}-msgpack
%requires_eq %{ak_python}-psycopg
%requires_eq %{ak_python}-psycopg-pool
%requires_eq %{ak_python}-structlog
BuildArch: noarch
%description -n %{python_flavor}-django-channels-postgres
%{ak_description}
Subpackage: Python library "django-channels-postgres"
Django channels layer using PostgreSQL NOTIFY/LISTEN.
%endif # end with python_django_channels_postgres
%if %{with python_django_dramatiq_postgres}
%package -n %{python_flavor}-django-dramatiq-postgres
Version: 0.1.0
Summary: Authentik - Python Django Dramatiq
BuildRequires: ( %{python_module cron-converter < 2} with %{python_module cron-converter >= 1} )
BuildRequires: ( %{python_module django < 6.0} with %{python_module django >= 4.2} )
BuildRequires: ( %{python_module django-pglock < 2} with %{python_module django-pglock >= 1.7} )
BuildRequires: ( %{python_module django-pgtrigger < 5} with %{python_module django-pgtrigger >= 4} )
BuildRequires: ( %{python_module dramatiq < 1.18} with %{python_module dramatiq >= 1.17} )
BuildRequires: ( %{python_module structlog < 26} with %{python_module structlog >= 25} )
BuildRequires: ( %{python_module tenacity < 10} with %{python_module tenacity >= 9} )
%requires_eq %{ak_python}-Django
%requires_eq %{ak_python}-cron-converter
%requires_eq %{ak_python}-django-pglock
%requires_eq %{ak_python}-django-pgtrigger
%requires_eq %{ak_python}-dramatiq
%requires_eq %{ak_python}-structlog
%requires_eq %{ak_python}-tenacity
BuildArch: noarch
# SECTION setup requirements
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
# /SECTION
%description -n %{python_flavor}-django-dramatiq-postgres
%{ak_description}
Subpackage: Python library "django-dramatiq-postgres"
Django and Dramatiq integration with postgres-specific features.
%endif # end with python_django_dramatiq_postgres
%if %{with python_django_postgres_cache}
%package -n %{python_flavor}-django-postgres-cache
Version: 0.1.0
Summary: Authentik - Python Django Postgres Cache
BuildRequires: ( %{python_module django < 6.0} with %{python_module django >= 4.2} )
BuildRequires: ( %{python_module django-postgres-extra < 2.1} with %{python_module django-postgres-extra >= 2.0} )
%requires_eq %{ak_python}-Django
%requires_eq %{ak_python}-django-postgres-extra
BuildArch: noarch
# SECTION setup requirements
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
# /SECTION
%description -n %{python_flavor}-django-postgres-cache
%{ak_description}
Subpackage: Python library "django-postgres-cache"
Improved Django Postgres Cache.
%endif # end with python_django_postgres_cache
%if %{with server}
%package server
Summary: Authentik Server
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: authentik = %{version}
BuildRequires: authentik-frontend = %{version}
BuildRequires: golang(API) >= 1.25
%requires_eq %{ak_python}-authentik
%requires_eq authentik
Recommends: authentik-frontend = %{version}
BuildArch: noarch
%description server
%{ak_description}
Server subpackage.
Go based coordinator and Gunicorn wrapper.
%endif # end with server
%if %{with worker}
%package worker
Summary: Authentik Worker
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: authentik = %{version}
%requires_eq %{ak_python}-authentik
%requires_eq authentik
%description worker
%{ak_description}
Worker subpackage.
This is a meta package not shipping any files.
%endif # end with worker
%if %{with outposts}
%package outpost-ldap
Summary: Authentik LDAP Outpost
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: golang(API) >= 1.25
%requires_eq %{ak_python}-authentik
%description outpost-ldap
%{ak_description}
This subpackage ships an outpost server for the LDAP provider.
%package outpost-proxy
Summary: Authentik Proxy Outpost
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: golang(API) >= 1.25
%requires_eq %{ak_python}-authentik
%description outpost-proxy
%{ak_description}
This subpackage ships an outpost server for the the Proxy provider.
%package outpost-rac
Summary: Authentik RAC Outpost
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: golang(API) >= 1.25
%requires_eq %{ak_python}-authentik
%description outpost-rac
%{ak_description}
This subpackage ships an outpost server for the Remote Access Control (RAC) provider.
%package outpost-radius
Summary: Authentik RADIUS Outpost
BuildRequires: %{ak_python}-authentik = %{version}
BuildRequires: golang(API) >= 1.25
%requires_eq %{ak_python}-authentik
%description outpost-radius
%{ak_description}
This subpackage ships an outpost server for the the RADIUS provider.
%endif # end with outposts
%prep
set +x
# just for easier detection of problems with the bcond/multibuild logic
echo 'Building component: %{component}'
echo 'main => %{with main}'
echo 'frontend => %{with frontend}'
echo 'python_authentik => %{with python_authentik}'
echo 'python_ak_guardian => %{with python_ak_guardian}'
echo 'python_django_channels_postgres => %{with python_django_channels_postgres}'
echo 'python_django_dramatiq_postgres => %{with python_django_dramatiq_postgres}'
echo 'python_django_postgres_cache => %{with python_django_postgres_cache}'
echo 'server => %{with server}'
echo 'worker => %{with worker}'
echo 'outposts => %{with outposts}'
set -x
%autosetup -a1 -p1
%if %{with frontend}
tar -C web -xf %{SOURCE2}
%endif
%if %{with main}
_putconf () {
sed \
-e 's?__AK_USER__?%{ak_user}?' \
-e 's?__AK_GROUP__?%{ak_group}?' \
-e 's?__AK_HOMEDIR__?%{ak_home_dir}?' \
-e 's?__AK_RUNDIR__?%{ak_run_dir}?' \
"$1" > "$2"
}
_putconf %{SOURCE3} sysusers.conf
_putconf %{SOURCE4} tmpfiles.conf
%endif
%if !%{with server}
# this is only needed by the upstream container entrypoint, which we do not use
# (in our container, we use catatonit)
sed -i /dumb-init/d pyproject.toml
# extract pytest options, as pytest macro ignores ones set via pyproject
echo '[pytest]' > pytest.toml
# skip addopts, we curate and add those to the pytest call manually (upstream uses pytest 8 style options, we use pytest 9)
sed '1,/^\[tool.pytest.ini_options\]$/d' pyproject.toml | grep -v ^addopts >> pytest.toml
# we do not install the tests module containing this function, and it's not useful in an offline environment anyways
grep get_local_ip authentik/root/test_plugin.py # if this fails, the replacement below is outdated
sed -i /get_local_ip/d authentik/root/test_plugin.py
# load default blueprints and GeoIP databases from distribution paths (geoip is not populated by default, but user should install the data there)
sed -Ei \
-e 's?^(blueprints_dir: ).*$?\1%{ak_blueprints_dir}?' \
-e 's?(: ")/geoip(/.*)$?\1%{ak_geoip_dir}\2?' \
authentik/lib/default.yml
grep blueprints_dir: authentik/lib/default.yml | grep %{ak_blueprints_dir}
grep -A2 context_processors: authentik/lib/default.yml | grep %{ak_geoip_dir}
# remove unnecessary shebangs
sed -i '1d' lifecycle/{migrate,wait_for_db}.py
# replace variable patched in earlier to point the python wrapper to the distribution path of the ak shell script
sed -i 's?__LIBEXECDIR__?%{_libexecdir}?' lifecycle/ak.py
# replace variable patched in earlier to point to a more secure place for storing mode and pid files
sed -i 's?__AK_HOMEDIR__?%{ak_home_dir}?' authentik/lib/utils/reflection.py lifecycle/ak
# replace variable patched in earlier to point to the directory static files are installed in
sed -i 's?__AK_WEBDIR__?%{ak_web_dir}?' authentik/root/settings.py
# replace variable patched in earlier to point to the directory the system_migrations directory is installed in
sed -i 's?__AK_DATADIR__?%{ak_data_dir}?' lifecycle/migrate.py
# 1. replace env interpreter for the dependency generator
# 2. have the ak shell script call the installed python scripts as custom modules or binaries instead of as relative modules which won't exist in the PWD
sed -i \
-e '1 s?^#!/usr/bin/env -S bash?#!%{_bindir}/bash?' \
-e 's? lifecycle.? ak_lifecycle.?' \
-e 's?python -m manage?%{_bindir}/ak-manage?' \
-e 's?python ?python%{python_version}?' \
lifecycle/ak
# the other python places too need to reference the installed module instead of a relative one
sed -i \
's/lifecycle.ak/ak_lifecycle.ak/' \
pyproject.toml
sed -i \
's/^from lifecycle./from ak_lifecycle./' \
lifecycle/system_migrations/*.py manage.py
%endif
%if %{with server}
sed -i \
-e 's?^from lifecycle.?from ak_lifecycle.?' \
-e 's?"lifecycle.?"ak_lifecycle.?' \
lifecycle/gunicorn.conf.py
# var needs to be string to allow for overwrite in ldflags later
# this is rather ugly, but a proper patch would probably require introducing some logic
sed -Ei \
-e '/^\s+"path"$/d' \
-e 's?^(var workerPidFile = ).*"(authentik-worker.pid)"\)$?\1"%{ak_home_dir}/\2"?' \
cmd/server/healthcheck.go
grep %{ak_home_dir} cmd/server/healthcheck.go
%endif
%build
_build_python () {
cd packages/$1
%pyproject_wheel
}
%if %{with frontend}
pushd web
PATH="$PATH:node_modules/.bin" \
npm run build
PATH="$PATH:../../node_modules/.bin" \
npm run build:sfe
%endif
%if %{with python_ak_guardian}
_build_python ak-guardian
%endif
%if %{with python_django_channels_postgres}
_build_python django-channels-postgres
%endif
%if %{with python_django_dramatiq_postgres}
_build_python django-dramatiq-postgres
%endif
%if %{with python_django_postgres_cache}
_build_python django-postgres-cache
%endif
%if %{with python_authentik}
# this one is in the top level
%pyproject_wheel
%endif
%if %{with server}
constpairs=(
'manageCmd=%{_bindir}/ak-manage'
'gunicornCmd=%{_bindir}/gunicorn'
'gunicornConf=%{ak_conf_dir}/gunicorn.conf.py'
'pidDir=%{ak_home_dir}' # this would better be ak_run_dir, but it doesn't get created in containers ...
)
constflags=''
for pair in "${constpairs[@]}"
do
constflags="$constflags -X \"goauthentik.io/internal/gounicorn.$pair\""
done
constflags="$constflags -X \"goauthentik.io/web.staticDir=%{ak_web_dir}/dist/\" -X \"main.workerPidFile=%{ak_home_dir}/authentik-worker.pid\""
go build \
-buildmode=pie -mod=vendor \
-ldflags="-s -w $constflags" \
-o ak-server \
./cmd/server
%endif
%if %{with outposts}
for o in %{ak_outposts}
do
go build \
-buildmode=pie -mod=vendor \
-ldflags='-s' \
-o ak-outpost-$o \
./cmd/$o
done
%endif
%if %{with main}
%sysusers_generate_pre sysusers.conf %{name}.sysusers %{name}.conf
%endif
%install
_install_python () {
cd packages/$1
%pyproject_install
}
%if %{with frontend}
install -d %{buildroot}%{ak_web_dir}
rsync \
--exclude='web/dist/node_modules' \
--exclude='web/dist/assets/node_modules' \
-r web/dist %{buildroot}%{ak_web_dir}
%fdupes %{buildroot}%{ak_web_dir}
%endif
%if %{with python_ak_guardian}
_install_python ak-guardian
%endif
%if %{with python_django_channels_postgres}
_install_python django-channels-postgres
%endif
%if %{with python_django_dramatiq_postgres}
_install_python django-dramatiq-postgres
%endif
%if %{with python_django_postgres_cache}
_install_python django-postgres-cache
%endif
%if %{with python_authentik}
%pyproject_install
%python_clone -a %{buildroot}%{_bindir}/ak
install manage.py %{buildroot}%{_bindir}/ak-manage
%python_clone -a %{buildroot}%{_bindir}/ak-manage
%python_group_libalternatives ak ak-manage
%python_expand install -d %{buildroot}%{$python_sitelib}/ak_lifecycle
%python_expand install -t %{buildroot}%{$python_sitelib}/ak_lifecycle -m 0644 lifecycle/{__init__,ak,migrate,wait_for_db,worker}.py
install -d %{buildroot}%{ak_blueprints_dir}
cp -r blueprints/* %{buildroot}%{ak_blueprints_dir}
cp -r lifecycle/system_migrations %{buildroot}%{ak_data_dir}
%endif
%if %{with server}
install -D -t %{buildroot}%{_bindir} ak-server
install -D -m 0644 -t %{buildroot}%{ak_conf_dir} lifecycle/gunicorn.conf.py
%endif
%if %{with outposts}
outposts=''
for o in %{ak_outposts}
do
outposts="$outposts ak-outpost-$o"
done
install -D -t %{buildroot}%{_bindir}$outposts
%endif
%if %{with main}
install -D -t %{buildroot}%{_libexecdir} lifecycle/ak
install -D -m0644 sysusers.conf %{buildroot}%{_sysusersdir}/%{name}.conf
install -D -m0644 tmpfiles.conf %{buildroot}%{_tmpfilesdir}/%{name}.conf
install -d -m0750 %{buildroot}%{ak_home_dir}
install -d -m0750 %{buildroot}%{ak_geoip_dir}
%else
%if !%{with server} && !%{with outposts}
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%endif
%endif
# only the main package ships tests
%if %{with python_authentik}
%check
%if !%{with checks}
echo 'Skipping tests!'
exit 0
%endif
# postgresql data directory
pgd=$PWD/postgresql
# role and db name
n=authentik
# when a previous local build failed, the tmp directory is not always getting cleared, preventing postgres from starting again
find /tmp -name '.s.*' -delete
initdb -D $pgd
# need to use /tmp as the socket directory because /run (the default) is not writable and
# pgd/PWD would exceed the maximum allowed path length (107 bytes)
pg_ctl start -D $pgd -l pg_log -o '-c unix_socket_directories=/tmp' -w || { cat pg_log ; tail $pgd/log/* ; exit 1 ; }
createuser -d -e -h /tmp -s $n
createdb -e -h /tmp -O $n $n
# avoid useless attempts to reach upstream sentry endpoint
# TODO: this does not work; it's not a problem, but yields lots of useless output about failed connections if a test fails
export AUTHENTIK_ERROR_REPORTING__ENABLED=false
export AUTHENTIK_BLUEPRINTS__DIR=%{buildroot}%{ak_blueprints_dir}
# openssl rand -base64 60
export AUTHENTIK_SECRET_KEY='CvK88R/qx9J9O5xCjm5pOlXWwgsTzcEaN773ON4Ws3NyrocipybmCkecvaeacTT9VGZW+mh91KHGHFIm'
export DJANGO_SETTINGS_MODULE=authentik.root.settings
# TEST EXCLUSIONS:
# test_hibp: requires access to haveibeenpwned.com
# test_api_validate: requires access to accounts.google.com (and all other available providers it could be patched to are online too)
# test_brand_subdomain: flaky with stray "tenants_test_flag" dict element
export PYTEST_ADDOPTS='
--import-mode=importlib
-p authentik.root.test_plugin
--ignore=authentik/tasks/setup.py
--ignore=tests/e2e
-k "not test_hibp and not test_api_validate and not test_brand_subdomain"
-vv -x
'
%python_expand env PYTHONPATH=%{buildroot}%{$python_sitelib} $python -m manage test authentik
pg_ctl stop -D $pgd -m immediate
%endif
%if %{with main}
# this breaks kiwi builds without sysusers2shadow, adding sysuser-shadow to bootstrap does not help :-(
%dnl %pre -f %{name}.sysusers.pre
%dnl %{sysusers_create_package %{name} sysusers}
#post
# https://suse.slack.com/archives/C02AF8LALDA/p1770032546462819
#tmpfiles_create
%endif
%if %{with frontend}
%files frontend
%license web/LICENSE.txt
%dir %{ak_data_dir}
%dir %{ak_web_dir}
%{ak_web_dir}/dist
%endif
%if %{with python_authentik}
%files %{python_files authentik}
%license LICENSE
%{python_sitelib}/%{name}
%{python_sitelib}/%{name}-%{upstream_version}.dist-info
%{python_sitelib}/ak_lifecycle
%python_alternative %{_bindir}/ak
%python_alternative %{_bindir}/ak-manage
%dir %{ak_data_dir}
%{ak_blueprints_dir}
%dir %{ak_data_dir}/system_migrations
%{ak_data_dir}/system_migrations/*.py
%endif
%if %{with python_ak_guardian}
%files %{python_files ak-guardian}
%license packages/ak-guardian/LICENSE
%{python_sitelib}/guardian/
%{python_sitelib}/ak_guardian-%{version}.dist-info
%endif
%if %{with python_django_channels_postgres}
%files %{python_files django-channels-postgres}
# no license file at the time of writing
%{python_sitelib}/django_channels_postgres
%{python_sitelib}/django_channels_postgres-%{version}.dist-info
%endif
%if %{with python_django_dramatiq_postgres}
%files %{python_files django-dramatiq-postgres}
%{python_sitelib}/django_dramatiq_postgres
%{python_sitelib}/django_dramatiq_postgres-%{version}.dist-info
%endif
%if %{with python_django_postgres_cache}
%files %{python_files django-postgres-cache}
%{python_sitelib}/django_postgres_cache
%{python_sitelib}/django_postgres_cache-%{version}.dist-info
%endif
%if %{with server}
%files server
%license LICENSE
%{_bindir}/ak-server
%dir %{ak_conf_dir}
%config %{ak_conf_dir}/gunicorn.conf.py
%endif
%if %{with worker}
%files worker
%endif
%if %{with outposts}
%files outpost-ldap
%{_bindir}/ak-outpost-ldap
%files outpost-proxy
%{_bindir}/ak-outpost-proxy
%files outpost-rac
%{_bindir}/ak-outpost-rac
%files outpost-radius
%{_bindir}/ak-outpost-radius
%endif
%if %{with main}
%files
%license LICENSE
%{_libexecdir}/ak
%{_sysusersdir}/%{name}.conf
%{_tmpfilesdir}/%{name}.conf
%attr(0750,%{ak_user},%{ak_group}) %dir %{ak_home_dir}
%attr(0750,%{ak_user},%{ak_group}) %dir %{ak_geoip_dir}
%attr(0755,%{ak_user},%{ak_group}) %dir %ghost %{_rundir}/%{name}
%endif
%changelog