management commands: add --schema option where relevant

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
Marc 'risson' Schmitt 2023-12-04 09:26:13 +01:00
parent fc851a8eff
commit 79fc46372c
No known key found for this signature in database
GPG Key ID: 9C3FA22FABF1AA8D
17 changed files with 104 additions and 42 deletions

View File

@ -3,6 +3,7 @@ from sys import exit as sys_exit
from django.core.management.base import BaseCommand, no_translations from django.core.management.base import BaseCommand, no_translations
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from tenant_schemas_celery.scheduler import Tenant
from authentik.blueprints.models import BlueprintInstance from authentik.blueprints.models import BlueprintInstance
from authentik.blueprints.v1.importer import Importer from authentik.blueprints.v1.importer import Importer
@ -16,6 +17,8 @@ class Command(BaseCommand):
@no_translations @no_translations
def handle(self, *args, **options): def handle(self, *args, **options):
"""Apply all blueprints in order, abort when one fails to import""" """Apply all blueprints in order, abort when one fails to import"""
for tenant in Tenant.objects.all():
with tenant:
for blueprint_path in options.get("blueprints", []): for blueprint_path in options.get("blueprints", []):
content = BlueprintInstance(path=blueprint_path).retrieve() content = BlueprintInstance(path=blueprint_path).retrieve()
importer = Importer.from_string(content) importer = Importer.from_string(content)

View File

@ -1,5 +1,6 @@
"""Export blueprint of current authentik install""" """Export blueprint of current authentik install"""
from django.core.management.base import BaseCommand, no_translations from django.core.management.base import BaseCommand, no_translations
from django_tenants.management.commands import TenantWrappedCommand
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.blueprints.v1.exporter import Exporter from authentik.blueprints.v1.exporter import Exporter
@ -7,7 +8,7 @@ from authentik.blueprints.v1.exporter import Exporter
LOGGER = get_logger() LOGGER = get_logger()
class Command(BaseCommand): class TCommand(BaseCommand):
"""Export blueprint of current authentik install""" """Export blueprint of current authentik install"""
@no_translations @no_translations
@ -15,3 +16,7 @@ class Command(BaseCommand):
"""Export blueprint of current authentik install""" """Export blueprint of current authentik install"""
exporter = Exporter() exporter = Exporter()
self.stdout.write(exporter.export_to_string()) self.stdout.write(exporter.export_to_string())
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -4,6 +4,8 @@ from django.contrib.auth.management import create_permissions
from django.core.management.base import BaseCommand, no_translations from django.core.management.base import BaseCommand, no_translations
from guardian.management import create_anonymous_user from guardian.management import create_anonymous_user
from authentik.tenants.models import Tenant
class Command(BaseCommand): class Command(BaseCommand):
"""Repair missing permissions""" """Repair missing permissions"""
@ -11,6 +13,8 @@ class Command(BaseCommand):
@no_translations @no_translations
def handle(self, *args, **options): def handle(self, *args, **options):
"""Check permissions for all apps""" """Check permissions for all apps"""
for tenant in Tenant.objects.all():
with tenant:
for app in apps.get_app_configs(): for app in apps.get_app_configs():
self.stdout.write(f"Checking app {app.name} ({app.label})\n") self.stdout.write(f"Checking app {app.name} ({app.label})\n")
create_permissions(app, verbosity=0) create_permissions(app, verbosity=0)

View File

