Merge branch 'main' into multi-tenant-django-tenants
This commit is contained in:
commit
9c7600e1f2
|
@ -19,6 +19,7 @@ from authentik.core.api.used_by import UsedByMixin
|
|||
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
|
||||
from authentik.core.expression.evaluator import PropertyMappingEvaluator
|
||||
from authentik.core.models import PropertyMapping
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.policies.api.exec import PolicyTestSerializer
|
||||
|
@ -95,6 +96,7 @@ class PropertyMappingViewSet(
|
|||
"description": subclass.__doc__,
|
||||
"component": subclass().component,
|
||||
"model_name": subclass._meta.model_name,
|
||||
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
|
||||
}
|
||||
)
|
||||
return Response(TypeCreateSerializer(data, many=True).data)
|
||||
|
|
|
@ -16,6 +16,7 @@ from rest_framework.viewsets import GenericViewSet
|
|||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.models import Provider
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
|
@ -113,6 +114,7 @@ class ProviderViewSet(
|
|||
"description": subclass.__doc__,
|
||||
"component": subclass().component,
|
||||
"model_name": subclass._meta.model_name,
|
||||
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
|
||||
}
|
||||
)
|
||||
data.append(
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.db.models import Model
|
|||
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
||||
from drf_spectacular.plumbing import build_basic_type
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from rest_framework.fields import CharField, IntegerField, JSONField
|
||||
from rest_framework.fields import BooleanField, CharField, IntegerField, JSONField
|
||||
from rest_framework.serializers import Serializer, SerializerMethodField, ValidationError
|
||||
|
||||
|
||||
|
@ -74,6 +74,7 @@ class TypeCreateSerializer(PassiveSerializer):
|
|||
description = CharField(required=True)
|
||||
component = CharField(required=True)
|
||||
model_name = CharField(required=True)
|
||||
requires_enterprise = BooleanField(default=False)
|
||||
|
||||
|
||||
class CacheSerializer(PassiveSerializer):
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import BooleanField, CharField, DateTimeField, IntegerField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
|
@ -20,6 +22,18 @@ from authentik.enterprise.models import License, LicenseKey
|
|||
from authentik.root.install_id import get_install_id
|
||||
|
||||
|
||||
class EnterpriseRequiredMixin:
|
||||
"""Mixin to validate that a valid enterprise license
|
||||
exists before allowing to safe the object"""
|
||||
|
||||
def validate(self, attrs: dict) -> dict:
|
||||
"""Check that a valid license exists"""
|
||||
total = LicenseKey.get_total()
|
||||
if not total.is_valid():
|
||||
raise ValidationError(_("Enterprise is required to create/update this object."))
|
||||
return super().validate(attrs)
|
||||
|
||||
|
||||
class LicenseSerializer(ModelSerializer):
|
||||
"""License Serializer"""
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
|
||||
|
||||
class AuthentikEnterpriseConfig(ManagedAppConfig):
|
||||
class EnterpriseConfig(ManagedAppConfig):
|
||||
"""Base app config for all enterprise apps"""
|
||||
|
||||
|
||||
class AuthentikEnterpriseConfig(EnterpriseConfig):
|
||||
"""Enterprise app config"""
|
||||
|
||||
name = "authentik.enterprise"
|
||||
|
|
|
@ -15,6 +15,7 @@ from structlog.stdlib import get_logger
|
|||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.models import Provider
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer
|
||||
from authentik.enterprise.providers.rac.models import Endpoint
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
|
@ -28,7 +29,7 @@ def user_endpoint_cache_key(user_pk: str) -> str:
|
|||
return f"goauthentik.io/providers/rac/endpoint_access/{user_pk}"
|
||||
|
||||
|
||||
class EndpointSerializer(ModelSerializer):
|
||||
class EndpointSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||
"""Endpoint Serializer"""
|
||||
|
||||
provider_obj = RACProviderSerializer(source="provider", read_only=True)
|
||||
|
@ -59,6 +60,7 @@ class EndpointSerializer(ModelSerializer):
|
|||
"property_mappings",
|
||||
"auth_mode",
|
||||
"launch_url",
|
||||
"maximum_connections",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@ from rest_framework.viewsets import ModelViewSet
|
|||
from authentik.core.api.propertymappings import PropertyMappingSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import JSONDictField
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.rac.models import RACPropertyMapping
|
||||
|
||||
|
||||
class RACPropertyMappingSerializer(PropertyMappingSerializer):
|
||||
class RACPropertyMappingSerializer(EnterpriseRequiredMixin, PropertyMappingSerializer):
|
||||
"""RACPropertyMapping Serializer"""
|
||||
|
||||
static_settings = JSONDictField()
|
||||
|
|
|
@ -4,10 +4,11 @@ from rest_framework.viewsets import ModelViewSet
|
|||
|
||||
from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.rac.models import RACProvider
|
||||
|
||||
|
||||
class RACProviderSerializer(ProviderSerializer):
|
||||
class RACProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
|
||||
"""RACProvider Serializer"""
|
||||
|
||||
outpost_set = ListField(child=CharField(), read_only=True, source="outpost_set.all")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""RAC app config"""
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
|
||||
|
||||
class AuthentikEnterpriseProviderRAC(ManagedAppConfig):
|
||||
class AuthentikEnterpriseProviderRAC(EnterpriseConfig):
|
||||
"""authentik enterprise rac app config"""
|
||||
|
||||
name = "authentik.enterprise.providers.rac"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 5.0 on 2024-01-03 23:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("authentik_providers_rac", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="endpoint",
|
||||
name="maximum_connections",
|
||||
field=models.IntegerField(default=1),
|
||||
),
|
||||
]
|
|
@ -35,7 +35,7 @@ class AuthenticationMode(models.TextChoices):
|
|||
|
||||
|
||||
class RACProvider(Provider):
|
||||
"""Remotely access computers/servers"""
|
||||
"""Remotely access computers/servers via RDP/SSH/VNC."""
|
||||
|
||||
settings = models.JSONField(default=dict)
|
||||
auth_mode = models.TextField(
|
||||
|
@ -81,6 +81,7 @@ class Endpoint(SerializerModel, PolicyBindingModel):
|
|||
settings = models.JSONField(default=dict)
|
||||
auth_mode = models.TextField(choices=AuthenticationMode.choices)
|
||||
provider = models.ForeignKey("RACProvider", on_delete=models.CASCADE)
|
||||
maximum_connections = models.IntegerField(default=1)
|
||||
|
||||
property_mappings = models.ManyToManyField(
|
||||
"authentik_core.PropertyMapping", default=None, blank=True
|
||||
|
|
|
@ -81,6 +81,7 @@ class TestEndpointsAPI(APITestCase):
|
|||
},
|
||||
"protocol": "rdp",
|
||||
"host": self.allowed.host,
|
||||
"maximum_connections": 1,
|
||||
"settings": {},
|
||||
"property_mappings": [],
|
||||
"auth_mode": "",
|
||||
|
@ -131,6 +132,7 @@ class TestEndpointsAPI(APITestCase):
|
|||
},
|
||||
"protocol": "rdp",
|
||||
"host": self.allowed.host,
|
||||
"maximum_connections": 1,
|
||||
"settings": {},
|
||||
"property_mappings": [],
|
||||
"auth_mode": "",
|
||||
|
@ -158,6 +160,7 @@ class TestEndpointsAPI(APITestCase):
|
|||
},
|
||||
"protocol": "rdp",
|
||||
"host": self.denied.host,
|
||||
"maximum_connections": 1,
|
||||
"settings": {},
|
||||
"property_mappings": [],
|
||||
"auth_mode": "",
|
||||
|
|
|
@ -5,11 +5,13 @@ from django.http import Http404, HttpRequest, HttpResponse
|
|||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from authentik.core.models import Application, AuthenticatedSession
|
||||
from authentik.core.views.interface import InterfaceView
|
||||
from authentik.enterprise.policy import EnterprisePolicyAccessView
|
||||
from authentik.enterprise.providers.rac.models import ConnectionToken, Endpoint, RACProvider
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.challenge import RedirectChallenge
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.models import in_memory_stage
|
||||
|
@ -43,6 +45,7 @@ class RACStartView(EnterprisePolicyAccessView):
|
|||
plan.insert_stage(
|
||||
in_memory_stage(
|
||||
RACFinalStage,
|
||||
application=self.application,
|
||||
endpoint=self.endpoint,
|
||||
provider=self.provider,
|
||||
)
|
||||
|
@ -77,29 +80,51 @@ class RACInterface(InterfaceView):
|
|||
class RACFinalStage(RedirectStage):
|
||||
"""RAC Connection final stage, set the connection token in the stage"""
|
||||
|
||||
endpoint: Endpoint
|
||||
provider: RACProvider
|
||||
application: Application
|
||||
|
||||
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||
endpoint: Endpoint = self.executor.current_stage.endpoint
|
||||
engine = PolicyEngine(endpoint, self.request.user, self.request)
|
||||
self.endpoint = self.executor.current_stage.endpoint
|
||||
self.provider = self.executor.current_stage.provider
|
||||
self.application = self.executor.current_stage.application
|
||||
# Check policies bound to endpoint directly
|
||||
engine = PolicyEngine(self.endpoint, self.request.user, self.request)
|
||||
engine.use_cache = False
|
||||
engine.build()
|
||||
passing = engine.result
|
||||
if not passing.passing:
|
||||
return self.executor.stage_invalid(", ".join(passing.messages))
|
||||
# Check if we're already at the maximum connection limit
|
||||
all_tokens = ConnectionToken.filter_not_expired(
|
||||
endpoint=self.endpoint,
|
||||
).exclude(endpoint__maximum_connections__lte=-1)
|
||||
if all_tokens.count() >= self.endpoint.maximum_connections:
|
||||
msg = [_("Maximum connection limit reached.")]
|
||||
# Check if any other tokens exist for the current user, and inform them
|
||||
# they are already connected
|
||||
if all_tokens.filter(session__user=self.request.user).exists():
|
||||
msg.append(_("(You are already connected in another tab/window)"))
|
||||
return self.executor.stage_invalid(" ".join(msg))
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_challenge(self, *args, **kwargs) -> RedirectChallenge:
|
||||
endpoint: Endpoint = self.executor.current_stage.endpoint
|
||||
provider: RACProvider = self.executor.current_stage.provider
|
||||
token = ConnectionToken.objects.create(
|
||||
provider=provider,
|
||||
endpoint=endpoint,
|
||||
provider=self.provider,
|
||||
endpoint=self.endpoint,
|
||||
settings=self.executor.plan.context.get("connection_settings", {}),
|
||||
session=AuthenticatedSession.objects.filter(
|
||||
session_key=self.request.session.session_key
|
||||
).first(),
|
||||
expires=now() + timedelta_from_string(provider.connection_expiry),
|
||||
expires=now() + timedelta_from_string(self.provider.connection_expiry),
|
||||
expiring=True,
|
||||
)
|
||||
Event.new(
|
||||
EventAction.AUTHORIZE_APPLICATION,
|
||||
authorized_application=self.application,
|
||||
flow=self.executor.plan.flow_pk,
|
||||
endpoint=self.endpoint.name,
|
||||
).from_http(self.request)
|
||||
setattr(
|
||||
self.executor.current_stage,
|
||||
"destination",
|
||||
|
|
|
@ -20,6 +20,7 @@ from authentik.core.models import (
|
|||
User,
|
||||
UserSourceConnection,
|
||||
)
|
||||
from authentik.enterprise.providers.rac.models import ConnectionToken
|
||||
from authentik.events.models import Event, EventAction, Notification
|
||||
from authentik.events.utils import model_to_dict
|
||||
from authentik.flows.models import FlowToken, Stage
|
||||
|
@ -54,6 +55,7 @@ IGNORED_MODELS = (
|
|||
SCIMUser,
|
||||
SCIMGroup,
|
||||
Reputation,
|
||||
ConnectionToken,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"
|
|||
GRANT_TYPE_PASSWORD = "password" # nosec
|
||||
GRANT_TYPE_DEVICE_CODE = "urn:ietf:params:oauth:grant-type:device_code"
|
||||
|
||||
CLIENT_ASSERTION_TYPE = "client_assertion_type"
|
||||
CLIENT_ASSERTION = "client_assertion"
|
||||
CLIENT_ASSERTION_TYPE = "client_assertion_type"
|
||||
CLIENT_ASSERTION_TYPE_JWT = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||
|
||||
PROMPT_NONE = "none"
|
||||
|
@ -18,9 +18,9 @@ PROMPT_LOGIN = "login"
|
|||
SCOPE_OPENID = "openid"
|
||||
SCOPE_OPENID_PROFILE = "profile"
|
||||
SCOPE_OPENID_EMAIL = "email"
|
||||
SCOPE_OFFLINE_ACCESS = "offline_access"
|
||||
|
||||
# https://www.iana.org/assignments/oauth-parameters/\
|
||||
# oauth-parameters.xhtml#pkce-code-challenge-method
|
||||
# https://www.iana.org/assignments/oauth-parameters/auth-parameters.xhtml#pkce-code-challenge-method
|
||||
PKCE_METHOD_PLAIN = "plain"
|
||||
PKCE_METHOD_S256 = "S256"
|
||||
|
||||
|
@ -36,6 +36,12 @@ SCOPE_GITHUB_USER_READ = "read:user"
|
|||
SCOPE_GITHUB_USER_EMAIL = "user:email"
|
||||
# Read info about teams
|
||||
SCOPE_GITHUB_ORG_READ = "read:org"
|
||||
SCOPE_GITHUB = {
|
||||
SCOPE_GITHUB_USER,
|
||||
SCOPE_GITHUB_USER_READ,
|
||||
SCOPE_GITHUB_USER_EMAIL,
|
||||
SCOPE_GITHUB_ORG_READ,
|
||||
}
|
||||
|
||||
ACR_AUTHENTIK_DEFAULT = "goauthentik.io/providers/oauth2/default"
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ class AuthorizeError(OAuth2Error):
|
|||
"account_selection_required": (
|
||||
"The End-User is required to select a session at the Authorization Server"
|
||||
),
|
||||
"consent_required": "The Authorization Server requires End-Userconsent",
|
||||
"consent_required": "The Authorization Server requires End-User consent",
|
||||
"invalid_request_uri": (
|
||||
"The request_uri in the Authorization Request returns an error or contains invalid data"
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.test import RequestFactory
|
|||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
@ -18,6 +19,7 @@ from authentik.providers.oauth2.models import (
|
|||
AuthorizationCode,
|
||||
GrantTypes,
|
||||
OAuth2Provider,
|
||||
ScopeMapping,
|
||||
)
|
||||
from authentik.providers.oauth2.tests.utils import OAuthTestCase
|
||||
from authentik.providers.oauth2.views.authorize import OAuthAuthorizationParams
|
||||
|
@ -172,14 +174,24 @@ class TestAuthorize(OAuthTestCase):
|
|||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_response_type(self):
|
||||
"""test response_type"""
|
||||
OAuth2Provider.objects.create(
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris="http://local.invalid/Foo",
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
]
|
||||
)
|
||||
)
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
|
@ -292,6 +304,7 @@ class TestAuthorize(OAuthTestCase):
|
|||
delta=5,
|
||||
)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_full_implicit(self):
|
||||
"""Test full authorization"""
|
||||
flow = create_test_flow()
|
||||
|
@ -302,6 +315,15 @@ class TestAuthorize(OAuthTestCase):
|
|||
redirect_uris="http://localhost",
|
||||
signing_key=self.keypair,
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
]
|
||||
)
|
||||
)
|
||||
Application.objects.create(name="app", slug="app", provider=provider)
|
||||
state = generate_id()
|
||||
user = create_test_admin_user()
|
||||
|
@ -409,6 +431,7 @@ class TestAuthorize(OAuthTestCase):
|
|||
delta=5,
|
||||
)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_full_form_post_id_token(self):
|
||||
"""Test full authorization (form_post response)"""
|
||||
flow = create_test_flow()
|
||||
|
@ -419,6 +442,15 @@ class TestAuthorize(OAuthTestCase):
|
|||
redirect_uris="http://localhost",
|
||||
signing_key=self.keypair,
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
]
|
||||
)
|
||||
)
|
||||
app = Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
|
||||
state = generate_id()
|
||||
user = create_test_admin_user()
|
||||
|
@ -440,6 +472,7 @@ class TestAuthorize(OAuthTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
)
|
||||
token: AccessToken = AccessToken.objects.filter(user=user).first()
|
||||
self.assertIsNotNone(token)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.test import RequestFactory
|
|||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
@ -21,6 +22,7 @@ from authentik.providers.oauth2.models import (
|
|||
AuthorizationCode,
|
||||
OAuth2Provider,
|
||||
RefreshToken,
|
||||
ScopeMapping,
|
||||
)
|
||||
from authentik.providers.oauth2.tests.utils import OAuthTestCase
|
||||
from authentik.providers.oauth2.views.token import TokenParams
|
||||
|
@ -136,21 +138,20 @@ class TestToken(OAuthTestCase):
|
|||
HTTP_AUTHORIZATION=f"Basic {header}",
|
||||
)
|
||||
access: AccessToken = AccessToken.objects.filter(user=user, provider=provider).first()
|
||||
refresh: RefreshToken = RefreshToken.objects.filter(user=user, provider=provider).first()
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{
|
||||
"access_token": access.token,
|
||||
"refresh_token": refresh.token,
|
||||
"token_type": TOKEN_TYPE,
|
||||
"expires_in": 3600,
|
||||
"id_token": provider.encode(
|
||||
refresh.id_token.to_dict(),
|
||||
access.id_token.to_dict(),
|
||||
),
|
||||
},
|
||||
)
|
||||
self.validate_jwt(access, provider)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_refresh_token_view(self):
|
||||
"""test request param"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
|
@ -159,6 +160,16 @@ class TestToken(OAuthTestCase):
|
|||
redirect_uris="http://local.invalid",
|
||||
signing_key=self.keypair,
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
"goauthentik.io/providers/oauth2/scope-offline_access",
|
||||
]
|
||||
)
|
||||
)
|
||||
# Needs to be assigned to an application for iss to be set
|
||||
self.app.provider = provider
|
||||
self.app.save()
|
||||
|
@ -170,6 +181,7 @@ class TestToken(OAuthTestCase):
|
|||
token=generate_id(),
|
||||
_id_token=dumps({}),
|
||||
auth_time=timezone.now(),
|
||||
_scope="offline_access",
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_providers_oauth2:token"),
|
||||
|
@ -201,6 +213,7 @@ class TestToken(OAuthTestCase):
|
|||
)
|
||||
self.validate_jwt(access, provider)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_refresh_token_view_invalid_origin(self):
|
||||
"""test request param"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
|
@ -209,6 +222,16 @@ class TestToken(OAuthTestCase):
|
|||
redirect_uris="http://local.invalid",
|
||||
signing_key=self.keypair,
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
"goauthentik.io/providers/oauth2/scope-offline_access",
|
||||
]
|
||||
)
|
||||
)
|
||||
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
||||
user = create_test_admin_user()
|
||||
token: RefreshToken = RefreshToken.objects.create(
|
||||
|
@ -217,6 +240,7 @@ class TestToken(OAuthTestCase):
|
|||
token=generate_id(),
|
||||
_id_token=dumps({}),
|
||||
auth_time=timezone.now(),
|
||||
_scope="offline_access",
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_providers_oauth2:token"),
|
||||
|
@ -247,6 +271,7 @@ class TestToken(OAuthTestCase):
|
|||
},
|
||||
)
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_refresh_token_revoke(self):
|
||||
"""test request param"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
|
@ -255,6 +280,16 @@ class TestToken(OAuthTestCase):
|
|||
redirect_uris="http://testserver",
|
||||
signing_key=self.keypair,
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-email",
|
||||
"goauthentik.io/providers/oauth2/scope-profile",
|
||||
"goauthentik.io/providers/oauth2/scope-offline_access",
|
||||
]
|
||||
)
|
||||
)
|
||||
# Needs to be assigned to an application for iss to be set
|
||||
self.app.provider = provider
|
||||
self.app.save()
|
||||
|
@ -266,6 +301,7 @@ class TestToken(OAuthTestCase):
|
|||
token=generate_id(),
|
||||
_id_token=dumps({}),
|
||||
auth_time=timezone.now(),
|
||||
_scope="offline_access",
|
||||
)
|
||||
# Create initial refresh token
|
||||
response = self.client.post(
|
||||
|
|
|
@ -10,7 +10,7 @@ from authentik.providers.oauth2.views.token import TokenView
|
|||
github_urlpatterns = [
|
||||
path(
|
||||
"login/oauth/authorize",
|
||||
AuthorizationFlowInitView.as_view(),
|
||||
AuthorizationFlowInitView.as_view(github_compat=True),
|
||||
name="github-authorize",
|
||||
),
|
||||
path(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""authentik OAuth2 Authorization views"""
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import InitVar, dataclass, field
|
||||
from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
from json import dumps
|
||||
|
@ -41,6 +41,8 @@ from authentik.providers.oauth2.constants import (
|
|||
PROMPT_CONSENT,
|
||||
PROMPT_LOGIN,
|
||||
PROMPT_NONE,
|
||||
SCOPE_GITHUB,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
SCOPE_OPENID,
|
||||
TOKEN_TYPE,
|
||||
)
|
||||
|
@ -66,7 +68,6 @@ from authentik.stages.consent.models import ConsentMode, ConsentStage
|
|||
from authentik.stages.consent.stage import (
|
||||
PLAN_CONTEXT_CONSENT_HEADER,
|
||||
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
||||
ConsentStageView,
|
||||
)
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
@ -86,7 +87,7 @@ class OAuthAuthorizationParams:
|
|||
redirect_uri: str
|
||||
response_type: str
|
||||
response_mode: Optional[str]
|
||||
scope: list[str]
|
||||
scope: set[str]
|
||||
state: str
|
||||
nonce: Optional[str]
|
||||
prompt: set[str]
|
||||
|
@ -101,8 +102,10 @@ class OAuthAuthorizationParams:
|
|||
code_challenge: Optional[str] = None
|
||||
code_challenge_method: Optional[str] = None
|
||||
|
||||
github_compat: InitVar[bool] = False
|
||||
|
||||
@staticmethod
|
||||
def from_request(request: HttpRequest) -> "OAuthAuthorizationParams":
|
||||
def from_request(request: HttpRequest, github_compat=False) -> "OAuthAuthorizationParams":
|
||||
"""
|
||||
Get all the params used by the Authorization Code Flow
|
||||
(and also for the Implicit and Hybrid).
|
||||
|
@ -154,7 +157,7 @@ class OAuthAuthorizationParams:
|
|||
response_type=response_type,
|
||||
response_mode=response_mode,
|
||||
grant_type=grant_type,
|
||||
scope=query_dict.get("scope", "").split(),
|
||||
scope=set(query_dict.get("scope", "").split()),
|
||||
state=state,
|
||||
nonce=query_dict.get("nonce"),
|
||||
prompt=ALLOWED_PROMPT_PARAMS.intersection(set(query_dict.get("prompt", "").split())),
|
||||
|
@ -162,9 +165,10 @@ class OAuthAuthorizationParams:
|
|||
max_age=int(max_age) if max_age else None,
|
||||
code_challenge=query_dict.get("code_challenge"),
|
||||
code_challenge_method=query_dict.get("code_challenge_method", "plain"),
|
||||
github_compat=github_compat,
|
||||
)
|
||||
|
||||
def __post_init__(self):
|
||||
def __post_init__(self, github_compat=False):
|
||||
self.provider: OAuth2Provider = OAuth2Provider.objects.filter(
|
||||
client_id=self.client_id
|
||||
).first()
|
||||
|
@ -172,7 +176,7 @@ class OAuthAuthorizationParams:
|
|||
LOGGER.warning("Invalid client identifier", client_id=self.client_id)
|
||||
raise ClientIdError(client_id=self.client_id)
|
||||
self.check_redirect_uri()
|
||||
self.check_scope()
|
||||
self.check_scope(github_compat)
|
||||
self.check_nonce()
|
||||
self.check_code_challenge()
|
||||
|
||||
|
@ -199,8 +203,8 @@ class OAuthAuthorizationParams:
|
|||
if not any(fullmatch(x, self.redirect_uri) for x in allowed_redirect_urls):
|
||||
LOGGER.warning(
|
||||
"Invalid redirect uri (regex comparison)",
|
||||
redirect_uri=self.redirect_uri,
|
||||
expected=allowed_redirect_urls,
|
||||
redirect_uri_given=self.redirect_uri,
|
||||
redirect_uri_expected=allowed_redirect_urls,
|
||||
)
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
|
||||
except RegexError as exc:
|
||||
|
@ -208,8 +212,8 @@ class OAuthAuthorizationParams:
|
|||
if not any(x == self.redirect_uri for x in allowed_redirect_urls):
|
||||
LOGGER.warning(
|
||||
"Invalid redirect uri (strict comparison)",
|
||||
redirect_uri=self.redirect_uri,
|
||||
expected=allowed_redirect_urls,
|
||||
redirect_uri_given=self.redirect_uri,
|
||||
redirect_uri_expected=allowed_redirect_urls,
|
||||
)
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
|
||||
if self.request:
|
||||
|
@ -217,24 +221,50 @@ class OAuthAuthorizationParams:
|
|||
self.redirect_uri, "request_not_supported", self.grant_type, self.state
|
||||
)
|
||||
|
||||
def check_scope(self):
|
||||
def check_scope(self, github_compat=False):
|
||||
"""Ensure openid scope is set in Hybrid flows, or when requesting an id_token"""
|
||||
if len(self.scope) == 0:
|
||||
default_scope_names = set(
|
||||
ScopeMapping.objects.filter(provider__in=[self.provider]).values_list(
|
||||
"scope_name", flat=True
|
||||
)
|
||||
default_scope_names = set(
|
||||
ScopeMapping.objects.filter(provider__in=[self.provider]).values_list(
|
||||
"scope_name", flat=True
|
||||
)
|
||||
)
|
||||
if len(self.scope) == 0:
|
||||
self.scope = default_scope_names
|
||||
LOGGER.info(
|
||||
"No scopes requested, defaulting to all configured scopes", scopes=self.scope
|
||||
)
|
||||
scopes_to_check = self.scope
|
||||
if github_compat:
|
||||
scopes_to_check = self.scope - SCOPE_GITHUB
|
||||
if not scopes_to_check.issubset(default_scope_names):
|
||||
LOGGER.info(
|
||||
"Application requested scopes not configured, setting to overlap",
|
||||
scope_allowed=default_scope_names,
|
||||
scope_given=self.scope,
|
||||
)
|
||||
self.scope = self.scope.intersection(default_scope_names)
|
||||
if SCOPE_OPENID not in self.scope and (
|
||||
self.grant_type == GrantTypes.HYBRID
|
||||
or self.response_type in [ResponseTypes.ID_TOKEN, ResponseTypes.ID_TOKEN_TOKEN]
|
||||
):
|
||||
LOGGER.warning("Missing 'openid' scope.")
|
||||
raise AuthorizeError(self.redirect_uri, "invalid_scope", self.grant_type, self.state)
|
||||
if SCOPE_OFFLINE_ACCESS in self.scope:
|
||||
# https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
if PROMPT_CONSENT not in self.prompt:
|
||||
raise AuthorizeError(
|
||||
self.redirect_uri, "consent_required", self.grant_type, self.state
|
||||
)
|
||||
if self.response_type not in [
|
||||
ResponseTypes.CODE,
|
||||
ResponseTypes.CODE_TOKEN,
|
||||
ResponseTypes.CODE_ID_TOKEN,
|
||||
ResponseTypes.CODE_ID_TOKEN_TOKEN,
|
||||
]:
|
||||
# offline_access requires a response type that has some sort of token
|
||||
# Spec says to ignore the scope when the response_type wouldn't result
|
||||
# in an authorization code being generated
|
||||
self.scope.remove(SCOPE_OFFLINE_ACCESS)
|
||||
|
||||
def check_nonce(self):
|
||||
"""Nonce parameter validation."""
|
||||
|
@ -297,6 +327,9 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
|||
"""OAuth2 Flow initializer, checks access to application and starts flow"""
|
||||
|
||||
params: OAuthAuthorizationParams
|
||||
# Enable GitHub compatibility (only allow for scopes which are handled
|
||||
# differently for github compat)
|
||||
github_compat = False
|
||||
|
||||
def pre_permission_check(self):
|
||||
"""Check prompt parameter before checking permission/authentication,
|
||||
|
@ -305,7 +338,9 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
|||
if len(self.request.GET) < 1:
|
||||
raise Http404
|
||||
try:
|
||||
self.params = OAuthAuthorizationParams.from_request(self.request)
|
||||
self.params = OAuthAuthorizationParams.from_request(
|
||||
self.request, github_compat=self.github_compat
|
||||
)
|
||||
except AuthorizeError as error:
|
||||
LOGGER.warning(error.description, redirect_uri=error.redirect_uri)
|
||||
raise RequestValidationError(error.get_response(self.request))
|
||||
|
@ -402,7 +437,7 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
|||
# OpenID clients can specify a `prompt` parameter, and if its set to consent we
|
||||
# need to inject a consent stage
|
||||
if PROMPT_CONSENT in self.params.prompt:
|
||||
if not any(isinstance(x.stage, ConsentStageView) for x in plan.bindings):
|
||||
if not any(isinstance(x.stage, ConsentStage) for x in plan.bindings):
|
||||
# Plan does not have any consent stage, so we add an in-memory one
|
||||
stage = ConsentStage(
|
||||
name="OAuth2 Provider In-memory consent stage",
|
||||
|
|
|
@ -41,6 +41,7 @@ from authentik.providers.oauth2.constants import (
|
|||
GRANT_TYPE_PASSWORD,
|
||||
GRANT_TYPE_REFRESH_TOKEN,
|
||||
PKCE_METHOD_S256,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
TOKEN_TYPE,
|
||||
)
|
||||
from authentik.providers.oauth2.errors import DeviceCodeError, TokenError, UserAuthError
|
||||
|
@ -459,7 +460,7 @@ class TokenView(View):
|
|||
op="authentik.providers.oauth2.post.response",
|
||||
):
|
||||
if self.params.grant_type == GRANT_TYPE_AUTHORIZATION_CODE:
|
||||
LOGGER.debug("Converting authorization code to refresh token")
|
||||
LOGGER.debug("Converting authorization code to access token")
|
||||
return TokenResponse(self.create_code_response())
|
||||
if self.params.grant_type == GRANT_TYPE_REFRESH_TOKEN:
|
||||
LOGGER.debug("Refreshing refresh token")
|
||||
|
@ -489,49 +490,56 @@ class TokenView(View):
|
|||
auth_time=self.params.authorization_code.auth_time,
|
||||
session_id=self.params.authorization_code.session_id,
|
||||
)
|
||||
access_token.id_token = IDToken.new(
|
||||
access_id_token = IDToken.new(
|
||||
self.provider,
|
||||
access_token,
|
||||
self.request,
|
||||
)
|
||||
access_id_token.nonce = self.params.authorization_code.nonce
|
||||
access_token.id_token = access_id_token
|
||||
access_token.save()
|
||||
|
||||
refresh_token_expiry = now + timedelta_from_string(self.provider.refresh_token_validity)
|
||||
refresh_token = RefreshToken(
|
||||
user=self.params.authorization_code.user,
|
||||
scope=self.params.authorization_code.scope,
|
||||
expires=refresh_token_expiry,
|
||||
provider=self.provider,
|
||||
auth_time=self.params.authorization_code.auth_time,
|
||||
session_id=self.params.authorization_code.session_id,
|
||||
)
|
||||
id_token = IDToken.new(
|
||||
self.provider,
|
||||
refresh_token,
|
||||
self.request,
|
||||
)
|
||||
id_token.nonce = self.params.authorization_code.nonce
|
||||
id_token.at_hash = access_token.at_hash
|
||||
refresh_token.id_token = id_token
|
||||
refresh_token.save()
|
||||
|
||||
# Delete old code
|
||||
self.params.authorization_code.delete()
|
||||
return {
|
||||
response = {
|
||||
"access_token": access_token.token,
|
||||
"refresh_token": refresh_token.token,
|
||||
"token_type": TOKEN_TYPE,
|
||||
"expires_in": int(
|
||||
timedelta_from_string(self.provider.access_token_validity).total_seconds()
|
||||
),
|
||||
"id_token": id_token.to_jwt(self.provider),
|
||||
"id_token": access_token.id_token.to_jwt(self.provider),
|
||||
}
|
||||
|
||||
if SCOPE_OFFLINE_ACCESS in self.params.authorization_code.scope:
|
||||
refresh_token_expiry = now + timedelta_from_string(self.provider.refresh_token_validity)
|
||||
refresh_token = RefreshToken(
|
||||
user=self.params.authorization_code.user,
|
||||
scope=self.params.authorization_code.scope,
|
||||
expires=refresh_token_expiry,
|
||||
provider=self.provider,
|
||||
auth_time=self.params.authorization_code.auth_time,
|
||||
session_id=self.params.authorization_code.session_id,
|
||||
)
|
||||
id_token = IDToken.new(
|
||||
self.provider,
|
||||
refresh_token,
|
||||
self.request,
|
||||
)
|
||||
id_token.nonce = self.params.authorization_code.nonce
|
||||
id_token.at_hash = access_token.at_hash
|
||||
refresh_token.id_token = id_token
|
||||
refresh_token.save()
|
||||
response["refresh_token"] = refresh_token.token
|
||||
|
||||
# Delete old code
|
||||
self.params.authorization_code.delete()
|
||||
return response
|
||||
|
||||
def create_refresh_response(self) -> dict[str, Any]:
|
||||
"""See https://datatracker.ietf.org/doc/html/rfc6749#section-6"""
|
||||
unauthorized_scopes = set(self.params.scope) - set(self.params.refresh_token.scope)
|
||||
if unauthorized_scopes:
|
||||
raise TokenError("invalid_scope")
|
||||
if SCOPE_OFFLINE_ACCESS not in self.params.scope:
|
||||
raise TokenError("invalid_scope")
|
||||
now = timezone.now()
|
||||
access_token_expiry = now + timedelta_from_string(self.provider.access_token_validity)
|
||||
access_token = AccessToken(
|
||||
|
@ -630,31 +638,34 @@ class TokenView(View):
|
|||
)
|
||||
access_token.save()
|
||||
|
||||
refresh_token_expiry = now + timedelta_from_string(self.provider.refresh_token_validity)
|
||||
refresh_token = RefreshToken(
|
||||
user=self.params.device_code.user,
|
||||
scope=self.params.device_code.scope,
|
||||
expires=refresh_token_expiry,
|
||||
provider=self.provider,
|
||||
auth_time=auth_event.created if auth_event else now,
|
||||
)
|
||||
id_token = IDToken.new(
|
||||
self.provider,
|
||||
refresh_token,
|
||||
self.request,
|
||||
)
|
||||
id_token.at_hash = access_token.at_hash
|
||||
refresh_token.id_token = id_token
|
||||
refresh_token.save()
|
||||
|
||||
# Delete device code
|
||||
self.params.device_code.delete()
|
||||
return {
|
||||
response = {
|
||||
"access_token": access_token.token,
|
||||
"refresh_token": refresh_token.token,
|
||||
"token_type": TOKEN_TYPE,
|
||||
"expires_in": int(
|
||||
timedelta_from_string(self.provider.access_token_validity).total_seconds()
|
||||
),
|
||||
"id_token": id_token.to_jwt(self.provider),
|
||||
"id_token": access_token.id_token.to_jwt(self.provider),
|
||||
}
|
||||
|
||||
if SCOPE_OFFLINE_ACCESS in self.params.scope:
|
||||
refresh_token_expiry = now + timedelta_from_string(self.provider.refresh_token_validity)
|
||||
refresh_token = RefreshToken(
|
||||
user=self.params.device_code.user,
|
||||
scope=self.params.device_code.scope,
|
||||
expires=refresh_token_expiry,
|
||||
provider=self.provider,
|
||||
auth_time=auth_event.created if auth_event else now,
|
||||
)
|
||||
id_token = IDToken.new(
|
||||
self.provider,
|
||||
refresh_token,
|
||||
self.request,
|
||||
)
|
||||
id_token.at_hash = access_token.at_hash
|
||||
refresh_token.id_token = id_token
|
||||
refresh_token.save()
|
||||
response["refresh_token"] = refresh_token.token
|
||||
|
||||
# Delete device code
|
||||
self.params.device_code.delete()
|
||||
return response
|
||||
|
|
|
@ -24,7 +24,10 @@ class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):
|
|||
|
||||
def get_app_label_verbose(self, instance: GroupObjectPermission) -> str:
|
||||
"""Get app label from permission's model"""
|
||||
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
||||
try:
|
||||
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
||||
except LookupError:
|
||||
return instance.content_type.app_label
|
||||
|
||||
def get_model_verbose(self, instance: GroupObjectPermission) -> str:
|
||||
"""Get model label from permission's model"""
|
||||
|
|
|
@ -24,7 +24,10 @@ class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):
|
|||
|
||||
def get_app_label_verbose(self, instance: UserObjectPermission) -> str:
|
||||
"""Get app label from permission's model"""
|
||||
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
||||
try:
|
||||
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
||||
except LookupError:
|
||||
return instance.content_type.app_label
|
||||
|
||||
def get_model_verbose(self, instance: UserObjectPermission) -> str:
|
||||
"""Get model label from permission's model"""
|
||||
|
|
|
@ -109,7 +109,10 @@ class BoundSessionMiddleware(SessionMiddleware):
|
|||
self.recheck_session_geo(configured_binding_geo, last_ip, new_ip)
|
||||
# If we got to this point without any error being raised, we need to
|
||||
# update the last saved IP to the current one
|
||||
request.session[SESSION_KEY_LAST_IP] = new_ip
|
||||
if SESSION_KEY_BINDING_NET in request.session or SESSION_KEY_BINDING_GEO in request.session:
|
||||
# Only set the last IP in the session if there's a binding specified
|
||||
# (== basically requires the user to be logged in)
|
||||
request.session[SESSION_KEY_LAST_IP] = new_ip
|
||||
AuthenticatedSession.objects.filter(session_key=request.session.session_key).update(
|
||||
last_ip=new_ip, last_user_agent=request.META.get("HTTP_USER_AGENT", "")
|
||||
)
|
||||
|
|
|
@ -9011,6 +9011,12 @@
|
|||
"prompt"
|
||||
],
|
||||
"title": "Auth mode"
|
||||
},
|
||||
"maximum_connections": {
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647,
|
||||
"title": "Maximum connections"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
|
@ -45,3 +45,14 @@ entries:
|
|||
# groups is not part of the official userinfo schema, but is a quasi-standard
|
||||
"groups": [group.name for group in request.user.ak_groups.all()],
|
||||
}
|
||||
- identifiers:
|
||||
managed: goauthentik.io/providers/oauth2/scope-offline_access
|
||||
model: authentik_providers_oauth2.scopemapping
|
||||
attrs:
|
||||
name: "authentik default OAuth Mapping: OpenID 'offline_access'"
|
||||
scope_name: offline_access
|
||||
description: "Access to request new tokens without interaction"
|
||||
expression: |
|
||||
# This scope grants the application a refresh token that can be used to refresh user data
|
||||
# and let the application access authentik without the users interaction
|
||||
return {}
|
||||
|
|
14
go.mod
14
go.mod
|
@ -23,15 +23,15 @@ require (
|
|||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/redis/go-redis/v9 v9.3.1
|
||||
github.com/redis/go-redis/v9 v9.4.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2023105.3
|
||||
goauthentik.io/api/v3 v3.2023105.5
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.15.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/oauth2 v0.16.0
|
||||
golang.org/x/sync v0.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||
)
|
||||
|
@ -74,9 +74,9 @@ require (
|
|||
go.opentelemetry.io/otel v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.17.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
|
|
28
go.sum
28
go.sum
|
@ -258,8 +258,8 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne
|
|||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds=
|
||||
github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
||||
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
|
@ -316,8 +316,8 @@ go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYO
|
|||
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
goauthentik.io/api/v3 v3.2023105.3 h1:x0pMJIKkbN198OOssqA94h8bO6ft9gwG8bpZqZL7WVg=
|
||||
goauthentik.io/api/v3 v3.2023105.3/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2023105.5 h1:wIL3Q0jry1g4kRWpH/Dv1sQqhzuL4BLC+uP/Tar1P/g=
|
||||
goauthentik.io/api/v3 v3.2023105.5/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -327,8 +327,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -394,16 +394,16 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -415,8 +415,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -452,8 +452,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-01-03 11:22+0000\n"
|
||||
"POT-Creation-Date: 2024-01-09 15:37+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -92,11 +92,11 @@ msgstr ""
|
|||
msgid "Brands"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/providers.py:120
|
||||
#: authentik/core/api/providers.py:122
|
||||
msgid "SAML Provider from Metadata"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/providers.py:121
|
||||
#: authentik/core/api/providers.py:123
|
||||
msgid "Create a SAML Provider by importing its Metadata."
|
||||
msgstr ""
|
||||
|
||||
|
@ -343,7 +343,7 @@ msgid "Powered by authentik"
|
|||
msgstr ""
|
||||
|
||||
#: authentik/core/views/apps.py:53
|
||||
#: authentik/providers/oauth2/views/authorize.py:395
|
||||
#: authentik/providers/oauth2/views/authorize.py:430
|
||||
#: authentik/providers/oauth2/views/device_init.py:70
|
||||
#: authentik/providers/saml/views/sso.py:70
|
||||
#, python-format
|
||||
|
@ -372,6 +372,10 @@ msgstr ""
|
|||
msgid "Certificate-Key Pairs"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/api.py:33
|
||||
msgid "Enterprise is required to create/update this object."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/models.py:183
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
@ -411,22 +415,30 @@ msgstr ""
|
|||
msgid "RAC Providers"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py:99
|
||||
#: authentik/enterprise/providers/rac/models.py:100
|
||||
msgid "RAC Endpoint"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py:100
|
||||
#: authentik/enterprise/providers/rac/models.py:101
|
||||
msgid "RAC Endpoints"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py:121
|
||||
#: authentik/enterprise/providers/rac/models.py:122
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py:122
|
||||
#: authentik/enterprise/providers/rac/models.py:123
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/views.py:103
|
||||
msgid "Maximum connection limit reached."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/views.py:107
|
||||
msgid "(You are already connected in another tab/window)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:289
|
||||
msgid "Event"
|
||||
msgstr ""
|
||||
|
@ -1282,7 +1294,7 @@ msgstr ""
|
|||
msgid "Device Tokens"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/oauth2/views/authorize.py:450
|
||||
#: authentik/providers/oauth2/views/authorize.py:485
|
||||
#: authentik/providers/saml/views/flows.py:87
|
||||
#, python-format
|
||||
msgid "Redirecting to %(app)s..."
|
||||
|
|
16
schema.yml
16
schema.yml
|
@ -19247,6 +19247,7 @@ paths:
|
|||
- tr
|
||||
- tt
|
||||
- udm
|
||||
- ug
|
||||
- uk
|
||||
- ur
|
||||
- uz
|
||||
|
@ -32108,6 +32109,10 @@ components:
|
|||
Build actual launch URL (the provider itself does not have one, just
|
||||
individual endpoints)
|
||||
readOnly: true
|
||||
maximum_connections:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
required:
|
||||
- auth_mode
|
||||
- host
|
||||
|
@ -32139,6 +32144,10 @@ components:
|
|||
format: uuid
|
||||
auth_mode:
|
||||
$ref: '#/components/schemas/AuthModeEnum'
|
||||
maximum_connections:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
required:
|
||||
- auth_mode
|
||||
- host
|
||||
|
@ -38118,6 +38127,10 @@ components:
|
|||
format: uuid
|
||||
auth_mode:
|
||||
$ref: '#/components/schemas/AuthModeEnum'
|
||||
maximum_connections:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
PatchedEventMatcherPolicyRequest:
|
||||
type: object
|
||||
description: Event Matcher Policy Serializer
|
||||
|
@ -43786,6 +43799,9 @@ components:
|
|||
type: string
|
||||
model_name:
|
||||
type: string
|
||||
requires_enterprise:
|
||||
type: boolean
|
||||
default: false
|
||||
required:
|
||||
- component
|
||||
- description
|
||||
|
|
|
@ -74,7 +74,7 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
slug="default-provider-authorization-implicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
|
@ -82,8 +82,8 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
authorization_flow=authorization_flow,
|
||||
)
|
||||
Application.objects.create(
|
||||
name="Grafana",
|
||||
slug="grafana",
|
||||
name=generate_id(),
|
||||
slug=generate_id(),
|
||||
provider=provider,
|
||||
)
|
||||
|
||||
|
@ -129,7 +129,7 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
slug="default-provider-authorization-explicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
|
@ -137,8 +137,8 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
authorization_flow=authorization_flow,
|
||||
)
|
||||
app = Application.objects.create(
|
||||
name="Grafana",
|
||||
slug="grafana",
|
||||
name=generate_id(),
|
||||
slug=generate_id(),
|
||||
provider=provider,
|
||||
)
|
||||
|
||||
|
@ -200,7 +200,7 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
slug="default-provider-authorization-explicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
|
@ -208,8 +208,8 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
|||
authorization_flow=authorization_flow,
|
||||
)
|
||||
app = Application.objects.create(
|
||||
name="Grafana",
|
||||
slug="grafana",
|
||||
name=generate_id(),
|
||||
slug=generate_id(),
|
||||
provider=provider,
|
||||
)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ from authentik.lib.generators import generate_id, generate_key
|
|||
from authentik.policies.expression.models import ExpressionPolicy
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.constants import (
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
|
@ -80,7 +81,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
slug="default-provider-authorization-implicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
|
@ -90,12 +91,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
Application.objects.create(
|
||||
name="Grafana",
|
||||
name=generate_id(),
|
||||
slug=self.app_slug,
|
||||
provider=provider,
|
||||
)
|
||||
|
@ -113,12 +119,8 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_implied(self):
|
||||
"""test OpenID Provider flow (default authorization flow with implied consent)"""
|
||||
|
@ -128,7 +130,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
slug="default-provider-authorization-implicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
|
@ -138,11 +140,16 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
Application.objects.create(
|
||||
name="Grafana",
|
||||
name=generate_id(),
|
||||
slug=self.app_slug,
|
||||
provider=provider,
|
||||
)
|
||||
|
@ -174,12 +181,8 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_logout(self):
|
||||
"""test OpenID Provider flow with logout"""
|
||||
|
@ -189,7 +192,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
slug="default-provider-authorization-implicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret,
|
||||
|
@ -199,12 +202,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
Application.objects.create(
|
||||
name="Grafana",
|
||||
name=generate_id(),
|
||||
slug=self.app_slug,
|
||||
provider=provider,
|
||||
)
|
||||
|
@ -244,12 +252,8 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_explicit(self):
|
||||
"""test OpenID Provider flow (default authorization flow with explicit consent)"""
|
||||
|
@ -259,7 +263,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
slug="default-provider-authorization-explicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
authorization_flow=authorization_flow,
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
client_id=self.client_id,
|
||||
|
@ -269,12 +273,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
app = Application.objects.create(
|
||||
name="Grafana",
|
||||
name=generate_id(),
|
||||
slug=self.app_slug,
|
||||
provider=provider,
|
||||
)
|
||||
|
@ -323,12 +332,8 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_denied(self):
|
||||
"""test OpenID Provider flow (default authorization with access deny)"""
|
||||
|
@ -338,7 +343,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
slug="default-provider-authorization-explicit-consent"
|
||||
)
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name="grafana",
|
||||
name=generate_id(),
|
||||
authorization_flow=authorization_flow,
|
||||
client_type=ClientTypes.CONFIDENTIAL,
|
||||
client_id=self.client_id,
|
||||
|
@ -348,12 +353,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
app = Application.objects.create(
|
||||
name="Grafana",
|
||||
name=generate_id(),
|
||||
slug=self.app_slug,
|
||||
provider=provider,
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ from authentik.lib.generators import generate_id, generate_key
|
|||
from authentik.policies.expression.models import ExpressionPolicy
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.constants import (
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
|
@ -29,7 +30,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
def setUp(self):
|
||||
self.client_id = generate_id()
|
||||
self.client_secret = generate_key()
|
||||
self.application_slug = "test"
|
||||
self.application_slug = generate_id()
|
||||
super().setUp()
|
||||
|
||||
def setup_client(self) -> Container:
|
||||
|
@ -37,7 +38,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
sleep(1)
|
||||
client: DockerClient = from_env()
|
||||
container = client.containers.run(
|
||||
image="ghcr.io/beryju/oidc-test-client:1.3",
|
||||
image="ghcr.io/beryju/oidc-test-client:2.1",
|
||||
detach=True,
|
||||
ports={
|
||||
"9009": "9009",
|
||||
|
@ -56,9 +57,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_redirect_uri_error(self):
|
||||
"""test OpenID Provider flow (invalid redirect URI, check error message)"""
|
||||
|
@ -78,10 +77,14 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
Application.objects.create(
|
||||
name=self.application_slug,
|
||||
slug=self.application_slug,
|
||||
|
@ -101,13 +104,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_implied(self):
|
||||
"""test OpenID Provider flow (default authorization flow with implied consent)"""
|
||||
"""test OpenID Provider flow (default authorization flow with implied consent)
|
||||
(due to offline_access a consent will still be triggered)"""
|
||||
sleep(1)
|
||||
# Bootstrap all needed objects
|
||||
authorization_flow = Flow.objects.get(
|
||||
|
@ -124,11 +126,15 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
Application.objects.create(
|
||||
app = Application.objects.create(
|
||||
name=self.application_slug,
|
||||
slug=self.application_slug,
|
||||
provider=provider,
|
||||
|
@ -137,6 +143,20 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
|
||||
self.driver.get("http://localhost:9009")
|
||||
self.login()
|
||||
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
|
||||
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||
|
||||
self.assertIn(
|
||||
app.name,
|
||||
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
|
||||
)
|
||||
consent_stage.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"[type=submit]",
|
||||
).click()
|
||||
|
||||
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre")))
|
||||
self.wait.until(ec.text_to_be_present_in_element((By.CSS_SELECTOR, "pre"), "{"))
|
||||
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
|
||||
|
@ -155,11 +175,9 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_explicit(self):
|
||||
"""test OpenID Provider flow (default authorization flow with explicit consent)"""
|
||||
sleep(1)
|
||||
|
@ -178,10 +196,14 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
app = Application.objects.create(
|
||||
name=self.application_slug,
|
||||
slug=self.application_slug,
|
||||
|
@ -224,9 +246,8 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_denied(self):
|
||||
"""test OpenID Provider flow (default authorization with access deny)"""
|
||||
|
@ -246,10 +267,14 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
app = Application.objects.create(
|
||||
name=self.application_slug,
|
||||
slug=self.application_slug,
|
||||
|
|
|
@ -15,6 +15,7 @@ from authentik.lib.generators import generate_id, generate_key
|
|||
from authentik.policies.expression.models import ExpressionPolicy
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.constants import (
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
|
@ -37,7 +38,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
sleep(1)
|
||||
client: DockerClient = from_env()
|
||||
container = client.containers.run(
|
||||
image="ghcr.io/beryju/oidc-test-client:1.3",
|
||||
image="ghcr.io/beryju/oidc-test-client:2.1",
|
||||
detach=True,
|
||||
ports={
|
||||
"9009": "9009",
|
||||
|
@ -56,9 +57,8 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_redirect_uri_error(self):
|
||||
"""test OpenID Provider flow (invalid redirect URI, check error message)"""
|
||||
|
@ -78,7 +78,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
|
@ -101,11 +106,9 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_implied(self):
|
||||
"""test OpenID Provider flow (default authorization flow with implied consent)"""
|
||||
sleep(1)
|
||||
|
@ -124,7 +127,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
|
@ -150,11 +158,9 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_consent_explicit(self):
|
||||
"""test OpenID Provider flow (default authorization flow with explicit consent)"""
|
||||
sleep(1)
|
||||
|
@ -173,7 +179,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
|
@ -215,9 +226,8 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_denied(self):
|
||||
"""test OpenID Provider flow (default authorization with access deny)"""
|
||||
|
@ -237,7 +247,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE]
|
||||
scope_name__in=[
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
SCOPE_OFFLINE_ACCESS,
|
||||
]
|
||||
)
|
||||
)
|
||||
provider.save()
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
"name": "@goauthentik/web-tests",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
||||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"@wdio/cli": "^8.27.1",
|
||||
"@wdio/local-runner": "^8.27.0",
|
||||
"@wdio/mocha-framework": "^8.27.0",
|
||||
|
@ -946,16 +946,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz",
|
||||
"integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz",
|
||||
"integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/type-utils": "6.17.0",
|
||||
"@typescript-eslint/utils": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/type-utils": "6.18.1",
|
||||
"@typescript-eslint/utils": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
|
@ -981,15 +981,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz",
|
||||
"integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz",
|
||||
"integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1009,13 +1009,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz",
|
||||
"integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz",
|
||||
"integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0"
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
|
@ -1026,13 +1026,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz",
|
||||
"integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz",
|
||||
"integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/utils": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"@typescript-eslint/utils": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
|
@ -1053,9 +1053,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz",
|
||||
"integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz",
|
||||
"integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
|
@ -1066,13 +1066,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz",
|
||||
"integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz",
|
||||
"integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -1118,17 +1118,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz",
|
||||
"integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz",
|
||||
"integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1143,12 +1143,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz",
|
||||
"integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz",
|
||||
"integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
||||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"@wdio/cli": "^8.27.1",
|
||||
"@wdio/local-runner": "^8.27.0",
|
||||
"@wdio/mocha-framework": "^8.27.0",
|
||||
|
|
|
@ -3,6 +3,92 @@
|
|||
This is the default UI for the authentik server. The documentation is going to be a little sparse
|
||||
for awhile, but at least let's get started.
|
||||
|
||||
# The Theory of the authentik UI
|
||||
|
||||
In Peter Naur's 1985 essay [Programming as Theory
|
||||
Building](https://pages.cs.wisc.edu/~remzi/Naur.pdf), programming is described as creating a mental
|
||||
model of how a program _should_ run, then writing the code to test if the program _can_ run that
|
||||
way.
|
||||
|
||||
The mental model for the authentik UI is straightforward. There are five "applications" within the
|
||||
UI, each with its own base URL, router, and responsibilities, and each application needs as many as
|
||||
three contexts in which to run.
|
||||
|
||||
The three contexts corresponds to objects in the API's `model` section, so let's use those names.
|
||||
|
||||
- The root `Config`. The root configuration object of the server, containing mostly caching and
|
||||
error reporting information. This is misleading, however; the `Config` object contains some user
|
||||
information, specifically a list of permissions the current user (or "no user") has.
|
||||
- The root `CurrentTenant`. This describes the `Brand` information UIs should use, such as themes,
|
||||
logos, favicon, and specific default flows for logging in, logging out, and recovering a user
|
||||
password.
|
||||
- The current `SessionUser`, the person logged in: username, display name, and various states.
|
||||
(Note: the authentik server permits administrators to "impersonate" any other user in order to
|
||||
debug their authentikation experience. If impersonation is active, the `user` field reflects that
|
||||
user, but it also includes a field, `original`, with the administrator's information.)
|
||||
|
||||
(There is a fourth context object, Version, but its use is limited to displaying version information
|
||||
and checking for upgrades. Just be aware that you will see it, but you will probably never interact
|
||||
with it.)
|
||||
|
||||
There are five applications. Two (`loading` and `api-browser`) are trivial applications whose
|
||||
insides are provided by third-party libraries (Patternfly and Rapidoc, respectively). The other
|
||||
three are actual applications. The descriptions below are wholly from the view of the user's
|
||||
experience:
|
||||
|
||||
- `Flow`: From a given URL, displays a form that requests information from the user to accomplish a
|
||||
task. Some tasks require the user to be logged in, but many (such as logging in itself!)
|
||||
obviously do not.
|
||||
- `User`: Provides the user with access to the applications they can access, plus a few user
|
||||
settings.
|
||||
- `Admin`: Provides someone with super-user permissions access to the administrative functions of
|
||||
the authentik server.
|
||||
|
||||
**Mental Model**
|
||||
|
||||
- Upon initialization, _every_ authentik UI application fetches `Config` and `CurrentTenant`. `User`
|
||||
and `Admin` will also attempt to load the `SessionUser`; if there is none, the user is kicked out
|
||||
to the `Flow` for logging into authentik itself.
|
||||
- `Config`, `CurrentTenant`, and `SessionUser`, are provided by the `@goauthentik/api` application,
|
||||
not by the codebase under `./web`. (Where you are now).
|
||||
- `Flow`, `User`, and `Admin` are all called `Interfaces` and are found in
|
||||
`./web/src/flow/FlowInterface`, `./web/src/user/UserInterface`, `./web/src/admin/AdminInterface`,
|
||||
respectively.
|
||||
|
||||
Inside each of these you will find, in a hierarchal order:
|
||||
|
||||
- The context layer described above
|
||||
- A theme managing layer
|
||||
- The orchestration layer:
|
||||
- web socket handler for server-generated events
|
||||
- The router
|
||||
- Individual routes for each vertical slice and its relationship to other objects:
|
||||
|
||||
Each slice corresponds to an object table on the server, and each slice _usually_ consists of the
|
||||
following:
|
||||
|
||||
- A paginated collection display, usually using the `Table` foundation (found in
|
||||
`./web/src/elements/Table`)
|
||||
- The ability to view an individual object from the collection, which you may be able to:
|
||||
- Edit
|
||||
- Delete
|
||||
- A form for creating a new object
|
||||
- Tabs showing that object's relationship to other objects
|
||||
- Interactive elements for changing or deleting those relationships, or creating new ones.
|
||||
- The ability to create new objects with which to have that relationship, if they're not part of
|
||||
the core objects (such as User->MFA authenticator apps, since the latter is not a "core" object
|
||||
and has no tab of its own).
|
||||
|
||||
We are still a bit "all over the place" with respect to sub-units and common units; there are
|
||||
folders `common`, `elements`, and `components`, and ideally they would be:
|
||||
|
||||
- `common`: non-UI related libraries all of our applications need
|
||||
- `elements`: UI elements shared among multiple applications that do not need context
|
||||
- `components`: UI elements shared among multiple that use one or more context
|
||||
|
||||
... but at the moment there are some context-sensitive elements, and some UI-related stuff in
|
||||
`common`.
|
||||
|
||||
# Comments
|
||||
|
||||
**NOTE:** The comments in this section are for specific changes to this repository that cannot be
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
"sourceLocale": "en",
|
||||
"targetLocales": [
|
||||
"en",
|
||||
"pseudo-LOCALE",
|
||||
"fr",
|
||||
"tr",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"ko",
|
||||
"nl",
|
||||
"pl",
|
||||
"zh_TW",
|
||||
"tr",
|
||||
"zh-Hans",
|
||||
"zh-Hant",
|
||||
"de"
|
||||
"zh-CN",
|
||||
"zh_TW",
|
||||
"pseudo-LOCALE"
|
||||
],
|
||||
"tsConfig": "./tsconfig.json",
|
||||
"output": {
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.5.3",
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@goauthentik/api": "^2023.10.5-1703968412",
|
||||
"@goauthentik/api": "^2023.10.5-1704382057",
|
||||
"@lit-labs/context": "^0.4.0",
|
||||
"@lit-labs/task": "^3.1.0",
|
||||
"@lit/localize": "^0.11.4",
|
||||
"@open-wc/lit-helpers": "^0.6.0",
|
||||
"@patternfly/elements": "^2.4.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^7.91.0",
|
||||
"@sentry/tracing": "^7.91.0",
|
||||
"@sentry/browser": "^7.92.0",
|
||||
"@sentry/tracing": "^7.92.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.1",
|
||||
|
@ -50,7 +50,7 @@
|
|||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
||||
"@babel/plugin-transform-runtime": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
|
@ -74,8 +74,8 @@
|
|||
"@types/codemirror": "5.60.15",
|
||||
"@types/grecaptcha": "^3.0.7",
|
||||
"@types/guacamole-common-js": "1.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
||||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"cross-env": "^7.0.3",
|
||||
|
@ -85,21 +85,21 @@
|
|||
"eslint-plugin-lit": "^1.11.0",
|
||||
"eslint-plugin-sonarjs": "^0.23.0",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"lit-analyzer": "^2.0.2",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.1.1",
|
||||
"pseudolocale": "^2.0.0",
|
||||
"pyright": "=1.1.338",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"rollup": "^4.9.2",
|
||||
"rollup": "^4.9.4",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-cssimport": "^1.0.3",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^7.6.7",
|
||||
"storybook-addon-mock": "^4.3.0",
|
||||
"ts-lit-plugin": "^2.0.1",
|
||||
"ts-lit-plugin": "^2.0.2",
|
||||
"tslib": "^2.6.2",
|
||||
"turnstile-types": "^1.2.0",
|
||||
"typescript": "^5.3.3",
|
||||
|
@ -1119,16 +1119,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-classes": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz",
|
||||
"integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==",
|
||||
"version": "7.23.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz",
|
||||
"integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.22.5",
|
||||
"@babel/helper-compilation-targets": "^7.22.15",
|
||||
"@babel/helper-compilation-targets": "^7.23.6",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-optimise-call-expression": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-replace-supers": "^7.22.20",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
|
@ -1833,9 +1832,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/preset-env": {
|
||||
"version": "7.23.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.7.tgz",
|
||||
"integrity": "sha512-SY27X/GtTz/L4UryMNJ6p4fH4nsgWbz84y9FE0bQeWJP6O5BhgVCt53CotQKHCOeXJel8VyhlhujhlltKms/CA==",
|
||||
"version": "7.23.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.8.tgz",
|
||||
"integrity": "sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.23.5",
|
||||
|
@ -1871,7 +1870,7 @@
|
|||
"@babel/plugin-transform-block-scoping": "^7.23.4",
|
||||
"@babel/plugin-transform-class-properties": "^7.23.3",
|
||||
"@babel/plugin-transform-class-static-block": "^7.23.4",
|
||||
"@babel/plugin-transform-classes": "^7.23.5",
|
||||
"@babel/plugin-transform-classes": "^7.23.8",
|
||||
"@babel/plugin-transform-computed-properties": "^7.23.3",
|
||||
"@babel/plugin-transform-destructuring": "^7.23.3",
|
||||
"@babel/plugin-transform-dotall-regex": "^7.23.3",
|
||||
|
@ -2913,9 +2912,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2023.10.5-1703968412",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.5-1703968412.tgz",
|
||||
"integrity": "sha512-/2QDgGkWGXOYDqH49/2hNs+U8TqdE94hkMrJc8A6L+NAy8x/zKAY39eUHs85jmwt013N5duD/jKiJsRftHsDig=="
|
||||
"version": "2023.10.5-1704382057",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.5-1704382057.tgz",
|
||||
"integrity": "sha512-nzmAQgTrFXiOwKDeHhq1gAfIMRilAcDPmNvjhqoQc3GQfWs5GG2lAGzIewWyxsfxNABsg+I0BYTPxN7ffD6tXw=="
|
||||
},
|
||||
"node_modules/@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
|
@ -4582,9 +4581,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz",
|
||||
"integrity": "sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz",
|
||||
"integrity": "sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
@ -4595,9 +4594,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.2.tgz",
|
||||
"integrity": "sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz",
|
||||
"integrity": "sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -4608,9 +4607,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.2.tgz",
|
||||
"integrity": "sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz",
|
||||
"integrity": "sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -4621,9 +4620,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.2.tgz",
|
||||
"integrity": "sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz",
|
||||
"integrity": "sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -4634,9 +4633,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.2.tgz",
|
||||
"integrity": "sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz",
|
||||
"integrity": "sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
@ -4647,9 +4646,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.2.tgz",
|
||||
"integrity": "sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz",
|
||||
"integrity": "sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -4660,9 +4659,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.2.tgz",
|
||||
"integrity": "sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz",
|
||||
"integrity": "sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -4673,9 +4672,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.2.tgz",
|
||||
"integrity": "sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz",
|
||||
"integrity": "sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
@ -4686,9 +4685,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.2.tgz",
|
||||
"integrity": "sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz",
|
||||
"integrity": "sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -4699,9 +4698,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.2.tgz",
|
||||
"integrity": "sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz",
|
||||
"integrity": "sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -4712,9 +4711,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.2.tgz",
|
||||
"integrity": "sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz",
|
||||
"integrity": "sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -4725,9 +4724,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.2.tgz",
|
||||
"integrity": "sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz",
|
||||
"integrity": "sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
@ -4738,9 +4737,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.2.tgz",
|
||||
"integrity": "sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz",
|
||||
"integrity": "sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -4751,98 +4750,98 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.91.0.tgz",
|
||||
"integrity": "sha512-SJKTSaz68F5YIwF79EttBm915M2LnacgZMYRnRumyTmMKnebGhYQLwWbZdpaDvOa1U18dgRajDX8Qed/8A3tXw==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.92.0.tgz",
|
||||
"integrity": "sha512-/jEALRtVqboxB9kcK2tag8QCO6XANTlGBb9RV3oeGXJe0DDNJXRq6wVZbfgztXJRrfgx4XVDcNt1pRVoGGG++g==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.91.0",
|
||||
"@sentry/types": "7.91.0",
|
||||
"@sentry/utils": "7.91.0"
|
||||
"@sentry/core": "7.92.0",
|
||||
"@sentry/types": "7.92.0",
|
||||
"@sentry/utils": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/tracing": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.91.0.tgz",
|
||||
"integrity": "sha512-JH5y6gs6BS0its7WF2DhySu7nkhPDfZcdpAXldxzIlJpqFkuwQKLU5nkYJpiIyZz1NHYYtW5aum2bV2oCOdDRA==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.92.0.tgz",
|
||||
"integrity": "sha512-ur55vPcUUUWFUX4eVLNP71ohswK7ZZpleNZw9Y1GfLqyI+0ILQUwjtzqItJrdClvVsdRZJMRmDV40Hp9Lbb9mA==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.91.0",
|
||||
"@sentry/types": "7.91.0",
|
||||
"@sentry/utils": "7.91.0"
|
||||
"@sentry/core": "7.92.0",
|
||||
"@sentry/types": "7.92.0",
|
||||
"@sentry/utils": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.91.0.tgz",
|
||||
"integrity": "sha512-lJv3x/xekzC/biiyAsVCioq2XnKNOZhI6jY3ZzLJZClYV8eKRi7D3KCsHRvMiCdGak1d/6sVp8F4NYY+YiWy1Q==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.92.0.tgz",
|
||||
"integrity": "sha512-loMr02/zQ38u8aQhYLtIBg0i5n3ps2e3GUXrt3CdsJQdkRYfa62gcrE7SzvoEpMVHTk7VOI4fWGht8cWw/1k3A==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/feedback": "7.91.0",
|
||||
"@sentry-internal/tracing": "7.91.0",
|
||||
"@sentry/core": "7.91.0",
|
||||
"@sentry/replay": "7.91.0",
|
||||
"@sentry/types": "7.91.0",
|
||||
"@sentry/utils": "7.91.0"
|
||||
"@sentry-internal/feedback": "7.92.0",
|
||||
"@sentry-internal/tracing": "7.92.0",
|
||||
"@sentry/core": "7.92.0",
|
||||
"@sentry/replay": "7.92.0",
|
||||
"@sentry/types": "7.92.0",
|
||||
"@sentry/utils": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.91.0.tgz",
|
||||
"integrity": "sha512-tu+gYq4JrTdrR+YSh5IVHF0fJi/Pi9y0HZ5H9HnYy+UMcXIotxf6hIEaC6ZKGeLWkGXffz2gKpQLe/g6vy/lPA==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.92.0.tgz",
|
||||
"integrity": "sha512-1Tly7YB2I1byI5xb0Cwrxs56Rhww+6mQ7m9P7rTmdC3/ijOzbEoohtYIUPwcooCEarpbEJe/tAayRx6BrH2UbQ==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.91.0",
|
||||
"@sentry/utils": "7.91.0"
|
||||
"@sentry/types": "7.92.0",
|
||||
"@sentry/utils": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/replay": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.91.0.tgz",
|
||||
"integrity": "sha512-XwbesnLLNtaVXKtDoyBB96GxJuhGi9zy3a662Ba/McmumCnkXrMQYpQPh08U7MgkTyDRgjDwm7PXDhiKpcb03g==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.92.0.tgz",
|
||||
"integrity": "sha512-G1t9Uvc9cR8VpNkElwvHIMGzykjIKikb10n0tfVd3e+rBPMCCjCPWOduwG6jZYxcvCjTpqmJh6NSLXxL/Mt4JA==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/tracing": "7.91.0",
|
||||
"@sentry/core": "7.91.0",
|
||||
"@sentry/types": "7.91.0",
|
||||
"@sentry/utils": "7.91.0"
|
||||
"@sentry-internal/tracing": "7.92.0",
|
||||
"@sentry/core": "7.92.0",
|
||||
"@sentry/types": "7.92.0",
|
||||
"@sentry/utils": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.91.0.tgz",
|
||||
"integrity": "sha512-IlSAMvqfCL/2TwwN4Tmk6bGMgilGruv5oIJ1GMenVZk53bHwjpjzMbd0ms8+S5zJwAgTQXoCbRhaFFrNmptteQ==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.92.0.tgz",
|
||||
"integrity": "sha512-1+TFFPVEdax4dNi68gin6MENiyGe9mOuNXfjulrP5eCzUEByus5HAxeDI/LLQ1hArfn048AzwSwKUsS2fO5sbg==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/tracing": "7.91.0"
|
||||
"@sentry-internal/tracing": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.91.0.tgz",
|
||||
"integrity": "sha512-bcQnb7J3P3equbCUc+sPuHog2Y47yGD2sCkzmnZBjvBT0Z1B4f36fI/5WjyZhTjLSiOdg3F2otwvikbMjmBDew==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.92.0.tgz",
|
||||
"integrity": "sha512-APmSOuZuoRGpbPpPeYIbMSplPjiWNLZRQa73QiXuTflW4Tu/ItDlU8hOa2+A6JKVkJCuD2EN6yUrxDGSMyNXeg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "7.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.91.0.tgz",
|
||||
"integrity": "sha512-fvxjrEbk6T6Otu++Ax9ntlQ0sGRiwSC179w68aC3u26Wr30FAIRKqHTCCdc2jyWk7Gd9uWRT/cq+g8NG/8BfSg==",
|
||||
"version": "7.92.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.92.0.tgz",
|
||||
"integrity": "sha512-3nEfrQ1z28b/2zgFGANPh5yMVtgwXmrasZxTvKbrAj+KWJpjrJHrIR84r9W277J44NMeZ5RhRW2uoDmuBslPnA==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.91.0"
|
||||
"@sentry/types": "7.92.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
@ -7265,9 +7264,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
|
||||
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
|
@ -7576,16 +7575,16 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz",
|
||||
"integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz",
|
||||
"integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/type-utils": "6.17.0",
|
||||
"@typescript-eslint/utils": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/type-utils": "6.18.1",
|
||||
"@typescript-eslint/utils": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
|
@ -7644,15 +7643,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz",
|
||||
"integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz",
|
||||
"integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -7672,13 +7671,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz",
|
||||
"integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz",
|
||||
"integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0"
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
|
@ -7689,13 +7688,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz",
|
||||
"integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz",
|
||||
"integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/utils": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"@typescript-eslint/utils": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
|
@ -7716,9 +7715,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz",
|
||||
"integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz",
|
||||
"integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
|
@ -7729,13 +7728,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz",
|
||||
"integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz",
|
||||
"integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/visitor-keys": "6.17.0",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/visitor-keys": "6.18.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -7814,17 +7813,17 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz",
|
||||
"integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz",
|
||||
"integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.17.0",
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/typescript-estree": "6.17.0",
|
||||
"@typescript-eslint/scope-manager": "6.18.1",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"@typescript-eslint/typescript-estree": "6.18.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -7872,12 +7871,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz",
|
||||
"integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==",
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz",
|
||||
"integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.17.0",
|
||||
"@typescript-eslint/types": "6.18.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -11456,9 +11455,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
@ -13327,9 +13326,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lit-analyzer": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-2.0.2.tgz",
|
||||
"integrity": "sha512-Is3cx8ypCVq5uNl8EKkPdlLuV3HDVntDVUeLNQlzTM2Je3uG5wHcn+06NB+yhCoa4rhwwXCjprU/7g21CSFqOA==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-2.0.3.tgz",
|
||||
"integrity": "sha512-XiAjnwVipNrKav7r3CSEZpWt+mwYxrhPRVC7h8knDmn/HWTzzWJvPe+mwBcL2brn4xhItAMzZhFC8tzzqHKmiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vscode/web-custom-data": "^0.4.2",
|
||||
|
@ -16419,10 +16418,13 @@
|
|||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.2.tgz",
|
||||
"integrity": "sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.4.tgz",
|
||||
"integrity": "sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
@ -16431,19 +16433,19 @@
|
|||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.9.2",
|
||||
"@rollup/rollup-android-arm64": "4.9.2",
|
||||
"@rollup/rollup-darwin-arm64": "4.9.2",
|
||||
"@rollup/rollup-darwin-x64": "4.9.2",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.9.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.9.2",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.9.2",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.9.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.9.2",
|
||||
"@rollup/rollup-linux-x64-musl": "4.9.2",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.9.2",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.9.2",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.9.2",
|
||||
"@rollup/rollup-android-arm-eabi": "4.9.4",
|
||||
"@rollup/rollup-android-arm64": "4.9.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.9.4",
|
||||
"@rollup/rollup-darwin-x64": "4.9.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.9.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.9.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.9.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.9.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.9.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.9.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.9.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.9.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.9.4",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
|
@ -17835,9 +17837,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ts-lit-plugin": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-lit-plugin/-/ts-lit-plugin-2.0.1.tgz",
|
||||
"integrity": "sha512-Y5G03aDiMYHMLzoZ50kdeVkzgVig2mBw6PVY2oI9PcWl3ONTcDyYq6rJ0QzhlACYWP8sT0dmaPMsHMObgNNvvg==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-lit-plugin/-/ts-lit-plugin-2.0.2.tgz",
|
||||
"integrity": "sha512-DPXlVxhjWHxg8AyBLcfSYt2JXgpANV1ssxxwjY98o26gD8MzeiM68HFW9c2VeDd1CjoR3w7B/6/uKxwBQe+ioA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lit-analyzer": "^2.0.1",
|
||||
|
|
|
@ -42,15 +42,15 @@
|
|||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.5.3",
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@goauthentik/api": "^2023.10.5-1703968412",
|
||||
"@goauthentik/api": "^2023.10.5-1704382057",
|
||||
"@lit-labs/context": "^0.4.0",
|
||||
"@lit-labs/task": "^3.1.0",
|
||||
"@lit/localize": "^0.11.4",
|
||||
"@open-wc/lit-helpers": "^0.6.0",
|
||||
"@patternfly/elements": "^2.4.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^7.91.0",
|
||||
"@sentry/tracing": "^7.91.0",
|
||||
"@sentry/browser": "^7.92.0",
|
||||
"@sentry/tracing": "^7.92.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.1",
|
||||
|
@ -75,7 +75,7 @@
|
|||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
||||
"@babel/plugin-transform-runtime": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
|
@ -99,8 +99,8 @@
|
|||
"@types/codemirror": "5.60.15",
|
||||
"@types/grecaptcha": "^3.0.7",
|
||||
"@types/guacamole-common-js": "1.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
||||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"cross-env": "^7.0.3",
|
||||
|
@ -110,21 +110,21 @@
|
|||
"eslint-plugin-lit": "^1.11.0",
|
||||
"eslint-plugin-sonarjs": "^0.23.0",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"lit-analyzer": "^2.0.2",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.1.1",
|
||||
"pseudolocale": "^2.0.0",
|
||||
"pyright": "=1.1.338",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"rollup": "^4.9.2",
|
||||
"rollup": "^4.9.4",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-cssimport": "^1.0.3",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^7.6.7",
|
||||
"storybook-addon-mock": "^4.3.0",
|
||||
"ts-lit-plugin": "^2.0.1",
|
||||
"ts-lit-plugin": "^2.0.2",
|
||||
"tslib": "^2.6.2",
|
||||
"turnstile-types": "^1.2.0",
|
||||
"typescript": "^5.3.3",
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { map } from "lit/directives/map.js";
|
||||
|
||||
import { AdminApi, CapabilitiesEnum, CoreApi, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
|
||||
import { AdminApi, CoreApi, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import type { SessionUser, UserSelf } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-admin-sidebar")
|
||||
export class AkAdminSidebar extends AKElement {
|
||||
export class AkAdminSidebar extends WithCapabilitiesConfig(AKElement) {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
open = true;
|
||||
|
||||
|
@ -27,9 +29,6 @@ export class AkAdminSidebar extends AKElement {
|
|||
@state()
|
||||
impersonation: UserSelf["username"] | null = null;
|
||||
|
||||
@consume({ context: authentikConfigContext })
|
||||
public config!: Config;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then((version) => {
|
||||
|
@ -201,7 +200,7 @@ export class AkAdminSidebar extends AKElement {
|
|||
}
|
||||
|
||||
renderEnterpriseMessage() {
|
||||
return this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
|
||||
return this.can(CapabilitiesEnum.IsEnterprise)
|
||||
? html`
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${msg("Enterprise")}</span>
|
||||
|
|
|
@ -74,10 +74,7 @@ export class AdminOverviewPage extends AKElement {
|
|||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
let name = this.user?.user.username;
|
||||
if (this.user?.user.name) {
|
||||
name = this.user.user.name;
|
||||
}
|
||||
const name = this.user?.user.name ?? this.user?.user.username;
|
||||
return html`<ak-page-header icon="" header="" description=${msg("General system status")}>
|
||||
<span slot="header"> ${msg(str`Welcome, ${name}.`)} </span>
|
||||
</ak-page-header>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import "@goauthentik/admin/applications/ProviderSelectModal";
|
||||
import { iconHelperText } from "@goauthentik/admin/helperText";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-file-input";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
|
@ -22,13 +25,7 @@ import { TemplateResult, html } from "lit";
|
|||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
Application,
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
PolicyEngineMode,
|
||||
Provider,
|
||||
} from "@goauthentik/api";
|
||||
import { Application, CoreApi, PolicyEngineMode, Provider } from "@goauthentik/api";
|
||||
|
||||
import "./components/ak-backchannel-input";
|
||||
import "./components/ak-provider-search-input";
|
||||
|
@ -48,7 +45,7 @@ export const policyOptions = [
|
|||
];
|
||||
|
||||
@customElement("ak-application-form")
|
||||
export class ApplicationForm extends ModelForm<Application, string> {
|
||||
export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Application, string>) {
|
||||
constructor() {
|
||||
super();
|
||||
this.handleConfirmBackchannelProviders = this.handleConfirmBackchannelProviders.bind(this);
|
||||
|
@ -93,8 +90,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
|||
applicationRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["metaIcon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
||||
|
@ -140,21 +136,21 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
|||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-text-input
|
||||
name="name"
|
||||
.value=${this.instance?.name}
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
label=${msg("Name")}
|
||||
required
|
||||
help=${msg("Application's display Name.")}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="slug"
|
||||
.value=${this.instance?.slug}
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
help=${msg("Internal application name used in URLs.")}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
.value=${this.instance?.group}
|
||||
value=${ifDefined(this.instance?.group)}
|
||||
label=${msg("Group")}
|
||||
help=${msg(
|
||||
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
||||
|
@ -163,7 +159,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
|||
<ak-provider-search-input
|
||||
name="provider"
|
||||
label=${msg("Provider")}
|
||||
.value=${this.instance?.provider}
|
||||
value=${ifDefined(this.instance?.provider ?? undefined)}
|
||||
help=${msg("Select a provider that this application should use.")}
|
||||
blankable
|
||||
></ak-provider-search-input>
|
||||
|
@ -209,11 +205,11 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
|||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-file-input
|
||||
label="${msg("Icon")}"
|
||||
name="metaIcon"
|
||||
.value=${this.instance?.metaIcon}
|
||||
value=${ifDefined(this.instance?.metaIcon ?? undefined)}
|
||||
current=${msg("Currently set to:")}
|
||||
></ak-file-input>
|
||||
${this.instance?.metaIcon
|
||||
|
|
|
@ -7,7 +7,7 @@ import "@goauthentik/components/ak-number-input";
|
|||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
|
@ -32,7 +32,7 @@ import {
|
|||
} from "./LDAPOptionsAndHelp";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-by-ldap")
|
||||
export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||
export class ApplicationWizardApplicationDetails extends WithBrandConfig(BaseProviderPanel) {
|
||||
render() {
|
||||
const provider = this.wizard.provider as LDAPProvider | undefined;
|
||||
const errors = this.wizard.errors.provider;
|
||||
|
@ -57,7 +57,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
|||
<ak-branded-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||
.currentFlow=${provider?.authorizationFlow}
|
||||
.brandFlow=${rootInterface()?.brand?.flowAuthentication}
|
||||
.brandFlow=${this.brand.flowAuthentication}
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
|
|
@ -3,7 +3,7 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
|||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
|
||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
|
@ -17,7 +17,7 @@ import { FlowsInstancesListDesignationEnum, RadiusProvider } from "@goauthentik/
|
|||
import BaseProviderPanel from "../BaseProviderPanel";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-by-radius")
|
||||
export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||
export class ApplicationWizardAuthenticationByRadius extends WithBrandConfig(BaseProviderPanel) {
|
||||
render() {
|
||||
const provider = this.wizard.provider as RadiusProvider | undefined;
|
||||
const errors = this.wizard.errors.provider;
|
||||
|
@ -42,7 +42,7 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
|||
<ak-branded-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||
.currentFlow=${provider?.authorizationFlow}
|
||||
.brandFlow=${rootInterface()?.brand?.flowAuthentication}
|
||||
.brandFlow=${this.brand.flowAuthentication}
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
|
||||
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
@ -14,7 +17,6 @@ import { customElement, property } from "lit/decorators.js";
|
|||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
DeniedActionEnum,
|
||||
Flow,
|
||||
FlowDesignationEnum,
|
||||
|
@ -24,7 +26,7 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-flow-form")
|
||||
export class FlowForm extends ModelForm<Flow, string> {
|
||||
export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
async loadInstance(pk: string): Promise<Flow> {
|
||||
const flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
|
||||
slug: pk,
|
||||
|
@ -54,8 +56,8 @@ export class FlowForm extends ModelForm<Flow, string> {
|
|||
flowRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["background"];
|
||||
if (icon || this.clearBackground) {
|
||||
await new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({
|
||||
|
@ -340,7 +342,7 @@ export class FlowForm extends ModelForm<Flow, string> {
|
|||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal
|
||||
label=${msg("Background")}
|
||||
name="background"
|
||||
|
|
|
@ -118,7 +118,7 @@ export class GroupViewPage extends AKElement {
|
|||
<div class="pf-c-description-list__text">
|
||||
<ak-status-label
|
||||
type="warning"
|
||||
?good=${this.group.isSuperuser}
|
||||
?good${this.group.isSuperuser}
|
||||
></ak-status-label>
|
||||
</div>
|
||||
</dd>
|
||||
|
|
|
@ -9,7 +9,11 @@ import { MessageLevel } from "@goauthentik/common/messages";
|
|||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
|
@ -33,7 +37,6 @@ import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
|||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
CoreUsersListTypeEnum,
|
||||
Group,
|
||||
|
@ -107,7 +110,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
|||
}
|
||||
|
||||
@customElement("ak-user-related-list")
|
||||
export class RelatedUserList extends Table<User> {
|
||||
export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Table<User>)) {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
|
@ -188,8 +191,7 @@ export class RelatedUserList extends Table<User> {
|
|||
|
||||
row(item: User): TemplateResult[] {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
item.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.pk}">
|
||||
<div>${item.username}</div>
|
||||
|
@ -293,7 +295,7 @@ export class RelatedUserList extends Table<User> {
|
|||
${msg("Set password")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${rootInterface()?.brand?.flowRecovery
|
||||
${this.brand?.flowRecovery
|
||||
? html`
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
|
|
|
@ -3,6 +3,7 @@ import { docLink } from "@goauthentik/common/global";
|
|||
import { groupBy } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
@ -220,24 +221,27 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
|||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Configuration")} name="config">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(
|
||||
this.instance ? this.instance.config : this.defaultConfig?.config,
|
||||
)}"
|
||||
></ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("See more here:")}
|
||||
<a
|
||||
target="_blank"
|
||||
href="${docLink("/docs/outposts?utm_source=authentik#configuration")}"
|
||||
>${msg("Documentation")}</a
|
||||
>
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
<ak-form-group aria-label="Advanced settings">
|
||||
<span slot="header"> ${msg("Advanced settings")} </span>
|
||||
<ak-form-element-horizontal label=${msg("Configuration")} name="config">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(
|
||||
this.instance ? this.instance.config : this.defaultConfig?.config,
|
||||
)}"
|
||||
></ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("See more here:")}
|
||||
<a
|
||||
target="_blank"
|
||||
href="${docLink("/docs/outposts?utm_source=authentik#configuration")}"
|
||||
>${msg("Documentation")}</a
|
||||
>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,24 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
|
|||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
import { CSSResult, TemplateResult, html, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { PropertymappingsApi, TypeCreate } from "@goauthentik/api";
|
||||
import { EnterpriseApi, LicenseSummary, PropertymappingsApi, TypeCreate } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-property-mapping-wizard-initial")
|
||||
export class InitialPropertyMappingWizardPage extends WizardPage {
|
||||
@property({ attribute: false })
|
||||
mappingTypes: TypeCreate[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
enterprise?: LicenseSummary;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFForm, PFButton, PFRadio];
|
||||
}
|
||||
|
@ -60,11 +63,20 @@ export class InitialPropertyMappingWizardPage extends WizardPage {
|
|||
];
|
||||
this.host.isValid = true;
|
||||
}}
|
||||
?disabled=${type.requiresEnterprise ? !this.enterprise?.hasLicense : false}
|
||||
/>
|
||||
<label class="pf-c-radio__label" for=${`${type.component}-${type.modelName}`}
|
||||
>${type.name}</label
|
||||
>
|
||||
<span class="pf-c-radio__description">${type.description}</span>
|
||||
${type.requiresEnterprise && !this.enterprise?.hasLicense
|
||||
? html`
|
||||
<ak-alert class="pf-c-radio__description" ?inline=${true}>
|
||||
${msg("Provider require enterprise.")}
|
||||
<a href="#/enterprise/licenses">${msg("Learn more")}</a>
|
||||
</ak-alert>
|
||||
`
|
||||
: nothing}
|
||||
</div>`;
|
||||
})}
|
||||
</form>`;
|
||||
|
@ -80,10 +92,16 @@ export class PropertyMappingWizard extends AKElement {
|
|||
@property({ attribute: false })
|
||||
mappingTypes: TypeCreate[] = [];
|
||||
|
||||
firstUpdated(): void {
|
||||
new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypesList().then((types) => {
|
||||
this.mappingTypes = types;
|
||||
});
|
||||
@state()
|
||||
enterprise?: LicenseSummary;
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
this.mappingTypes = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsAllTypesList();
|
||||
this.enterprise = await new EnterpriseApi(
|
||||
DEFAULT_CONFIG,
|
||||
).enterpriseLicenseSummaryRetrieve();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
|
|
@ -4,6 +4,7 @@ import "@goauthentik/admin/providers/proxy/ProxyProviderForm";
|
|||
import "@goauthentik/admin/providers/saml/SAMLProviderForm";
|
||||
import "@goauthentik/admin/providers/saml/SAMLProviderImportForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/elements/Alert";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/forms/ProxyForm";
|
||||
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
||||
|
@ -13,8 +14,8 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
|
|||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
import { CSSResult, TemplateResult, html, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
|
@ -22,13 +23,16 @@ import PFHint from "@patternfly/patternfly/components/Hint/hint.css";
|
|||
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { ProvidersApi, TypeCreate } from "@goauthentik/api";
|
||||
import { EnterpriseApi, LicenseSummary, ProvidersApi, TypeCreate } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-provider-wizard-initial")
|
||||
export class InitialProviderWizardPage extends WizardPage {
|
||||
@property({ attribute: false })
|
||||
providerTypes: TypeCreate[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
enterprise?: LicenseSummary;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFForm, PFHint, PFButton, PFRadio];
|
||||
}
|
||||
|
@ -79,9 +83,18 @@ export class InitialProviderWizardPage extends WizardPage {
|
|||
this.host.steps = ["initial", `type-${type.component}`];
|
||||
this.host.isValid = true;
|
||||
}}
|
||||
?disabled=${type.requiresEnterprise ? !this.enterprise?.hasLicense : false}
|
||||
/>
|
||||
<label class="pf-c-radio__label" for=${type.component}>${type.name}</label>
|
||||
<span class="pf-c-radio__description">${type.description}</span>
|
||||
${type.requiresEnterprise && !this.enterprise?.hasLicense
|
||||
? html`
|
||||
<ak-alert class="pf-c-radio__description" ?inline=${true}>
|
||||
${msg("Provider require enterprise.")}
|
||||
<a href="#/enterprise/licenses">${msg("Learn more")}</a>
|
||||
</ak-alert>
|
||||
`
|
||||
: nothing}
|
||||
</div>`;
|
||||
})}
|
||||
</form>`;
|
||||
|
@ -100,15 +113,19 @@ export class ProviderWizard extends AKElement {
|
|||
@property({ attribute: false })
|
||||
providerTypes: TypeCreate[] = [];
|
||||
|
||||
@state()
|
||||
enterprise?: LicenseSummary;
|
||||
|
||||
@property({ attribute: false })
|
||||
finalHandler: () => Promise<void> = () => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
firstUpdated(): void {
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||
this.providerTypes = types;
|
||||
});
|
||||
async firstUpdated(): Promise<void> {
|
||||
this.providerTypes = await new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList();
|
||||
this.enterprise = await new EnterpriseApi(
|
||||
DEFAULT_CONFIG,
|
||||
).enterpriseLicenseSummaryRetrieve();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
@ -121,7 +138,11 @@ export class ProviderWizard extends AKElement {
|
|||
return this.finalHandler();
|
||||
}}
|
||||
>
|
||||
<ak-provider-wizard-initial slot="initial" .providerTypes=${this.providerTypes}>
|
||||
<ak-provider-wizard-initial
|
||||
slot="initial"
|
||||
.providerTypes=${this.providerTypes}
|
||||
.enterprise=${this.enterprise}
|
||||
>
|
||||
</ak-provider-wizard-initial>
|
||||
${this.providerTypes.map((type) => {
|
||||
return html`
|
||||
|
|
|
@ -3,7 +3,7 @@ import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
|
|||
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
|
@ -25,7 +25,7 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-provider-ldap-form")
|
||||
export class LDAPProviderFormPage extends BaseProviderForm<LDAPProvider> {
|
||||
export class LDAPProviderFormPage extends WithBrandConfig(BaseProviderForm<LDAPProvider>) {
|
||||
async loadInstance(pk: number): Promise<LDAPProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
|
||||
id: pk,
|
||||
|
@ -68,7 +68,7 @@ export class LDAPProviderFormPage extends BaseProviderForm<LDAPProvider> {
|
|||
<ak-branded-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||
.currentFlow=${this.instance?.authorizationFlow}
|
||||
.brandFlow=${rootInterface()?.brand?.flowAuthentication}
|
||||
.brandFlow=${this.brand?.flowAuthentication}
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Flow used for users to authenticate.")}</p>
|
||||
|
|
|
@ -290,9 +290,13 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
|
|||
let selected = false;
|
||||
if (!provider?.propertyMappings) {
|
||||
selected =
|
||||
scope.managed?.startsWith(
|
||||
// By default select all managed scope mappings, except offline_access
|
||||
(scope.managed?.startsWith(
|
||||
"goauthentik.io/providers/oauth2/scope-",
|
||||
) || false;
|
||||
) &&
|
||||
scope.managed !==
|
||||
"goauthentik.io/providers/oauth2/scope-offline_access") ||
|
||||
false;
|
||||
} else {
|
||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
||||
return su == scope.pk;
|
||||
|
|
|
@ -106,6 +106,23 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
|||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Hostname/IP to connect to.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Maximum concurrent connections")}
|
||||
name="maximumConnections"
|
||||
?required=${true}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.maximumConnections, 1)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Property mappings")}
|
||||
?required=${true}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
@ -14,7 +14,7 @@ import { customElement } from "lit/decorators.js";
|
|||
import { FlowsInstancesListDesignationEnum, ProvidersApi, RadiusProvider } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-provider-radius-form")
|
||||
export class RadiusProviderFormPage extends BaseProviderForm<RadiusProvider> {
|
||||
export class RadiusProviderFormPage extends WithBrandConfig(BaseProviderForm<RadiusProvider>) {
|
||||
loadInstance(pk: number): Promise<RadiusProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersRadiusRetrieve({
|
||||
id: pk,
|
||||
|
@ -57,7 +57,7 @@ export class RadiusProviderFormPage extends BaseProviderForm<RadiusProvider> {
|
|||
<ak-branded-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||
.currentFlow=${this.instance?.authorizationFlow}
|
||||
.brandFlow=${rootInterface()?.brand?.flowAuthentication}
|
||||
.brandFlow=${this.brand?.flowAuthentication}
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Flow used for users to authenticate.")}</p>
|
||||
|
|
|
@ -4,9 +4,12 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
|||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
@ -17,7 +20,6 @@ import { customElement, property, state } from "lit/decorators.js";
|
|||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
OAuthSource,
|
||||
OAuthSourceRequest,
|
||||
|
@ -28,7 +30,7 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-oauth-form")
|
||||
export class OAuthSourceForm extends BaseSourceForm<OAuthSource> {
|
||||
export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuthSource>) {
|
||||
async loadInstance(pk: string): Promise<OAuthSource> {
|
||||
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
|
||||
slug: pk,
|
||||
|
@ -318,7 +320,7 @@ export class OAuthSourceForm extends BaseSourceForm<OAuthSource> {
|
|||
/>
|
||||
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
|
|
@ -2,10 +2,13 @@ import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
|||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
@ -16,7 +19,6 @@ import { customElement, property, state } from "lit/decorators.js";
|
|||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PlexSource,
|
||||
SourcesApi,
|
||||
|
@ -24,7 +26,7 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-plex-form")
|
||||
export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
||||
export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSource>) {
|
||||
async loadInstance(pk: string): Promise<PlexSource> {
|
||||
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesPlexRetrieve({
|
||||
slug: pk,
|
||||
|
@ -63,8 +65,7 @@ export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
|||
plexSourceRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["icon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
||||
|
@ -255,7 +256,7 @@ export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
|||
/>
|
||||
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
|
|
@ -5,7 +5,10 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
|||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
|
@ -18,7 +21,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
|||
|
||||
import {
|
||||
BindingTypeEnum,
|
||||
CapabilitiesEnum,
|
||||
DigestAlgorithmEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
NameIdPolicyEnum,
|
||||
|
@ -29,7 +31,7 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-saml-form")
|
||||
export class SAMLSourceForm extends BaseSourceForm<SAMLSource> {
|
||||
export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSource>) {
|
||||
@state()
|
||||
clearIcon = false;
|
||||
|
||||
|
@ -149,7 +151,7 @@ export class SAMLSourceForm extends BaseSourceForm<SAMLSource> {
|
|||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
|
|
@ -12,6 +12,11 @@ import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
|||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||
import "@goauthentik/elements/TreeView";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
|
@ -33,14 +38,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
|||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
ResponseError,
|
||||
SessionUser,
|
||||
User,
|
||||
UserPath,
|
||||
} from "@goauthentik/api";
|
||||
import { CoreApi, ResponseError, SessionUser, User, UserPath } from "@goauthentik/api";
|
||||
|
||||
export const requestRecoveryLink = (user: User) =>
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
|
@ -93,7 +91,7 @@ const recoveryButtonStyles = css`
|
|||
`;
|
||||
|
||||
@customElement("ak-user-list")
|
||||
export class UserListPage extends TablePage<User> {
|
||||
export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePage<User>)) {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
|
@ -244,8 +242,7 @@ export class UserListPage extends TablePage<User> {
|
|||
|
||||
row(item: User): TemplateResult[] {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
item.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.pk}">
|
||||
<div>${item.username}</div>
|
||||
|
@ -355,7 +352,7 @@ export class UserListPage extends TablePage<User> {
|
|||
${msg("Set password")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${rootInterface()?.brand?.flowRecovery
|
||||
${this.brand.flowRecovery
|
||||
? html`
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
|
|
|
@ -22,8 +22,9 @@ import {
|
|||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import "@goauthentik/components/events/UserEvents";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { WithCapabilitiesConfig } from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/PageHeader";
|
||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
|
@ -60,7 +61,7 @@ import {
|
|||
import "./UserDevicesTable";
|
||||
|
||||
@customElement("ak-user-view")
|
||||
export class UserViewPage extends AKElement {
|
||||
export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
|
||||
@property({ type: Number })
|
||||
set userId(id: number) {
|
||||
me().then((me) => {
|
||||
|
@ -163,8 +164,7 @@ export class UserViewPage extends AKElement {
|
|||
|
||||
renderActionButtons(user: User) {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
user.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && user.pk !== this.me?.user.pk;
|
||||
|
||||
return html`<div class="ak-button-collection">
|
||||
<ak-forms-modal>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { createContext } from "@lit-labs/context";
|
||||
|
||||
import { type Config } from "@goauthentik/api";
|
||||
import type { Config, CurrentBrand } from "@goauthentik/api";
|
||||
|
||||
export const authentikConfigContext = createContext<Config>(Symbol("authentik-config-context"));
|
||||
|
||||
export const authentikBrandContext = createContext<CurrentBrand>(Symbol("authentik-brand-context"));
|
||||
|
||||
export default authentikConfigContext;
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import { brand, config } from "@goauthentik/common/api/config";
|
||||
import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { UIConfig } from "@goauthentik/common/ui/config";
|
||||
import { adaptCSS } from "@goauthentik/common/utils";
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
|
||||
|
||||
import { ContextProvider } from "@lit-labs/context";
|
||||
import { localized } from "@lit/localize";
|
||||
import { CSSResult, LitElement } from "lit";
|
||||
import { state } from "lit/decorators.js";
|
||||
import { LitElement } from "lit";
|
||||
|
||||
import AKGlobal from "@goauthentik/common/styles/authentik.css";
|
||||
import ThemeDark from "@goauthentik/common/styles/theme-dark.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
import { AdoptedStyleSheetsElement } from "./types";
|
||||
|
||||
type AkInterface = HTMLElement & {
|
||||
getTheme: () => Promise<UiThemeEnum>;
|
||||
brand?: CurrentBrand;
|
||||
|
@ -25,13 +23,6 @@ type AkInterface = HTMLElement & {
|
|||
export const rootInterface = <T extends AkInterface>(): T | undefined =>
|
||||
(document.body.querySelector("[data-ak-interface-root]") as T) ?? undefined;
|
||||
|
||||
export function ensureCSSStyleSheet(css: CSSStyleSheet | CSSResult): CSSStyleSheet {
|
||||
if (css instanceof CSSResult) {
|
||||
return css.styleSheet!;
|
||||
}
|
||||
return css;
|
||||
}
|
||||
|
||||
let css: Promise<string[]> | undefined;
|
||||
function fetchCustomCSS(): Promise<string[]> {
|
||||
if (!css) {
|
||||
|
@ -52,10 +43,6 @@ function fetchCustomCSS(): Promise<string[]> {
|
|||
return css;
|
||||
}
|
||||
|
||||
export interface AdoptedStyleSheetsElement {
|
||||
adoptedStyleSheets: readonly CSSStyleSheet[];
|
||||
}
|
||||
|
||||
const QUERY_MEDIA_COLOR_LIGHT = "(prefers-color-scheme: light)";
|
||||
|
||||
@localized()
|
||||
|
@ -175,49 +162,3 @@ export class AKElement extends LitElement {
|
|||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
export class Interface extends AKElement implements AkInterface {
|
||||
@state()
|
||||
brand?: CurrentBrand;
|
||||
|
||||
@state()
|
||||
uiConfig?: UIConfig;
|
||||
|
||||
_configContext = new ContextProvider(this, {
|
||||
context: authentikConfigContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
|
||||
_config?: Config;
|
||||
|
||||
@state()
|
||||
set config(c: Config) {
|
||||
this._config = c;
|
||||
this._configContext.setValue(c);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
get config(): Config | undefined {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)];
|
||||
brand().then((brand) => (this.brand = brand));
|
||||
config().then((config) => (this.config = config));
|
||||
this.dataset.akInterfaceRoot = "true";
|
||||
}
|
||||
|
||||
_activateTheme(root: AdoptedStyleSheetsElement, theme: UiThemeEnum): void {
|
||||
super._activateTheme(root, theme);
|
||||
super._activateTheme(document, theme);
|
||||
}
|
||||
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
if (!this.uiConfig) {
|
||||
this.uiConfig = await uiConfig();
|
||||
}
|
||||
return this.uiConfig.theme?.base || UiThemeEnum.Automatic;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import { brand, config } from "@goauthentik/common/api/config";
|
||||
import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
||||
import {
|
||||
authentikBrandContext,
|
||||
authentikConfigContext,
|
||||
} from "@goauthentik/elements/AuthentikContexts";
|
||||
import type { AdoptedStyleSheetsElement } from "@goauthentik/elements/types";
|
||||
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
|
||||
|
||||
import { ContextProvider } from "@lit-labs/context";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
import { AKElement } from "../Base";
|
||||
|
||||
type AkInterface = HTMLElement & {
|
||||
getTheme: () => Promise<UiThemeEnum>;
|
||||
brand?: CurrentBrand;
|
||||
uiConfig?: UIConfig;
|
||||
config?: Config;
|
||||
};
|
||||
|
||||
export class Interface extends AKElement implements AkInterface {
|
||||
@state()
|
||||
uiConfig?: UIConfig;
|
||||
|
||||
_configContext = new ContextProvider(this, {
|
||||
context: authentikConfigContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
|
||||
_config?: Config;
|
||||
|
||||
@state()
|
||||
set config(c: Config) {
|
||||
this._config = c;
|
||||
this._configContext.setValue(c);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
get config(): Config | undefined {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
_brandContext = new ContextProvider(this, {
|
||||
context: authentikBrandContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
|
||||
_brand?: CurrentBrand;
|
||||
|
||||
@state()
|
||||
set brand(c: CurrentBrand) {
|
||||
this._brand = c;
|
||||
this._brandContext.setValue(c);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
get brand(): CurrentBrand | undefined {
|
||||
return this._brand;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)];
|
||||
brand().then((brand) => (this.brand = brand));
|
||||
config().then((config) => (this.config = config));
|
||||
this.dataset.akInterfaceRoot = "true";
|
||||
}
|
||||
|
||||
_activateTheme(root: AdoptedStyleSheetsElement, theme: UiThemeEnum): void {
|
||||
super._activateTheme(root, theme);
|
||||
super._activateTheme(document, theme);
|
||||
}
|
||||
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
if (!this.uiConfig) {
|
||||
this.uiConfig = await uiConfig();
|
||||
}
|
||||
return this.uiConfig.theme?.base || UiThemeEnum.Automatic;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
import type { Config } from "@goauthentik/api";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Constructor<T = object> = new (...args: any[]) => T;
|
||||
|
||||
export function WithAuthentikConfig<T extends Constructor<LitElement>>(
|
||||
superclass: T,
|
||||
subscribe = true,
|
||||
) {
|
||||
abstract class WithAkConfigProvider extends superclass {
|
||||
@consume({ context: authentikConfigContext, subscribe })
|
||||
public authentikConfig!: Config;
|
||||
}
|
||||
return WithAkConfigProvider;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
import type { CurrentBrand } from "@goauthentik/api";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Constructor<T = object> = abstract new (...args: any[]) => T;
|
||||
|
||||
export function WithBrandConfig<T extends Constructor<LitElement>>(
|
||||
superclass: T,
|
||||
subscribe = true,
|
||||
) {
|
||||
abstract class WithBrandProvider extends superclass {
|
||||
@consume({ context: authentikBrandContext, subscribe })
|
||||
public brand!: CurrentBrand;
|
||||
}
|
||||
return WithBrandProvider;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
import { CapabilitiesEnum } from "@goauthentik/api";
|
||||
import { Config } from "@goauthentik/api";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Constructor<T = object> = abstract new (...args: any[]) => T;
|
||||
|
||||
// Using a unique, lexically scoped, and locally static symbol as the field name for the context
|
||||
// means that it's inaccessible to any child class looking for it. It's one of the strongest privacy
|
||||
// guarantees in JavaScript.
|
||||
|
||||
class WCC {
|
||||
public static readonly capabilitiesConfig: unique symbol = Symbol();
|
||||
}
|
||||
|
||||
/**
|
||||
* withCapabilitiesContext mixes in a single method to any LitElement, `can()`, which takes a
|
||||
* CapabilitiesEnum and returns true or false.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* After importing, simply mixin this function:
|
||||
*
|
||||
* ```
|
||||
* export class AkMyNiftyNewFeature extends withCapabilitiesContext(AKElement) {
|
||||
* ```
|
||||
*
|
||||
* And then if you need to check on a capability:
|
||||
*
|
||||
* ```
|
||||
* if (this.can(CapabilitiesEnum.IsEnterprise) { ... }
|
||||
* ```
|
||||
*
|
||||
* This code re-exports CapabilitiesEnum, so you won't have to import it on a separate line if you
|
||||
* don't need anything else from the API.
|
||||
*
|
||||
* Passing `true` as the second mixin argument will cause the inheriting class to subscribe to the
|
||||
* configuration context. Should the context be explicitly reset, all active web components that are
|
||||
* currently active and subscribed to the context will automatically have a `requestUpdate()`
|
||||
* triggered with the new configuration.
|
||||
*
|
||||
*/
|
||||
|
||||
export function WithCapabilitiesConfig<T extends Constructor<LitElement>>(
|
||||
superclass: T,
|
||||
subscribe = true,
|
||||
) {
|
||||
abstract class CapabilitiesContext extends superclass {
|
||||
@consume({ context: authentikConfigContext, subscribe })
|
||||
private [WCC.capabilitiesConfig]!: Config;
|
||||
|
||||
can(c: CapabilitiesEnum) {
|
||||
if (!this[WCC.capabilitiesConfig]) {
|
||||
throw new Error(
|
||||
"ConfigContext: Attempted to access site configuration before initialization.",
|
||||
);
|
||||
}
|
||||
return this[WCC.capabilitiesConfig].capabilities.includes(c);
|
||||
}
|
||||
}
|
||||
|
||||
return CapabilitiesContext;
|
||||
}
|
||||
|
||||
export { CapabilitiesEnum };
|
|
@ -0,0 +1,4 @@
|
|||
import { Interface } from "./Interface";
|
||||
|
||||
export { Interface };
|
||||
export default Interface;
|
|
@ -8,7 +8,8 @@ import {
|
|||
} from "@goauthentik/common/constants";
|
||||
import { currentInterface } from "@goauthentik/common/sentry";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
@ -23,7 +24,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import { EventsApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-page-header")
|
||||
export class PageHeader extends AKElement {
|
||||
export class PageHeader extends WithBrandConfig(AKElement) {
|
||||
@property()
|
||||
icon?: string;
|
||||
|
||||
|
@ -35,9 +36,8 @@ export class PageHeader extends AKElement {
|
|||
|
||||
@property()
|
||||
set header(value: string) {
|
||||
const brand = rootInterface()?.brand;
|
||||
const currentIf = currentInterface();
|
||||
let title = brand?.brandingTitle || TITLE_DEFAULT;
|
||||
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
||||
if (currentIf === "admin") {
|
||||
title = `${msg("Admin")} - ${title}`;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ const LOCALE_TABLE: LocaleRow[] = [
|
|||
["es", /^es([_-]|$)/i, () => msg("Spanish"), async () => await import("@goauthentik/locales/es")],
|
||||
["de", /^de([_-]|$)/i, () => msg("German"), async () => await import("@goauthentik/locales/de")],
|
||||
["fr", /^fr([_-]|$)/i, () => msg("French"), async () => await import("@goauthentik/locales/fr")],
|
||||
["ko", /^ko([_-]|$)/i, () => msg("Korean"), async () => await import("@goauthentik/locales/ko")],
|
||||
["nl", /^nl([_-]|$)/i, () => msg("Dutch"), async () => await import("@goauthentik/locales/nl")],
|
||||
["pl", /^pl([_-]|$)/i, () => msg("Polish"), async () => await import("@goauthentik/locales/pl")],
|
||||
["tr", /^tr([_-]|$)/i, () => msg("Turkish"), async () => await import("@goauthentik/locales/tr")],
|
||||
["zh-Hant", /^zh[_-](HK|Hant)/i, () => msg("Chinese (traditional)"), async () => await import("@goauthentik/locales/zh-Hant")],
|
||||
|
|
|
@ -21,10 +21,8 @@ export class EnterpriseStatusBanner extends AKElement {
|
|||
return [PFBanner];
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((b) => {
|
||||
this.summary = b;
|
||||
});
|
||||
async firstUpdated(): Promise<void> {
|
||||
this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve();
|
||||
}
|
||||
|
||||
renderBanner(): TemplateResult {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
@ -27,7 +27,7 @@ export const DefaultBrand: CurrentBrand = {
|
|||
};
|
||||
|
||||
@customElement("ak-sidebar-brand")
|
||||
export class SidebarBrand extends AKElement {
|
||||
export class SidebarBrand extends WithBrandConfig(AKElement) {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
|
@ -85,10 +85,7 @@ export class SidebarBrand extends AKElement {
|
|||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img
|
||||
src="${first(
|
||||
rootInterface()?.brand?.brandingLogo,
|
||||
DefaultBrand.brandingLogo,
|
||||
)}"
|
||||
src=${this.brand?.brandingLogo ?? DefaultBrand.brandingLogo}
|
||||
alt="authentik Logo"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export interface AdoptedStyleSheetsElement {
|
||||
adoptedStyleSheets: readonly CSSStyleSheet[];
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import { CSSResult } from "lit";
|
||||
|
||||
export const ensureCSSStyleSheet = (css: CSSStyleSheet | CSSResult): CSSStyleSheet =>
|
||||
css instanceof CSSResult ? css.styleSheet! : css;
|
|
@ -1,5 +1,5 @@
|
|||
import { TITLE_DEFAULT } from "@goauthentik/app/common/constants";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import Guacamole from "guacamole-common-js";
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { globalAK } from "@goauthentik/common/global";
|
|||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/flow/sources/apple/AppleLoginInit";
|
||||
|
|
|
@ -89,6 +89,9 @@ export class AuthenticatorValidateStage
|
|||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
:host([theme="dark"]) .authenticator-button {
|
||||
color: var(--ak-dark-foreground) !important;
|
||||
}
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Divider";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { LOCALES } from "@goauthentik/elements/ak-locale-context/definitions";
|
||||
import "@goauthentik/elements/forms/FormElement";
|
||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
|
@ -20,7 +23,6 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
|||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
PromptChallenge,
|
||||
PromptChallengeResponseRequest,
|
||||
PromptTypeEnum,
|
||||
|
@ -28,7 +30,9 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-stage-prompt")
|
||||
export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeResponseRequest> {
|
||||
export class PromptStage extends WithCapabilitiesConfig(
|
||||
BaseStage<PromptChallenge, PromptChallengeResponseRequest>,
|
||||
) {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
|
@ -193,10 +197,7 @@ ${prompt.initialValue}</textarea
|
|||
</div> `;
|
||||
})}`;
|
||||
case PromptTypeEnum.AkLocale: {
|
||||
const inDebug = rootInterface()?.config?.capabilities.includes(
|
||||
CapabilitiesEnum.CanDebug,
|
||||
);
|
||||
const locales = inDebug
|
||||
const locales = this.can(CapabilitiesEnum.CanDebug)
|
||||
? LOCALES
|
||||
: LOCALES.filter((locale) => locale.code !== "debug");
|
||||
const options = locales.map(
|
||||
|
|
|
@ -15,10 +15,13 @@ export const targetLocales = [
|
|||
`en`,
|
||||
`es`,
|
||||
`fr`,
|
||||
`ko`,
|
||||
`nl`,
|
||||
`pl`,
|
||||
`pseudo-LOCALE`,
|
||||
`tr`,
|
||||
`zh_TW`,
|
||||
`zh-CN`,
|
||||
`zh-Hans`,
|
||||
`zh-Hant`,
|
||||
] as const;
|
||||
|
@ -32,10 +35,13 @@ export const allLocales = [
|
|||
`en`,
|
||||
`es`,
|
||||
`fr`,
|
||||
`ko`,
|
||||
`nl`,
|
||||
`pl`,
|
||||
`pseudo-LOCALE`,
|
||||
`tr`,
|
||||
`zh_TW`,
|
||||
`zh-CN`,
|
||||
`zh-Hans`,
|
||||
`zh-Hant`,
|
||||
] as const;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CSRFHeaderName } from "@goauthentik/common/api/middleware";
|
|||
import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { first, getCookie } from "@goauthentik/common/utils";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import "rapidoc";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Interface } from "@goauthentik/app/elements/Base";
|
||||
import { Interface } from "@goauthentik/app/elements/Interface";
|
||||
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { UserDisplay } from "@goauthentik/common/ui/config";
|
|||
import { me } from "@goauthentik/common/users";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
|
|
|
@ -2,14 +2,15 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { refreshMe } from "@goauthentik/common/users";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
import { StageHost } from "@goauthentik/flow/stages/base";
|
||||
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
|
@ -21,7 +22,6 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import {
|
||||
ChallengeChoices,
|
||||
ChallengeTypes,
|
||||
CurrentBrand,
|
||||
FlowChallengeResponseRequest,
|
||||
FlowErrorChallenge,
|
||||
FlowsApi,
|
||||
|
@ -31,13 +31,19 @@ import {
|
|||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-settings-flow-executor")
|
||||
export class UserSettingsFlowExecutor extends AKElement implements StageHost {
|
||||
export class UserSettingsFlowExecutor
|
||||
extends WithBrandConfig(AKElement, true)
|
||||
implements StageHost
|
||||
{
|
||||
@property()
|
||||
flowSlug?: string;
|
||||
|
||||
<<<<<<< HEAD
|
||||
@state()
|
||||
brand?: CurrentBrand;
|
||||
|
||||
=======
|
||||
>>>>>>> main
|
||||
private _challenge?: ChallengeTypes;
|
||||
|
||||
@property({ attribute: false })
|
||||
|
@ -87,7 +93,6 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
|
|||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.brand = rootInterface()?.brand;
|
||||
this.flowSlug = this.brand?.flowUserSettings;
|
||||
if (!this.flowSlug) {
|
||||
return;
|
||||
|
|
|
@ -6192,6 +6192,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6222,6 +6234,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -6467,6 +6467,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6497,6 +6509,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -6108,6 +6108,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6138,6 +6150,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -8143,6 +8143,18 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
|||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
<target>Détermine combien de temps une session dure avant déconnexion et ré-authorisation.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -8173,6 +8185,12 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
351
web/xliff/ko.xlf
351
web/xliff/ko.xlf
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="ko" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
|
@ -443,11 +443,6 @@
|
|||
<source>Client IP</source>
|
||||
<target>클라이언트 IP</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s45f9e7ce0897f9e5">
|
||||
<source>Tenant</source>
|
||||
<target>테넌트</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s2152f3482784705f">
|
||||
<source>Recent events</source>
|
||||
|
@ -612,8 +607,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>URL "<x id="0" equiv-text="${this.url}"/>" 을 찾을 수 없습니다.</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>URL "<x id="0" equiv-text="${this.url}"/>" 을 찾을 수 없습니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
|
@ -1054,8 +1049,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa8384c9c26731f83">
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>리디렉션 URI를 허용하려면 이 값을 ".*"로 설정합니다. 이로 인해 발생할 수 있는 보안상의 영향에 유의하세요.</target>
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>리디렉션 URI를 허용하려면 이 값을 ".*"로 설정합니다. 이로 인해 발생할 수 있는 보안상의 영향에 유의하세요.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s55787f4dfcdce52b">
|
||||
|
@ -1792,8 +1787,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 "fa-test"를 사용합니다.</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 "fa-test"를 사용합니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
|
@ -2972,7 +2967,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>그룹 구성원이 포함된 필드입니다. 'memberUid' 필드를 사용하는 경우 값에 상대적인 고유 이름이 포함된 것으로 가정합니다 (예:'memberUid=some-user' 대신 'memberUid=cn=some-user,ou=groups,...').</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -3617,16 +3612,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
<source>Update Token</source>
|
||||
<target>토큰 업데이트</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0af6301e76e2a2a5">
|
||||
<source>Successfully updated tenant.</source>
|
||||
<target>테넌트를 성공적으로 업데이트했습니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sf55c7c06dbc2c8c6">
|
||||
<source>Successfully created tenant.</source>
|
||||
<target>테넌트를 성공적으로 만들었습니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s41706a202b6c40f1">
|
||||
<source>Domain</source>
|
||||
|
@ -3642,11 +3627,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
<source>Default</source>
|
||||
<target>기본</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se1c85959463f53df">
|
||||
<source>Use this tenant for each domain that doesn't have a dedicated tenant.</source>
|
||||
<target>전용 테넌트가 없는 각 도메인에 이 테넌트를 사용하세요.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc19838ca8c135c1b">
|
||||
<source>Branding settings</source>
|
||||
|
@ -3764,29 +3744,14 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7b1fba26d245cb1c">
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>아카이브에 외부 로깅 솔루션을 사용하는 경우, 이 값을 "minutes=5"로 설정할 수 있습니다.</target>
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>아카이브에 외부 로깅 솔루션을 사용하는 경우, 이 값을 "minutes=5"로 설정할 수 있습니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s44536d20bb5c8257">
|
||||
<source>This setting only affects new Events, as the expiration is saved per-event.</source>
|
||||
<target>T만료는 이벤트별로 저장되므로 설정은 새 이벤트에만 영향을 줍니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s3bb51cabb02b997e">
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>서식: "weeks=3;days=2;hours=3,seconds=2".</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s04bfd02201db5ab8">
|
||||
<source>Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this tenant.</source>
|
||||
<target>YAML 또는 JSON을 사용하여 사용자 지정 속성을 설정합니다. 이 테넌트가 요청을 처리하는 경우 여기에서 설정한 모든 속성은 사용자가 상속받게 됩니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f9e79189a3d19e2">
|
||||
<source>Tenants</source>
|
||||
<target>테넌트</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s164be9a7537b99f6">
|
||||
<source>Configure visual settings and defaults for different domains.</source>
|
||||
|
@ -3797,21 +3762,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
<source>Default?</source>
|
||||
<target>기본값?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s69a56a3022c4be7f">
|
||||
<source>Tenant(s)</source>
|
||||
<target>테넌트</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1b606acd76ba2c4c">
|
||||
<source>Update Tenant</source>
|
||||
<target>테넌트 업데이트</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s773aa6621d7e37b7">
|
||||
<source>Create Tenant</source>
|
||||
<target>테넌트 생성</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s8cb7bb82e96d5d77">
|
||||
<source>Policies</source>
|
||||
|
@ -3967,8 +3917,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>정말 <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>" 을(를) 업데이트 하시겠습니까?</target>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>정말 <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>" 을(를) 업데이트 하시겠습니까?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
|
@ -4081,11 +4031,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
<source>Recovery link cannot be emailed, user has no email address saved.</source>
|
||||
<target>복구 링크를 이메일로 보낼 수 없습니다. 사용자가 저장한 이메일 주소가 없습니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s63d89a6ae0969c30">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active tenant.</source>
|
||||
<target>사용자가 직접 비밀번호를 재설정할 수 있도록 하려면 현재 활성 상태인 테넌트에서 복구 플로우를 구성하세요.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s720594461542943f">
|
||||
<source>Add User</source>
|
||||
|
@ -5052,8 +4997,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>YubiKey 같은 "로밍" 인증기</target>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>YubiKey 같은 "로밍" 인증기</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sfffba7b23d8fb40c">
|
||||
|
@ -5387,8 +5332,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s3b7b519444181264">
|
||||
|
@ -5436,7 +5381,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>기간을 0 이상으로 설정하면, 사용자에게 '로그인 상태 유지'를 선택할 수 있는 옵션이 제공되며, 이 경우 세션이 여기에 지정된 시간만큼 연장됩니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -7813,10 +7758,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<source>A copy of this recovery link has been placed in your clipboard</source>
|
||||
<target>이 복구 링크의 사본이 클립보드에 저장되었습니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5b8ee296ed258568">
|
||||
<source>The current tenant must have a recovery flow configured to use a recovery link</source>
|
||||
<target>현재 테넌트에 복구 링크를 사용하도록 구성된 복구 플로우가 있어야 합니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s895514dda9cb9c94">
|
||||
<source>Create recovery link</source>
|
||||
<target>복구 링크 생성</target>
|
||||
|
@ -7926,8 +7867,8 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<target>사용자 생성과 <x id="0" equiv-text="${this.group.name}"/> 그룹 추가에 성공했습니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s824e0943a7104668">
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>이 사용자는 "<x id="0" equiv-text="${this.targetGroup.name}"/>" 그룹에 추가됩니다.</target>
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>이 사용자는 "<x id="0" equiv-text="${this.targetGroup.name}"/>" 그룹에 추가됩니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||
<source>Pretend user exists</source>
|
||||
|
@ -8004,6 +7945,260 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s047a5f0211fedc72">
|
||||
<source>Require Outpost (flow can only be executed from an outpost).</source>
|
||||
<target>Outpost필요 (플로우는 Outpost에서만 실행할 수 있음).</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa2ea0fcd3ffa80e0">
|
||||
<source>Connection expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3271da6c18c25b18">
|
||||
<source>Connection settings.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f4ca2148183d692">
|
||||
<source>Successfully updated endpoint.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5adee855dbe191d9">
|
||||
<source>Successfully created endpoint.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61e136c0658e27d5">
|
||||
<source>Protocol</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa062b019ff0c8809">
|
||||
<source>RDP</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97f9bf19fa5b57d1">
|
||||
<source>SSH</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7c100119e9ffcc32">
|
||||
<source>VNC</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b05f9d8801fc14f">
|
||||
<source>Host</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb474f652a2c2fc76">
|
||||
<source>Hostname/IP to connect to.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8276649077e8715c">
|
||||
<source>Endpoint(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf1dabfe0fe8a75ad">
|
||||
<source>Update Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s008496c7716b9812">
|
||||
<source>These bindings control which users will have access to this endpoint. Users must also have access to the application.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8465a2caa2d9ea5d">
|
||||
<source>Endpoints</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9857d883d8eb98fc">
|
||||
<source>General settings</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2066881798a1b96">
|
||||
<source>RDP settings</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb864dc36a463a155">
|
||||
<source>Ignore server certificate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s20366a8d1eaaca54">
|
||||
<source>Enable wallpaper</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1e44c5350ef7598c">
|
||||
<source>Enable font-smoothing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s04ff5d6ae711e6d6">
|
||||
<source>Enable full window dragging</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sab21e1f62676b56c">
|
||||
<source>Successfully updated brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa43e43fd3a23e22d">
|
||||
<source>Successfully created brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41b3f9b4c98aabd9">
|
||||
<source>Use this brand for each domain that doesn't have a dedicated brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s17260b71484b307f">
|
||||
<source>Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s79fc990a2b58f27f">
|
||||
<source>Brands</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02774bc46a167346">
|
||||
<source>Brand(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s801bf3d03f4a3ff1">
|
||||
<source>Update Brand</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5c3efec5330e0000">
|
||||
<source>Create Brand</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s663ccbfdf27e8dd0">
|
||||
<source>Network binding</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb108a06693c67753">
|
||||
<source>No binding</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5aab90c74f1233b8">
|
||||
<source>Bind ASN</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s488303b048afe83b">
|
||||
<source>Bind ASN and Network</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3268dcfe0c8234dc">
|
||||
<source>Bind ASN, Network and IP</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s226381aca231644f">
|
||||
<source>Configure if sessions created by this stage should be bound to the Networks they were created in.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2555a1f20f3fd93e">
|
||||
<source>GeoIP binding</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d63c78f93c9a92e">
|
||||
<source>Bind Continent</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s395d5863b3a259b5">
|
||||
<source>Bind Continent and Country</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s625ea0c32b4b136c">
|
||||
<source>Bind Continent, Country and City</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc7a1a88961be90">
|
||||
<source>Configure if sessions created by this stage should be bound to their GeoIP-based location</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa06cd519ff151b6d">
|
||||
<source>RAC</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s634e2fd82c397576">
|
||||
<source>Successfully updated settings.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb8e4edaea6f1d935">
|
||||
<source>Avatars</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s945856050217c828">
|
||||
<source>Configure how authentik should show avatars for users. The following values can be set:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf4ef4c8ce713f775">
|
||||
<source>Disables per-user avatars and just shows a 1x1 pixel transparent picture</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5446842a7e4a963b">
|
||||
<source>Uses gravatar with the user's email address</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35363b9e1cc2abd3">
|
||||
<source>Generated avatars based on the user's name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s48110ca292cad513">
|
||||
<source>Any URL: If you want to use images hosted on another server, you can set any URL. Additionally, these placeholders can be used:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbe1dfda044bdc93b">
|
||||
<source>The user's username</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s653f257c9c2d4dc5">
|
||||
<source>The email address, md5 hashed</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9c9183cd80916b4f">
|
||||
<source>The user's UPN, if set (otherwise an empty string)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="h4963ed14d7e239a9">
|
||||
<source>An attribute path like
|
||||
<x id="0" equiv-text="<code>"/>attributes.something.avatar<x id="1" equiv-text="</code>"/>, which can be used in
|
||||
combination with the file field to allow users to upload custom
|
||||
avatars for themselves.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4c80c34a67a6f1c9">
|
||||
<source>Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="h2fafcc3ebafea2f8">
|
||||
<source>For example, setting this to <x id="0" equiv-text="<code>"/>gravatar,initials<x id="1" equiv-text="</code>"/> will
|
||||
attempt to get an avatar from Gravatar, and if the user has not
|
||||
configured on there, it will fallback to a generated avatar.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea247dfb18f696d2">
|
||||
<source>Default user change name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s078ffec0257621c0">
|
||||
<source>Enable the ability for users to change their name.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s26ad11b21253152b">
|
||||
<source>Default user change email</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5fc6c14d106f40d3">
|
||||
<source>Enable the ability for users to change their email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s23564aa839755965">
|
||||
<source>Default user change username</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d816a95ca43a99d">
|
||||
<source>Enable the ability for users to change their username.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s57b52b60ed5e2bc7">
|
||||
<source>Footer links</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7349802b2f7f99c2">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s166b59f3cc5d8ec3">
|
||||
<source>GDPR compliance</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb8b23770f899e5bb">
|
||||
<source>When enabled, all the events caused by a user will be deleted upon the user's deletion.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29501761df0fe837">
|
||||
<source>Impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f503553d8432487">
|
||||
<source>Globally enable/disable impersonation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="see1eb81c1f734079">
|
||||
<source>System settings</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28b99b59541f54ca">
|
||||
<source>Connection failed after <x id="0" equiv-text="${this.connectionAttempt}"/> attempts.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7c7d956418e1c8c8">
|
||||
<source>Re-connecting in <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> second(s).</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfc003381f593d943">
|
||||
<source>Connecting...</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31aa94a0b3c7edb2">
|
||||
<source>Select endpoint to connect to</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
1061
web/xliff/nl.xlf
1061
web/xliff/nl.xlf
File diff suppressed because it is too large
Load Diff
|
@ -6315,6 +6315,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6345,6 +6357,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -8040,6 +8040,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -8070,6 +8082,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -6101,6 +6101,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6131,6 +6143,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8145,6 +8145,22 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
<target>设置会话在被断开连接并需要重新授权之前持续的时间。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
<target>提供程序需要企业版。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
<target>了解更多</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
<target>最大并发连接数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
<target>允许到此端点的最大并发连接数。可以设置为 -1 以禁用限制。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -8175,6 +8191,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -6149,6 +6149,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -6179,6 +6191,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -8203,6 +8203,22 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
<target>设置会话在被断开连接并需要重新授权之前持续的时间。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
<target>提供程序需要企业版。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
<target>了解更多</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
<target>最大并发连接数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
<target>允许到此端点的最大并发连接数。可以设置为 -1 以禁用限制。</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -8025,6 +8025,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="s6dd297c217729828">
|
||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc7f34824150bfb8">
|
||||
<source>Provider require enterprise.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||
<source>Learn more</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc39f6abf0daedb0f">
|
||||
<source>Maximum concurrent connections</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62418cbcd2a25498">
|
||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s744401846fea6e76">
|
||||
<source>Brand</source>
|
||||
</trans-unit>
|
||||
|
@ -8055,6 +8067,12 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="sa9d13ce9e83aac17">
|
||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94d61907ee22a8c1">
|
||||
<source>Korean</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95d56e58f816d211">
|
||||
<source>Dutch</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6709b81e1ed4e39f">
|
||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -65,4 +65,6 @@ This designates a flow for general setup. This designation doesn't have any cons
|
|||
|
||||
Flows can be imported and exported to share with other people, the community and for troubleshooting. Flows can be imported to apply new functionality and apply existing workflows.
|
||||
|
||||
Download our [Example flows](./examples/flows.md) and then import them into your authentik instance.
|
||||
|
||||
Starting with authentik 2022.8, flows will be exported as YAML, but JSON-based flows can still be imported.
|
||||
|
|
|
@ -35,12 +35,20 @@ To access the user's email address, a scope of `user:email` is required. To acce
|
|||
|
||||
### `authorization_code`:
|
||||
|
||||
This grant is used to convert an authorization code to a refresh token. The authorization code is retrieved through the Authorization flow, and can only be used once, and expires quickly.
|
||||
This grant is used to convert an authorization code to an access token (and optionally refresh token). The authorization code is retrieved through the Authorization flow, and can only be used once, and expires quickly.
|
||||
|
||||
:::info
|
||||
Starting with authentik 2024.1, applications only receive an access token. To receive a refresh token, applications must be allowed to request the `offline_access` scope in authentik and also be configured to request the scope.
|
||||
:::
|
||||
|
||||
### `refresh_token`:
|
||||
|
||||
Refresh tokens can be used as long-lived tokens to access user data, and further renew the refresh token down the road.
|
||||
|
||||
:::info
|
||||
Starting with authentik 2024.1, this grant requires the `offline_access` scope.
|
||||
:::
|
||||
|
||||
### `client_credentials`:
|
||||
|
||||
See [Machine-to-machine authentication](./client_credentials)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue