tenants: initial implementation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
a1b6e09e8a
commit
ff611f21cd
|
@ -14,13 +14,6 @@ from authentik.core.api.utils import PassiveSerializer
|
|||
from authentik.lib.config import CONFIG
|
||||
|
||||
|
||||
class FooterLinkSerializer(PassiveSerializer):
|
||||
"""Links returned in Config API"""
|
||||
|
||||
href = CharField(read_only=True)
|
||||
name = CharField(read_only=True)
|
||||
|
||||
|
||||
class Capabilities(models.TextChoices):
|
||||
"""Define capabilities which influence which APIs can/should be used"""
|
||||
|
||||
|
@ -30,10 +23,6 @@ class Capabilities(models.TextChoices):
|
|||
class ConfigSerializer(PassiveSerializer):
|
||||
"""Serialize authentik Config into DRF Object"""
|
||||
|
||||
branding_logo = CharField(read_only=True)
|
||||
branding_title = CharField(read_only=True)
|
||||
ui_footer_links = ListField(child=FooterLinkSerializer(), read_only=True)
|
||||
|
||||
error_reporting_enabled = BooleanField(read_only=True)
|
||||
error_reporting_environment = CharField(read_only=True)
|
||||
error_reporting_send_pii = BooleanField(read_only=True)
|
||||
|
@ -59,12 +48,9 @@ class ConfigView(APIView):
|
|||
"""Retrive public configuration options"""
|
||||
config = ConfigSerializer(
|
||||
{
|
||||
"branding_logo": CONFIG.y("authentik.branding.logo"),
|
||||
"branding_title": CONFIG.y("authentik.branding.title"),
|
||||
"error_reporting_enabled": CONFIG.y("error_reporting.enabled"),
|
||||
"error_reporting_environment": CONFIG.y("error_reporting.environment"),
|
||||
"error_reporting_send_pii": CONFIG.y("error_reporting.send_pii"),
|
||||
"ui_footer_links": CONFIG.y("authentik.footer_links"),
|
||||
"capabilities": self.get_capabilities(),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -100,6 +100,7 @@ from authentik.stages.user_delete.api import UserDeleteStageViewSet
|
|||
from authentik.stages.user_login.api import UserLoginStageViewSet
|
||||
from authentik.stages.user_logout.api import UserLogoutStageViewSet
|
||||
from authentik.stages.user_write.api import UserWriteStageViewSet
|
||||
from authentik.tenants.api import TenantViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
|
@ -111,6 +112,7 @@ router.register("core/groups", GroupViewSet)
|
|||
router.register("core/users", UserViewSet)
|
||||
router.register("core/user_consent", UserConsentViewSet)
|
||||
router.register("core/tokens", TokenViewSet)
|
||||
router.register("core/tenants", TenantViewSet)
|
||||
|
||||
router.register("outposts/instances", OutpostViewSet)
|
||||
router.register("outposts/service_connections/all", ServiceConnectionViewSet)
|
||||
|
|
|
@ -48,9 +48,6 @@ outposts:
|
|||
authentik:
|
||||
avatars: gravatar # gravatar or none
|
||||
geoip: ""
|
||||
branding:
|
||||
title: authentik
|
||||
logo: /static/dist/assets/icons/icon_left_brand.svg
|
||||
# Optionally add links to the footer on the login page
|
||||
footer_links:
|
||||
- name: Documentation
|
||||
|
|
|
@ -6,7 +6,7 @@ class AuthentikManagedConfig(AppConfig):
|
|||
"""authentik Managed app"""
|
||||
|
||||
name = "authentik.managed"
|
||||
label = "authentik_Managed"
|
||||
label = "authentik_managed"
|
||||
verbose_name = "authentik Managed"
|
||||
|
||||
def ready(self) -> None:
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# Generated by Django 3.2.3 on 2021-05-25 12:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_policies_event_matcher", "0014_alter_eventmatcherpolicy_app"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="eventmatcherpolicy",
|
||||
name="app",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("authentik.admin", "authentik Admin"),
|
||||
("authentik.api", "authentik API"),
|
||||
("authentik.events", "authentik Events"),
|
||||
("authentik.crypto", "authentik Crypto"),
|
||||
("authentik.flows", "authentik Flows"),
|
||||
("authentik.outposts", "authentik Outpost"),
|
||||
("authentik.lib", "authentik lib"),
|
||||
("authentik.policies", "authentik Policies"),
|
||||
("authentik.policies.dummy", "authentik Policies.Dummy"),
|
||||
(
|
||||
"authentik.policies.event_matcher",
|
||||
"authentik Policies.Event Matcher",
|
||||
),
|
||||
("authentik.policies.expiry", "authentik Policies.Expiry"),
|
||||
("authentik.policies.expression", "authentik Policies.Expression"),
|
||||
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
|
||||
("authentik.policies.password", "authentik Policies.Password"),
|
||||
("authentik.policies.reputation", "authentik Policies.Reputation"),
|
||||
("authentik.providers.proxy", "authentik Providers.Proxy"),
|
||||
("authentik.providers.ldap", "authentik Providers.LDAP"),
|
||||
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
|
||||
("authentik.providers.saml", "authentik Providers.SAML"),
|
||||
("authentik.recovery", "authentik Recovery"),
|
||||
("authentik.sources.ldap", "authentik Sources.LDAP"),
|
||||
("authentik.sources.oauth", "authentik Sources.OAuth"),
|
||||
("authentik.sources.plex", "authentik Sources.Plex"),
|
||||
("authentik.sources.saml", "authentik Sources.SAML"),
|
||||
(
|
||||
"authentik.stages.authenticator_duo",
|
||||
"authentik Stages.Authenticator.Duo",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_static",
|
||||
"authentik Stages.Authenticator.Static",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_totp",
|
||||
"authentik Stages.Authenticator.TOTP",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_validate",
|
||||
"authentik Stages.Authenticator.Validate",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_webauthn",
|
||||
"authentik Stages.Authenticator.WebAuthn",
|
||||
),
|
||||
("authentik.stages.captcha", "authentik Stages.Captcha"),
|
||||
("authentik.stages.consent", "authentik Stages.Consent"),
|
||||
("authentik.stages.deny", "authentik Stages.Deny"),
|
||||
("authentik.stages.dummy", "authentik Stages.Dummy"),
|
||||
("authentik.stages.email", "authentik Stages.Email"),
|
||||
(
|
||||
"authentik.stages.identification",
|
||||
"authentik Stages.Identification",
|
||||
),
|
||||
("authentik.stages.invitation", "authentik Stages.User Invitation"),
|
||||
("authentik.stages.password", "authentik Stages.Password"),
|
||||
("authentik.stages.prompt", "authentik Stages.Prompt"),
|
||||
("authentik.stages.user_delete", "authentik Stages.User Delete"),
|
||||
("authentik.stages.user_login", "authentik Stages.User Login"),
|
||||
("authentik.stages.user_logout", "authentik Stages.User Logout"),
|
||||
("authentik.stages.user_write", "authentik Stages.User Write"),
|
||||
("authentik.tenants", "authentik Tenants"),
|
||||
("authentik.core", "authentik Core"),
|
||||
("authentik.managed", "authentik Managed"),
|
||||
],
|
||||
default="",
|
||||
help_text="Match events created by selected application. When left empty, all applications are matched.",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -127,6 +127,7 @@ INSTALLED_APPS = [
|
|||
"authentik.stages.user_login",
|
||||
"authentik.stages.user_logout",
|
||||
"authentik.stages.user_write",
|
||||
"authentik.tenants",
|
||||
"rest_framework",
|
||||
"django_filters",
|
||||
"drf_spectacular",
|
||||
|
@ -208,6 +209,7 @@ MIDDLEWARE = [
|
|||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"authentik.core.middleware.RequestIDMiddleware",
|
||||
"authentik.tenants.middleware.TenantMiddleware",
|
||||
"authentik.events.middleware.AuditMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
"""Serializer for tenant models"""
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, ListField
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
||||
class FooterLinkSerializer(PassiveSerializer):
|
||||
"""Links returned in Config API"""
|
||||
|
||||
href = CharField(read_only=True)
|
||||
name = CharField(read_only=True)
|
||||
|
||||
|
||||
class TenantSerializer(ModelSerializer):
|
||||
"""Tenant Serializer"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Tenant
|
||||
fields = [
|
||||
"tenant_uuid",
|
||||
"domain",
|
||||
"default",
|
||||
"branding_title",
|
||||
"branding_logo",
|
||||
"flow_authentication",
|
||||
"flow_invalidation",
|
||||
"flow_recovery",
|
||||
"flow_enrollment",
|
||||
"flow_unenrollment",
|
||||
]
|
||||
|
||||
|
||||
class CurrentTenantSerializer(PassiveSerializer):
|
||||
"""Partial tenant information for styling"""
|
||||
|
||||
branding_title = CharField()
|
||||
branding_logo = CharField()
|
||||
ui_footer_links = ListField(
|
||||
child=FooterLinkSerializer(),
|
||||
read_only=True,
|
||||
default=CONFIG.y("authentik.footer_links"),
|
||||
)
|
||||
|
||||
|
||||
class TenantViewSet(ModelViewSet):
|
||||
"""Tenant Viewset"""
|
||||
|
||||
queryset = Tenant.objects.all()
|
||||
serializer_class = TenantSerializer
|
||||
search_fields = [
|
||||
"domain",
|
||||
"branding_title",
|
||||
]
|
||||
ordering = ["domain"]
|
||||
|
||||
@extend_schema(
|
||||
responses=CurrentTenantSerializer(many=False),
|
||||
)
|
||||
@action(methods=["GET"], detail=False, permission_classes=[AllowAny])
|
||||
# pylint: disable=invalid-name, unused-argument
|
||||
def current(self, request: Request) -> Response:
|
||||
"""Get current tenant"""
|
||||
tenant: Tenant = request._request.tenant
|
||||
return Response(CurrentTenantSerializer(tenant).data)
|
|
@ -0,0 +1,10 @@
|
|||
"""authentik tenant app"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthentikTenantsConfig(AppConfig):
|
||||
"""authentik Tenant app"""
|
||||
|
||||
name = "authentik.tenants"
|
||||
label = "authentik_tenants"
|
||||
verbose_name = "authentik Tenants"
|
|
@ -0,0 +1,22 @@
|
|||
"""Inject tenant into current request"""
|
||||
from typing import Callable
|
||||
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
|
||||
from authentik.tenants.utils import get_tenant_for_request
|
||||
|
||||
|
||||
class TenantMiddleware:
|
||||
"""Add current tenant to http request"""
|
||||
|
||||
get_response: Callable[[HttpRequest], HttpResponse]
|
||||
|
||||
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||
if not hasattr(request, "tenant"):
|
||||
tenant = get_tenant_for_request(request)
|
||||
setattr(request, "tenant", tenant)
|
||||
return self.get_response(request)
|
|
@ -0,0 +1,95 @@
|
|||
# Generated by Django 3.2.3 on 2021-05-29 12:18
|
||||
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("authentik_flows", "0018_oob_flows"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Tenant",
|
||||
fields=[
|
||||
(
|
||||
"tenant_uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"domain",
|
||||
models.TextField(
|
||||
help_text="Domain that activates this tenant. Can be a superset, i.e. `a.b` for `aa.b` and `ba.b`"
|
||||
),
|
||||
),
|
||||
("default", models.BooleanField(default=False)),
|
||||
("branding_title", models.TextField(default="authentik")),
|
||||
(
|
||||
"branding_logo",
|
||||
models.TextField(
|
||||
default="/static/dist/assets/icons/icon_left_brand.svg"
|
||||
),
|
||||
),
|
||||
(
|
||||
"flow_authentication",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="tenant_authentication",
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
(
|
||||
"flow_enrollment",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="tenant_enrollment",
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
(
|
||||
"flow_invalidation",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="tenant_invalidation",
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
(
|
||||
"flow_recovery",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="tenant_recovery",
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
(
|
||||
"flow_unenrollment",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="tenant_unenrollment",
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Tenant",
|
||||
"verbose_name_plural": "Tenants",
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,51 @@
|
|||
"""tenant models"""
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentik.flows.models import Flow
|
||||
|
||||
|
||||
class Tenant(models.Model):
|
||||
"""Single tenant"""
|
||||
|
||||
tenant_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
domain = models.TextField(
|
||||
help_text=_(
|
||||
"Domain that activates this tenant. "
|
||||
"Can be a superset, i.e. `a.b` for `aa.b` and `ba.b`"
|
||||
)
|
||||
)
|
||||
default = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
branding_title = models.TextField(default="authentik")
|
||||
branding_logo = models.TextField(
|
||||
default="/static/dist/assets/icons/icon_left_brand.svg"
|
||||
)
|
||||
|
||||
flow_authentication = models.ForeignKey(
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_authentication"
|
||||
)
|
||||
flow_invalidation = models.ForeignKey(
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_invalidation"
|
||||
)
|
||||
flow_recovery = models.ForeignKey(
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_recovery"
|
||||
)
|
||||
flow_enrollment = models.ForeignKey(
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_enrollment"
|
||||
)
|
||||
flow_unenrollment = models.ForeignKey(
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_unenrollment"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.domain
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _("Tenant")
|
||||
verbose_name_plural = _("Tenants")
|
|
@ -0,0 +1,17 @@
|
|||
"""Tenant utilities"""
|
||||
from django.db.models import Q
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
_q_default = Q(default=True)
|
||||
|
||||
|
||||
def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
||||
"""Get tenant object for current request"""
|
||||
db_tenants = Tenant.objects.filter(
|
||||
Q(domain__iendswith=request.get_host()) | _q_default
|
||||
)
|
||||
if not db_tenants.exists():
|
||||
return Tenant()
|
||||
return db_tenants.first()
|
408
schema.yml
408
schema.yml
|
@ -1712,6 +1712,231 @@ paths:
|
|||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/api/v2beta/core/tenants/:
|
||||
get:
|
||||
operationId: core_tenants_list
|
||||
description: Tenant Viewset
|
||||
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
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedTenantList'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
post:
|
||||
operationId: core_tenants_create
|
||||
description: Tenant Viewset
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/api/v2beta/core/tenants/{tenant_uuid}/:
|
||||
get:
|
||||
operationId: core_tenants_retrieve
|
||||
description: Tenant Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: tenant_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Tenant.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
put:
|
||||
operationId: core_tenants_update
|
||||
description: Tenant Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: tenant_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Tenant.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TenantRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
patch:
|
||||
operationId: core_tenants_partial_update
|
||||
description: Tenant Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: tenant_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Tenant.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTenantRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTenantRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTenantRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
delete:
|
||||
operationId: core_tenants_destroy
|
||||
description: Tenant Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: tenant_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Tenant.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/api/v2beta/core/tenants/current/:
|
||||
get:
|
||||
operationId: core_tenants_current_retrieve
|
||||
description: Get current tenant
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
- {}
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CurrentTenant'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/api/v2beta/core/tokens/:
|
||||
get:
|
||||
operationId: core_tokens_list
|
||||
|
@ -15291,6 +15516,7 @@ components:
|
|||
- authentik.stages.user_login
|
||||
- authentik.stages.user_logout
|
||||
- authentik.stages.user_write
|
||||
- authentik.tenants
|
||||
- authentik.core
|
||||
- authentik.managed
|
||||
type: string
|
||||
|
@ -16151,17 +16377,6 @@ components:
|
|||
type: object
|
||||
description: Serialize authentik Config into DRF Object
|
||||
properties:
|
||||
branding_logo:
|
||||
type: string
|
||||
readOnly: true
|
||||
branding_title:
|
||||
type: string
|
||||
readOnly: true
|
||||
ui_footer_links:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FooterLink'
|
||||
readOnly: true
|
||||
error_reporting_enabled:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
|
@ -16176,13 +16391,10 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/CapabilitiesEnum'
|
||||
required:
|
||||
- branding_logo
|
||||
- branding_title
|
||||
- capabilities
|
||||
- error_reporting_enabled
|
||||
- error_reporting_environment
|
||||
- error_reporting_send_pii
|
||||
- ui_footer_links
|
||||
ConsentChallenge:
|
||||
type: object
|
||||
description: Challenge info for consent screens
|
||||
|
@ -16298,6 +16510,28 @@ components:
|
|||
required:
|
||||
- x_cord
|
||||
- y_cord
|
||||
CurrentTenant:
|
||||
type: object
|
||||
description: Partial tenant information for styling
|
||||
properties:
|
||||
branding_title:
|
||||
type: string
|
||||
branding_logo:
|
||||
type: string
|
||||
ui_footer_links:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FooterLink'
|
||||
readOnly: true
|
||||
default:
|
||||
- href: https://goauthentik.io/docs/
|
||||
name: Documentation
|
||||
- href: https://goauthentik.io/
|
||||
name: authentik Website
|
||||
required:
|
||||
- branding_logo
|
||||
- branding_title
|
||||
- ui_footer_links
|
||||
DenyStage:
|
||||
type: object
|
||||
description: DenyStage Serializer
|
||||
|
@ -20893,6 +21127,41 @@ components:
|
|||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedTenantList:
|
||||
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/Tenant'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedTokenList:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -22830,6 +23099,40 @@ components:
|
|||
type: string
|
||||
description: The human-readable name of this device.
|
||||
maxLength: 64
|
||||
PatchedTenantRequest:
|
||||
type: object
|
||||
description: Tenant Serializer
|
||||
properties:
|
||||
domain:
|
||||
type: string
|
||||
description: Domain that activates this tenant. Can be a superset, i.e.
|
||||
`a.b` for `aa.b` and `ba.b`
|
||||
default:
|
||||
type: boolean
|
||||
branding_title:
|
||||
type: string
|
||||
branding_logo:
|
||||
type: string
|
||||
flow_authentication:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_invalidation:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_recovery:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_enrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_unenrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
PatchedTokenRequest:
|
||||
type: object
|
||||
description: Token Serializer
|
||||
|
@ -24779,6 +25082,83 @@ components:
|
|||
- task_description
|
||||
- task_finish_timestamp
|
||||
- task_name
|
||||
Tenant:
|
||||
type: object
|
||||
description: Tenant Serializer
|
||||
properties:
|
||||
tenant_uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
domain:
|
||||
type: string
|
||||
description: Domain that activates this tenant. Can be a superset, i.e.
|
||||
`a.b` for `aa.b` and `ba.b`
|
||||
default:
|
||||
type: boolean
|
||||
branding_title:
|
||||
type: string
|
||||
branding_logo:
|
||||
type: string
|
||||
flow_authentication:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_invalidation:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_recovery:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_enrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_unenrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
required:
|
||||
- domain
|
||||
- tenant_uuid
|
||||
TenantRequest:
|
||||
type: object
|
||||
description: Tenant Serializer
|
||||
properties:
|
||||
domain:
|
||||
type: string
|
||||
description: Domain that activates this tenant. Can be a superset, i.e.
|
||||
`a.b` for `aa.b` and `ba.b`
|
||||
default:
|
||||
type: boolean
|
||||
branding_title:
|
||||
type: string
|
||||
branding_logo:
|
||||
type: string
|
||||
flow_authentication:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_invalidation:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_recovery:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_enrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
flow_unenrollment:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
required:
|
||||
- domain
|
||||
Token:
|
||||
type: object
|
||||
description: Token Serializer
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Config, Configuration, Middleware, ResponseContext, RootApi } from "authentik-api";
|
||||
import { Config, Configuration, CoreApi, CurrentTenant, Middleware, ResponseContext, RootApi, Tenant } from "authentik-api";
|
||||
import { getCookie } from "../utils";
|
||||
import { API_DRAWER_MIDDLEWARE } from "../elements/notifications/APIDrawer";
|
||||
import { MessageMiddleware } from "../elements/messages/Middleware";
|
||||
|
@ -20,6 +20,14 @@ export function config(): Promise<Config> {
|
|||
return globalConfigPromise;
|
||||
}
|
||||
|
||||
let globalTenantPromise: Promise<CurrentTenant>;
|
||||
export function tenant(): Promise<CurrentTenant> {
|
||||
if (!globalTenantPromise) {
|
||||
globalTenantPromise = new CoreApi(DEFAULT_CONFIG).coreTenantsCurrentRetrieve();
|
||||
}
|
||||
return globalTenantPromise;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG = new Configuration({
|
||||
basePath: "",
|
||||
headers: {
|
||||
|
|
|
@ -5,7 +5,7 @@ import AKGlobal from "../authentik.css";
|
|||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import { EVENT_SIDEBAR_TOGGLE, TITLE_DEFAULT } from "../constants";
|
||||
import { config } from "../api/Config";
|
||||
import { tenant } from "../api/Config";
|
||||
|
||||
@customElement("ak-page-header")
|
||||
export class PageHeader extends LitElement {
|
||||
|
@ -18,11 +18,11 @@ export class PageHeader extends LitElement {
|
|||
|
||||
@property()
|
||||
set header(value: string) {
|
||||
config().then(config => {
|
||||
tenant().then(tenant => {
|
||||
if (value !== "") {
|
||||
document.title = `${value} - ${config.brandingTitle}`;
|
||||
document.title = `${value} - ${tenant.brandingTitle}`;
|
||||
} else {
|
||||
document.title = config.brandingTitle || TITLE_DEFAULT;
|
||||
document.title = tenant.brandingTitle || TITLE_DEFAULT;
|
||||
}
|
||||
});
|
||||
this._header = value;
|
||||
|
|
|
@ -6,29 +6,25 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import AKGlobal from "../../authentik.css";
|
||||
|
||||
import { configureSentry } from "../../api/Sentry";
|
||||
import { Config } from "authentik-api";
|
||||
import { CurrentTenant } from "authentik-api";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { EVENT_SIDEBAR_TOGGLE } from "../../constants";
|
||||
import { tenant } from "../../api/Config";
|
||||
|
||||
// If the viewport is wider than MIN_WIDTH, the sidebar
|
||||
// is shown besides the content, and not overlayed.
|
||||
export const MIN_WIDTH = 1200;
|
||||
|
||||
export const DefaultConfig: Config = {
|
||||
export const DefaultTenant: CurrentTenant = {
|
||||
brandingLogo: " /static/dist/assets/icons/icon_left_brand.svg",
|
||||
brandingTitle: "authentik",
|
||||
|
||||
errorReportingEnabled: false,
|
||||
errorReportingEnvironment: "",
|
||||
errorReportingSendPii: false,
|
||||
uiFooterLinks: [],
|
||||
capabilities: [],
|
||||
};
|
||||
|
||||
@customElement("ak-sidebar-brand")
|
||||
export class SidebarBrand extends LitElement {
|
||||
@property({attribute: false})
|
||||
config: Config = DefaultConfig;
|
||||
tenant: CurrentTenant = DefaultTenant;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
|
@ -68,7 +64,8 @@ export class SidebarBrand extends LitElement {
|
|||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
configureSentry(true).then((c) => {this.config = c;});
|
||||
configureSentry(true);
|
||||
tenant().then(tenant => this.tenant = tenant);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
@ -89,7 +86,7 @@ export class SidebarBrand extends LitElement {
|
|||
` : html``}
|
||||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img src="${ifDefined(this.config.brandingLogo)}" alt="authentik icon" loading="lazy" />
|
||||
<img src="${ifDefined(this.tenant.brandingLogo)}" alt="authentik icon" loading="lazy" />
|
||||
</div>
|
||||
</a>`;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ import "./stages/password/PasswordStage";
|
|||
import "./stages/prompt/PromptStage";
|
||||
import "./sources/plex/PlexLoginInit";
|
||||
import { StageHost } from "./stages/base";
|
||||
import { ChallengeChoices, Config, FlowChallengeRequest, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
|
||||
import { config, DEFAULT_CONFIG } from "../api/Config";
|
||||
import { ChallengeChoices, CurrentTenant, FlowChallengeRequest, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
|
||||
import { DEFAULT_CONFIG, tenant } from "../api/Config";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PFSize } from "../elements/Spinner";
|
||||
|
@ -46,7 +46,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
loading = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
config?: Config;
|
||||
tenant?: CurrentTenant;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css`
|
||||
|
@ -85,11 +85,11 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
}
|
||||
|
||||
private postUpdate(): void {
|
||||
config().then(config => {
|
||||
tenant().then(tenant => {
|
||||
if (this.challenge?.title) {
|
||||
document.title = `${this.challenge.title} - ${config.brandingTitle}`;
|
||||
document.title = `${this.challenge.title} - ${tenant.brandingTitle}`;
|
||||
} else {
|
||||
document.title = config.brandingTitle || TITLE_DEFAULT;
|
||||
document.title = tenant.brandingTitle || TITLE_DEFAULT;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -115,9 +115,8 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
configureSentry().then((config) => {
|
||||
this.config = config;
|
||||
});
|
||||
configureSentry();
|
||||
tenant().then(tenant => this.tenant = tenant);
|
||||
this.loading = true;
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
|
||||
flowSlug: this.flowSlug,
|
||||
|
@ -255,7 +254,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
<div class="ak-login-container">
|
||||
<header class="pf-c-login__header">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img src="${ifDefined(this.config?.brandingLogo)}" alt="authentik icon" />
|
||||
<img src="${ifDefined(this.tenant?.brandingLogo)}" alt="authentik icon" />
|
||||
</div>
|
||||
</header>
|
||||
<div class="pf-c-login__main">
|
||||
|
@ -264,12 +263,12 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
<footer class="pf-c-login__footer">
|
||||
<p></p>
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
${until(this.config?.uiFooterLinks?.map((link) => {
|
||||
${until(this.tenant?.uiFooterLinks?.map((link) => {
|
||||
return html`<li>
|
||||
<a href="${link.href || ""}">${link.name}</a>
|
||||
</li>`;
|
||||
}))}
|
||||
${this.config?.brandingTitle != "authentik" ? html`
|
||||
${this.tenant?.brandingTitle != "authentik" ? html`
|
||||
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>` : html``}
|
||||
</ul>
|
||||
</footer>
|
||||
|
|
|
@ -522,6 +522,14 @@ msgstr "Changelog"
|
|||
msgid "Characters which are considered as symbols."
|
||||
msgstr "Characters which are considered as symbols."
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Check"
|
||||
msgstr "Check"
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Check Application access"
|
||||
msgstr "Check Application access"
|
||||
|
||||
#: src/pages/policies/reputation/ReputationPolicyForm.ts
|
||||
msgid "Check IP"
|
||||
msgstr "Check IP"
|
||||
|
@ -530,6 +538,10 @@ msgstr "Check IP"
|
|||
msgid "Check Username"
|
||||
msgstr "Check Username"
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Check access"
|
||||
msgstr "Check access"
|
||||
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
msgid "Check status"
|
||||
msgstr "Check status"
|
||||
|
@ -1625,6 +1637,7 @@ msgstr "Hide service-accounts"
|
|||
#: src/pages/sources/plex/PlexSourceForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
#: src/pages/stages/password/PasswordStageForm.ts
|
||||
#: src/pages/stages/prompt/PromptStageForm.ts
|
||||
#: src/pages/stages/prompt/PromptStageForm.ts
|
||||
|
@ -1866,6 +1879,7 @@ msgid "Loading"
|
|||
msgstr "Loading"
|
||||
|
||||
#: src/elements/Spinner.ts
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/applications/ApplicationForm.ts
|
||||
#: src/pages/events/RuleForm.ts
|
||||
#: src/pages/events/RuleForm.ts
|
||||
|
@ -1916,6 +1930,7 @@ msgstr "Loading"
|
|||
#: src/pages/stages/email/EmailStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
#: src/pages/stages/password/PasswordStageForm.ts
|
||||
#: src/pages/stages/prompt/PromptStageForm.ts
|
||||
#: src/pages/stages/prompt/PromptStageForm.ts
|
||||
|
@ -1986,6 +2001,7 @@ msgstr "Maximum age (in days)"
|
|||
msgid "Members"
|
||||
msgstr "Members"
|
||||
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/policies/PolicyTestForm.ts
|
||||
#: src/pages/system-tasks/SystemTaskListPage.ts
|
||||
|
@ -2130,6 +2146,7 @@ msgstr "Need an account?"
|
|||
msgid "New version available!"
|
||||
msgstr "New version available!"
|
||||
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/crypto/CertificateKeyPairListPage.ts
|
||||
#: src/pages/groups/GroupListPage.ts
|
||||
#: src/pages/groups/MemberSelectModal.ts
|
||||
|
@ -2408,6 +2425,7 @@ msgstr "Parent"
|
|||
msgid "Pass policy?"
|
||||
msgstr "Pass policy?"
|
||||
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/policies/PolicyTestForm.ts
|
||||
msgid "Passing"
|
||||
|
@ -2887,6 +2905,10 @@ msgstr "Select an identification method."
|
|||
msgid "Select one of the sources below to login."
|
||||
msgstr "Select one of the sources below to login."
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP."
|
||||
msgstr "Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP."
|
||||
|
||||
#: src/pages/groups/MemberSelectModal.ts
|
||||
msgid "Select users to add"
|
||||
msgstr "Select users to add"
|
||||
|
@ -3054,6 +3076,7 @@ msgstr "Source {0}"
|
|||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/pages/sources/SourcesListPage.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Sources"
|
||||
msgstr "Sources"
|
||||
|
||||
|
@ -3321,6 +3344,7 @@ msgstr "Successfully imported flow."
|
|||
msgid "Successfully imported provider."
|
||||
msgstr "Successfully imported provider."
|
||||
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/policies/PolicyTestForm.ts
|
||||
#: src/pages/property-mappings/PropertyMappingTestForm.ts
|
||||
msgid "Successfully sent test-request."
|
||||
|
@ -3516,6 +3540,7 @@ msgstr "Task finished with warnings"
|
|||
msgid "Template"
|
||||
msgstr "Template"
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/events/TransportListPage.ts
|
||||
#: src/pages/policies/PolicyListPage.ts
|
||||
#: src/pages/policies/PolicyListPage.ts
|
||||
|
@ -3925,6 +3950,7 @@ msgstr "Use this redirect URL:"
|
|||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/events/EventListPage.ts
|
||||
#: src/pages/policies/PolicyBindingForm.ts
|
||||
|
@ -4178,6 +4204,7 @@ msgstr ""
|
|||
msgid "X509 Subject"
|
||||
msgstr "X509 Subject"
|
||||
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/crypto/CertificateKeyPairListPage.ts
|
||||
#: src/pages/groups/GroupListPage.ts
|
||||
#: src/pages/groups/MemberSelectModal.ts
|
||||
|
|
|
@ -518,6 +518,14 @@ msgstr ""
|
|||
msgid "Characters which are considered as symbols."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check Application access"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check IP"
|
||||
msgstr ""
|
||||
|
@ -526,6 +534,10 @@ msgstr ""
|
|||
msgid "Check Username"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check access"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check status"
|
||||
msgstr ""
|
||||
|
@ -1620,6 +1632,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Hold control/command to select multiple items."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1911,6 +1924,8 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1981,6 +1996,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Messages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2133,6 +2149,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2400,6 +2417,7 @@ msgstr ""
|
|||
msgid "Pass policy?"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Passing"
|
||||
|
@ -2879,6 +2897,10 @@ msgstr ""
|
|||
msgid "Select one of the sources below to login."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Select users to add"
|
||||
msgstr ""
|
||||
|
@ -3044,6 +3066,7 @@ msgstr ""
|
|||
msgid "Source {0}"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Sources"
|
||||
|
@ -3313,6 +3336,7 @@ msgstr ""
|
|||
msgid "Successfully imported provider."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Successfully sent test-request."
|
||||
|
@ -3513,6 +3537,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Test"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3923,6 +3948,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4175,6 +4201,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
|
|
Reference in New Issue