@ -2,6 +2,7 @@
from sys import exit as sys_exit from sys import exit as sys_exit
from django.core.management.base import BaseCommand, no_translations from django.core.management.base import BaseCommand, no_translations
from django_tenants.management.commands import TenantWrappedCommand
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
@ -11,7 +12,7 @@ from authentik.crypto.models import CertificateKeyPair
LOGGER = get_logger() LOGGER = get_logger()
class Command(BaseCommand): class TCommand(BaseCommand):
"""Import certificate""" """Import certificate"""
@no_translations @no_translations
@ -49,3 +50,7 @@ class Command(BaseCommand):
parser.add_argument("--certificate", type=str, required=True) parser.add_argument("--certificate", type=str, required=True)
parser.add_argument("--private-key", type=str, required=False) parser.add_argument("--private-key", type=str, required=False)
parser.add_argument("--name", type=str, required=True) parser.add_argument("--name", type=str, required=True)
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -1,5 +1,6 @@
"""SCIM Sync""" """SCIM Sync"""
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django_tenants.management.commands import TenantWrappedCommand
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.providers.scim.models import SCIMProvider from authentik.providers.scim.models import SCIMProvider
@ -8,7 +9,7 @@ from authentik.providers.scim.tasks import scim_sync
LOGGER = get_logger() LOGGER = get_logger()
class Command(BaseCommand): class TCommand(BaseCommand):
"""Run sync for an SCIM Provider""" """Run sync for an SCIM Provider"""
def add_arguments(self, parser): def add_arguments(self, parser):
@ -21,3 +22,7 @@ class Command(BaseCommand):
LOGGER.warning("Provider does not exist", name=provider_name) LOGGER.warning("Provider does not exist", name=provider_name)
continue continue
scim_sync.delay(provider.pk).get() scim_sync.delay(provider.pk).get()
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -1,11 +1,12 @@
"""authentik recovery create_admin_group""" """authentik recovery create_admin_group"""
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_tenants.management.commands import TenantWrappedCommand
from authentik.core.models import Group, User from authentik.core.models import Group, User
class Command(BaseCommand): class TCommand(BaseCommand):
"""Create admin group if the default group gets deleted""" """Create admin group if the default group gets deleted"""
help = _("Create admin group if the default group gets deleted.") help = _("Create admin group if the default group gets deleted.")
@ -28,3 +29,7 @@ class Command(BaseCommand):
) )
group.users.add(user) group.users.add(user)
self.stdout.write(f"User '{username}' successfully added to the group 'authentik Admins'.") self.stdout.write(f"User '{username}' successfully added to the group 'authentik Admins'.")
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -7,11 +7,12 @@ from django.urls import reverse
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_tenants.management.commands import TenantWrappedCommand
from authentik.core.models import Token, TokenIntents, User from authentik.core.models import Token, TokenIntents, User
class Command(BaseCommand): class TCommand(BaseCommand):
"""Create Token used to recover access""" """Create Token used to recover access"""
help = _("Create a Key which can be used to restore access to authentik.") help = _("Create a Key which can be used to restore access to authentik.")
@ -50,3 +51,7 @@ class Command(BaseCommand):
f"Store this link safely, as it will allow anyone to access authentik as {user}." f"Store this link safely, as it will allow anyone to access authentik as {user}."
) )
self.stdout.write(self.get_url(token)) self.stdout.write(self.get_url(token))
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -4,6 +4,7 @@ from io import StringIO
from django.core.management import call_command from django.core.management import call_command
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django_tenants.utils import get_public_schema_name
from authentik.core.models import Token, TokenIntents, User from authentik.core.models import Token, TokenIntents, User
@ -18,7 +19,13 @@ class TestRecovery(TestCase):
"""Test creation of a new key""" """Test creation of a new key"""
out = StringIO() out = StringIO()
self.assertEqual(len(Token.objects.all()), 0) self.assertEqual(len(Token.objects.all()), 0)
call_command("create_recovery_key", "1", self.user.username, stdout=out) call_command(
"create_recovery_key",
"1",
self.user.username,
schema=get_public_schema_name(),
stdout=out,
)
token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user) token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user)
self.assertIn(token.key, out.getvalue()) self.assertIn(token.key, out.getvalue())
self.assertEqual(len(Token.objects.all()), 1) self.assertEqual(len(Token.objects.all()), 1)
@ -27,13 +34,19 @@ class TestRecovery(TestCase):
"""Test creation of a new key (invalid)""" """Test creation of a new key (invalid)"""
out = StringIO() out = StringIO()
self.assertEqual(len(Token.objects.all()), 0) self.assertEqual(len(Token.objects.all()), 0)
call_command("create_recovery_key", "1", "foo", stderr=out) call_command("create_recovery_key", "1", "foo", schema=get_public_schema_name(), stderr=out)
self.assertIn("not found", out.getvalue()) self.assertIn("not found", out.getvalue())
def test_recovery_view(self): def test_recovery_view(self):
"""Test recovery view""" """Test recovery view"""
out = StringIO() out = StringIO()
call_command("create_recovery_key", "1", self.user.username, stdout=out) call_command(
"create_recovery_key",
"1",
self.user.username,
schema=get_public_schema_name(),
stdout=out,
)
token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user) token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user)
self.client.get(reverse("authentik_recovery:use-token", kwargs={"key": token.key})) self.client.get(reverse("authentik_recovery:use-token", kwargs={"key": token.key}))
self.assertEqual(int(self.client.session["_auth_user_id"]), token.user.pk) self.assertEqual(int(self.client.session["_auth_user_id"]), token.user.pk)
@ -46,12 +59,14 @@ class TestRecovery(TestCase):
def test_recovery_admin_group_invalid(self): def test_recovery_admin_group_invalid(self):
"""Test creation of admin group""" """Test creation of admin group"""
out = StringIO() out = StringIO()
call_command("create_admin_group", "1", stderr=out) call_command("create_admin_group", "1", schema=get_public_schema_name(), stderr=out)
self.assertIn("not found", out.getvalue()) self.assertIn("not found", out.getvalue())
def test_recovery_admin_group(self): def test_recovery_admin_group(self):
"""Test creation of admin group""" """Test creation of admin group"""
out = StringIO() out = StringIO()
call_command("create_admin_group", self.user.username, stdout=out) call_command(
"create_admin_group", self.user.username, schema=get_public_schema_name(), stdout=out
)
self.assertIn("successfully added to", out.getvalue()) self.assertIn("successfully added to", out.getvalue())
self.assertTrue(self.user.is_superuser) self.assertTrue(self.user.is_superuser)

