Repository: goauthentik/authentik

Issue:
Backup/Restore Functionality
**Is your feature request related to a problem? Please describe.**
No.

**Describe the solution you'd like**
It's be great if there were a backup/restore function in passbook. For instance a section of the web interface which can generate a ZIP of the configuration to be downloaded. Support for uploading backups for restore would also be useful.   
The sort of functionality I have in mind is already present in Sonarr/Radarr/Lidarr, so perhaps it could be modelled on that?

As a stretch, scheduled backups would also be great, à la Sonarr/Radarr/Lidarr.

**Describe alternatives you've considered**
Backing up the volume like a pleb.

**Additional context**
N/A.



Discussion:

Resolving patch:
diff --git a/passbook/lib/tasks.py b/passbook/lib/tasks.py
deleted file mode 100644
--- a/passbook/lib/tasks.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""passbook misc tasks"""
-from django.core import management
-from structlog import get_logger
-
-from passbook.root.celery import CELERY_APP
-
-LOGGER = get_logger()
-
-
-@CELERY_APP.task()
-def backup_database():  # pragma: no cover
-    """Backup database"""
-    management.call_command("dbbackup")
-    LOGGER.info("Successfully backed up database.")
diff --git a/passbook/providers/oauth2/forms.py b/passbook/providers/oauth2/forms.py
--- a/passbook/providers/oauth2/forms.py
+++ b/passbook/providers/oauth2/forms.py
@@ -12,7 +12,7 @@
     generate_client_id,
     generate_client_secret,
 )
-from passbook.providers.oauth2.models import OAuth2Provider, ScopeMapping
+from passbook.providers.oauth2.models import JWTAlgorithms, OAuth2Provider, ScopeMapping
 
 
 class OAuth2ProviderForm(forms.ModelForm):
@@ -32,7 +32,10 @@ def __init__(self, *args, **kwargs):
 
     def clean_jwt_alg(self):
         """Ensure that when RS256 is selected, a certificate-key-pair is selected"""
-        if "rsa_key" not in self.cleaned_data:
+        if (
+            self.data["rsa_key"] == ""
+            and self.cleaned_data["jwt_alg"] == JWTAlgorithms.RS256
+        ):
             raise ValidationError(
                 _("RS256 requires a Certificate-Key-Pair to be selected.")
             )
diff --git a/passbook/root/settings.py b/passbook/root/settings.py
--- a/passbook/root/settings.py
+++ b/passbook/root/settings.py
@@ -272,7 +272,7 @@ def j_print(event: str, log_level: str = "info", **kwargs):
         "options": {"queue": "passbook_scheduled"},
     }
 }
-CELERY_CREATE_MISSING_QUEUES = True
+CELERY_TASK_CREATE_MISSING_QUEUES = True
 CELERY_TASK_DEFAULT_QUEUE = "passbook"
 CELERY_BROKER_URL = (
     f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
@@ -284,24 +284,25 @@ def j_print(event: str, log_level: str = "info", **kwargs):
 )
 
 # Database backup
-if CONFIG.y("postgresql.backup"):
+DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
+DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
+DBBACKUP_CONNECTOR_MAPPING = {
+    "django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector"
+}
+if CONFIG.y("postgresql.s3_backup"):
     DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
-    DBBACKUP_CONNECTOR_MAPPING = {
-        "django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector"
+    DBBACKUP_STORAGE_OPTIONS = {
+        "access_key": CONFIG.y("postgresql.s3_backup.access_key"),
+        "secret_key": CONFIG.y("postgresql.s3_backup.secret_key"),
+        "bucket_name": CONFIG.y("postgresql.s3_backup.bucket"),
+        "region_name": CONFIG.y("postgresql.s3_backup.region", "eu-central-1"),
+        "default_acl": "private",
+        "endpoint_url": CONFIG.y("postgresql.s3_backup.host"),
     }
-    AWS_ACCESS_KEY_ID = CONFIG.y("postgresql.backup.access_key")
-    AWS_SECRET_ACCESS_KEY = CONFIG.y("postgresql.backup.secret_key")
-    AWS_STORAGE_BUCKET_NAME = CONFIG.y("postgresql.backup.bucket")
-    AWS_S3_ENDPOINT_URL = CONFIG.y("postgresql.backup.host")
-    AWS_DEFAULT_ACL = None
     j_print(
-        "Database backup to S3 is configured.", host=CONFIG.y("postgresql.backup.host")
+        "Database backup to S3 is configured.",
+        host=CONFIG.y("postgresql.s3_backup.host"),
     )
-    # Add automatic task to backup
-    CELERY_BEAT_SCHEDULE["db_backup"] = {
-        "task": "passbook.lib.tasks.backup_database",
-        "schedule": crontab(minute=0, hour=0),  # Run every day, midnight
-    }
 
 # Sentry integration
 _ERROR_REPORTING = CONFIG.y_bool("error_reporting.enabled", False)
@@ -400,6 +401,7 @@ def j_print(event: str, log_level: str = "info", **kwargs):
     "urllib3": "WARNING",
     "websockets": "WARNING",
     "daphne": "WARNING",
+    "dbbackup": "ERROR",
 }
 for handler_name, level in _LOGGING_HANDLER_MAP.items():
     # pyright: reportGeneralTypeIssues=false


Test patch: