diff --git a/authentik/lib/config.py b/authentik/lib/config.py index dd5500f8e..ec96b0e9b 100644 --- a/authentik/lib/config.py +++ b/authentik/lib/config.py @@ -34,6 +34,7 @@ REDIS_ENV_KEYS = [ f"{ENV_PREFIX}_REDIS__TLS_REQS", ] +# Old key -> new key DEPRECATIONS = { "redis.broker_url": "broker.url", "redis.broker_transport_options": "broker.transport_options", @@ -200,12 +201,13 @@ class ConfigLoader: root[key] = value return root - def refresh(self, key: str): + def refresh(self, key: str, default=None, sep=".") -> Any: """Update a single value""" - attr: Attr = get_path_from_dict(self.raw, key) + attr: Attr = get_path_from_dict(self.raw, key, sep=sep, default=Attr(default)) if attr.source_type != Attr.Source.URI: - return + return attr.value attr.value = self.parse_uri(attr.source).value + return attr.value def parse_uri(self, value: str) -> Attr: """Parse string values which start with a URI""" diff --git a/authentik/lib/default.yml b/authentik/lib/default.yml index 297ea1124..4b86bd37e 100644 --- a/authentik/lib/default.yml +++ b/authentik/lib/default.yml @@ -53,9 +53,6 @@ cache: # result_backend: # url: "" -paths: - media: ./media - debug: false remote_debug: false @@ -125,3 +122,20 @@ web: worker: concurrency: 2 + +storage: + media: + backend: file # or s3 + file: + path: ./media + s3: + # How to talk to s3 + # region: "us-east-1" + # use_ssl: True + # endpoint: "https://s3.us-east-1.amazonaws.com" + # access_key: "" + # secret_key: "" + # bucket_name: "authentik-media" + # How to render file URLs + # custom_domain: null + secure_urls: True diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 0f4bb5b9d..60f08eacb 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -17,10 +17,6 @@ from authentik.lib.utils.reflection import get_env from authentik.stages.password import BACKEND_APP_PASSWORD, BACKEND_INBUILT, BACKEND_LDAP BASE_DIR = Path(__file__).absolute().parent.parent.parent -STATICFILES_DIRS = [BASE_DIR / Path("web")] -MEDIA_ROOT = BASE_DIR / Path("media") -DEFAULT_FILE_STORAGE = "django_tenants.files.storage.TenantFileSystemStorage" -MULTITENANT_RELATIVE_MEDIA_ROOT = "tenants" DEBUG = CONFIG.get_bool("debug") SECRET_KEY = CONFIG.get("secret_key") @@ -392,8 +388,54 @@ if _ERROR_REPORTING: # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.1/howto/static-files/ +STATICFILES_DIRS = [BASE_DIR / Path("web")] STATIC_URL = "/static/" -MEDIA_URL = "/media/" + +STORAGES = { + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, +} + + +# Media files +if CONFIG.get("storage.media.backend", "file") == "s3": + STORAGES["default"] = { + "BACKEND": "authentik.root.storages.S3Storage", + "OPTIONS": { + # How to talk to S3 + "session_profile": CONFIG.get("storage.media.s3.session_profile", None), + "access_key": CONFIG.get("storage.media.s3.access_key", None), + "secret_key": CONFIG.get("storage.media.s3.secret_key", None), + "security_token": CONFIG.get("storage.media.s3.security_token", None), + "region_name": CONFIG.get("storage.media.s3.region", None), + "use_ssl": CONFIG.get_bool("storage.media.s3.use_ssl", True), + "endpoint_url": CONFIG.get("storage.media.s3.endpoint", None), + "bucket_name": CONFIG.get("storage.media.s3.bucket_name"), + "default_acl": "private", + "querystring_auth": True, + "signature_version": "s3v4", + "file_overwrite": False, + "location": "media", + "url_protocol": "https:" + if CONFIG.get("storage.media.s3.secure_urls", True) + else "http:", + "custom_domain": CONFIG.get("storage.media.s3.custom_domain", None), + }, + } +# Fallback on file storage backend +else: + STORAGES["default"] = { + "BACKEND": "authentik.root.storages.FileStorage", + "OPTIONS": { + "location": Path(CONFIG.get("storage.media.file.path")), + "base_url": "/media/", + }, + } + # Compatibility for apps not supporting top-level STORAGES + # such as django-tenants + MEDIA_ROOT = STORAGES["default"]["OPTIONS"]["location"] + MEDIA_URL = STORAGES["default"]["OPTIONS"]["base_url"] TEST = False TEST_RUNNER = "authentik.root.test_runner.PytestTestRunner" diff --git a/authentik/root/storages.py b/authentik/root/storages.py new file mode 100644 index 000000000..6fd87e336 --- /dev/null +++ b/authentik/root/storages.py @@ -0,0 +1,110 @@ +"""authentik storage backends""" +import os + +from django.conf import settings +from django.core.exceptions import SuspiciousOperation +from django.core.files.storage import FileSystemStorage +from django.db import connection +from storages.backends.s3 import S3Storage as BaseS3Storage +from storages.utils import clean_name, safe_join + +from authentik.lib.config import CONFIG + + +class FileStorage(FileSystemStorage): + @property + def base_location(self): + return os.path.join( + self._value_or_setting(self._location, settings.MEDIA_ROOT), connection.schema_name + ) + + @property + def location(self): + return os.path.abspath(self.base_location) + + @property + def base_url(self): + if self._base_url is not None and not self._base_url.endswith("/"): + self._base_url += "/" + return f"{self._base_url}/{connection.schema_name}/" + + +# pylint: disable=abstract-method +class S3Storage(BaseS3Storage): + """S3 storage backend""" + + @property + def session_profile(self) -> str | None: + """Get session profile""" + return CONFIG.refresh("storage.media.s3.session_profile", None) + + @session_profile.setter + def session_profile(self, value: str): + pass + + @property + def access_key(self) -> str | None: + """Get access key""" + return CONFIG.refresh("storage.media.s3.access_key", None) + + @access_key.setter + def access_key(self, value: str): + pass + + @property + def secret_key(self) -> str | None: + """Get secret key""" + return CONFIG.refresh("storage.media.s3.secret_key", None) + + @secret_key.setter + def secret_key(self, value: str): + pass + + @property + def security_token(self) -> str | None: + """Get security token""" + return CONFIG.refresh("storage.media.s3.security_token", None) + + @security_token.setter + def security_token(self, value: str): + pass + + def _normalize_name(self, name): + try: + return safe_join(self.location, connection.schema_name, name) + except ValueError: + raise SuspiciousOperation("Attempted access to '%s' denied." % name) + + # This is a fix for https://github.com/jschneier/django-storages/pull/839 + # pylint: disable=arguments-differ,no-member + def url(self, name, parameters=None, expire=None, http_method=None): + # Preserve the trailing slash after normalizing the path. + name = self._normalize_name(clean_name(name)) + params = parameters.copy() if parameters else {} + if expire is None: + expire = self.querystring_expire + + params["Bucket"] = self.bucket.name + params["Key"] = name + url = self.bucket.meta.client.generate_presigned_url( + "get_object", + Params=params, + ExpiresIn=expire, + HttpMethod=http_method, + ) + + if self.custom_domain: + # Key parameter can't be empty. Use "/" and remove it later. + params["Key"] = "/" + root_url_signed = self.bucket.meta.client.generate_presigned_url( + "get_object", Params=params, ExpiresIn=expire + ) + # Remove signing parameter and previously added key "/". + root_url = self._strip_signing_parameters(root_url_signed)[:-1] + # Replace bucket domain with custom domain. + custom_url = "{}//{}/".format(self.url_protocol, self.custom_domain) + url = url.replace(root_url, custom_url) + + if self.querystring_auth: + return url + return self._strip_signing_parameters(url) diff --git a/blueprints/schema.json b/blueprints/schema.json index b76ed74bb..47656f070 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -5416,8 +5416,22 @@ "title": "Icon" }, "provider_type": { - "type": [], - "enum": [], + "type": "string", + "enum": [ + "apple", + "azuread", + "discord", + "facebook", + "github", + "google", + "mailcow", + "openidconnect", + "okta", + "patreon", + "reddit", + "twitch", + "twitter" + ], "title": "Provider type" }, "request_token_url": { diff --git a/internal/config/struct.go b/internal/config/struct.go index e7be9733e..2931c7a7a 100644 --- a/internal/config/struct.go +++ b/internal/config/struct.go @@ -2,7 +2,7 @@ package config type Config struct { // Core specific config - Paths PathsConfig `yaml:"paths"` + Storage StorageConfig `yaml:"storage"` LogLevel string `yaml:"log_level" env:"AUTHENTIK_LOG_LEVEL"` ErrorReporting ErrorReportingConfig `yaml:"error_reporting"` Redis RedisConfig `yaml:"redis"` @@ -45,8 +45,17 @@ type ListenConfig struct { TrustedProxyCIDRs []string `yaml:"trusted_proxy_cidrs" env:"AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS"` } -type PathsConfig struct { - Media string `yaml:"media"` +type StorageConfig struct { + Media StorageMediaConfig `yaml:"media"` +} + +type StorageMediaConfig struct { + Backend string `yaml:"backend" env:"AUTHENTIK_STORAGE_MEDIA_BACKEND"` + File StorageFileConfig `yaml:"file"` +} + +type StorageFileConfig struct { + Path string `yaml:"path" env:"AUTHENTIK_STORAGE_MEDIA_FILE_PATH"` } type ErrorReportingConfig struct { diff --git a/internal/web/static.go b/internal/web/static.go index c4f6dbcf4..c4c79efe0 100644 --- a/internal/web/static.go +++ b/internal/web/static.go @@ -6,6 +6,7 @@ import ( "github.com/go-http-utils/etag" "github.com/gorilla/mux" + "goauthentik.io/internal/config" "goauthentik.io/internal/constants" "goauthentik.io/internal/utils/web" @@ -17,8 +18,6 @@ func (ws *WebServer) configureStatic() { statRouter.Use(ws.staticHeaderMiddleware) indexLessRouter := statRouter.NewRoute().Subrouter() indexLessRouter.Use(web.DisableIndex) - // Media files, always local - fs := http.FileServer(http.Dir(config.Get().Paths.Media)) distFs := http.FileServer(http.Dir("./web/dist")) distHandler := http.StripPrefix("/static/dist/", distFs) authentikHandler := http.StripPrefix("/static/authentik/", http.FileServer(http.Dir("./web/authentik"))) @@ -35,7 +34,11 @@ func (ws *WebServer) configureStatic() { indexLessRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs)) indexLessRouter.PathPrefix("/if/user/assets").Handler(http.StripPrefix("/if/user", distFs)) - indexLessRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fs)) + // Media files, if backend is file + if config.Get().Storage.Media.Backend == "file" { + fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path)) + indexLessRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fsMedia)) + } statRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", helpHandler)) statRouter.PathPrefix("/help").Handler(http.RedirectHandler("/if/help/", http.StatusMovedPermanently)) diff --git a/poetry.lock b/poetry.lock index 30df16a27..eca348731 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "aiohttp" version = "3.9.1" description = "Async http client/server framework (asyncio)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -99,6 +100,7 @@ speedups = ["Brotli", "aiodns", "brotlicffi"] name = "aiohttp-retry" version = "2.8.3" description = "Simple retry client for aiohttp" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -113,6 +115,7 @@ aiohttp = "*" name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -127,6 +130,7 @@ frozenlist = ">=1.1.0" name = "amqp" version = "5.2.0" description = "Low-level AMQP client for Python (fork of amqplib)." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -141,6 +145,7 @@ vine = ">=5.0.0,<6.0.0" name = "annotated-types" version = "0.6.0" description = "Reusable constraint types to use with typing.Annotated" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -152,6 +157,7 @@ files = [ name = "anyio" version = "4.2.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -172,6 +178,7 @@ trio = ["trio (>=0.23)"] name = "argon2-cffi" version = "23.1.0" description = "Argon2 for Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -192,6 +199,7 @@ typing = ["mypy"] name = "argon2-cffi-bindings" version = "21.2.0" description = "Low-level CFFI bindings for Argon2" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -229,6 +237,7 @@ tests = ["pytest"] name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -243,6 +252,7 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "asn1crypto" version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" optional = false python-versions = "*" files = [ @@ -254,6 +264,7 @@ files = [ name = "astroid" version = "3.0.2" description = "An abstract syntax tree for Python with inference support." +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -265,6 +276,7 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -283,6 +295,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "autobahn" version = "23.6.2" description = "WebSocket client & server library, WAMP real-time framework" +category = "main" optional = false python-versions = ">=3.9" files = [ @@ -311,6 +324,7 @@ xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8. name = "automat" version = "22.10.0" description = "Self-service finite-state machines for the programmer on the go." +category = "main" optional = false python-versions = "*" files = [ @@ -329,6 +343,7 @@ visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"] name = "autopep8" version = "2.0.4" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -343,6 +358,7 @@ pycodestyle = ">=2.10.0" name = "bandit" version = "1.7.6" description = "Security oriented static analyser for python code." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -366,6 +382,7 @@ yaml = ["PyYAML"] name = "bcrypt" version = "4.1.2" description = "Modern password hashing for your software and your servers" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -406,6 +423,7 @@ typecheck = ["mypy"] name = "billiard" version = "4.2.0" description = "Python multiprocessing fork with improvements and bugfixes" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -417,6 +435,7 @@ files = [ name = "black" version = "23.12.0" description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -457,10 +476,51 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "boto3" +version = "1.33.12" +description = "The AWS SDK for Python" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "boto3-1.33.12-py3-none-any.whl", hash = "sha256:475efcff30401041e9c348e20613eca90ab14a224e2f978ca80de98ba3499435"}, + {file = "boto3-1.33.12.tar.gz", hash = "sha256:2225edaea2fa17274f62707c12d9f7803c998af7089fe8a1ec8e4f1ebf47677e"}, +] + +[package.dependencies] +botocore = ">=1.33.12,<1.34.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.8.2,<0.9.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.33.12" +description = "Low-level, data-driven core of boto 3." +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "botocore-1.33.12-py3-none-any.whl", hash = "sha256:48b9cfb9c5f7f9634a71782f16a324acb522b65856ad46be69efe04c3322b23c"}, + {file = "botocore-1.33.12.tar.gz", hash = "sha256:067c94fa88583c04ae897d48a11d2be09f280363b8e794b82d78d631d3a3e910"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.19.17)"] + [[package]] name = "bump2version" version = "1.0.1" description = "Version-bump your software with a single command!" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -472,6 +532,7 @@ files = [ name = "cachetools" version = "5.3.2" description = "Extensible memoizing collections and decorators" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -483,6 +544,7 @@ files = [ name = "cbor2" version = "5.5.1" description = "CBOR (de)serializer with extensive tag support" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -534,6 +596,7 @@ test = ["coverage (>=7)", "hypothesis", "pytest"] name = "celery" version = "5.3.6" description = "Distributed Task Queue." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -589,6 +652,7 @@ zstd = ["zstandard (==0.22.0)"] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -600,6 +664,7 @@ files = [ name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -664,6 +729,7 @@ pycparser = "*" name = "channels" version = "4.0.0" description = "Brings async, event-driven capabilities to Django 3.2 and up." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -684,6 +750,7 @@ tests = ["async-timeout", "coverage (>=4.5,<5.0)", "pytest", "pytest-asyncio", " name = "channels-redis" version = "4.1.0" description = "Redis-backed ASGI channel layer implementation" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -705,6 +772,7 @@ tests = ["async-timeout", "cryptography (>=1.3.0)", "pytest", "pytest-asyncio", name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -804,6 +872,7 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -818,6 +887,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-didyoumean" version = "0.3.0" description = "Enables git-like *did-you-mean* feature in click" +category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -832,6 +902,7 @@ click = ">=7" name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +category = "main" optional = false python-versions = "*" files = [ @@ -849,6 +920,7 @@ dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] name = "click-repl" version = "0.3.0" description = "REPL plugin for Click" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -867,6 +939,7 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] name = "codespell" version = "2.2.6" description = "Codespell" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -884,6 +957,7 @@ types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -895,6 +969,7 @@ files = [ name = "constantly" version = "23.10.4" description = "Symbolic constants in Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -906,6 +981,7 @@ files = [ name = "coverage" version = "7.3.3" description = "Code coverage measurement for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -970,6 +1046,7 @@ toml = ["tomli"] name = "cryptography" version = "41.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1015,6 +1092,7 @@ test-randomorder = ["pytest-randomly"] name = "dacite" version = "1.8.1" description = "Simple creation of data classes from dictionaries." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1028,6 +1106,7 @@ dev = ["black", "coveralls", "mypy", "pre-commit", "pylint", "pytest (>=5)", "py name = "daphne" version = "4.0.0" description = "Django ASGI (HTTP/WebSocket) server" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1047,6 +1126,7 @@ tests = ["django", "hypothesis", "pytest", "pytest-asyncio"] name = "debugpy" version = "1.8.0" description = "An implementation of the Debug Adapter Protocol for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1074,6 +1154,7 @@ files = [ name = "deepmerge" version = "1.1.0" description = "a toolset to deeply merge python dictionaries." +category = "main" optional = false python-versions = "*" files = [ @@ -1085,6 +1166,7 @@ files = [ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1096,6 +1178,7 @@ files = [ name = "dill" version = "0.3.7" description = "serialize all of Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1110,6 +1193,7 @@ graph = ["objgraph (>=1.7.2)"] name = "django" version = "4.2.8" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1130,6 +1214,7 @@ bcrypt = ["bcrypt"] name = "django-filter" version = "23.5" description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1144,6 +1229,7 @@ Django = ">=3.2" name = "django-guardian" version = "2.4.0" description = "Implementation of per object permissions for Django." +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1158,6 +1244,7 @@ Django = ">=2.2" name = "django-model-utils" version = "4.3.1" description = "Django model mixins and utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1172,6 +1259,7 @@ Django = ">=3.2" name = "django-prometheus" version = "2.3.1" description = "Django middlewares to monitor your application with Prometheus.io." +category = "main" optional = false python-versions = "*" files = [ @@ -1186,6 +1274,7 @@ prometheus-client = ">=0.7" name = "django-redis" version = "5.4.0" description = "Full featured redis cache backend for Django." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1204,6 +1293,7 @@ hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] name = "django-silk" version = "5.0.4" description = "Silky smooth profiling for the Django Framework" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1217,10 +1307,36 @@ Django = ">=3.2" gprof2dot = ">=2017.09.19" sqlparse = "*" +[[package]] +name = "django-storages" +version = "1.14.2" +description = "Support for many storage backends in Django" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-storages-1.14.2.tar.gz", hash = "sha256:51b36af28cc5813b98d5f3dfe7459af638d84428c8df4a03990c7d74d1bea4e5"}, + {file = "django_storages-1.14.2-py3-none-any.whl", hash = "sha256:1db759346b52ada6c2efd9f23d8241ecf518813eb31db9e2589207174f58f6ad"}, +] + +[package.dependencies] +boto3 = {version = ">=1.4.4", optional = true, markers = "extra == \"s3\""} +Django = ">=3.2" + +[package.extras] +azure = ["azure-core (>=1.13)", "azure-storage-blob (>=12)"] +boto3 = ["boto3 (>=1.4.4)"] +dropbox = ["dropbox (>=7.2.1)"] +google = ["google-cloud-storage (>=1.27)"] +libcloud = ["apache-libcloud"] +s3 = ["boto3 (>=1.4.4)"] +sftp = ["paramiko (>=1.15)"] + [[package]] name = "django-tenants" version = "3.5.0" description = "Tenant support for Django using PostgreSQL schemas." +category = "main" optional = false python-versions = "*" files = [] @@ -1239,6 +1355,7 @@ resolved_reference = "52cf8f61bae62f6e89309ccc86193c82ab075def" name = "djangorestframework" version = "3.14.0" description = "Web APIs for Django, made easy." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1254,6 +1371,7 @@ pytz = "*" name = "djangorestframework-guardian" version = "0.3.0" description = "django-guardian support for Django REST Framework" +category = "main" optional = false python-versions = "*" files = [ @@ -1270,6 +1388,7 @@ djangorestframework = "*" name = "dnspython" version = "2.4.2" description = "DNS toolkit" +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1289,6 +1408,7 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"] name = "docker" version = "7.0.0" description = "A Python library for the Docker Engine API." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1310,6 +1430,7 @@ websockets = ["websocket-client (>=1.3.0)"] name = "drf-jsonschema-serializer" version = "2.0.0" description = "JSON Schema support for Django REST Framework" +category = "dev" optional = false python-versions = "*" files = [ @@ -1333,6 +1454,7 @@ tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[co name = "drf-spectacular" version = "0.27.0" description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1356,6 +1478,7 @@ sidecar = ["drf-spectacular-sidecar"] name = "dumb-init" version = "1.2.5.post1" description = "Simple wrapper script which proxies signals to a child" +category = "main" optional = false python-versions = "*" files = [ @@ -1370,6 +1493,7 @@ files = [ name = "duo-client" version = "5.2.0" description = "Reference client for Duo Security APIs" +category = "main" optional = false python-versions = "*" files = [ @@ -1385,6 +1509,7 @@ six = "*" name = "email-validator" version = "2.1.0.post1" description = "A robust email address syntax and deliverability validation library." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1400,6 +1525,7 @@ idna = ">=2.0.0" name = "facebook-sdk" version = "3.1.0" description = "This client library is designed to support the Facebook Graph API and the official Facebook JavaScript SDK, which is the canonical way to implement Facebook authentication." +category = "main" optional = false python-versions = "*" files = [ @@ -1414,6 +1540,7 @@ requests = "*" name = "flower" version = "2.0.1" description = "Celery Flower" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1432,6 +1559,7 @@ tornado = ">=5.0.0,<7.0.0" name = "freezegun" version = "1.3.1" description = "Let your Python tests travel through time" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1446,6 +1574,7 @@ python-dateutil = ">=2.7" name = "frozenlist" version = "1.4.1" description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1532,6 +1661,7 @@ files = [ name = "geoip2" version = "4.8.0" description = "MaxMind GeoIP2 API" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1552,6 +1682,7 @@ test = ["mocket (>=3.11.1)"] name = "gitdb" version = "4.0.11" description = "Git Object Database" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1566,6 +1697,7 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.40" description = "GitPython is a Python library used to interact with Git repositories" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1583,6 +1715,7 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre name = "google-auth" version = "2.25.2" description = "Google Authentication Library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1606,6 +1739,7 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] name = "gprof2dot" version = "2022.7.29" description = "Generate a dot graph from the output of several profilers." +category = "dev" optional = false python-versions = ">=2.7" files = [ @@ -1617,6 +1751,7 @@ files = [ name = "gunicorn" version = "21.2.0" description = "WSGI HTTP Server for UNIX" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1637,6 +1772,7 @@ tornado = ["tornado (>=0.2)"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1648,6 +1784,7 @@ files = [ name = "httptools" version = "0.6.1" description = "A collection of framework independent HTTP protocol utils." +category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -1696,6 +1833,7 @@ test = ["Cython (>=0.29.24,<0.30.0)"] name = "humanize" version = "4.9.0" description = "Python humanize utilities" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1710,6 +1848,7 @@ tests = ["freezegun", "pytest", "pytest-cov"] name = "hyperlink" version = "21.0.0" description = "A featureful, immutable, and correct URL for Python." +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1724,6 +1863,7 @@ idna = ">=2.5" name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1735,6 +1875,7 @@ files = [ name = "importlib-metadata" version = "7.0.0" description = "Read metadata from Python packages" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1754,6 +1895,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "incremental" version = "22.10.0" description = "\"A small library that versions your Python projects.\"" +category = "main" optional = false python-versions = "*" files = [ @@ -1769,6 +1911,7 @@ scripts = ["click (>=6.0)", "twisted (>=16.4.0)"] name = "inflection" version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1780,6 +1923,7 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1791,6 +1935,7 @@ files = [ name = "isort" version = "5.13.2" description = "A Python utility / library to sort Python imports." +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1805,6 +1950,7 @@ colors = ["colorama (>=0.4.6)"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1818,10 +1964,23 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + [[package]] name = "jsonpatch" version = "1.33" description = "Apply JSON-Patches (RFC 6902)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ @@ -1836,6 +1995,7 @@ jsonpointer = ">=1.9" name = "jsonpointer" version = "2.4" description = "Identify specific nodes in a JSON document (RFC 6901)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ @@ -1847,6 +2007,7 @@ files = [ name = "jsonschema" version = "4.20.0" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1868,6 +2029,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jsonschema-specifications" version = "2023.11.2" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1882,6 +2044,7 @@ referencing = ">=0.31.0" name = "kombu" version = "5.3.4" description = "Messaging library for Python." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1914,6 +2077,7 @@ zookeeper = ["kazoo (>=2.8.0)"] name = "kubernetes" version = "27.2.0" description = "Kubernetes python client" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1931,7 +2095,7 @@ requests = "*" requests-oauthlib = "*" six = ">=1.9.0" urllib3 = ">=1.24.2" -websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" [package.extras] adal = ["adal (>=1.0.2)"] @@ -1940,6 +2104,7 @@ adal = ["adal (>=1.0.2)"] name = "ldap3" version = "2.9.1" description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library" +category = "main" optional = false python-versions = "*" files = [ @@ -1954,6 +2119,7 @@ pyasn1 = ">=0.4.6" name = "lxml" version = "4.9.3" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -2061,6 +2227,7 @@ source = ["Cython (>=0.29.35)"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2085,6 +2252,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2154,6 +2322,7 @@ files = [ name = "maxminddb" version = "2.5.1" description = "Reader for the MaxMind DB format" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2214,6 +2383,7 @@ setuptools = ">=68.2.2" name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2225,6 +2395,7 @@ files = [ name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2236,6 +2407,7 @@ files = [ name = "msgpack" version = "1.0.7" description = "MessagePack serializer" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2301,6 +2473,7 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2384,6 +2557,7 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2395,6 +2569,7 @@ files = [ name = "netaddr" version = "0.9.0" description = "A network address manipulation library for Python" +category = "dev" optional = false python-versions = "*" files = [ @@ -2406,6 +2581,7 @@ files = [ name = "oauthlib" version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2422,6 +2598,7 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "opencontainers" version = "0.0.14" description = "Python module for oci specifications" +category = "main" optional = false python-versions = "*" files = [ @@ -2432,6 +2609,7 @@ files = [ name = "outcome" version = "1.3.0.post0" description = "Capture the outcome of Python function calls." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2446,6 +2624,7 @@ attrs = ">=19.2.0" name = "packaging" version = "23.2" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2457,6 +2636,7 @@ files = [ name = "paramiko" version = "3.4.0" description = "SSH2 protocol library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2478,6 +2658,7 @@ invoke = ["invoke (>=2.0)"] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2489,6 +2670,7 @@ files = [ name = "pbr" version = "6.0.0" description = "Python Build Reasonableness" +category = "dev" optional = false python-versions = ">=2.6" files = [ @@ -2500,6 +2682,7 @@ files = [ name = "pdoc" version = "14.2.0" description = "API Documentation for Python Projects" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2519,6 +2702,7 @@ dev = ["hypothesis", "mypy", "pdoc-pyo3-sample-library (==1.0.11)", "pygments (> name = "platformdirs" version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2534,6 +2718,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2549,6 +2734,7 @@ testing = ["pytest", "pytest-benchmark"] name = "prometheus-client" version = "0.19.0" description = "Python client for the Prometheus monitoring system." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2563,6 +2749,7 @@ twisted = ["twisted"] name = "prompt-toolkit" version = "3.0.43" description = "Library for building powerful interactive command lines in Python" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -2577,6 +2764,7 @@ wcwidth = "*" name = "psycopg" version = "3.1.15" description = "PostgreSQL database adapter for Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2601,6 +2789,7 @@ test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6 name = "psycopg-c" version = "3.1.15" description = "PostgreSQL database adapter for Python -- C optimisation distribution" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2611,6 +2800,7 @@ files = [ name = "pyasn1" version = "0.5.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2622,6 +2812,7 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2636,6 +2827,7 @@ pyasn1 = ">=0.4.6,<0.6.0" name = "pycodestyle" version = "2.11.1" description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2647,6 +2839,7 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2658,6 +2851,7 @@ files = [ name = "pycryptodome" version = "3.19.0" description = "Cryptographic library for Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2699,6 +2893,7 @@ files = [ name = "pydantic" version = "2.5.2" description = "Data validation using Python type hints" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2719,6 +2914,7 @@ email = ["email-validator (>=2.0.0)"] name = "pydantic-core" version = "2.14.5" description = "" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2836,6 +3032,7 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" name = "pydantic-scim" version = "0.0.8" description = "Pydantic types for SCIM" +category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -2853,6 +3050,7 @@ pydantic = [ name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2868,6 +3066,7 @@ windows-terminal = ["colorama (>=0.4.6)"] name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2885,6 +3084,7 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pylint" version = "3.0.3" description = "python code static checker" +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -2895,7 +3095,10 @@ files = [ [package.dependencies] astroid = ">=3.0.1,<=3.1.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +dill = [ + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, +] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" @@ -2909,6 +3112,7 @@ testutils = ["gitpython (>3)"] name = "pylint-django" version = "2.5.5" description = "A Pylint plugin to help Pylint understand the Django web framework" +category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -2927,6 +3131,7 @@ with-django = ["Django (>=2.2)"] name = "pylint-plugin-utils" version = "0.8.2" description = "Utilities and helpers for writing Pylint plugins" +category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -2941,6 +3146,7 @@ pylint = ">=1.7" name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2967,6 +3173,7 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyopenssl" version = "23.3.0" description = "Python wrapper module around the OpenSSL library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2985,6 +3192,7 @@ test = ["flaky", "pretend", "pytest (>=3.0.1)"] name = "pyrad" version = "2.4" description = "RADIUS tools" +category = "dev" optional = false python-versions = "*" files = [ @@ -3000,6 +3208,7 @@ six = "*" name = "pysocks" version = "1.7.1" description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3012,6 +3221,7 @@ files = [ name = "pytest" version = "7.4.3" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3032,6 +3242,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-django" version = "4.7.0" description = "A Django plugin for pytest." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3050,6 +3261,7 @@ testing = ["Django", "django-configurations (>=2.0)"] name = "pytest-github-actions-annotate-failures" version = "0.2.0" description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3064,6 +3276,7 @@ pytest = ">=4.0.0" name = "pytest-randomly" version = "3.15.0" description = "Pytest plugin to randomly order tests and control random.seed." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3078,6 +3291,7 @@ pytest = "*" name = "pytest-timeout" version = "2.2.0" description = "pytest plugin to abort hanging tests" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3092,6 +3306,7 @@ pytest = ">=5.0.0" name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3106,6 +3321,7 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3120,6 +3336,7 @@ cli = ["click (>=5.0)"] name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" files = [ @@ -3131,6 +3348,7 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" +category = "main" optional = false python-versions = "*" files = [ @@ -3154,6 +3372,7 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3213,6 +3432,7 @@ files = [ name = "redis" version = "5.0.1" description = "Python client for Redis database and key-value store" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3228,6 +3448,7 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" name = "referencing" version = "0.32.0" description = "JSON Referencing + Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3243,6 +3464,7 @@ rpds-py = ">=0.7.0" name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3264,6 +3486,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-mock" version = "1.11.0" description = "Mock out responses from the requests package" +category = "dev" optional = false python-versions = "*" files = [ @@ -3283,6 +3506,7 @@ test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "tes name = "requests-oauthlib" version = "1.3.1" description = "OAuthlib authentication support for Requests." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3301,6 +3525,7 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "rich" version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -3319,6 +3544,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "rpds-py" version = "0.15.2" description = "Python bindings to Rust's persistent data structures (rpds)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3427,6 +3653,7 @@ files = [ name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" +category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -3441,6 +3668,7 @@ pyasn1 = ">=0.1.3" name = "ruff" version = "0.1.8" description = "An extremely fast Python linter and code formatter, written in Rust." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3463,10 +3691,29 @@ files = [ {file = "ruff-0.1.8.tar.gz", hash = "sha256:f7ee467677467526cfe135eab86a40a0e8db43117936ac4f9b469ce9cdb3fb62"}, ] +[[package]] +name = "s3transfer" +version = "0.8.2" +description = "An Amazon S3 Transfer Manager" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "s3transfer-0.8.2-py3-none-any.whl", hash = "sha256:c9e56cbe88b28d8e197cf841f1f0c130f246595e77ae5b5a05b69fe7cb83de76"}, + {file = "s3transfer-0.8.2.tar.gz", hash = "sha256:368ac6876a9e9ed91f6bc86581e319be08188dc60d50e0d56308ed5765446283"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + [[package]] name = "selenium" version = "4.16.0" description = "" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3484,6 +3731,7 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]} name = "sentry-sdk" version = "1.39.1" description = "Python client for Sentry (https://sentry.io)" +category = "main" optional = false python-versions = "*" files = [ @@ -3529,6 +3777,7 @@ tornado = ["tornado (>=5)"] name = "service-identity" version = "23.1.0" description = "Service identity verification for pyOpenSSL & cryptography." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3553,6 +3802,7 @@ tests = ["coverage[toml] (>=5.0.2)", "pytest"] name = "setuptools" version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3569,6 +3819,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -3580,6 +3831,7 @@ files = [ name = "smmap" version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3591,6 +3843,7 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3602,6 +3855,7 @@ files = [ name = "sortedcontainers" version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "dev" optional = false python-versions = "*" files = [ @@ -3613,6 +3867,7 @@ files = [ name = "sqlparse" version = "0.4.4" description = "A non-validating SQL parser." +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -3629,6 +3884,7 @@ test = ["pytest", "pytest-cov"] name = "stevedore" version = "5.1.0" description = "Manage dynamic plugins for Python applications" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3643,6 +3899,7 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" name = "structlog" version = "23.2.0" description = "Structured Logging for Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3660,6 +3917,7 @@ typing = ["mypy (>=1.4)", "rich", "twisted"] name = "swagger-spec-validator" version = "3.0.3" description = "Validation of Swagger specifications" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3676,6 +3934,7 @@ typing-extensions = "*" name = "tenant-schemas-celery" version = "2.2.0" description = "Celery integration for django-tenant-schemas and django-tenants" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3689,6 +3948,7 @@ celery = "*" name = "tomlkit" version = "0.12.3" description = "Style preserving TOML library" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3700,6 +3960,7 @@ files = [ name = "tornado" version = "6.4" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" optional = false python-versions = ">= 3.8" files = [ @@ -3720,6 +3981,7 @@ files = [ name = "trio" version = "0.23.2" description = "A friendly Python library for async concurrency and I/O" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3739,6 +4001,7 @@ sortedcontainers = "*" name = "trio-websocket" version = "0.11.1" description = "WebSocket library for Trio" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3754,6 +4017,7 @@ wsproto = ">=0.14" name = "twilio" version = "8.11.0" description = "Twilio API client and TwiML generator" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3771,6 +4035,7 @@ requests = ">=2.0.0" name = "twisted" version = "23.10.0" description = "An asynchronous networking framework written in Python" +category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -3810,6 +4075,7 @@ windows-platform = ["pywin32 (!=226)", "pywin32 (!=226)", "twisted[all-non-platf name = "twisted-iocpsupport" version = "1.0.4" description = "An extension for use in the twisted I/O Completion Ports reactor." +category = "main" optional = false python-versions = "*" files = [ @@ -3838,6 +4104,7 @@ files = [ name = "txaio" version = "23.1.1" description = "Compatibility API between asyncio/Twisted/Trollius" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3854,6 +4121,7 @@ twisted = ["twisted (>=20.3.0)", "zope.interface (>=5.2.0)"] name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3865,6 +4133,7 @@ files = [ name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" +category = "main" optional = false python-versions = ">=2" files = [ @@ -3876,6 +4145,7 @@ files = [ name = "ua-parser" version = "0.18.0" description = "Python port of Browserscope's user agent parser" +category = "main" optional = false python-versions = "*" files = [ @@ -3887,6 +4157,7 @@ files = [ name = "uritemplate" version = "4.1.1" description = "Implementation of RFC 6570 URI Templates" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3896,27 +4167,47 @@ files = [ [[package]] name = "urllib3" -version = "2.1.0" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.dependencies] +certifi = {version = "*", optional = true, markers = "extra == \"secure\""} +cryptography = {version = ">=1.9", optional = true, markers = "extra == \"secure\""} +idna = {version = ">=2.0.0", optional = true, markers = "extra == \"secure\""} +pyopenssl = {version = ">=17.1.0", optional = true, markers = "extra == \"secure\""} pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} +urllib3-secure-extra = {version = "*", optional = true, markers = "extra == \"secure\""} [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "urllib3-secure-extra" +version = "0.1.0" +description = "Marker library to detect whether urllib3 was installed with the deprecated [secure] extra" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "urllib3-secure-extra-0.1.0.tar.gz", hash = "sha256:ee9409cbfeb4b8609047be4c32fb4317870c602767e53fd8a41005ebe6a41dff"}, + {file = "urllib3_secure_extra-0.1.0-py2.py3-none-any.whl", hash = "sha256:f7adcb108b4d12a4b26b99eb60e265d087f435052a76aefa396b6ee85e9a6ef9"}, +] + [[package]] name = "uvicorn" version = "0.24.0.post1" description = "The lightning-fast ASGI server." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3931,7 +4222,7 @@ h11 = ">=0.8" httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -3942,6 +4233,7 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "uvloop" version = "0.19.0" description = "Fast implementation of asyncio event loop on top of libuv" +category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -3986,6 +4278,7 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" name = "vine" version = "5.1.0" description = "Python promises." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3997,6 +4290,7 @@ files = [ name = "watchdog" version = "3.0.0" description = "Filesystem events monitoring" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4036,6 +4330,7 @@ watchmedo = ["PyYAML (>=3.10)"] name = "watchfiles" version = "0.21.0" description = "Simple, modern and high performance file watching and code reload in python." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4123,6 +4418,7 @@ anyio = ">=3.0.0" name = "wcwidth" version = "0.2.12" description = "Measures the displayed width of unicode strings in a terminal" +category = "main" optional = false python-versions = "*" files = [ @@ -4134,6 +4430,7 @@ files = [ name = "webauthn" version = "1.11.1" description = "Pythonic WebAuthn" +category = "main" optional = false python-versions = "*" files = [ @@ -4152,6 +4449,7 @@ pyOpenSSL = ">=23.2.0" name = "websocket-client" version = "1.7.0" description = "WebSocket client for Python with low level API options" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4168,6 +4466,7 @@ test = ["websockets"] name = "websockets" version = "12.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4249,6 +4548,7 @@ files = [ name = "wsproto" version = "1.2.0" description = "WebSockets state-machine based protocol implementation" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -4263,6 +4563,7 @@ h11 = ">=0.9.0,<1" name = "xmlsec" version = "1.3.13" description = "Python bindings for the XML Security Library" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -4288,6 +4589,7 @@ lxml = ">=3.8" name = "yarl" version = "1.9.4" description = "Yet another URL library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4391,6 +4693,7 @@ multidict = ">=4.0" name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4406,6 +4709,7 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p name = "zope-interface" version = "6.1" description = "Interfaces for Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4459,6 +4763,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] name = "zxcvbn" version = "4.4.28" description = "" +category = "main" optional = false python-versions = "*" files = [ @@ -4468,4 +4773,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "~3.12" -content-hash = "cd3ba23870e29c7b2d912cccf8414de94b8d1c167976874b1e87e36cd1791a10" +content-hash = "a8334ecd25bd160da48ace07f04282cbe08180843ed97e314947c7746976123b" diff --git a/pyproject.toml b/pyproject.toml index 75c976b27..feac7e438 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,6 +133,7 @@ django-guardian = "*" django-model-utils = "*" django-prometheus = "*" django-redis = "*" +django-storages = { extras = ["s3"], version = "*" } # See https://github.com/django-tenants/django-tenants/pull/959 # See https://github.com/django-tenants/django-tenants/pull/997 django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch="authentik-fixes" } diff --git a/schema.yml b/schema.yml index 84afd1151..8541e5317 100644 --- a/schema.yml +++ b/schema.yml @@ -28487,6 +28487,46 @@ components: * `authentik.blueprints` - authentik Blueprints * `authentik.core` - authentik Core * `authentik.enterprise` - authentik Enterprise + AppleChallengeResponseRequest: + type: object + description: Pseudo class for plex response + properties: + component: + type: string + minLength: 1 + default: ak-source-oauth-apple + AppleLoginChallenge: + type: object + description: Special challenge for apple-native authentication flow, which happens + on the client. + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-source-oauth-apple + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + client_id: + type: string + scope: + type: string + redirect_uri: + type: string + state: + type: string + required: + - client_id + - redirect_uri + - scope + - state + - type Application: type: object description: Application Serializer @@ -28988,6 +29028,47 @@ components: - client_id - client_secret - name + AuthenticatorSMSChallenge: + type: object + description: SMS Setup challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-authenticator-sms + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + phone_number_required: + type: boolean + default: true + required: + - pending_user + - pending_user_avatar + - type + AuthenticatorSMSChallengeResponseRequest: + type: object + description: SMS Challenge response, device is set by get_response_instance + properties: + component: + type: string + minLength: 1 + default: ak-stage-authenticator-sms + code: + type: integer + phone_number: + type: string + minLength: 1 AuthenticatorSMSStage: type: object description: AuthenticatorSMSStage Serializer @@ -29113,6 +29194,44 @@ components: - from_number - name - provider + AuthenticatorStaticChallenge: + type: object + description: Static authenticator challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-authenticator-static + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + codes: + type: array + items: + type: string + required: + - codes + - pending_user + - pending_user_avatar + - type + AuthenticatorStaticChallengeResponseRequest: + type: object + description: Pseudo class for static response + properties: + component: + type: string + minLength: 1 + default: ak-stage-authenticator-static AuthenticatorStaticStage: type: object description: AuthenticatorStaticStage Serializer @@ -29199,6 +29318,46 @@ components: minimum: 0 required: - name + AuthenticatorTOTPChallenge: + type: object + description: TOTP Setup challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-authenticator-totp + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + config_url: + type: string + required: + - config_url + - pending_user + - pending_user_avatar + - type + AuthenticatorTOTPChallengeResponseRequest: + type: object + description: TOTP Challenge response, device is set by get_response_instance + properties: + component: + type: string + minLength: 1 + default: ak-stage-authenticator-totp + code: + type: integer + required: + - code AuthenticatorTOTPStage: type: object description: AuthenticatorTOTPStage Serializer @@ -29383,6 +29542,104 @@ components: * `discouraged` - Discouraged required: - name + AuthenticatorValidationChallenge: + type: object + description: Authenticator challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-authenticator-validate + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + device_challenges: + type: array + items: + $ref: '#/components/schemas/DeviceChallenge' + configuration_stages: + type: array + items: + $ref: '#/components/schemas/SelectableStage' + required: + - configuration_stages + - device_challenges + - pending_user + - pending_user_avatar + - type + AuthenticatorValidationChallengeResponseRequest: + type: object + description: Challenge used for Code-based and WebAuthn authenticators + properties: + component: + type: string + minLength: 1 + default: ak-stage-authenticator-validate + selected_challenge: + $ref: '#/components/schemas/DeviceChallengeRequest' + selected_stage: + type: string + minLength: 1 + code: + type: string + minLength: 1 + webauthn: + type: object + additionalProperties: {} + duo: + type: integer + AuthenticatorWebAuthnChallenge: + type: object + description: WebAuthn Challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-authenticator-webauthn + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + registration: + type: object + additionalProperties: {} + required: + - pending_user + - pending_user_avatar + - registration + - type + AuthenticatorWebAuthnChallengeResponseRequest: + type: object + description: WebAuthn Challenge response + properties: + component: + type: string + minLength: 1 + default: ak-stage-authenticator-webauthn + response: + type: object + additionalProperties: {} + required: + - response AutoSubmitChallengeResponseRequest: type: object description: Pseudo class for autosubmit response @@ -29473,9 +29730,7 @@ components: path: type: string default: '' - context: - type: object - additionalProperties: {} + context: {} last_applied: type: string format: date-time @@ -29495,8 +29750,6 @@ components: type: string readOnly: true metadata: - type: object - additionalProperties: {} readOnly: true content: type: string @@ -29518,9 +29771,7 @@ components: path: type: string default: '' - context: - type: object - additionalProperties: {} + context: {} enabled: type: boolean content: @@ -29590,9 +29841,7 @@ components: format: uuid nullable: true description: Web Certificate used by the authentik Core webserver. - attributes: - type: object - additionalProperties: {} + attributes: {} required: - brand_uuid - domain @@ -29645,9 +29894,7 @@ components: format: uuid nullable: true description: Web Certificate used by the authentik Core webserver. - attributes: - type: object - additionalProperties: {} + attributes: {} required: - domain Cache: @@ -29673,6 +29920,50 @@ components: * `can_impersonate` - Can Impersonate * `can_debug` - Can Debug * `is_enterprise` - Is Enterprise + CaptchaChallenge: + type: object + description: Site public key + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-captcha + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + site_key: + type: string + js_url: + type: string + required: + - js_url + - pending_user + - pending_user_avatar + - site_key + - type + CaptchaChallengeResponseRequest: + type: object + description: Validate captcha token + properties: + component: + type: string + minLength: 1 + default: ak-stage-captcha + token: + type: string + minLength: 1 + required: + - token CaptchaStage: type: object description: CaptchaStage Serializer @@ -29875,10 +30166,20 @@ components: ChallengeTypes: oneOf: - $ref: '#/components/schemas/AccessDeniedChallenge' + - $ref: '#/components/schemas/AppleLoginChallenge' - $ref: '#/components/schemas/AuthenticatorDuoChallenge' + - $ref: '#/components/schemas/AuthenticatorSMSChallenge' + - $ref: '#/components/schemas/AuthenticatorStaticChallenge' + - $ref: '#/components/schemas/AuthenticatorTOTPChallenge' + - $ref: '#/components/schemas/AuthenticatorValidationChallenge' + - $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge' - $ref: '#/components/schemas/AutosubmitChallenge' + - $ref: '#/components/schemas/CaptchaChallenge' - $ref: '#/components/schemas/ConsentChallenge' + - $ref: '#/components/schemas/DummyChallenge' + - $ref: '#/components/schemas/EmailChallenge' - $ref: '#/components/schemas/FlowErrorChallenge' + - $ref: '#/components/schemas/IdentificationChallenge' - $ref: '#/components/schemas/OAuthDeviceCodeChallenge' - $ref: '#/components/schemas/OAuthDeviceCodeFinishChallenge' - $ref: '#/components/schemas/PasswordChallenge' @@ -29886,14 +30187,25 @@ components: - $ref: '#/components/schemas/PromptChallenge' - $ref: '#/components/schemas/RedirectChallenge' - $ref: '#/components/schemas/ShellChallenge' + - $ref: '#/components/schemas/UserLoginChallenge' discriminator: propertyName: component mapping: ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge' + ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge' ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge' + ak-stage-authenticator-sms: '#/components/schemas/AuthenticatorSMSChallenge' + ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge' + ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge' + ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge' + ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge' ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge' + ak-stage-captcha: '#/components/schemas/CaptchaChallenge' ak-stage-consent: '#/components/schemas/ConsentChallenge' + ak-stage-dummy: '#/components/schemas/DummyChallenge' + ak-stage-email: '#/components/schemas/EmailChallenge' ak-stage-flow-error: '#/components/schemas/FlowErrorChallenge' + ak-stage-identification: '#/components/schemas/IdentificationChallenge' ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallenge' ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallenge' ak-stage-password: '#/components/schemas/PasswordChallenge' @@ -29901,6 +30213,7 @@ components: ak-stage-prompt: '#/components/schemas/PromptChallenge' xak-flow-redirect: '#/components/schemas/RedirectChallenge' xak-flow-shell: '#/components/schemas/ShellChallenge' + ak-stage-user-login: '#/components/schemas/UserLoginChallenge' ClientTypeEnum: enum: - confidential @@ -30083,10 +30396,24 @@ components: cancel_url: type: string layout: - $ref: '#/components/schemas/FlowLayoutEnum' + $ref: '#/components/schemas/ContextualFlowInfoLayoutEnum' required: - cancel_url - layout + ContextualFlowInfoLayoutEnum: + enum: + - stacked + - content_left + - content_right + - sidebar_left + - sidebar_right + type: string + description: |- + * `stacked` - STACKED + * `content_left` - CONTENT_LEFT + * `content_right` - CONTENT_RIGHT + * `sidebar_left` - SIDEBAR_LEFT + * `sidebar_right` - SIDEBAR_RIGHT Coordinate: type: object description: Coordinates for diagrams @@ -30244,6 +30571,38 @@ components: - type - verbose_name - verbose_name_plural + DeviceChallenge: + type: object + description: Single device challenge + properties: + device_class: + type: string + device_uid: + type: string + challenge: + type: object + additionalProperties: {} + required: + - challenge + - device_class + - device_uid + DeviceChallengeRequest: + type: object + description: Single device challenge + properties: + device_class: + type: string + minLength: 1 + device_uid: + type: string + minLength: 1 + challenge: + type: object + additionalProperties: {} + required: + - challenge + - device_class + - device_uid DeviceClassesEnum: enum: - static @@ -30400,6 +30759,33 @@ components: required: - domain - tenant + DummyChallenge: + type: object + description: Dummy challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-dummy + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + required: + - type + DummyChallengeResponseRequest: + type: object + description: Dummy challenge response + properties: + component: + type: string + minLength: 1 + default: ak-stage-dummy DummyPolicy: type: object description: Dummy Policy Serializer @@ -30574,6 +30960,35 @@ components: * `success` - Success * `waiting` - Waiting * `invalid` - Invalid + EmailChallenge: + type: object + description: Email challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-email + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + required: + - type + EmailChallengeResponseRequest: + type: object + description: |- + Email challenge resposen. No fields. This challenge is + always declared invalid to give the user a chance to retry + properties: + component: + type: string + minLength: 1 + default: ak-stage-email EmailStage: type: object description: EmailStage Serializer @@ -30750,16 +31165,12 @@ components: format: uuid readOnly: true title: Event uuid - user: - type: object - additionalProperties: {} + user: {} action: $ref: '#/components/schemas/EventActions' app: type: string - context: - type: object - additionalProperties: {} + context: {} client_ip: type: string nullable: true @@ -30770,9 +31181,7 @@ components: expires: type: string format: date-time - brand: - type: object - additionalProperties: {} + brand: {} required: - action - app @@ -31255,17 +31664,13 @@ components: type: object description: Event Serializer properties: - user: - type: object - additionalProperties: {} + user: {} action: $ref: '#/components/schemas/EventActions' app: type: string minLength: 1 - context: - type: object - additionalProperties: {} + context: {} client_ip: type: string nullable: true @@ -31273,9 +31678,7 @@ components: expires: type: string format: date-time - brand: - type: object - additionalProperties: {} + brand: {} required: - action - app @@ -31609,25 +32012,47 @@ components: - title FlowChallengeResponseRequest: oneOf: + - $ref: '#/components/schemas/AppleChallengeResponseRequest' - $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest' + - $ref: '#/components/schemas/AuthenticatorSMSChallengeResponseRequest' + - $ref: '#/components/schemas/AuthenticatorStaticChallengeResponseRequest' + - $ref: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest' + - $ref: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest' + - $ref: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest' - $ref: '#/components/schemas/AutoSubmitChallengeResponseRequest' + - $ref: '#/components/schemas/CaptchaChallengeResponseRequest' - $ref: '#/components/schemas/ConsentChallengeResponseRequest' + - $ref: '#/components/schemas/DummyChallengeResponseRequest' + - $ref: '#/components/schemas/EmailChallengeResponseRequest' + - $ref: '#/components/schemas/IdentificationChallengeResponseRequest' - $ref: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest' - $ref: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest' - $ref: '#/components/schemas/PasswordChallengeResponseRequest' - $ref: '#/components/schemas/PlexAuthenticationChallengeResponseRequest' - $ref: '#/components/schemas/PromptChallengeResponseRequest' + - $ref: '#/components/schemas/UserLoginChallengeResponseRequest' discriminator: propertyName: component mapping: + ak-source-oauth-apple: '#/components/schemas/AppleChallengeResponseRequest' ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest' + ak-stage-authenticator-sms: '#/components/schemas/AuthenticatorSMSChallengeResponseRequest' + ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallengeResponseRequest' + ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest' + ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest' + ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest' ak-stage-autosubmit: '#/components/schemas/AutoSubmitChallengeResponseRequest' + ak-stage-captcha: '#/components/schemas/CaptchaChallengeResponseRequest' ak-stage-consent: '#/components/schemas/ConsentChallengeResponseRequest' + ak-stage-dummy: '#/components/schemas/DummyChallengeResponseRequest' + ak-stage-email: '#/components/schemas/EmailChallengeResponseRequest' + ak-stage-identification: '#/components/schemas/IdentificationChallengeResponseRequest' ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest' ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest' ak-stage-password: '#/components/schemas/PasswordChallengeResponseRequest' ak-source-plex: '#/components/schemas/PlexAuthenticationChallengeResponseRequest' ak-stage-prompt: '#/components/schemas/PromptChallengeResponseRequest' + ak-stage-user-login: '#/components/schemas/UserLoginChallengeResponseRequest' FlowDesignationEnum: enum: - authentication @@ -31749,11 +32174,11 @@ components: - sidebar_right type: string description: |- - * `stacked` - STACKED - * `content_left` - CONTENT_LEFT - * `content_right` - CONTENT_RIGHT - * `sidebar_left` - SIDEBAR_LEFT - * `sidebar_right` - SIDEBAR_RIGHT + * `stacked` - Stacked + * `content_left` - Content Left + * `content_right` - Content Right + * `sidebar_left` - Sidebar Left + * `sidebar_right` - Sidebar Right FlowRequest: type: object description: Flow Serializer @@ -32211,6 +32636,68 @@ components: format: uuid required: - name + IdentificationChallenge: + type: object + description: Identification challenges with all UI elements + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-identification + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + user_fields: + type: array + items: + type: string + nullable: true + password_fields: + type: boolean + application_pre: + type: string + enroll_url: + type: string + recovery_url: + type: string + passwordless_url: + type: string + primary_action: + type: string + sources: + type: array + items: + $ref: '#/components/schemas/LoginSource' + show_source_labels: + type: boolean + required: + - password_fields + - primary_action + - show_source_labels + - type + - user_fields + IdentificationChallengeResponseRequest: + type: object + description: Identification challenge + properties: + component: + type: string + minLength: 1 + default: ak-stage-identification + uid_field: + type: string + minLength: 1 + password: + type: string + nullable: true + required: + - uid_field IdentificationStage: type: object description: IdentificationStage Serializer @@ -32556,8 +33043,6 @@ components: description: Return internal model name readOnly: true kubeconfig: - type: object - additionalProperties: {} description: Paste your kubeconfig here. authentik will automatically use the currently selected context. verify_ssl: @@ -32582,8 +33067,6 @@ components: description: If enabled, use the local connection. Required Docker socket/Kubernetes Integration kubeconfig: - type: object - additionalProperties: {} description: Paste your kubeconfig here. authentik will automatically use the currently selected context. verify_ssl: @@ -33342,6 +33825,17 @@ components: type: string required: - link + LoginChallengeTypes: + oneOf: + - $ref: '#/components/schemas/RedirectChallenge' + - $ref: '#/components/schemas/PlexAuthenticationChallenge' + - $ref: '#/components/schemas/AppleLoginChallenge' + discriminator: + propertyName: component + mapping: + xak-flow-redirect: '#/components/schemas/RedirectChallenge' + ak-source-plex: '#/components/schemas/PlexAuthenticationChallenge' + ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge' LoginMetrics: type: object description: Login Metrics per 1h @@ -33365,6 +33859,20 @@ components: - authorizations - logins - logins_failed + LoginSource: + type: object + description: Serializer for Login buttons of sources + properties: + name: + type: string + icon_url: + type: string + nullable: true + challenge: + $ref: '#/components/schemas/LoginChallengeTypes' + required: + - challenge + - name Metadata: type: object description: Serializer for blueprint metadata @@ -34174,9 +34682,7 @@ components: starts with http it is returned as-is readOnly: true provider_type: - allOf: - - $ref: '#/components/schemas/ProviderTypeEnum' - description: '' + $ref: '#/components/schemas/ProviderTypeEnum' request_token_url: type: string nullable: true @@ -34214,9 +34720,7 @@ components: type: string oidc_jwks_url: type: string - oidc_jwks: - type: object - additionalProperties: {} + oidc_jwks: {} required: - callback_url - component @@ -34274,9 +34778,7 @@ components: type: string minLength: 1 provider_type: - allOf: - - $ref: '#/components/schemas/ProviderTypeEnum' - description: '' + $ref: '#/components/schemas/ProviderTypeEnum' request_token_url: type: string nullable: true @@ -34315,9 +34817,7 @@ components: type: string oidc_jwks_url: type: string - oidc_jwks: - type: object - additionalProperties: {} + oidc_jwks: {} required: - consumer_key - consumer_secret @@ -36230,9 +36730,7 @@ components: path: type: string default: '' - context: - type: object - additionalProperties: {} + context: {} enabled: type: boolean content: @@ -36286,9 +36784,7 @@ components: format: uuid nullable: true description: Web Certificate used by the authentik Core webserver. - attributes: - type: object - additionalProperties: {} + attributes: {} PatchedCaptchaStageRequest: type: object description: CaptchaStage Serializer @@ -36697,17 +37193,13 @@ components: type: object description: Event Serializer properties: - user: - type: object - additionalProperties: {} + user: {} action: $ref: '#/components/schemas/EventActions' app: type: string minLength: 1 - context: - type: object - additionalProperties: {} + context: {} client_ip: type: string nullable: true @@ -36715,9 +37207,7 @@ components: expires: type: string format: date-time - brand: - type: object - additionalProperties: {} + brand: {} PatchedExpressionPolicyRequest: type: object description: Group Membership Policy Serializer @@ -36960,8 +37450,6 @@ components: description: If enabled, use the local connection. Required Docker socket/Kubernetes Integration kubeconfig: - type: object - additionalProperties: {} description: Paste your kubeconfig here. authentik will automatically use the currently selected context. verify_ssl: @@ -37383,9 +37871,7 @@ components: type: string minLength: 1 provider_type: - allOf: - - $ref: '#/components/schemas/ProviderTypeEnum' - description: '' + $ref: '#/components/schemas/ProviderTypeEnum' request_token_url: type: string nullable: true @@ -37424,9 +37910,7 @@ components: type: string oidc_jwks_url: type: string - oidc_jwks: - type: object - additionalProperties: {} + oidc_jwks: {} PatchedOutpostRequest: type: object description: Outpost Serializer @@ -38221,8 +38705,6 @@ components: minLength: 1 description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' footer_links: - type: object - additionalProperties: {} description: The option configures the footer links on the flow executor pages. gdpr_compliance: @@ -39357,8 +39839,35 @@ components: - authorization_flow - name ProviderTypeEnum: - enum: [] - type: boolean + enum: + - apple + - azuread + - discord + - facebook + - github + - google + - mailcow + - openidconnect + - okta + - patreon + - reddit + - twitch + - twitter + type: string + description: |- + * `apple` - Apple + * `azuread` - Azure AD + * `discord` - Discord + * `facebook` - Facebook + * `github` - GitHub + * `google` - Google + * `mailcow` - Mailcow + * `openidconnect` - OpenID Connect + * `okta` - Okta + * `patreon` - Patreon + * `reddit` - Reddit + * `twitch` - Twitch + * `twitter` - Twitter ProxyMode: enum: - proxy @@ -39908,9 +40417,7 @@ components: type: string ip: type: string - ip_geo_data: - type: object - additionalProperties: {} + ip_geo_data: {} score: type: integer maximum: 9223372036854775807 @@ -40981,6 +41488,24 @@ components: - expression - name - scope_name + SelectableStage: + type: object + description: Serializer for stages which can be selected by users + properties: + pk: + type: string + format: uuid + name: + type: string + verbose_name: + type: string + meta_model_name: + type: string + required: + - meta_model_name + - name + - pk + - verbose_name ServiceConnection: type: object description: ServiceConnection Serializer @@ -41077,8 +41602,6 @@ components: type: string description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' footer_links: - type: object - additionalProperties: {} description: The option configures the footer links on the flow executor pages. gdpr_compliance: @@ -41110,8 +41633,6 @@ components: minLength: 1 description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' footer_links: - type: object - additionalProperties: {} description: The option configures the footer links on the flow executor pages. gdpr_compliance: @@ -42148,6 +42669,43 @@ components: additionalProperties: {} required: - name + UserLoginChallenge: + type: object + description: Empty challenge + properties: + type: + $ref: '#/components/schemas/ChallengeChoices' + flow_info: + $ref: '#/components/schemas/ContextualFlowInfo' + component: + type: string + default: ak-stage-user-login + response_errors: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/ErrorDetail' + pending_user: + type: string + pending_user_avatar: + type: string + required: + - pending_user + - pending_user_avatar + - type + UserLoginChallengeResponseRequest: + type: object + description: User login challenge + properties: + component: + type: string + minLength: 1 + default: ak-stage-user-login + remember_me: + type: boolean + required: + - remember_me UserLoginStage: type: object description: UserLoginStage Serializer diff --git a/scripts/create_bucket.sh b/scripts/create_bucket.sh new file mode 100644 index 000000000..0d4979faf --- /dev/null +++ b/scripts/create_bucket.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env -S bash -e + +AWS_ACCESS_KEY_ID=accessKey1 AWS_SECRET_ACCESS_KEY=secretKey1 aws \ + s3api \ + --endpoint-url http://localhost:8020 \ + create-bucket \ + --acl private \ + --bucket authentik-media + +AWS_ACCESS_KEY_ID=accessKey1 AWS_SECRET_ACCESS_KEY=secretKey1 aws \ + s3api \ + --endpoint-url http://localhost:8020 \ + put-bucket-cors \ + --bucket authentik-media \ + --cors-configuration \ + '{"CORSRules": [{"AllowedOrigins": ["*"], "AllowedHeaders": ["Authorization"], "AllowedMethods": ["GET"], "MaxAgeSeconds": 3000}]}' diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 658d7a0db..77495ce08 100644 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -18,7 +18,24 @@ services: ports: - 127.0.0.1:6379:6379 restart: always + s3: + container_name: s3 + image: docker.io/zenko/cloudserver + environment: + REMOTE_MANAGEMENT_DISABLE: "1" + SCALITY_ACCESS_KEY_ID: accessKey1 + SCALITY_SECRET_ACCESS_KEY: secretKey1 + ports: + - 8020:8000 + volumes: + - s3-data:/usr/src/app/localData + - s3-metadata:/usr/scr/app/localMetadata + restart: always volumes: db-data: driver: local + s3-data: + driver: local + s3-metadata: + driver: local diff --git a/scripts/generate_config.py b/scripts/generate_config.py index 66f437996..2351cab09 100644 --- a/scripts/generate_config.py +++ b/scripts/generate_config.py @@ -19,6 +19,19 @@ with open("local.env.yml", "w", encoding="utf-8") as _config: "blueprints_dir": "./blueprints", "cert_discovery_dir": "./certs", "geoip": "tests/GeoLite2-City-Test.mmdb", + "storage": { + "media": { + "backend": "file", + "s3": { + "endpoint": "http://localhost:8020", + "access_key": "accessKey1", + "secret_key": "secretKey1", + "bucket_name": "authentik-media", + "custom_domain": "localhost:8020/authentik-media", + "secure_urls": False, + }, + }, + }, "tenants": { "enabled": False, "api_key": generate_id(), diff --git a/website/docs/installation/configuration.mdx b/website/docs/installation/configuration.mdx index 28d8e5f50..e2d15eb7c 100644 --- a/website/docs/installation/configuration.mdx +++ b/website/docs/installation/configuration.mdx @@ -116,7 +116,7 @@ To check if your config has been applied correctly, you can run the following co `AUTHENTIK_REDIS__CACHE_TIMEOUT_REPUTATION` only applies to the cache expiry, see [`AUTHENTIK_REPUTATION__EXPIRY`](#authentik_reputation__expiry) to control how long reputation is persisted for. ::: -## Listen Setting +## Listen Settings - `AUTHENTIK_LISTEN__HTTP`: Listening address:port (e.g. `0.0.0.0:9000`) for HTTP (Applies to Server and Proxy outpost) - `AUTHENTIK_LISTEN__HTTPS`: Listening address:port (e.g. `0.0.0.0:9443`) for HTTPS (Applies to Server and Proxy outpost) @@ -130,6 +130,22 @@ To check if your config has been applied correctly, you can run the following co Requests directly coming from one an address within a CIDR specified here are able to set proxy headers, such as `X-Forwarded-For`. Requests coming from other addresses will not be able to set these headers. +## Media Storage Settings + +These settings affect where media files are stored. Those files include applications and sources icons. By default, they are stored on disk in the `/media` directory of the authentik container. S3 storage is also supported. + +- `AUTHENTIK_STORAGE_MEDIA_BACKEND`: Where to store files. Valid values are `file` and `s3`. For `file` storage, files are stored in a `/media` directory in the container. For `s3`, see below. +- `AUTHENTIK_STORAGE_MEDIA_S3_REGION`: S3 region where the bucket has been created. May be omitted depending on which S3 provider you use. No default. +- `AUTHENTIK_STORAGE_MEDIA_S3_USE__SSL`: Whether to use HTTPS when talking to the S3 storage providers. Defaults to `true`. +- `AUTHENTIK_STORAGE_MEDIA_S3_ENDPOINT`: Endpoint to use to talk to the S3 storage provider. Override the previous region and use_ssl settings. Must be a valid URL in the form of `https://s3.provider`. No default. +- `AUTHENTIK_STORAGE_MEDIA_S3_SESSION__PROFILE`: Profile to use when using AWS SDK authentication. No default. Supports hot-reloading. +- `AUTHENTIK_STORAGE_MEDIA_S3_ACCESS__KEY`: Access key to authenticate to S3. May be omitted if using AWS SDK authentication. Supports hot-reloading. +- `AUTHENTIK_STORAGE_MEDIA_S3_SECRET__KEY`: Secret key to authenticate to S3. May be omitted if using AWS SDK authentication. Supports hot-reloading. +- `AUTHENTIK_STORAGE_MEDIA_S3_SECURITY__TOKEN`: Security token to authenticate to S3. May be omitted. Supports hot-reloading. +- `AUTHENTIK_STORAGE_MEDIA_S3_BUCKET__NAME`: Name of the bucket to use to store files. +- `AUTHENTIK_STORAGE_MEDIA_S3_CUSTOM__DOMAIN`: Domain to use to create URLs for users. Mainly useful for non-AWS providers. May include a port. Must include the bucket. Example: `s3.company:8080/authentik-media`. +- `AUTHENTIK_STORAGE_MEDIA_S3_SECURE__URLS`: Whether URLS created for users use `http` or `https`. Defaults to `true`. + ## authentik Settings ### `AUTHENTIK_SECRET_KEY` diff --git a/website/docs/installation/storage-s3.md b/website/docs/installation/storage-s3.md new file mode 100644 index 000000000..bbe536369 --- /dev/null +++ b/website/docs/installation/storage-s3.md @@ -0,0 +1,104 @@ +--- +title: S3 storage setup +--- + +### Preparation + +First, create a user on your S3 storage provider and get access credentials for S3, hereafter referred as `access_key` and `secret_key`. + +You'll also need to know which endpoint authentik is going to use to access the S3 API, hereafter referred as `https://s3.provider`. + +The bucket in which authentik is going to store files is going to be called `authentik-media`. You may need to change this name depending on your S3 provider limitations. Also, we're suffixing the bucket name with `-media` as authentik currently only stores media files, but may use other buckets in the future. + +The domain used to access authentik is going to be referred to as `authentik.company`. + +You will also need the AWS CLI. + +### S3 configuration + +#### Bucket creation + +Let's create the bucket in which authentik is going to store files: + +```bash +AWS_ACCESS_KEY_ID=access_key AWS_SECRET_ACCESS_KEY=secret_key aws s3api --endpoint-url=https://s3.provider create-bucket --bucket=authentik-media --acl=private +``` + +If using AWS S3, you can omit the `--endpoint-url` option, but may need to specify the `--region` option. If using Google Cloud Storage, refer to its documentation on how to create buckets. + +The bucket ACL is set to private, although that is not strictly necessary, as an ACL associated with each object stored in the bucket will be private as well. + +#### CORS policy + +Next, let's associate a CORS policy to the bucket, to allow the authentik web interface to show images stored in the bucket. + +First, save the following file locally as `cors.json`: + +```json +{ + "CORSRules": [ + { + "AllowedOrigins": ["authentik.company"], + "AllowedHeaders": ["Authorization"], + "AllowedMethods": ["GET"], + "MaxAgeSeconds": 3000 + } + ] +} +``` + +If authentik is accessed from multiple domains, you can add them to the `AllowedOrigins` list. + +Let's apply that policy to the bucket: + +```bash +AWS_ACCESS_KEY_ID=access_key AWS_SECRET_ACCESS_KEY=secret_key aws s3api --endpoint-url=https://s3.provider put-bucket-cors --bucket=authentik-media --cors-configuration=file://cors.json +``` + +### Configuring authentik + +Add the following to your `.env` file: + +```env +AUTHENTIK_STORAGE_MEDIA_BACKEND=s3 +AUTHENTIK_STORAGE_MEDIA_S3_ACCESS__KEY=access_key +AUTHENTIK_STORAGE_MEDIA_S3_SECRET__KEY=secret_key +AUTHENTIK_STORAGE_MEDIA_S3_BUCKET__NAME=authentik-media +``` + +If you're using AWS S3 as your S3 provider, add the following: + +```env +AUTHENTIK_STORAGE_MEDIA_S3_REGION=us-east-1 # Use the region of the bucket +``` + +If you're not using AWS S3 as your S3 provider, add the following: + +```env +AUTHENTIK_STORAGE_MEDIA_S3_ENDPOINT=https://s3.provider +AUTHENTIK_STORAGE_MEDIA_S3_CUSTOM__DOMAIN=s3.provider/authentik-media +``` + +The `ENDPOINT` setting specifies how authentik talks to the S3 provider. + +The `CUSTOM__DOMAIN` setting specifies how URLs are constructed to be shown on the web interface. For example, an object stored at `application-icons/application.png` with a `CUSTOM__DOMAIN` setting of `s3.provider/authentik-media` will result in a URL of `https://s3.provider/authentik-media/application-icons/application.png`. You can also use subdomains for your buckets depending on what your S3 provider offers: `authentik-media.s3.provider`. Whether HTTPS is used is controlled by the `AUTHENTIK_STORAGE_MEDIA_S3_SECURE__URLS` which defaults to true. + +For more control over settings, refer to the [configuration reference](./configuration.mdx#media-storage-settings) + +### Migrating between storage backends + +The following section assumes that the local storage path is `/media` and the bucket name is `authentik-media`. It also assumes you have a working `aws` CLI that can interact with the bucket. + +#### From file to s3 + +Follow the setup steps above, and then migrate the files from your local directory to s3: + +```bash +aws s3 sync /media s3://authentik-media +``` + +#### From s3 to file + +```bash +aws s3 sync s3://authentik-media /media +``` diff --git a/website/docs/releases/2024/v2024.1.md b/website/docs/releases/2024/v2024.1.md index c6ffd0f28..54c488a32 100644 --- a/website/docs/releases/2024/v2024.1.md +++ b/website/docs/releases/2024/v2024.1.md @@ -23,6 +23,10 @@ slug: "/releases/2024.1" Previously the identification stage would only continue if a user matching the user identifier exists. While this was the intended functionality, this release adds an option to continue to the next stage even if no matching user was found. "Pretend" users cannot authenticate nor receive emails, and don't exist in the database. **This feature is enabled by default.** +- S3 file storage + + Media files can now be stored on S3. Follow the [setup guide](../../installation/storage-s3.md) to get started. + ## Upgrading This release does not introduce any new requirements. diff --git a/website/sidebars.js b/website/sidebars.js index 2cedc2a33..992c7bf1d 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -27,6 +27,7 @@ const docsSidebar = { "installation/automated-install", "installation/air-gapped", "installation/monitoring", + "installation/storage-s3", ], }, {