use wrapper for get_tenant, give fallback interfaces
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
c6f8290ca1
commit
c0262f0802
|
@ -18,6 +18,7 @@ from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.lib.utils.reflection import get_env
|
from authentik.lib.utils.reflection import get_env
|
||||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||||
from authentik.outposts.models import Outpost
|
from authentik.outposts.models import Outpost
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
|
|
||||||
class RuntimeDict(TypedDict):
|
class RuntimeDict(TypedDict):
|
||||||
|
@ -77,7 +78,7 @@ class SystemSerializer(PassiveSerializer):
|
||||||
|
|
||||||
def get_tenant(self, request: Request) -> str:
|
def get_tenant(self, request: Request) -> str:
|
||||||
"""Currently active tenant"""
|
"""Currently active tenant"""
|
||||||
return str(request._request.tenant)
|
return str(get_tenant(request))
|
||||||
|
|
||||||
def get_server_time(self, request: Request) -> datetime:
|
def get_server_time(self, request: Request) -> datetime:
|
||||||
"""Current server time"""
|
"""Current server time"""
|
||||||
|
|
|
@ -76,7 +76,7 @@ from authentik.interfaces.views import reverse_interface
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||||
def _create_recovery_link(self) -> tuple[Optional[str], Optional[Token]]:
|
def _create_recovery_link(self) -> tuple[Optional[str], Optional[Token]]:
|
||||||
"""Create a recovery link (when the current tenant has a recovery flow set),
|
"""Create a recovery link (when the current tenant has a recovery flow set),
|
||||||
that can either be shown to an admin or sent to the user directly"""
|
that can either be shown to an admin or sent to the user directly"""
|
||||||
tenant: Tenant = self.request._request.tenant
|
tenant = get_tenant(self.request)
|
||||||
# Check that there is a recovery flow, if not return an error
|
# Check that there is a recovery flow, if not return an error
|
||||||
flow = tenant.flow_recovery
|
flow = tenant.flow_recovery
|
||||||
if not flow:
|
if not flow:
|
||||||
|
|
|
@ -33,6 +33,7 @@ from authentik.lib.models import (
|
||||||
)
|
)
|
||||||
from authentik.lib.utils.http import get_client_ip
|
from authentik.lib.utils.http import get_client_ip
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
|
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
|
||||||
|
@ -168,7 +169,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||||
including the users attributes"""
|
including the users attributes"""
|
||||||
final_attributes = {}
|
final_attributes = {}
|
||||||
if request and hasattr(request, "tenant"):
|
if request and hasattr(request, "tenant"):
|
||||||
always_merger.merge(final_attributes, request.tenant.attributes)
|
always_merger.merge(final_attributes, get_tenant(request).attributes)
|
||||||
for group in self.ak_groups.all().order_by("name"):
|
for group in self.ak_groups.all().order_by("name"):
|
||||||
always_merger.merge(final_attributes, group.attributes)
|
always_merger.merge(final_attributes, group.attributes)
|
||||||
always_merger.merge(final_attributes, self.attributes)
|
always_merger.merge(final_attributes, self.attributes)
|
||||||
|
@ -227,7 +228,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOGGER.warning("Failed to get default locale", exc=exc)
|
LOGGER.warning("Failed to get default locale", exc=exc)
|
||||||
if request:
|
if request:
|
||||||
return request.tenant.locale
|
return get_tenant(request).locale
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -41,8 +41,7 @@ from authentik.lib.utils.http import get_client_ip, get_http_session
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.utils import get_fallback_tenant, get_tenant
|
||||||
from authentik.tenants.utils import DEFAULT_TENANT
|
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -57,7 +56,7 @@ def default_event_duration():
|
||||||
|
|
||||||
def default_tenant():
|
def default_tenant():
|
||||||
"""Get a default value for tenant"""
|
"""Get a default value for tenant"""
|
||||||
return sanitize_dict(model_to_dict(DEFAULT_TENANT))
|
return sanitize_dict(model_to_dict(get_fallback_tenant()))
|
||||||
|
|
||||||
|
|
||||||
class NotificationTransportError(SentryIgnoredException):
|
class NotificationTransportError(SentryIgnoredException):
|
||||||
|
@ -227,7 +226,7 @@ class Event(SerializerModel, ExpiringModel):
|
||||||
wrapped = self.context["http_request"]["args"][QS_QUERY]
|
wrapped = self.context["http_request"]["args"][QS_QUERY]
|
||||||
self.context["http_request"]["args"] = QueryDict(wrapped)
|
self.context["http_request"]["args"] = QueryDict(wrapped)
|
||||||
if hasattr(request, "tenant"):
|
if hasattr(request, "tenant"):
|
||||||
tenant: Tenant = request.tenant
|
tenant = get_tenant(request)
|
||||||
# Because self.created only gets set on save, we can't use it's value here
|
# Because self.created only gets set on save, we can't use it's value here
|
||||||
# hence we set self.created to now and then use it
|
# hence we set self.created to now and then use it
|
||||||
self.created = now()
|
self.created = now()
|
||||||
|
|
|
@ -21,7 +21,7 @@ from authentik.flows.models import Flow
|
||||||
from authentik.interfaces.models import Interface, InterfaceType
|
from authentik.interfaces.models import Interface, InterfaceType
|
||||||
from authentik.lib.utils.urls import reverse_with_qs
|
from authentik.lib.utils.urls import reverse_with_qs
|
||||||
from authentik.tenants.api import CurrentTenantSerializer
|
from authentik.tenants.api import CurrentTenantSerializer
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ def reverse_interface(
|
||||||
request: HttpRequest, interface_type: InterfaceType, query: Optional[QueryDict] = None, **kwargs
|
request: HttpRequest, interface_type: InterfaceType, query: Optional[QueryDict] = None, **kwargs
|
||||||
):
|
):
|
||||||
"""Reverse URL to configured default interface"""
|
"""Reverse URL to configured default interface"""
|
||||||
tenant: Tenant = request.tenant
|
tenant = get_tenant(request)
|
||||||
interface: Interface = None
|
interface: Interface = None
|
||||||
|
|
||||||
if interface_type == InterfaceType.USER:
|
if interface_type == InterfaceType.USER:
|
||||||
|
@ -90,7 +90,7 @@ class InterfaceView(View):
|
||||||
"""Get template context"""
|
"""Get template context"""
|
||||||
return {
|
return {
|
||||||
"config_json": dumps(ConfigView(request=Request(self.request)).get_config().data),
|
"config_json": dumps(ConfigView(request=Request(self.request)).get_config().data),
|
||||||
"tenant_json": dumps(CurrentTenantSerializer(self.request.tenant).data),
|
"tenant_json": dumps(CurrentTenantSerializer(get_tenant(self.request)).data),
|
||||||
"version_family": f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}",
|
"version_family": f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}",
|
||||||
"version_subdomain": f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}",
|
"version_subdomain": f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}",
|
||||||
"build": get_build_hash(),
|
"build": get_build_hash(),
|
||||||
|
|
|
@ -12,6 +12,7 @@ from authentik.lib.utils.http import get_http_session
|
||||||
from authentik.policies.models import Policy
|
from authentik.policies.models import Policy
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
RE_LOWER = re.compile("[a-z]")
|
RE_LOWER = re.compile("[a-z]")
|
||||||
|
@ -143,7 +144,8 @@ class PasswordPolicy(Policy):
|
||||||
user_inputs.append(request.user.name)
|
user_inputs.append(request.user.name)
|
||||||
user_inputs.append(request.user.email)
|
user_inputs.append(request.user.email)
|
||||||
if request.http_request:
|
if request.http_request:
|
||||||
user_inputs.append(request.http_request.tenant.branding_title)
|
tenant = get_tenant(request.http_request)
|
||||||
|
user_inputs.append(tenant.branding_title)
|
||||||
# Only calculate result for the first 100 characters, as with over 100 char
|
# Only calculate result for the first 100 characters, as with over 100 char
|
||||||
# long passwords we can be reasonably sure that they'll surpass the score anyways
|
# long passwords we can be reasonably sure that they'll surpass the score anyways
|
||||||
# See https://github.com/dropbox/zxcvbn#runtime-latency
|
# See https://github.com/dropbox/zxcvbn#runtime-latency
|
||||||
|
|
|
@ -27,7 +27,7 @@ from authentik.stages.consent.stage import (
|
||||||
PLAN_CONTEXT_CONSENT_HEADER,
|
PLAN_CONTEXT_CONSENT_HEADER,
|
||||||
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
||||||
)
|
)
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
QS_KEY_CODE = "code" # nosec
|
QS_KEY_CODE = "code" # nosec
|
||||||
|
@ -89,7 +89,7 @@ class DeviceEntryView(View):
|
||||||
"""View used to initiate the device-code flow, url entered by endusers"""
|
"""View used to initiate the device-code flow, url entered by endusers"""
|
||||||
|
|
||||||
def dispatch(self, request: HttpRequest) -> HttpResponse:
|
def dispatch(self, request: HttpRequest) -> HttpResponse:
|
||||||
tenant: Tenant = request.tenant
|
tenant = get_tenant(request)
|
||||||
device_flow = tenant.flow_device_code
|
device_flow = tenant.flow_device_code
|
||||||
if not device_flow:
|
if not device_flow:
|
||||||
LOGGER.info("Tenant has no device code flow configured", tenant=tenant)
|
LOGGER.info("Tenant has no device code flow configured", tenant=tenant)
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from authentik.providers.oauth2.constants import SCOPE_GITHUB_ORG_READ, SCOPE_GITHUB_USER_EMAIL
|
from authentik.providers.oauth2.constants import SCOPE_GITHUB_ORG_READ, SCOPE_GITHUB_USER_EMAIL
|
||||||
from authentik.providers.oauth2.models import RefreshToken
|
from authentik.providers.oauth2.models import RefreshToken
|
||||||
from authentik.providers.oauth2.utils import protected_resource_view
|
from authentik.providers.oauth2.utils import protected_resource_view
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
|
@ -76,6 +77,7 @@ class GitHubUserTeamsView(View):
|
||||||
def get(self, request: HttpRequest, token: RefreshToken) -> HttpResponse:
|
def get(self, request: HttpRequest, token: RefreshToken) -> HttpResponse:
|
||||||
"""Emulate GitHub's /user/teams API Endpoint"""
|
"""Emulate GitHub's /user/teams API Endpoint"""
|
||||||
user = token.user
|
user = token.user
|
||||||
|
tenant = get_tenant(request)
|
||||||
|
|
||||||
orgs_response = []
|
orgs_response = []
|
||||||
for org in user.ak_groups.all():
|
for org in user.ak_groups.all():
|
||||||
|
@ -97,7 +99,7 @@ class GitHubUserTeamsView(View):
|
||||||
"created_at": "",
|
"created_at": "",
|
||||||
"updated_at": "",
|
"updated_at": "",
|
||||||
"organization": {
|
"organization": {
|
||||||
"login": slugify(request.tenant.branding_title),
|
"login": slugify(tenant.branding_title),
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"node_id": "",
|
"node_id": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -109,7 +111,7 @@ class GitHubUserTeamsView(View):
|
||||||
"public_members_url": "",
|
"public_members_url": "",
|
||||||
"avatar_url": "",
|
"avatar_url": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"name": request.tenant.branding_title,
|
"name": tenant.branding_title,
|
||||||
"company": "",
|
"company": "",
|
||||||
"blog": "",
|
"blog": "",
|
||||||
"location": "",
|
"location": "",
|
||||||
|
|
|
@ -17,6 +17,7 @@ from authentik.flows.challenge import (
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage
|
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage
|
||||||
from authentik.stages.authenticator_totp.settings import OTP_TOTP_ISSUER
|
from authentik.stages.authenticator_totp.settings import OTP_TOTP_ISSUER
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
SESSION_TOTP_DEVICE = "totp_device"
|
SESSION_TOTP_DEVICE = "totp_device"
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ class AuthenticatorTOTPStageView(ChallengeStageView):
|
||||||
data={
|
data={
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"config_url": device.config_url.replace(
|
"config_url": device.config_url.replace(
|
||||||
OTP_TOTP_ISSUER, quote(self.request.tenant.branding_title)
|
OTP_TOTP_ISSUER, quote(get_tenant(self.request).branding_title)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,6 +33,7 @@ from authentik.stages.authenticator_validate.models import AuthenticatorValidate
|
||||||
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
|
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
|
||||||
from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE
|
from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE
|
||||||
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
|
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ def validate_challenge_duo(device_pk: int, stage_view: StageView, user: User) ->
|
||||||
type=__(
|
type=__(
|
||||||
"%(brand_name)s Login request"
|
"%(brand_name)s Login request"
|
||||||
% {
|
% {
|
||||||
"brand_name": stage_view.request.tenant.branding_title,
|
"brand_name": get_tenant(stage_view.request).branding_title,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
display_username=user.username,
|
display_username=user.username,
|
||||||
|
|
|
@ -19,7 +19,7 @@ from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, Duo
|
||||||
from authentik.stages.authenticator_validate.challenge import validate_challenge_duo
|
from authentik.stages.authenticator_validate.challenge import validate_challenge_duo
|
||||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||||
from authentik.stages.user_login.models import UserLoginStage
|
from authentik.stages.user_login.models import UserLoginStage
|
||||||
from authentik.tenants.utils import get_tenant_for_request
|
from authentik.tenants.utils import lookup_tenant_for_request
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||||
|
@ -36,7 +36,7 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||||
middleware = SessionMiddleware(dummy_get_response)
|
middleware = SessionMiddleware(dummy_get_response)
|
||||||
middleware.process_request(request)
|
middleware.process_request(request)
|
||||||
request.session.save()
|
request.session.save()
|
||||||
setattr(request, "tenant", get_tenant_for_request(request))
|
setattr(request, "tenant", lookup_tenant_for_request(request))
|
||||||
|
|
||||||
stage = AuthenticatorDuoStage.objects.create(
|
stage = AuthenticatorDuoStage.objects.create(
|
||||||
name=generate_id(),
|
name=generate_id(),
|
||||||
|
|
|
@ -29,6 +29,7 @@ from authentik.flows.challenge import (
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage, WebAuthnDevice
|
from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage, WebAuthnDevice
|
||||||
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
|
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
SESSION_KEY_WEBAUTHN_CHALLENGE = "authentik/stages/authenticator_webauthn/challenge"
|
SESSION_KEY_WEBAUTHN_CHALLENGE = "authentik/stages/authenticator_webauthn/challenge"
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
|
||||||
|
|
||||||
registration_options: PublicKeyCredentialCreationOptions = generate_registration_options(
|
registration_options: PublicKeyCredentialCreationOptions = generate_registration_options(
|
||||||
rp_id=get_rp_id(self.request),
|
rp_id=get_rp_id(self.request),
|
||||||
rp_name=self.request.tenant.branding_title,
|
rp_name=get_tenant(self.request).branding_title,
|
||||||
user_id=user.uid,
|
user_id=user.uid,
|
||||||
user_name=user.username,
|
user_name=user.username,
|
||||||
user_display_name=user.name,
|
user_display_name=user.name,
|
||||||
|
|
|
@ -18,6 +18,7 @@ from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
from authentik.tenants.utils import get_tenant
|
||||||
|
|
||||||
|
|
||||||
class FooterLinkSerializer(PassiveSerializer):
|
class FooterLinkSerializer(PassiveSerializer):
|
||||||
|
@ -139,5 +140,4 @@ class TenantViewSet(UsedByMixin, ModelViewSet):
|
||||||
@action(methods=["GET"], detail=False, permission_classes=[AllowAny])
|
@action(methods=["GET"], detail=False, permission_classes=[AllowAny])
|
||||||
def current(self, request: Request) -> Response:
|
def current(self, request: Request) -> Response:
|
||||||
"""Get current tenant"""
|
"""Get current tenant"""
|
||||||
tenant: Tenant = request._request.tenant
|
return Response(CurrentTenantSerializer(get_tenant(request)).data)
|
||||||
return Response(CurrentTenantSerializer(tenant).data)
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.http.response import HttpResponse
|
||||||
from django.utils.translation import activate
|
from django.utils.translation import activate
|
||||||
from sentry_sdk.api import set_tag
|
from sentry_sdk.api import set_tag
|
||||||
|
|
||||||
from authentik.tenants.utils import get_tenant_for_request
|
from authentik.tenants.utils import lookup_tenant_for_request
|
||||||
|
|
||||||
|
|
||||||
class TenantMiddleware:
|
class TenantMiddleware:
|
||||||
|
@ -19,7 +19,7 @@ class TenantMiddleware:
|
||||||
|
|
||||||
def __call__(self, request: HttpRequest) -> HttpResponse:
|
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||||
if not hasattr(request, "tenant"):
|
if not hasattr(request, "tenant"):
|
||||||
tenant = get_tenant_for_request(request)
|
tenant = lookup_tenant_for_request(request)
|
||||||
setattr(request, "tenant", tenant)
|
setattr(request, "tenant", tenant)
|
||||||
set_tag("authentik.tenant_uuid", tenant.tenant_uuid.hex)
|
set_tag("authentik.tenant_uuid", tenant.tenant_uuid.hex)
|
||||||
set_tag("authentik.tenant_domain", tenant.domain)
|
set_tag("authentik.tenant_domain", tenant.domain)
|
||||||
|
|
|
@ -13,7 +13,7 @@ def migrate_set_default(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
|
||||||
from authentik.blueprints.models import BlueprintInstance
|
from authentik.blueprints.models import BlueprintInstance
|
||||||
from authentik.blueprints.v1.importer import Importer
|
from authentik.blueprints.v1.importer import Importer
|
||||||
from authentik.blueprints.v1.tasks import blueprints_discover
|
from authentik.blueprints.v1.tasks import blueprints_discovery
|
||||||
from authentik.interfaces.models import InterfaceType
|
from authentik.interfaces.models import InterfaceType
|
||||||
|
|
||||||
# If we don't have any tenants yet, we don't need wait for the default interface blueprint
|
# If we don't have any tenants yet, we don't need wait for the default interface blueprint
|
||||||
|
@ -22,7 +22,7 @@ def migrate_set_default(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
|
||||||
interface_blueprint = BlueprintInstance.objects.filter(path="system/interfaces.yaml").first()
|
interface_blueprint = BlueprintInstance.objects.filter(path="system/interfaces.yaml").first()
|
||||||
if not interface_blueprint:
|
if not interface_blueprint:
|
||||||
blueprints_discover.delay().get()
|
blueprints_discovery.delay().get()
|
||||||
interface_blueprint = BlueprintInstance.objects.filter(
|
interface_blueprint = BlueprintInstance.objects.filter(
|
||||||
path="system/interfaces.yaml"
|
path="system/interfaces.yaml"
|
||||||
).first()
|
).first()
|
||||||
|
|
|
@ -7,8 +7,6 @@ from rest_framework.serializers import Serializer
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.flows.models import Flow
|
|
||||||
from authentik.interfaces.models import Interface
|
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
from authentik.lib.utils.time import timedelta_string_validator
|
from authentik.lib.utils.time import timedelta_string_validator
|
||||||
|
|
||||||
|
@ -34,38 +32,56 @@ class Tenant(SerializerModel):
|
||||||
branding_favicon = models.TextField(default="/static/dist/assets/icons/icon.png")
|
branding_favicon = models.TextField(default="/static/dist/assets/icons/icon.png")
|
||||||
|
|
||||||
flow_authentication = models.ForeignKey(
|
flow_authentication = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_authentication"
|
"authentik_flows.Flow",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="tenant_authentication",
|
||||||
)
|
)
|
||||||
flow_invalidation = models.ForeignKey(
|
flow_invalidation = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_invalidation"
|
"authentik_flows.Flow",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="tenant_invalidation",
|
||||||
)
|
)
|
||||||
flow_recovery = models.ForeignKey(
|
flow_recovery = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_recovery"
|
"authentik_flows.Flow", null=True, on_delete=models.SET_NULL, related_name="tenant_recovery"
|
||||||
)
|
)
|
||||||
flow_unenrollment = models.ForeignKey(
|
flow_unenrollment = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_unenrollment"
|
"authentik_flows.Flow",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="tenant_unenrollment",
|
||||||
)
|
)
|
||||||
flow_user_settings = models.ForeignKey(
|
flow_user_settings = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_user_settings"
|
"authentik_flows.Flow",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="tenant_user_settings",
|
||||||
)
|
)
|
||||||
flow_device_code = models.ForeignKey(
|
flow_device_code = models.ForeignKey(
|
||||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_device_code"
|
"authentik_flows.Flow",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="tenant_device_code",
|
||||||
)
|
)
|
||||||
|
|
||||||
interface_flow = models.ForeignKey(
|
interface_flow = models.ForeignKey(
|
||||||
Interface,
|
"authentik_interfaces.Interface",
|
||||||
|
default=None,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
null=True,
|
null=True,
|
||||||
related_name="tenant_flow",
|
related_name="tenant_flow",
|
||||||
)
|
)
|
||||||
interface_user = models.ForeignKey(
|
interface_user = models.ForeignKey(
|
||||||
Interface,
|
"authentik_interfaces.Interface",
|
||||||
|
default=None,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
null=True,
|
null=True,
|
||||||
related_name="tenant_user",
|
related_name="tenant_user",
|
||||||
)
|
)
|
||||||
interface_admin = models.ForeignKey(
|
interface_admin = models.ForeignKey(
|
||||||
Interface,
|
"authentik_interfaces.Interface",
|
||||||
|
default=None,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
null=True,
|
null=True,
|
||||||
related_name="tenant_admin",
|
related_name="tenant_admin",
|
||||||
|
|
|
@ -75,7 +75,7 @@ class TestTenants(APITestCase):
|
||||||
)
|
)
|
||||||
factory = RequestFactory()
|
factory = RequestFactory()
|
||||||
request = factory.get("/")
|
request = factory.get("/")
|
||||||
request.tenant = tenant
|
setattr(request, "tenant", tenant)
|
||||||
event = Event.new(action=EventAction.SYSTEM_EXCEPTION, message="test").from_http(request)
|
event = Event.new(action=EventAction.SYSTEM_EXCEPTION, message="test").from_http(request)
|
||||||
self.assertEqual(event.expires.day, (event.created + timedelta_from_string("weeks=3")).day)
|
self.assertEqual(event.expires.day, (event.created + timedelta_from_string("weeks=3")).day)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -1,27 +1,47 @@
|
||||||
"""Tenant utilities"""
|
"""Tenant utilities"""
|
||||||
from functools import cache
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.db.models import Value as V
|
from django.db.models import Value as V
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
|
from rest_framework.request import Request
|
||||||
from sentry_sdk.hub import Hub
|
from sentry_sdk.hub import Hub
|
||||||
|
|
||||||
from authentik import get_full_version
|
from authentik import get_full_version
|
||||||
|
from authentik.interfaces.models import Interface, InterfaceType
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
_q_default = Q(default=True)
|
_q_default = Q(default=True)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
|
||||||
def get_fallback_tenant():
|
def get_fallback_tenant():
|
||||||
"""Get fallback tenant"""
|
"""Get fallback tenant"""
|
||||||
return Tenant(domain="fallback")
|
|
||||||
|
fallback_interface = Interface(
|
||||||
|
url_name="fallback",
|
||||||
|
type=InterfaceType.FLOW,
|
||||||
|
template="Fallback interface",
|
||||||
|
)
|
||||||
|
return Tenant(
|
||||||
|
domain="fallback",
|
||||||
|
interface_flow=fallback_interface,
|
||||||
|
interface_user=fallback_interface,
|
||||||
|
interface_admin=fallback_interface,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
def get_tenant(request: HttpRequest | Request) -> "Tenant":
|
||||||
|
"""Get the request's tenant, falls back to a fallback tenant object"""
|
||||||
|
if isinstance(request, Request):
|
||||||
|
request = request._request
|
||||||
|
return getattr(request, "tenant", get_fallback_tenant())
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_tenant_for_request(request: HttpRequest) -> "Tenant":
|
||||||
"""Get tenant object for current request"""
|
"""Get tenant object for current request"""
|
||||||
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
db_tenants = (
|
db_tenants = (
|
||||||
Tenant.objects.annotate(host_domain=V(request.get_host()))
|
Tenant.objects.annotate(host_domain=V(request.get_host()))
|
||||||
.filter(Q(host_domain__iendswith=F("domain")) | _q_default)
|
.filter(Q(host_domain__iendswith=F("domain")) | _q_default)
|
||||||
|
@ -29,13 +49,13 @@ def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
||||||
)
|
)
|
||||||
tenants = list(db_tenants.all())
|
tenants = list(db_tenants.all())
|
||||||
if len(tenants) < 1:
|
if len(tenants) < 1:
|
||||||
return DEFAULT_TENANT
|
return get_fallback_tenant()
|
||||||
return tenants[0]
|
return tenants[0]
|
||||||
|
|
||||||
|
|
||||||
def context_processor(request: HttpRequest) -> dict[str, Any]:
|
def context_processor(request: HttpRequest) -> dict[str, Any]:
|
||||||
"""Context Processor that injects tenant object into every template"""
|
"""Context Processor that injects tenant object into every template"""
|
||||||
tenant = getattr(request, "tenant", DEFAULT_TENANT)
|
tenant = getattr(request, "tenant", get_fallback_tenant())
|
||||||
trace = ""
|
trace = ""
|
||||||
span = Hub.current.scope.span
|
span = Hub.current.scope.span
|
||||||
if span:
|
if span:
|
||||||
|
@ -46,6 +66,3 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
|
||||||
"sentry_trace": trace,
|
"sentry_trace": trace,
|
||||||
"version": get_full_version(),
|
"version": get_full_version(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_TENANT = get_fallback_tenant()
|
|
||||||
|
|
446
schema.yml
446
schema.yml
|
@ -3676,6 +3676,24 @@ paths:
|
||||||
description: flow_user_settings
|
description: flow_user_settings
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- name: interface_admin
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: interface_admin
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: interface_flow
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: interface_flow
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: interface_user
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: interface_user
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- name: ordering
|
- name: ordering
|
||||||
required: false
|
required: false
|
||||||
in: query
|
in: query
|
||||||
|
@ -7731,6 +7749,295 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/interfaces/:
|
||||||
|
get:
|
||||||
|
operationId: interfaces_list
|
||||||
|
description: Interface serializer
|
||||||
|
parameters:
|
||||||
|
- name: ordering
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: Which field to use when ordering the results.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: page
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: A page number within the paginated result set.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: page_size
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: Number of results to return per page.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: search
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: A search term.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: template
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: type
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- admin
|
||||||
|
- flow
|
||||||
|
- user
|
||||||
|
description: |-
|
||||||
|
* `user` - User
|
||||||
|
* `admin` - Admin
|
||||||
|
* `flow` - Flow
|
||||||
|
|
||||||
|
* `user` - User
|
||||||
|
* `admin` - Admin
|
||||||
|
* `flow` - Flow
|
||||||
|
- in: query
|
||||||
|
name: url_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PaginatedInterfaceList'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
post:
|
||||||
|
operationId: interfaces_create
|
||||||
|
description: Interface serializer
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/InterfaceRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Interface'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
/interfaces/{interface_uuid}/:
|
||||||
|
get:
|
||||||
|
operationId: interfaces_retrieve
|
||||||
|
description: Interface serializer
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: interface_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this interface.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Interface'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
put:
|
||||||
|
operationId: interfaces_update
|
||||||
|
description: Interface serializer
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: interface_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this interface.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/InterfaceRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Interface'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
patch:
|
||||||
|
operationId: interfaces_partial_update
|
||||||
|
description: Interface serializer
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: interface_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this interface.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchedInterfaceRequest'
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Interface'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
delete:
|
||||||
|
operationId: interfaces_destroy
|
||||||
|
description: Interface serializer
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: interface_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this interface.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: No response body
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
/interfaces/{interface_uuid}/used_by/:
|
||||||
|
get:
|
||||||
|
operationId: interfaces_used_by_list
|
||||||
|
description: Get a list of all objects that use this object
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: interface_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this interface.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- interfaces
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/UsedBy'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/managed/blueprints/:
|
/managed/blueprints/:
|
||||||
get:
|
get:
|
||||||
operationId: managed_blueprints_list
|
operationId: managed_blueprints_list
|
||||||
|
@ -26307,6 +26614,7 @@ components:
|
||||||
- authentik.admin
|
- authentik.admin
|
||||||
- authentik.api
|
- authentik.api
|
||||||
- authentik.crypto
|
- authentik.crypto
|
||||||
|
- authentik.interfaces
|
||||||
- authentik.events
|
- authentik.events
|
||||||
- authentik.flows
|
- authentik.flows
|
||||||
- authentik.lib
|
- authentik.lib
|
||||||
|
@ -26357,6 +26665,7 @@ components:
|
||||||
* `authentik.admin` - authentik Admin
|
* `authentik.admin` - authentik Admin
|
||||||
* `authentik.api` - authentik API
|
* `authentik.api` - authentik API
|
||||||
* `authentik.crypto` - authentik Crypto
|
* `authentik.crypto` - authentik Crypto
|
||||||
|
* `authentik.interfaces` - authentik Interfaces
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
* `authentik.flows` - authentik Flows
|
* `authentik.flows` - authentik Flows
|
||||||
* `authentik.lib` - authentik lib
|
* `authentik.lib` - authentik lib
|
||||||
|
@ -29074,6 +29383,7 @@ components:
|
||||||
* `authentik.admin` - authentik Admin
|
* `authentik.admin` - authentik Admin
|
||||||
* `authentik.api` - authentik API
|
* `authentik.api` - authentik API
|
||||||
* `authentik.crypto` - authentik Crypto
|
* `authentik.crypto` - authentik Crypto
|
||||||
|
* `authentik.interfaces` - authentik Interfaces
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
* `authentik.flows` - authentik Flows
|
* `authentik.flows` - authentik Flows
|
||||||
* `authentik.lib` - authentik lib
|
* `authentik.lib` - authentik lib
|
||||||
|
@ -29184,6 +29494,7 @@ components:
|
||||||
* `authentik.admin` - authentik Admin
|
* `authentik.admin` - authentik Admin
|
||||||
* `authentik.api` - authentik API
|
* `authentik.api` - authentik API
|
||||||
* `authentik.crypto` - authentik Crypto
|
* `authentik.crypto` - authentik Crypto
|
||||||
|
* `authentik.interfaces` - authentik Interfaces
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
* `authentik.flows` - authentik Flows
|
* `authentik.flows` - authentik Flows
|
||||||
* `authentik.lib` - authentik lib
|
* `authentik.lib` - authentik lib
|
||||||
|
@ -30297,6 +30608,55 @@ components:
|
||||||
* `api` - Intent Api
|
* `api` - Intent Api
|
||||||
* `recovery` - Intent Recovery
|
* `recovery` - Intent Recovery
|
||||||
* `app_password` - Intent App Password
|
* `app_password` - Intent App Password
|
||||||
|
Interface:
|
||||||
|
type: object
|
||||||
|
description: Interface serializer
|
||||||
|
properties:
|
||||||
|
interface_uuid:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
readOnly: true
|
||||||
|
url_name:
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/InterfaceTypeEnum'
|
||||||
|
template:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- interface_uuid
|
||||||
|
- template
|
||||||
|
- type
|
||||||
|
- url_name
|
||||||
|
InterfaceRequest:
|
||||||
|
type: object
|
||||||
|
description: Interface serializer
|
||||||
|
properties:
|
||||||
|
url_name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 50
|
||||||
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/InterfaceTypeEnum'
|
||||||
|
template:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
required:
|
||||||
|
- template
|
||||||
|
- type
|
||||||
|
- url_name
|
||||||
|
InterfaceTypeEnum:
|
||||||
|
enum:
|
||||||
|
- user
|
||||||
|
- admin
|
||||||
|
- flow
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
* `user` - User
|
||||||
|
* `admin` - Admin
|
||||||
|
* `flow` - Flow
|
||||||
InvalidResponseActionEnum:
|
InvalidResponseActionEnum:
|
||||||
enum:
|
enum:
|
||||||
- retry
|
- retry
|
||||||
|
@ -33051,6 +33411,41 @@ components:
|
||||||
required:
|
required:
|
||||||
- pagination
|
- pagination
|
||||||
- results
|
- results
|
||||||
|
PaginatedInterfaceList:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
pagination:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
next:
|
||||||
|
type: number
|
||||||
|
previous:
|
||||||
|
type: number
|
||||||
|
count:
|
||||||
|
type: number
|
||||||
|
current:
|
||||||
|
type: number
|
||||||
|
total_pages:
|
||||||
|
type: number
|
||||||
|
start_index:
|
||||||
|
type: number
|
||||||
|
end_index:
|
||||||
|
type: number
|
||||||
|
required:
|
||||||
|
- next
|
||||||
|
- previous
|
||||||
|
- count
|
||||||
|
- current
|
||||||
|
- total_pages
|
||||||
|
- start_index
|
||||||
|
- end_index
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Interface'
|
||||||
|
required:
|
||||||
|
- pagination
|
||||||
|
- results
|
||||||
PaginatedInvitationList:
|
PaginatedInvitationList:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -35870,6 +36265,7 @@ components:
|
||||||
* `authentik.admin` - authentik Admin
|
* `authentik.admin` - authentik Admin
|
||||||
* `authentik.api` - authentik API
|
* `authentik.api` - authentik API
|
||||||
* `authentik.crypto` - authentik Crypto
|
* `authentik.crypto` - authentik Crypto
|
||||||
|
* `authentik.interfaces` - authentik Interfaces
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
* `authentik.flows` - authentik Flows
|
* `authentik.flows` - authentik Flows
|
||||||
* `authentik.lib` - authentik lib
|
* `authentik.lib` - authentik lib
|
||||||
|
@ -36121,6 +36517,20 @@ components:
|
||||||
description: Specify which sources should be shown.
|
description: Specify which sources should be shown.
|
||||||
show_source_labels:
|
show_source_labels:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
PatchedInterfaceRequest:
|
||||||
|
type: object
|
||||||
|
description: Interface serializer
|
||||||
|
properties:
|
||||||
|
url_name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 50
|
||||||
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/InterfaceTypeEnum'
|
||||||
|
template:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
PatchedInvitationRequest:
|
PatchedInvitationRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Invitation Serializer
|
description: Invitation Serializer
|
||||||
|
@ -37405,6 +37815,18 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
|
interface_admin:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_user:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_flow:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
event_retention:
|
event_retention:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
@ -40576,6 +40998,18 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
|
interface_admin:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_user:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_flow:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
event_retention:
|
event_retention:
|
||||||
type: string
|
type: string
|
||||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||||
|
@ -40634,6 +41068,18 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
|
interface_admin:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_user:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
interface_flow:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
event_retention:
|
event_retention:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
|
Reference in a new issue