View File

@ -2,6 +2,7 @@
from json import dumps from json import dumps
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django_tenants.management.commands import TenantWrappedCommand
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.sources.ldap.models import LDAPSource from authentik.sources.ldap.models import LDAPSource
@ -9,7 +10,7 @@ from authentik.sources.ldap.models import LDAPSource
LOGGER = get_logger() LOGGER = get_logger()
class Command(BaseCommand): class TCommand(BaseCommand):
"""Check connectivity to LDAP servers for a source""" """Check connectivity to LDAP servers for a source"""
def add_arguments(self, parser): def add_arguments(self, parser):
@ -22,3 +23,7 @@ class Command(BaseCommand):
for source in sources.order_by("slug"): for source in sources.order_by("slug"):
status = source.check_connection() status = source.check_connection()
self.stdout.write(dumps(status, indent=4)) self.stdout.write(dumps(status, indent=4))
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -1,5 +1,6 @@
"""LDAP Sync""" """LDAP Sync"""
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django_tenants.management.commands import TenantWrappedCommand
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.sources.ldap.models import LDAPSource from authentik.sources.ldap.models import LDAPSource
@ -11,7 +12,7 @@ from authentik.sources.ldap.tasks import ldap_sync_paginator
LOGGER = get_logger() LOGGER = get_logger()
class Command(BaseCommand): class TCommand(BaseCommand):
"""Run sync for an LDAP Source""" """Run sync for an LDAP Source"""
def add_arguments(self, parser): def add_arguments(self, parser):
@ -30,3 +31,7 @@ class Command(BaseCommand):
) )
for task in tasks: for task in tasks:
task() task()
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -2,13 +2,14 @@
from uuid import uuid4 from uuid import uuid4
from django.core.management.base import BaseCommand, no_translations from django.core.management.base import BaseCommand, no_translations
from django_tenants.management.commands import TenantWrappedCommand
from authentik.stages.email.models import EmailStage from authentik.stages.email.models import EmailStage
from authentik.stages.email.tasks import send_mail from authentik.stages.email.tasks import send_mail
from authentik.stages.email.utils import TemplateEmailMessage from authentik.stages.email.utils import TemplateEmailMessage
class Command(BaseCommand): class TCommand(BaseCommand):
"""Send a test-email with global settings""" """Send a test-email with global settings"""
@no_translations @no_translations
@ -40,4 +41,8 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("to", type=str) parser.add_argument("to", type=str)
parser.add_argument("-s", "--stage", type=str) parser.add_argument("-S", "--stage", type=str)
class Command(TenantWrappedCommand):
COMMAND = TCommand

View File

@ -8,7 +8,7 @@ title: Export
Requires authentik 2022.8.2 Requires authentik 2022.8.2
::: :::
To migrate existing configurations to blueprints, run `ak export_blueprint` within any authentik Worker container. This will output a blueprint for most currently created objects. Some objects will not be exported as they might have dependencies on other things. To migrate existing configurations to blueprints, run `ak export_blueprint --schema public` within any authentik Worker container. This will output a blueprint for most currently created objects. Some objects will not be exported as they might have dependencies on other things.
Exported blueprints don't use any of the YAML Tags, they just contain a list of entries as they are in the database. Exported blueprints don't use any of the YAML Tags, they just contain a list of entries as they are in the database.

View File

@ -62,9 +62,9 @@ Files are checked every 5 minutes, and will trigger an Outpost refresh if the fi
Starting with authentik 2022.9, you can also import certificates with any folder structure directly. To do this, run the following command within the worker container: Starting with authentik 2022.9, you can also import certificates with any folder structure directly. To do this, run the following command within the worker container:
```shell ```shell
ak import_certificate --certificate /certs/mycert.pem --private-key /certs/something.pem --name test ak import_certificate --schema public --certificate /certs/mycert.pem --private-key /certs/something.pem --name test
# --private-key can be omitted to only import a certificate, i.e. to trust other connections # --private-key can be omitted to only import a certificate, i.e. to trust other connections
# ak import_certificate --certificate /certs/othercert.pem --name test2 # ak import_certificate --schema public --certificate /certs/othercert.pem --name test2
``` ```
This will import the certificate into authentik under the given name. This command is idempotent, meaning you can run it via a cron-job and authentik will only update the certificate when it changes. This will import the certificate into authentik under the given name. This command is idempotent, meaning you can run it via a cron-job and authentik will only update the certificate when it changes.

View File

@ -9,19 +9,19 @@ Some hosting providers block outgoing SMTP ports, in which case you'll have to h
To test if an email stage, or the global email settings are configured correctly, you can run the following command: To test if an email stage, or the global email settings are configured correctly, you can run the following command:
``` ```
ak test_email <to address> [-s <stage name>] ak test_email --schema public <to address> [-S <stage name>]
``` ```
If you omit the `-s` parameter, the email will be sent using the global settings. Otherwise, the settings of the specified stage will be used. If you omit the `-S` parameter, the email will be sent using the global settings. Otherwise, the settings of the specified stage will be used.
To run this command with docker-compose, use To run this command with docker-compose, use
``` ```
docker-compose exec worker ak test_email [...] docker-compose exec worker ak test_email --schema public [...]
``` ```
To run this command with Kubernetes, use To run this command with Kubernetes, use
``` ```
kubectl exec -it deployment/authentik-worker -c authentik -- ak test_email [...] kubectl exec -it deployment/authentik-worker -c authentik -- ak test_email --schema public [...]
``` ```

View File

@ -5,23 +5,23 @@ title: Troubleshooting LDAP Synchronization
To troubleshoot LDAP sources, you can run the command below to run a synchronization in the foreground and see any errors or warnings that might happen directly To troubleshoot LDAP sources, you can run the command below to run a synchronization in the foreground and see any errors or warnings that might happen directly
``` ```
docker-compose run --rm worker ldap_sync *slug of the source* docker-compose run --rm worker ldap_sync --schema public *slug of the source*
``` ```
or, for Kubernetes, run or, for Kubernetes, run
``` ```
kubectl exec -it deployment/authentik-worker -c authentik -- ak ldap_sync *slug of the source* kubectl exec -it deployment/authentik-worker -c authentik -- ak ldap_sync --schema public *slug of the source*
``` ```
Starting with authentik 2023.10, you can also run command below to explicitly check the connectivity to the configured LDAP Servers: Starting with authentik 2023.10, you can also run command below to explicitly check the connectivity to the configured LDAP Servers:
``` ```
docker-compose run --rm worker ldap_check_connection *slug of the source* docker-compose run --rm worker ldap_check_connection --schema public *slug of the source*
``` ```
or, for Kubernetes, run or, for Kubernetes, run
``` ```
kubectl exec -it deployment/authentik-worker -c authentik -- ak ldap_check_connection *slug of the source* kubectl exec -it deployment/authentik-worker -c authentik -- ak ldap_check_connection --schema public *slug of the source*
``` ```

View File

@ -11,19 +11,19 @@ This recovery key will give whoever has the link direct access to your instances
To create the key, run the following command: To create the key, run the following command:
``` ```
docker-compose run --rm server create_recovery_key 10 akadmin docker-compose run --rm server create_recovery_key --schema public 10 akadmin
``` ```
For Kubernetes, run For Kubernetes, run
``` ```
kubectl exec -it deployment/authentik-worker -c authentik -- ak create_recovery_key 10 akadmin kubectl exec -it deployment/authentik-worker -c authentik -- ak create_recovery_key --schema public 10 akadmin
``` ```
or, for CLI, run or, for CLI, run
``` ```
ak create_recovery_key 10 akadmin ak create_recovery_key 10 --schema public akadmin
``` ```
This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years. This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years.

View File

@ -7,11 +7,11 @@ If all of the Admin groups have been deleted, or misconfigured during sync, you
Run the following command, where _username_ is the user you want to add to the newly created group: Run the following command, where _username_ is the user you want to add to the newly created group:
``` ```
docker-compose run --rm server create_admin_group username docker-compose run --rm server create_admin_group --schema public username
``` ```
or, for Kubernetes, run or, for Kubernetes, run
``` ```
kubectl exec -it deployment/authentik-worker -c authentik -- ak create_admin_group username kubectl exec -it deployment/authentik-worker -c authentik -- ak create_admin_group --schema public username
``` ```